#define HEIGHT 720
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
+#include <string.h>
#include <math.h>
#include <time.h>
+#include <assert.h>
#include <string>
#include <vector>
+#include <map>
#include <SDL/SDL.h>
#include <SDL/SDL_opengl.h>
#include <GL/gl.h>
#include <GL/glext.h>
+#include "effect.h"
#include "util.h"
#include "widgets.h"
#include "texture_enum.h"
float gamma_r = 1.0f, gamma_g = 1.0f, gamma_b = 1.0f;
float gain_r = 1.0f, gain_g = 1.0f, gain_b = 1.0f;
-struct ImageFormat {
- enum { FORMAT_RGB, FORMAT_RGBA } format;
+enum PixelFormat { FORMAT_RGB, FORMAT_RGBA };
+
+enum ColorSpace {
+ COLORSPACE_sRGB = 0,
+ COLORSPACE_REC_709 = 0, // Same as sRGB.
+ COLORSPACE_REC_601_525 = 1,
+ COLORSPACE_REC_601_625 = 2,
+};
- // Note: sRGB and 709 use the same colorspace primaries.
- enum { COLORSPACE_sRGB, COLORSPACE_REC_601_525, COLORSPACE_REC_601_625, COLORSPACE_REC_709 } color_space;
+enum GammaCurve {
+ GAMMA_LINEAR = 0,
+ GAMMA_sRGB = 1,
+ GAMMA_REC_601 = 2,
+ GAMMA_REC_709 = 2, // Same as Rec. 601.
+};
- // Note: Rec. 601 and 709 use the same gamma curve.
- enum { LINEAR_LIGHT, GAMMA_sRGB, GAMMA_REC_601, GAMMA_REC_709 } gamma_curve;
+struct ImageFormat {
+ PixelFormat pixel_format;
+ ColorSpace color_space;
+ GammaCurve gamma_curve;
};
enum EffectId {
LIFT_GAMMA_GAIN,
};
-class Effect {
-public:
- virtual bool needs_linear_light() { return true; }
- virtual bool needs_srgb_primaries() { return true; }
- virtual bool needs_many_samples() { return false; }
- virtual bool needs_mipmaps() { return false; }
- bool set_float(const std::string& key, float value);
- bool set_float_array(const std::string&, const float *values, size_t num_values);
+// Can alias on a float[3].
+struct RGBTriplet {
+ RGBTriplet(float r, float g, float b)
+ : r(r), g(g), b(b) {}
+
+ float r, g, b;
+};
+
+class GammaExpansionEffect : public Effect {
+public:
+ GammaExpansionEffect()
+ : source_curve(GAMMA_LINEAR)
+ {
+ register_int("source_curve", (int *)&source_curve);
+ }
+
+private:
+ GammaCurve source_curve;
+};
+
+class ColorSpaceConversionEffect : public Effect {
+public:
+ ColorSpaceConversionEffect()
+ : source_space(COLORSPACE_sRGB),
+ destination_space(COLORSPACE_sRGB)
+ {
+ register_int("source_space", (int *)&source_space);
+ register_int("destination_space", (int *)&destination_space);
+ }
+
+private:
+ ColorSpace source_space, destination_space;
+};
+
+class ColorSpaceConversionEffect;
+
+class LiftGammaGainEffect : public Effect {
+public:
+ LiftGammaGainEffect()
+ : lift(0.0f, 0.0f, 0.0f),
+ gamma(1.0f, 1.0f, 1.0f),
+ gain(1.0f, 1.0f, 1.0f),
+ saturation(1.0f)
+ {
+ register_vec3("lift", (float *)&lift);
+ register_vec3("gamma", (float *)&gamma);
+ register_vec3("gain", (float *)&gain);
+ register_float("saturation", &saturation);
+ }
private:
- bool register_float(const std::string& key, float value);
- bool register_float_array(const std::string& key, float *values, size_t num_values);
-};
+ RGBTriplet lift, gamma, gain;
+ float saturation;
+};
class EffectChain {
public:
- void set_size(unsigned width, unsigned height);
+ EffectChain(unsigned width, unsigned height);
void add_input(const ImageFormat &format);
+
+ // The pointer is owned by EffectChain.
Effect *add_effect(EffectId effect);
+
void add_output(const ImageFormat &format);
void render(unsigned char *src, unsigned char *dst);
unsigned width, height;
ImageFormat input_format, output_format;
std::vector<Effect *> effects;
+
+ ColorSpace current_color_space;
+ GammaCurve current_gamma_curve;
};
+EffectChain::EffectChain(unsigned width, unsigned height)
+ : width(width), height(height) {}
+
+void EffectChain::add_input(const ImageFormat &format)
+{
+ input_format = format;
+ current_color_space = format.color_space;
+ current_gamma_curve = format.gamma_curve;
+}
+
+void EffectChain::add_output(const ImageFormat &format)
+{
+ output_format = format;
+}
+
+Effect *instantiate_effect(EffectId effect)
+{
+ switch (effect) {
+ case GAMMA_CONVERSION:
+ return new GammaExpansionEffect();
+ case RGB_PRIMARIES_CONVERSION:
+ return new GammaExpansionEffect();
+ case LIFT_GAMMA_GAIN:
+ return new LiftGammaGainEffect();
+ }
+ assert(false);
+}
+
+Effect *EffectChain::add_effect(EffectId effect_id)
+{
+ Effect *effect = instantiate_effect(effect_id);
+
+ if (effect->needs_linear_light() && current_gamma_curve != GAMMA_LINEAR) {
+ GammaExpansionEffect *gamma_conversion = new GammaExpansionEffect();
+ gamma_conversion->set_int("source_curve", current_gamma_curve);
+ effects.push_back(gamma_conversion);
+ current_gamma_curve = GAMMA_LINEAR;
+ }
+
+ if (effect->needs_srgb_primaries() && current_color_space != COLORSPACE_sRGB) {
+ assert(current_gamma_curve == GAMMA_LINEAR);
+ ColorSpaceConversionEffect *colorspace_conversion = new ColorSpaceConversionEffect();
+ colorspace_conversion->set_int("source_space", current_color_space);
+ colorspace_conversion->set_int("destination_space", COLORSPACE_sRGB);
+ effects.push_back(colorspace_conversion);
+ current_color_space = COLORSPACE_sRGB;
+ }
+
+ effects.push_back(effect);
+ return effect;
+}
+
GLhandleARB read_shader(const char* filename, GLenum type)
{
static char buf[131072];