Add color space conversions on input and output.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 1 Oct 2012 21:44:26 +0000 (23:44 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Mon, 1 Oct 2012 21:44:26 +0000 (23:44 +0200)
colorspace_conversion_effect.cpp
colorspace_conversion_effect.glsl [new file with mode: 0644]
effect_chain.cpp
util.cpp
util.h

index 8a0199e..4cef413 100644 (file)
@@ -1,6 +1,20 @@
+#include <assert.h>
+
 #include "colorspace_conversion_effect.h"
 #include "util.h"
 
+// Color coordinates from Rec. 709; sRGB uses the same primaries.
+double rec709_x_R = 0.640,  rec709_x_G = 0.300,  rec709_x_B = 0.150;
+double rec709_y_R = 0.330,  rec709_y_G = 0.600,  rec709_y_B = 0.060;
+double rec709_Y_R = 0.2126, rec709_Y_G = 0.7152, rec709_Y_B = 0.0722;
+
+// Color coordinates from Rec. 601. (Separate for 525- and 625-line systems.)
+double rec601_525_x_R = 0.630, rec601_525_x_G = 0.310, rec601_525_x_B = 0.155;
+double rec601_525_y_R = 0.340, rec601_525_y_G = 0.595, rec601_525_y_B = 0.070;
+double rec601_625_x_R = 0.640, rec601_625_x_G = 0.290, rec601_625_x_B = 0.150;
+double rec601_625_y_R = 0.330, rec601_625_y_G = 0.600, rec601_625_y_B = 0.060;
+double rec601_Y_R = 0.299, rec601_Y_G = 0.587, rec601_Y_B = 0.114;
+
 ColorSpaceConversionEffect::ColorSpaceConversionEffect()
        : source_space(COLORSPACE_sRGB),
          destination_space(COLORSPACE_sRGB)
@@ -9,7 +23,76 @@ ColorSpaceConversionEffect::ColorSpaceConversionEffect()
        register_int("destination_space", (int *)&destination_space);
 }
 
+void get_xyz_matrix(ColorSpace space, Matrix3x3 m)
+{
+       double x_R, x_G, x_B;
+       double y_R, y_G, y_B;
+       double Y_R, Y_G, Y_B;
+
+       switch (space) {
+       case COLORSPACE_REC_709:  // And sRGB.
+               x_R = rec709_x_R; x_G = rec709_x_G; x_B = rec709_x_B;
+               y_R = rec709_y_R; y_G = rec709_y_G; y_B = rec709_y_B;
+               Y_R = rec709_Y_R; Y_G = rec709_Y_G; Y_B = rec709_Y_B;
+               break;
+       case COLORSPACE_REC_601_525:
+               x_R = rec601_525_x_R; x_G = rec601_525_x_G; x_B = rec601_525_x_B;
+               y_R = rec601_525_y_R; y_G = rec601_525_y_G; y_B = rec601_525_y_B;
+               Y_R = rec601_Y_R;     Y_G = rec601_Y_G;     Y_B = rec601_Y_B;
+               break;
+       case COLORSPACE_REC_601_625:
+               x_R = rec601_625_x_R; x_G = rec601_625_x_G; x_B = rec601_625_x_B;
+               y_R = rec601_625_y_R; y_G = rec601_625_y_G; y_B = rec601_625_y_B;
+               Y_R = rec601_Y_R;     Y_G = rec601_Y_G;     Y_B = rec601_Y_B;
+               break;
+       default:
+               assert(false);
+       }
+
+       // Convert xyY -> XYZ.
+       double X_R, X_G, X_B;
+       X_R = Y_R * x_R / y_R;
+       X_G = Y_G * x_G / y_G;
+       X_B = Y_B * x_B / y_B;
+       
+       double Z_R, Z_G, Z_B;
+       Z_R = Y_R * (1.0f - x_R - y_R) / y_R;
+       Z_G = Y_G * (1.0f - x_G - y_G) / y_G;
+       Z_B = Y_B * (1.0f - x_B - y_B) / y_B;
+
+       m[0] = X_R; m[3] = X_G; m[6] = X_B;
+       m[1] = Y_R; m[4] = Y_G; m[7] = Y_B;
+       m[2] = Z_R; m[5] = Z_G; m[8] = Z_B;
+}
+
 std::string ColorSpaceConversionEffect::output_glsl()
 {
-       return read_file("todo.glsl");
+       // Create a matrix to convert from source space -> XYZ,
+       // another matrix to convert from XYZ -> destination space,
+       // and then concatenate the two.
+       //
+       // Since we right-multiply the RGB column vector, the matrix
+       // concatenation order needs to be the opposite of the operation order.
+       Matrix3x3 m;
+
+       Matrix3x3 source_space_to_xyz;
+       Matrix3x3 destination_space_to_xyz;
+       Matrix3x3 xyz_to_destination_space;
+
+       get_xyz_matrix(source_space, source_space_to_xyz);
+       get_xyz_matrix(destination_space, destination_space_to_xyz);
+       invert_3x3_matrix(destination_space_to_xyz, xyz_to_destination_space);
+       
+       multiply_3x3_matrices(xyz_to_destination_space, source_space_to_xyz, m);
+
+       char buf[1024];
+       sprintf(buf,
+               "const mat3 PREFIX(conversion_matrix) = mat3(\n"
+               "    %.8f, %.8f, %.8f,\n"
+               "    %.8f, %.8f, %.8f,\n"
+               "    %.8f, %.8f, %.8f);\n\n",
+               m[0], m[3], m[6],
+               m[1], m[4], m[7],
+               m[2], m[5], m[8]);
+       return buf + read_file("colorspace_conversion_effect.glsl");
 }
diff --git a/colorspace_conversion_effect.glsl b/colorspace_conversion_effect.glsl
new file mode 100644 (file)
index 0000000..ea858fb
--- /dev/null
@@ -0,0 +1,8 @@
+// Colorspace conversion (needs to be done in linear space).
+// The matrix is computed on the host and baked into the shader at compile time.
+
+vec4 FUNCNAME(vec2 tc) {
+       vec4 x = LAST_INPUT(tc);
+       x.rgb = PREFIX(conversion_matrix) * x.rgb;
+       return x;
+}
index 3ccab44..5d9eaa3 100644 (file)
@@ -132,7 +132,13 @@ std::string replace_prefix(const std::string &text, const std::string &prefix)
 
 void EffectChain::finalize()
 {
-       // TODO: If we want a non-sRGB output color space, convert.
+       if (current_color_space != output_format.color_space) {
+               ColorSpaceConversionEffect *colorspace_conversion = new ColorSpaceConversionEffect();
+               colorspace_conversion->set_int("source_space", current_color_space);
+               colorspace_conversion->set_int("destination_space", output_format.color_space);
+               effects.push_back(colorspace_conversion);
+               current_color_space = output_format.color_space;
+       }
 
        if (current_gamma_curve != output_format.gamma_curve) {
                if (current_gamma_curve != GAMMA_LINEAR) {
index c18f009..2176786 100644 (file)
--- a/util.cpp
+++ b/util.cpp
@@ -88,3 +88,45 @@ GLhandleARB compile_shader(const std::string &shader_src, GLenum type)
        return obj;
 }
 
+void multiply_3x3_matrices(const Matrix3x3 a, const Matrix3x3 b, Matrix3x3 result)
+{
+        result[0] = a[0] * b[0] + a[3] * b[1] + a[6] * b[2];
+        result[1] = a[1] * b[0] + a[4] * b[1] + a[7] * b[2];
+        result[2] = a[2] * b[0] + a[5] * b[1] + a[8] * b[2];
+
+        result[3] = a[0] * b[3] + a[3] * b[4] + a[6] * b[5];
+        result[4] = a[1] * b[3] + a[4] * b[4] + a[7] * b[5];
+        result[5] = a[2] * b[3] + a[5] * b[4] + a[8] * b[5];
+
+        result[6] = a[0] * b[6] + a[3] * b[7] + a[6] * b[8];
+        result[7] = a[1] * b[6] + a[4] * b[7] + a[7] * b[8];
+        result[8] = a[2] * b[6] + a[5] * b[7] + a[8] * b[8];
+}
+
+void invert_3x3_matrix(const Matrix3x3 m, Matrix3x3 result)
+{
+       double inv_det = 1.0 / (
+               m[6] * m[1] * m[5] - m[6] * m[2] * m[4] -
+               m[3] * m[1] * m[8] + m[3] * m[2] * m[7] +
+               m[0] * m[4] * m[8] - m[0] * m[5] * m[7]);
+
+       result[0] = inv_det * (m[4] * m[8] - m[5] * m[7]);
+       result[1] = inv_det * (m[2] * m[7] - m[1] * m[8]);
+       result[2] = inv_det * (m[1] * m[5] - m[2] * m[4]);
+
+       result[3] = inv_det * (m[6] * m[5] - m[3] * m[8]);
+       result[4] = inv_det * (m[0] * m[8] - m[6] * m[2]);
+       result[5] = inv_det * (m[3] * m[2] - m[0] * m[5]);
+
+       result[6] = inv_det * (m[3] * m[7] - m[6] * m[4]);
+       result[7] = inv_det * (m[6] * m[1] - m[0] * m[7]);
+       result[8] = inv_det * (m[0] * m[4] - m[3] * m[1]);
+}
+
+void print_3x3_matrix(const Matrix3x3 m)
+{
+       printf("%6.4f %6.4f %6.4f\n", m[0], m[3], m[6]);
+       printf("%6.4f %6.4f %6.4f\n", m[1], m[4], m[7]);
+       printf("%6.4f %6.4f %6.4f\n", m[2], m[5], m[8]);
+       printf("\n");
+}
diff --git a/util.h b/util.h
index ab0ee5d..498e5f2 100644 (file)
--- a/util.h
+++ b/util.h
 // assumes h in [0, 2pi> or [-pi, pi>
 void hsv2rgb(float h, float s, float v, float *r, float *g, float *b);
 
+// Column major (same as OpenGL).
+typedef double Matrix3x3[9];
+
 std::string read_file(const std::string &filename);
 GLhandleARB compile_shader(const std::string &shader_src, GLenum type);
+void multiply_3x3_matrices(const Matrix3x3 a, const Matrix3x3 b, Matrix3x3 result);
+void invert_3x3_matrix(const Matrix3x3 m, Matrix3x3 result);
+void print_3x3_matrix(const Matrix3x3 m);
 
 #ifdef NDEBUG
 #define check_error()