Merge "Apply transfer function when rendering with linear textures"
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 8a6e038..5cf52c6 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -605,6 +605,7 @@
         } else {
             mDescription.hasExternalTexture = true;
         }
+        mDescription.hasLinearTexture = mOutGlop->fill.texture.texture->isLinear();
     }
 
     mDescription.hasColors = mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::Color;
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index e70982f..5c8f8e9 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -87,6 +87,7 @@
 #define PROGRAM_HAS_ROUND_RECT_CLIP 43
 
 #define PROGRAM_HAS_GAMMA_CORRECTION 44
+#define PROGRAM_HAS_LINEAR_TEXTURE 45
 
 ///////////////////////////////////////////////////////////////////////////////
 // Types
@@ -162,7 +163,10 @@
     bool hasDebugHighlight;
     bool hasRoundRectClip;
 
+    // Extra gamma correction used for text
     bool hasGammaCorrection;
+    // Set when sampling an image in linear space
+    bool hasLinearTexture;
 
     /**
      * Resets this description. All fields are reset back to the default
@@ -205,6 +209,7 @@
         hasRoundRectClip = false;
 
         hasGammaCorrection = false;
+        hasLinearTexture = false;
     }
 
     /**
@@ -275,6 +280,7 @@
         if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
         if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP;
         if (hasGammaCorrection) key |= programid(0x1) << PROGRAM_HAS_GAMMA_CORRECTION;
+        if (hasLinearTexture) key |= programid(0x1) << PROGRAM_HAS_LINEAR_TEXTURE;
         return key;
     }
 
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 42ef762..00de992 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -161,9 +161,35 @@
         "uniform vec4 roundRectInnerRectLTRB;\n"
         "uniform float roundRectRadius;\n";
 
+const char* gFS_OETF[2] = {
+         "\nvec4 OETF(const vec4 linear) {\n"
+         "    return linear;\n"
+         "}\n",
+          // We expect linear data to be scRGB so we mirror the gamma function
+         "\nvec4 OETF(const vec4 linear) {"
+          "    return vec4(sign(linear.rgb) * OETF_sRGB(abs(linear.rgb)), linear.a);\n"
+          "}\n",
+};
+
+const char* gFS_Transfer_Functions = R"__SHADER__(
+        float OETF_sRGB(const float linear) {
+            // IEC 61966-2-1:1999
+            return linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+        }
+
+        vec3 OETF_sRGB(const vec3 linear) {
+            return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+        }
+
+        float EOTF_sRGB(float srgb) {
+            // IEC 61966-2-1:1999
+            return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+        }
+)__SHADER__";
+
 // Dithering must be done in the quantization space
 // When we are writing to an sRGB framebuffer, we must do the following:
-//     EOCF(OECF(color) + dither)
+//     EOTF(OETF(color) + dither)
 // The dithering pattern is generated with a triangle noise generator in the range [-0.0,1.0]
 // TODO: Handle linear fp16 render targets
 const char* gFS_Gradient_Functions = R"__SHADER__(
@@ -173,20 +199,6 @@
             highp float xy = p.x * p.y;
             return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;
         }
-
-        float OECF_sRGB(const float linear) {
-            // IEC 61966-2-1:1999
-            return linear <= 0.0031308 ? linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
-        }
-
-        vec3 OECF_sRGB(const vec3 linear) {
-            return vec3(OECF_sRGB(linear.r), OECF_sRGB(linear.g), OECF_sRGB(linear.b));
-        }
-
-        float EOCF_sRGB(float srgb) {
-            // IEC 61966-2-1:1999
-            return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
-        }
 )__SHADER__";
 const char* gFS_Gradient_Preamble[2] = {
         // Linear framebuffer
@@ -195,8 +207,8 @@
         "}\n"
         "\nvec4 gammaMix(const vec4 a, const vec4 b, float v) {\n"
         "    vec4 c = mix(a, b, v);\n"
-        "    c.a = EOCF_sRGB(c.a);\n" // This is technically incorrect but preserves compatibility
-        "    return vec4(OECF_sRGB(c.rgb) * c.a, c.a);\n"
+        "    c.a = EOTF_sRGB(c.a);\n" // This is technically incorrect but preserves compatibility
+        "    return vec4(OETF_sRGB(c.rgb) * c.a, c.a);\n"
         "}\n",
         // sRGB framebuffer
         "\nvec4 dither(const vec4 color) {\n"
@@ -232,52 +244,6 @@
 const char* gFS_Main_AddDither =
         "    fragColor = dither(fragColor);\n";
 
-// Fast cases
-const char* gFS_Fast_SingleColor =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = color;\n"
-        "}\n\n";
-const char* gFS_Fast_SingleTexture =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = texture2D(baseSampler, outTexCoords);\n"
-        "}\n\n";
-const char* gFS_Fast_SingleModulateTexture =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = color.a * texture2D(baseSampler, outTexCoords);\n"
-        "}\n\n";
-const char* gFS_Fast_SingleA8Texture =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = texture2D(baseSampler, outTexCoords);\n"
-        "}\n\n";
-const char* gFS_Fast_SingleA8Texture_ApplyGamma =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = vec4(0.0, 0.0, 0.0, pow(texture2D(baseSampler, outTexCoords).a, GAMMA));\n"
-        "}\n\n";
-const char* gFS_Fast_SingleModulateA8Texture =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = color * texture2D(baseSampler, outTexCoords).a;\n"
-        "}\n\n";
-const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = color * gamma(texture2D(baseSampler, outTexCoords).a, color.rgb);\n"
-        "}\n\n";
-const char* gFS_Fast_SingleGradient[2] = {
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = dither(texture2D(gradientSampler, linear));\n"
-        "}\n\n",
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = dither(gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n"
-        "}\n\n",
-};
-const char* gFS_Fast_SingleModulateGradient[2] = {
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = dither(color.a * texture2D(gradientSampler, linear));\n"
-        "}\n\n",
-        "\nvoid main(void) {\n"
-        "    gl_FragColor = dither(color.a * gammaMix(startColor, endColor, clamp(linear, 0.0, 1.0)));\n"
-        "}\n\n"
-};
-
 // General case
 const char* gFS_Main_FetchColor =
         "    fragColor = color;\n";
@@ -290,7 +256,7 @@
         "    fragColor *= texture2D(baseSampler, vec2(alpha, 0.5)).a;\n";
 const char* gFS_Main_FetchTexture[2] = {
         // Don't modulate
-        "    fragColor = texture2D(baseSampler, outTexCoords);\n",
+        "    fragColor = OETF(texture2D(baseSampler, outTexCoords));\n",
         // Modulate
         "    fragColor = color * texture2D(baseSampler, outTexCoords);\n"
 };
@@ -321,9 +287,9 @@
         "    vec4 gradientColor = gammaMix(startColor, endColor, clamp(index - floor(index), 0.0, 1.0));\n"
 };
 const char* gFS_Main_FetchBitmap =
-        "    vec4 bitmapColor = texture2D(bitmapSampler, outBitmapTexCoords);\n";
+        "    vec4 bitmapColor = OETF(texture2D(bitmapSampler, outBitmapTexCoords));\n";
 const char* gFS_Main_FetchBitmapNpot =
-        "    vec4 bitmapColor = texture2D(bitmapSampler, wrap(outBitmapTexCoords));\n";
+        "    vec4 bitmapColor = OETF(texture2D(bitmapSampler, wrap(outBitmapTexCoords)));\n";
 const char* gFS_Main_BlendShadersBG =
         "    fragColor = blendShaders(gradientColor, bitmapColor)";
 const char* gFS_Main_BlendShadersGB =
@@ -649,71 +615,6 @@
         shader.appendFormat(gFS_Gamma_Preamble, Properties::textGamma, 1.0f / Properties::textGamma);
     }
 
-    // Optimization for common cases
-    if (!description.hasVertexAlpha
-            && !blendFramebuffer
-            && !description.hasColors
-            && description.colorOp == ProgramDescription::ColorFilterMode::None
-            && !description.hasDebugHighlight
-            && !description.hasRoundRectClip) {
-        bool fast = false;
-
-        const bool noShader = !description.hasGradient && !description.hasBitmap;
-        const bool singleTexture = (description.hasTexture || description.hasExternalTexture) &&
-                !description.hasAlpha8Texture && noShader;
-        const bool singleA8Texture = description.hasTexture &&
-                description.hasAlpha8Texture && noShader;
-        const bool singleGradient = !description.hasTexture && !description.hasExternalTexture &&
-                description.hasGradient && !description.hasBitmap &&
-                description.gradientType == ProgramDescription::kGradientLinear;
-
-        if (singleColor) {
-            shader.append(gFS_Fast_SingleColor);
-            fast = true;
-        } else if (singleTexture) {
-            if (!description.modulate) {
-                shader.append(gFS_Fast_SingleTexture);
-            } else {
-                shader.append(gFS_Fast_SingleModulateTexture);
-            }
-            fast = true;
-        } else if (singleA8Texture) {
-            if (!description.modulate) {
-                if (description.hasGammaCorrection) {
-                    shader.append(gFS_Fast_SingleA8Texture_ApplyGamma);
-                } else {
-                    shader.append(gFS_Fast_SingleA8Texture);
-                }
-            } else {
-                if (description.hasGammaCorrection) {
-                    shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma);
-                } else {
-                    shader.append(gFS_Fast_SingleModulateA8Texture);
-                }
-            }
-            fast = true;
-        } else if (singleGradient) {
-            shader.append(gFS_Gradient_Functions);
-            shader.append(gFS_Gradient_Preamble[mHasSRGB]);
-            if (!description.modulate) {
-                shader.append(gFS_Fast_SingleGradient[description.isSimpleGradient]);
-            } else {
-                shader.append(gFS_Fast_SingleModulateGradient[description.isSimpleGradient]);
-            }
-            fast = true;
-        }
-
-        if (fast) {
-#if DEBUG_PROGRAMS
-                PROGRAM_LOGD("*** Fast case:\n");
-                PROGRAM_LOGD("*** Generated fragment shader:\n\n");
-                printLongString(shader);
-#endif
-
-            return shader;
-        }
-    }
-
     if (description.hasBitmap) {
         if (description.isShaderBitmapExternal) {
             shader.append(gFS_Uniforms_BitmapExternalSampler);
@@ -736,6 +637,13 @@
     if (description.useShaderBasedWrap) {
         generateTextureWrap(shader, description.bitmapWrapS, description.bitmapWrapT);
     }
+    if (description.hasGradient || description.hasLinearTexture) {
+        shader.append(gFS_Transfer_Functions);
+    }
+    if (description.hasBitmap || ((description.hasTexture || description.hasExternalTexture) &&
+            !description.hasAlpha8Texture)) {
+        shader.append(gFS_OETF[description.hasLinearTexture && !mHasSRGB]);
+    }
     if (description.hasGradient) {
         shader.append(gFS_Gradient_Functions);
         shader.append(gFS_Gradient_Preamble[mHasSRGB]);
@@ -827,10 +735,10 @@
     // End the shader
     shader.append(gFS_Footer);
 
-#if DEBUG_PROGRAMS
+//#if DEBUG_PROGRAMS
         PROGRAM_LOGD("*** Generated fragment shader:\n\n");
         printLongString(shader);
-#endif
+//#endif
 
     return shader;
 }
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
index f6850a1..50af9c8 100644
--- a/libs/hwui/Texture.cpp
+++ b/libs/hwui/Texture.cpp
@@ -48,6 +48,10 @@
     }
 }
 
+bool Texture::isLinear() const {
+    return mInternalFormat == GL_RGBA16F;
+}
+
 void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force) {
 
     if (force || wrapS != mWrapS || wrapT != mWrapT) {
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index b8397cc..ce9d4dc 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -128,6 +128,11 @@
     }
 
     /**
+     * Returns true if this texture uses a linear encoding format.
+     */
+    bool isLinear() const;
+
+    /**
      * Generation of the backing bitmap,
      */
     uint32_t generation = 0;