#include <GL/gl.h>
#include <GL/glext.h>
+#include "effect.h"
+#include "effect_chain.h"
#include "util.h"
#include "widgets.h"
#include "texture_enum.h"
float gain_theta = 0.0f, gain_rad = 0.0f, gain_v = 0.25f;
float saturation = 1.0f;
-float lift_r = 0.0f, lift_g = 0.0f, lift_b = 0.0f;
-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;
-
-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,
-};
-
-enum GammaCurve {
- GAMMA_LINEAR = 0,
- GAMMA_sRGB = 1,
- GAMMA_REC_601 = 2,
- GAMMA_REC_709 = 2, // Same as Rec. 601.
-};
-
-struct ImageFormat {
- PixelFormat pixel_format;
- ColorSpace color_space;
- GammaCurve gamma_curve;
-};
-
-enum EffectId {
- // Mostly for internal use.
- GAMMA_CONVERSION = 0,
- RGB_PRIMARIES_CONVERSION,
-
- // Color.
- 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; }
-
- // Neither of these take ownership.
- bool set_int(const std::string&, int value);
- bool set_float(const std::string &key, float value);
- bool set_vec3(const std::string &key, const float *values);
-
-protected:
- // Neither of these take ownership.
- void register_int(const std::string &key, int *value);
- void register_float(const std::string &key, float *value);
- void register_vec3(const std::string &key, float *values);
-
-private:
- std::map<std::string, int *> params_int;
- std::map<std::string, float *> params_float;
- std::map<std::string, float *> params_vec3;
-};
-
-bool Effect::set_int(const std::string &key, int value)
-{
- if (params_int.count(key) == 0) {
- return false;
- }
- *params_int[key] = value;
- return true;
-}
-
-bool Effect::set_float(const std::string &key, float value)
-{
- if (params_float.count(key) == 0) {
- return false;
- }
- *params_float[key] = value;
- return true;
-}
-
-bool Effect::set_vec3(const std::string &key, const float *values)
-{
- if (params_vec3.count(key) == 0) {
- return false;
- }
- memcpy(params_vec3[key], values, sizeof(float) * 3);
- return true;
-}
-
-void Effect::register_int(const std::string &key, int *value)
-{
- assert(params_int.count(key) == 0);
- params_int[key] = value;
-}
-
-void Effect::register_float(const std::string &key, float *value)
-{
- assert(params_float.count(key) == 0);
- params_float[key] = value;
-}
-
-void Effect::register_vec3(const std::string &key, float *values)
-{
- assert(params_vec3.count(key) == 0);
- params_vec3[key] = 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:
- RGBTriplet lift, gamma, gain;
- float saturation;
-};
-
-class EffectChain {
-public:
- 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);
-
-private:
- 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];
- FILE *fp = fopen(filename, "r");
- if (fp == NULL) {
- perror(filename);
- exit(1);
- }
-
- int len = fread(buf, 1, sizeof(buf), fp);
- fclose(fp);
-
- GLhandleARB obj = glCreateShaderObjectARB(type);
- const GLchar* source[] = { buf };
- const GLint length[] = { len };
- glShaderSource(obj, 1, source, length);
- glCompileShader(obj);
-
- GLchar info_log[4096];
- GLsizei log_length = sizeof(info_log) - 1;
- glGetShaderInfoLog(obj, log_length, &log_length, info_log);
- info_log[log_length] = 0;
- printf("shader compile log: %s\n", info_log);
-
- GLint status;
- glGetShaderiv(obj, GL_COMPILE_STATUS, &status);
- if (status == GL_FALSE) {
- exit(1);
- }
-
- return obj;
-}
-
-void draw_picture_quad(GLint prog, int frame)
+void update_hsv(Effect *lift_gamma_gain_effect, Effect *saturation_effect)
{
- glUseProgramObjectARB(prog);
- check_error();
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, SOURCE_IMAGE);
- glUniform1i(glGetUniformLocation(prog, "tex"), 0);
-
- glActiveTexture(GL_TEXTURE1);
- glBindTexture(GL_TEXTURE_1D, SRGB_LUT);
- glUniform1i(glGetUniformLocation(prog, "srgb_tex"), 1);
-
- glActiveTexture(GL_TEXTURE2);
- glBindTexture(GL_TEXTURE_1D, SRGB_REVERSE_LUT);
- glUniform1i(glGetUniformLocation(prog, "srgb_reverse_tex"), 2);
-
- glUniform3f(glGetUniformLocation(prog, "lift"), lift_r, lift_g, lift_b);
- //glUniform3f(glGetUniformLocation(prog, "gamma"), gamma_r, gamma_g, gamma_b);
- glUniform3f(glGetUniformLocation(prog, "inv_gamma_22"),
- 2.2f / gamma_r,
- 2.2f / gamma_g,
- 2.2f / gamma_b);
- glUniform3f(glGetUniformLocation(prog, "gain_pow_inv_gamma"),
- pow(gain_r, 1.0f / gamma_r),
- pow(gain_g, 1.0f / gamma_g),
- pow(gain_b, 1.0f / gamma_b));
- glUniform1f(glGetUniformLocation(prog, "saturation"), saturation);
-
- glDisable(GL_BLEND);
- check_error();
- glDisable(GL_DEPTH_TEST);
- check_error();
- glDepthMask(GL_FALSE);
- check_error();
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glOrtho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0);
-
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
-// glClear(GL_COLOR_BUFFER_BIT);
- check_error();
-
- glBegin(GL_QUADS);
-
- glTexCoord2f(0.0f, 1.0f);
- glVertex2f(0.0f, 0.0f);
-
- glTexCoord2f(1.0f, 1.0f);
- glVertex2f(1.0f, 0.0f);
+ RGBTriplet lift(0.0f, 0.0f, 0.0f);
+ RGBTriplet gamma(1.0f, 1.0f, 1.0f);
+ RGBTriplet gain(1.0f, 1.0f, 1.0f);
- glTexCoord2f(1.0f, 0.0f);
- glVertex2f(1.0f, 1.0f);
-
- glTexCoord2f(0.0f, 0.0f);
- glVertex2f(0.0f, 1.0f);
-
- glEnd();
- check_error();
-}
+ hsv2rgb(lift_theta, lift_rad, lift_v, &lift.r, &lift.g, &lift.b);
+ hsv2rgb(gamma_theta, gamma_rad, gamma_v * 2.0f, &gamma.r, &gamma.g, &gamma.b);
+ hsv2rgb(gain_theta, gain_rad, gain_v * 4.0f, &gain.r, &gain.g, &gain.b);
-void update_hsv()
-{
- hsv2rgb(lift_theta, lift_rad, lift_v, &lift_r, &lift_g, &lift_b);
- hsv2rgb(gamma_theta, gamma_rad, gamma_v * 2.0f, &gamma_r, &gamma_g, &gamma_b);
- hsv2rgb(gain_theta, gain_rad, gain_v * 4.0f, &gain_r, &gain_g, &gain_b);
+ bool ok = lift_gamma_gain_effect->set_vec3("lift", (float *)&lift);
+ ok = ok && lift_gamma_gain_effect->set_vec3("gamma", (float *)&gamma);
+ ok = ok && lift_gamma_gain_effect->set_vec3("gain", (float *)&gain);
+ assert(ok);
if (saturation < 0.0) {
saturation = 0.0;
}
-
- printf("lift: %f %f %f\n", lift_r, lift_g, lift_b);
- printf("gamma: %f %f %f\n", gamma_r, gamma_g, gamma_b);
- printf("gain: %f %f %f\n", gain_r, gain_g, gain_b);
- printf("saturation: %f\n", saturation);
- printf("\n");
+ ok = saturation_effect->set_float("saturation", saturation);
+ assert(ok);
}
void mouse(int x, int y)
} else if (yf >= 0.6f && yf < 0.62f && xf < 0.2f) {
saturation = (xf / 0.2f) * 4.0f;
}
-
- update_hsv();
}
-void load_texture(const char *filename)
+unsigned char *load_image(const char *filename, unsigned *w, unsigned *h)
{
SDL_Surface *img = IMG_Load(filename);
if (img == NULL) {
}
SDL_UnlockSurface(img);
-#if 1
- // we will convert to sRGB in the shader
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img->w, img->h, 0, GL_RGB, GL_UNSIGNED_BYTE, dst_pixels);
- check_error();
-#else
- // implicit sRGB conversion in hardware
- glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8, img->w, img->h, 0, GL_RGB, GL_UNSIGNED_BYTE, dst_pixels);
- check_error();
-#endif
- free(dst_pixels);
+ *w = img->w;
+ *h = img->h;
+
SDL_FreeSurface(img);
+
+ return dst_pixels;
}
void write_ppm(const char *filename, unsigned char *screenbuf)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
check_error();
- load_texture("blg_wheels_woman_1.jpg");
+ unsigned img_w, img_h;
+ unsigned char *src_img = load_image("blg_wheels_woman_1.jpg", &img_w, &img_h);
+
+ EffectChain chain(WIDTH, HEIGHT);
+
+ ImageFormat inout_format;
+ inout_format.pixel_format = FORMAT_RGB;
+ inout_format.color_space = COLORSPACE_sRGB;
+ inout_format.gamma_curve = GAMMA_sRGB;
+
+ chain.add_input(inout_format);
+ Effect *lift_gamma_gain_effect = chain.add_effect(EFFECT_LIFT_GAMMA_GAIN);
+ Effect *saturation_effect = chain.add_effect(EFFECT_SATURATION);
+ chain.add_output(inout_format);
+ chain.finalize();
+
//glGenerateMipmap(GL_TEXTURE_2D);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 4);
//check_error();
- //load_texture("maserati_gts_wallpaper_1280x720_01.jpg");
- //load_texture("90630d1295075297-console-games-wallpapers-wallpaper_need_for_speed_prostreet_09_1920x1080.jpg");
- //load_texture("glacier-lake-1280-720-4087.jpg");
-
#if 0
// sRGB reverse LUT
glBindTexture(GL_TEXTURE_1D, SRGB_REVERSE_LUT);
glBufferData(GL_PIXEL_PACK_BUFFER_ARB, WIDTH * HEIGHT * 4, NULL, GL_STREAM_READ);
make_hsv_wheel_texture();
- update_hsv();
-
- int prog = glCreateProgram();
- GLhandleARB vs_obj = read_shader("vs.glsl", GL_VERTEX_SHADER);
- GLhandleARB fs_obj = read_shader("fs.glsl", GL_FRAGMENT_SHADER);
- glAttachObjectARB(prog, vs_obj);
- check_error();
- glAttachObjectARB(prog, fs_obj);
- check_error();
- glLinkProgram(prog);
- check_error();
-
- GLchar info_log[4096];
- GLsizei log_length = sizeof(info_log) - 1;
- log_length = sizeof(info_log) - 1;
- glGetProgramInfoLog(prog, log_length, &log_length, info_log);
- info_log[log_length] = 0;
- printf("link: %s\n", info_log);
struct timespec start, now;
int frame = 0, screenshot = 0;
++frame;
- draw_picture_quad(prog, frame);
+ update_hsv(lift_gamma_gain_effect, saturation_effect);
+ chain.render_to_screen(src_img);
glReadPixels(0, 0, WIDTH, HEIGHT, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, BUFFER_OFFSET(0));
check_error();