Add shader-based text gamma correction

To enable it, the system property ro.hwui.text_gamma_shader must be
set to true. For testing, DEBUG_FONT_RENDERER_FORCE_SHADER_GAMMA
can be set to 1 in libhwui/Debug.h.

Change-Id: If345c6b71b67ecf1ef2e8847b71f30f3ef251a27
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index a7f1277..fc60279 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -159,6 +159,9 @@
         // PorterDuff
         "uniform vec4 colorBlend;\n"
 };
+const char* gFS_Uniforms_Gamma =
+        "uniform float gamma;\n";
+
 const char* gFS_Main =
         "\nvoid main(void) {\n"
         "    lowp vec4 fragColor;\n";
@@ -184,10 +187,18 @@
         "\nvoid main(void) {\n"
         "    gl_FragColor = texture2D(sampler, 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(sampler, outTexCoords).a, gamma));\n"
+        "}\n\n";
 const char* gFS_Fast_SingleModulateA8Texture =
         "\nvoid main(void) {\n"
         "    gl_FragColor = color * texture2D(sampler, outTexCoords).a;\n"
         "}\n\n";
+const char* gFS_Fast_SingleModulateA8Texture_ApplyGamma =
+        "\nvoid main(void) {\n"
+        "    gl_FragColor = color * pow(texture2D(sampler, outTexCoords).a, gamma);\n"
+        "}\n\n";
 const char* gFS_Fast_SingleGradient =
         "\nvoid main(void) {\n"
         "    gl_FragColor = texture2D(gradientSampler, linear);\n"
@@ -202,6 +213,8 @@
         "    fragColor = color;\n";
 const char* gFS_Main_ModulateColor =
         "    fragColor *= color.a;\n";
+const char* gFS_Main_ModulateColor_ApplyGamma =
+        "    fragColor *= pow(color.a, gamma);\n";
 const char* gFS_Main_AccountForAA =
         "    if (widthProportion < boundaryWidth) {\n"
         "        fragColor *= (widthProportion * inverseBoundaryWidth);\n"
@@ -517,6 +530,9 @@
     if (description.hasBitmap && description.isPoint) {
         shader.append(gFS_Header_Uniforms_PointHasBitmap);
     }
+    if (description.hasGammaCorrection) {
+        shader.append(gFS_Uniforms_Gamma);
+    }
 
     // Optimization for common cases
     if (!description.isAA && !blendFramebuffer &&
@@ -544,9 +560,17 @@
             fast = true;
         } else if (singleA8Texture) {
             if (!description.modulate) {
-                shader.append(gFS_Fast_SingleA8Texture);
+                if (description.hasGammaCorrection) {
+                    shader.append(gFS_Fast_SingleA8Texture_ApplyGamma);
+                } else {
+                    shader.append(gFS_Fast_SingleA8Texture);
+                }
             } else {
-                shader.append(gFS_Fast_SingleModulateA8Texture);
+                if (description.hasGammaCorrection) {
+                    shader.append(gFS_Fast_SingleModulateA8Texture_ApplyGamma);
+                } else {
+                    shader.append(gFS_Fast_SingleModulateA8Texture);
+                }
             }
             fast = true;
         } else if (singleGradient) {
@@ -643,7 +667,11 @@
             }
         }
         if (description.modulate && applyModulate) {
-            shader.append(gFS_Main_ModulateColor);
+            if (description.hasGammaCorrection) {
+                shader.append(gFS_Main_ModulateColor_ApplyGamma);
+            } else {
+                shader.append(gFS_Main_ModulateColor);
+            }
         }
         // Apply the color op if needed
         shader.append(gFS_Main_ApplyColorOp[description.colorOp]);