Clip below-zero (out-of-gamut) colors in LiftGammaGainEffect.
authorSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 28 Sep 2013 22:16:48 +0000 (00:16 +0200)
committerSteinar H. Gunderson <sgunderson@bigfoot.com>
Sat, 28 Sep 2013 22:16:48 +0000 (00:16 +0200)
pow(x, y) for x < 0 is undefined, and behaves differently on nVidia
and Intel. This can reasonably happen when having inputs from a different
gamut, or just a complex chain before the LGG effect; there's nothing
in Movit that prohibits out-of-sRGB-gamut colors. Thus, we need to detect
such inputs and clamp them.

We could in theory check for the special case of y=1 (no gamma change)
and allow negative values through then, but this wouldn't seem like a good
solution, especially if animating gamma.

Found and debugged by Christophe Thommeret.

lift_gamma_gain_effect.frag
lift_gamma_gain_effect_test.cpp

index 775941e..9047d64 100644 (file)
@@ -6,6 +6,10 @@ vec4 FUNCNAME(vec2 tc) {
        vec4 x = INPUT(tc);
 
        x.rgb /= x.aaa;
+
+       // pow() of negative numbers is undefined, so clip out-of-gamut values.
+       x.rgb = max(x.rgb, 0.0);
+
        x.rgb = pow(x.rgb, vec3(1.0/2.2));
        x.rgb += PREFIX(lift) * (vec3(1) - x.rgb);
        x.rgb = pow(x.rgb, PREFIX(inv_gamma_22));
index 0b63063..9850ba2 100644 (file)
@@ -94,3 +94,23 @@ TEST(LiftGammaGainEffectTest, Gamma22IsApproximatelysRGB) {
 
        expect_equal(data, out_data, 4, 5);
 }
+
+TEST(LiftGammaGainEffectTest, OutOfGamutColorsAreClipped) {
+       float data[] = {
+               -0.5f, 0.3f, 0.0f, 1.0f,
+                0.5f, 0.0f, 0.0f, 1.0f,
+                0.0f, 1.5f, 0.5f, 0.3f,
+       };
+       float expected_data[] = {
+                0.0f, 0.3f, 0.0f, 1.0f,  // Clipped to zero.
+                0.5f, 0.0f, 0.0f, 1.0f,
+                0.0f, 1.5f, 0.5f, 0.3f,
+       };
+
+       float out_data[3 * 4];
+       EffectChainTester tester(data, 1, 3, FORMAT_RGBA_POSTMULTIPLIED_ALPHA, COLORSPACE_sRGB, GAMMA_LINEAR);
+       tester.get_chain()->add_effect(new LiftGammaGainEffect());
+       tester.run(out_data, GL_RGBA, COLORSPACE_sRGB, GAMMA_LINEAR);
+
+       expect_equal(expected_data, out_data, 4, 3);
+}