Gamma correction for distance field text.

Handles both non-LCD and LCD text. Uses a texture to look up the gamma correction values for a given text color or luminance.

BUG=skia:

Committed: https://skia.googlesource.com/skia/+/4d517fdbb145cb95e5e935470df331e1b6667cfc

R=reed@google.com, bungeman@google.com, robertphillips@google.com, bsalomon@google.com

Author: jvanverth@google.com

Review URL: https://codereview.chromium.org/258883002
diff --git a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
index 6196cce..29f0985 100755
--- a/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
+++ b/src/gpu/effects/GrDistanceFieldTextureEffect.cpp
@@ -15,6 +15,19 @@
 
 #include "SkDistanceFieldGen.h"
 
+// To get optical sizes people don't complain about when we blit correctly,
+// we need to slightly bold each glyph. On the Mac, we need a larger bold value.
+#if defined(SK_BUILD_FOR_MAC)
+#define SK_DistanceFieldLCDFactor    "0.33"
+#define SK_DistanceFieldNonLCDFactor "0.25"
+#else
+#define SK_DistanceFieldLCDFactor    "0.05"
+#define SK_DistanceFieldNonLCDFactor "0.05"
+#endif
+
+// Assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2
+#define SK_DistanceFieldAAFactor     "0.7071"
+
 class GrGLDistanceFieldTextureEffect : public GrGLVertexEffect {
 public:
     GrGLDistanceFieldTextureEffect(const GrBackendEffectFactory& factory,
@@ -56,7 +69,8 @@
                                        kVec2f_GrSLType);
         builder->fsCodeAppend(";\n");
         builder->fsCodeAppend("\tfloat distance = "
-                     SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n");
+                          SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ")"
+                          "+ " SK_DistanceFieldNonLCDFactor ";\n");
 
         // we adjust for the effect of the transformation on the distance by using
         // the length of the gradient of the texture coordinates. We use st coordinates
@@ -66,8 +80,7 @@
         builder->fsCodeAppend("\tfloat afwidth;\n");
         if (dfTexEffect.isSimilarity()) {
             // this gives us a smooth step across approximately one fragment
-            // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
-            builder->fsCodeAppend("\tafwidth = 0.7071*dFdx(st.x);\n");
+            builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*dFdx(st.x);\n");
         } else {
             builder->fsCodeAppend("\tvec2 Jdx = dFdx(st);\n");
             builder->fsCodeAppend("\tvec2 Jdy = dFdy(st);\n");
@@ -88,12 +101,25 @@
             builder->fsCodeAppend("\t                 uv_grad.x*Jdx.y + uv_grad.y*Jdy.y);\n");
 
             // this gives us a smooth step across approximately one fragment
-            // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
-            builder->fsCodeAppend("\tafwidth = 0.7071*length(grad);\n");
+            builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*length(grad);\n");
         }
-
         builder->fsCodeAppend("\tfloat val = smoothstep(-afwidth, afwidth, distance);\n");
 
+#ifdef SK_GAMMA_APPLY_TO_A8
+        // adjust based on gamma
+        const char* luminanceUniName = NULL;
+        // width, height, 1/(3*width)
+        fLuminanceUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                            kFloat_GrSLType, "Luminance",
+                                            &luminanceUniName);
+
+        builder->fsCodeAppendf("\tuv = vec2(val, %s);\n", luminanceUniName);
+        builder->fsCodeAppend("\tvec4 gammaColor = ");
+        builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType);
+        builder->fsCodeAppend(";\n");
+        builder->fsCodeAppend("\tval = gammaColor.r;\n");
+#endif
+
         builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
                                    (GrGLSLExpr4(inputColor) * GrGLSLExpr1("val")).c_str());
     }
@@ -110,6 +136,15 @@
                        SkIntToScalar(fTextureSize.width()),
                        SkIntToScalar(fTextureSize.height()));
         }
+#ifdef SK_GAMMA_APPLY_TO_A8
+        const GrDistanceFieldTextureEffect& dfTexEffect =
+                                              drawEffect.castEffect<GrDistanceFieldTextureEffect>();
+        float luminance = dfTexEffect.getLuminance();
+        if (luminance != fLuminance) {
+            uman.set1f(fLuminanceUni, luminance);
+            fLuminance = luminance;
+        }
+#endif
     }
 
     static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
@@ -122,6 +157,8 @@
 private:
     GrGLUniformManager::UniformHandle fTextureSizeUni;
     SkISize                           fTextureSize;
+    GrGLUniformManager::UniformHandle fLuminanceUni;
+    float                             fLuminance;
 
     typedef GrGLVertexEffect INHERITED;
 };
@@ -130,10 +167,22 @@
 
 GrDistanceFieldTextureEffect::GrDistanceFieldTextureEffect(GrTexture* texture,
                                                            const GrTextureParams& params,
+#ifdef SK_GAMMA_APPLY_TO_A8
+                                                           GrTexture* gamma,
+                                                           const GrTextureParams& gammaParams,
+                                                           float luminance,
+#endif
                                                            bool similarity)
     : fTextureAccess(texture, params)
+#ifdef SK_GAMMA_APPLY_TO_A8
+    , fGammaTextureAccess(gamma, gammaParams)
+    , fLuminance(luminance)
+#endif
     , fIsSimilarity(similarity) {
     this->addTextureAccess(&fTextureAccess);
+#ifdef SK_GAMMA_APPLY_TO_A8
+    this->addTextureAccess(&fGammaTextureAccess);
+#endif
     this->addVertexAttrib(kVec2f_GrSLType);
 }
 
@@ -166,6 +215,10 @@
                                                      GrTexture* textures[]) {
     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
                                       GrEffectUnitTest::kAlphaTextureIdx;
+#ifdef SK_GAMMA_APPLY_TO_A8
+    int texIdx2 = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                       GrEffectUnitTest::kAlphaTextureIdx;
+#endif
     static const SkShader::TileMode kTileModes[] = {
         SkShader::kClamp_TileMode,
         SkShader::kRepeat_TileMode,
@@ -177,8 +230,16 @@
     };
     GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
                                                            GrTextureParams::kNone_FilterMode);
+#ifdef SK_GAMMA_APPLY_TO_A8
+    GrTextureParams params2(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
+                                                            GrTextureParams::kNone_FilterMode);
+#endif
 
     return GrDistanceFieldTextureEffect::Create(textures[texIdx], params,
+#ifdef SK_GAMMA_APPLY_TO_A8
+                                                textures[texIdx2], params2,
+                                                random->nextF(),
+#endif
                                                 random->nextBool());
 }
 
@@ -211,7 +272,7 @@
         fsCoordName = fsCoordNamePtr;
 
         const char* attrName0 =
-        builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0])->c_str();
+                   builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0])->c_str();
         builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attrName0);
 
         const char* textureSizeUniName = NULL;
@@ -237,22 +298,24 @@
         builder->fsAppendTextureLookup(samplers[0], "uv", kVec2f_GrSLType);
         builder->fsCodeAppend(";\n");
         builder->fsCodeAppend("\tvec3 distance;\n");
-        builder->fsCodeAppend("\tdistance.y = "
-                     SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n");
+        builder->fsCodeAppend("\tdistance.y = texColor.r;\n");
         // red is distance to left offset
         builder->fsCodeAppend("\tvec2 uv_adjusted = uv - offset;\n");
         builder->fsCodeAppend("\ttexColor = ");
         builder->fsAppendTextureLookup(samplers[0], "uv_adjusted", kVec2f_GrSLType);
         builder->fsCodeAppend(";\n");
-        builder->fsCodeAppend("\tdistance.x = "
-                     SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n");
+        builder->fsCodeAppend("\tdistance.x = texColor.r;\n");
         // blue is distance to right offset
         builder->fsCodeAppend("\tuv_adjusted = uv + offset;\n");
         builder->fsCodeAppend("\ttexColor = ");
         builder->fsAppendTextureLookup(samplers[0], "uv_adjusted", kVec2f_GrSLType);
         builder->fsCodeAppend(";\n");
-        builder->fsCodeAppend("\tdistance.z = "
-                     SK_DistanceFieldMultiplier "*(texColor.r - " SK_DistanceFieldThreshold ");\n");
+        builder->fsCodeAppend("\tdistance.z = texColor.r;\n");
+
+        builder->fsCodeAppend("\tdistance = "
+            "vec3(" SK_DistanceFieldMultiplier ")*(distance - vec3(" SK_DistanceFieldThreshold"))"
+            "+ vec3(" SK_DistanceFieldLCDFactor ");\n");
+
         // we adjust for the effect of the transformation on the distance by using
         // the length of the gradient of the texture coordinates. We use st coordinates
         // to ensure we're mapping 1:1 from texel space to pixel space.
@@ -264,8 +327,7 @@
         builder->fsCodeAppend("\tfloat afwidth;\n");
         if (dfTexEffect.isUniformScale()) {
             // this gives us a smooth step across approximately one fragment
-            // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
-            builder->fsCodeAppend("\tafwidth = 0.7071*dx;\n");
+            builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*dx;\n");
         } else {
             builder->fsCodeAppend("\tvec2 uv_grad;\n");
             if (builder->ctxInfo().caps()->dropsTileOnZeroDivide()) {
@@ -283,12 +345,36 @@
             builder->fsCodeAppend("\t                 uv_grad.x*Jdx.y + uv_grad.y*Jdy.y);\n");
 
             // this gives us a smooth step across approximately one fragment
-            // (assuming a radius of the diagonal of the fragment, hence a factor of sqrt(2)/2)
-            builder->fsCodeAppend("\tafwidth = 0.7071*length(grad);\n");
+            builder->fsCodeAppend("\tafwidth = " SK_DistanceFieldAAFactor "*length(grad);\n");
         }
 
         builder->fsCodeAppend("\tvec4 val = vec4(smoothstep(vec3(-afwidth), vec3(afwidth), distance), 1.0);\n");
 
+        // adjust based on gamma
+        const char* textColorUniName = NULL;
+        // width, height, 1/(3*width)
+        fTextColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
+                                            kVec3f_GrSLType, "TextColor",
+                                            &textColorUniName);
+
+        builder->fsCodeAppendf("\tuv = vec2(val.x, %s.x);\n", textColorUniName);
+        builder->fsCodeAppend("\tvec4 gammaColor = ");
+        builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType);
+        builder->fsCodeAppend(";\n");
+        builder->fsCodeAppend("\tval.x = gammaColor.r;\n");
+
+        builder->fsCodeAppendf("\tuv = vec2(val.y, %s.y);\n", textColorUniName);
+        builder->fsCodeAppend("\tgammaColor = ");
+        builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType);
+        builder->fsCodeAppend(";\n");
+        builder->fsCodeAppend("\tval.y = gammaColor.r;\n");
+
+        builder->fsCodeAppendf("\tuv = vec2(val.z, %s.z);\n", textColorUniName);
+        builder->fsCodeAppend("\tgammaColor = ");
+        builder->fsAppendTextureLookup(samplers[1], "uv", kVec2f_GrSLType);
+        builder->fsCodeAppend(";\n");
+        builder->fsCodeAppend("\tval.z = gammaColor.r;\n");
+
         builder->fsCodeAppendf("\t%s = %s;\n", outputColor,
                                (GrGLSLExpr4(inputColor) * GrGLSLExpr4("val")).c_str());
     }
@@ -296,12 +382,13 @@
     virtual void setData(const GrGLUniformManager& uman,
                          const GrDrawEffect& drawEffect) SK_OVERRIDE {
         SkASSERT(fTextureSizeUni.isValid());
+        SkASSERT(fTextColorUni.isValid());
 
+        const GrDistanceFieldLCDTextureEffect& dfTexEffect =
+                                    drawEffect.castEffect<GrDistanceFieldLCDTextureEffect>();
         GrTexture* texture = drawEffect.effect()->get()->texture(0);
         if (texture->width() != fTextureSize.width() ||
             texture->height() != fTextureSize.height()) {
-            const GrDistanceFieldLCDTextureEffect& dfTexEffect =
-                                           drawEffect.castEffect<GrDistanceFieldLCDTextureEffect>();
             fTextureSize = SkISize::Make(texture->width(), texture->height());
             float delta = 1.0f/(3.0f*texture->width());
             if (dfTexEffect.useBGR()) {
@@ -312,40 +399,55 @@
                        SkIntToScalar(fTextureSize.height()),
                        delta);
         }
+
+        GrColor textColor = dfTexEffect.getTextColor();
+        if (textColor != fTextColor) {
+            static const float ONE_OVER_255 = 1.f / 255.f;
+            uman.set3f(fTextColorUni,
+                       GrColorUnpackR(textColor) * ONE_OVER_255,
+                       GrColorUnpackG(textColor) * ONE_OVER_255,
+                       GrColorUnpackB(textColor) * ONE_OVER_255);
+            fTextColor = textColor;
+        }
     }
 
     static inline EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) {
         const GrDistanceFieldLCDTextureEffect& dfTexEffect =
                                            drawEffect.castEffect<GrDistanceFieldLCDTextureEffect>();
 
-        int uniformScale = dfTexEffect.isUniformScale() ? 0x01 : 0x00;
-        int useBGR = dfTexEffect.useBGR() ? 0x10 : 0x00;
-        return uniformScale | useBGR;
+        return dfTexEffect.isUniformScale() ? 0x01 : 0x00;;
     }
 
 private:
     GrGLUniformManager::UniformHandle fTextureSizeUni;
     SkISize                           fTextureSize;
+    GrGLUniformManager::UniformHandle fTextColorUni;
+    SkColor                           fTextColor;
 
     typedef GrGLVertexEffect INHERITED;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrDistanceFieldLCDTextureEffect::GrDistanceFieldLCDTextureEffect(GrTexture* texture,
-                                                                 const GrTextureParams& params,
-                                                                 bool uniformScale,
-                                                                 bool useBGR)
+GrDistanceFieldLCDTextureEffect::GrDistanceFieldLCDTextureEffect(
+                                                  GrTexture* texture, const GrTextureParams& params,
+                                                  GrTexture* gamma, const GrTextureParams& gParams,
+                                                  SkColor textColor,
+                                                  bool uniformScale, bool useBGR)
     : fTextureAccess(texture, params)
+    , fGammaTextureAccess(gamma, gParams)
+    , fTextColor(textColor)
     , fUniformScale(uniformScale)
     , fUseBGR(useBGR) {
     this->addTextureAccess(&fTextureAccess);
+    this->addTextureAccess(&fGammaTextureAccess);
     this->addVertexAttrib(kVec2f_GrSLType);
 }
 
 bool GrDistanceFieldLCDTextureEffect::onIsEqual(const GrEffect& other) const {
-    const GrDistanceFieldLCDTextureEffect& cte = CastEffect<GrDistanceFieldLCDTextureEffect>(other);
-    return fTextureAccess == cte.fTextureAccess;
+    const GrDistanceFieldLCDTextureEffect& cte = 
+                                            CastEffect<GrDistanceFieldLCDTextureEffect>(other);
+    return (fTextureAccess == cte.fTextureAccess && fGammaTextureAccess == cte.fGammaTextureAccess);
 }
 
 void GrDistanceFieldLCDTextureEffect::getConstantColorComponents(GrColor* color,
@@ -371,7 +473,9 @@
                                                          const GrDrawTargetCaps&,
                                                          GrTexture* textures[]) {
     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
-    GrEffectUnitTest::kAlphaTextureIdx;
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    int texIdx2 = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                       GrEffectUnitTest::kAlphaTextureIdx;
     static const SkShader::TileMode kTileModes[] = {
         SkShader::kClamp_TileMode,
         SkShader::kRepeat_TileMode,
@@ -383,7 +487,14 @@
     };
     GrTextureParams params(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
                            GrTextureParams::kNone_FilterMode);
-
+    GrTextureParams params2(tileModes, random->nextBool() ? GrTextureParams::kBilerp_FilterMode :
+                           GrTextureParams::kNone_FilterMode);
+    GrColor textColor = GrColorPackRGBA(random->nextULessThan(256),
+                                        random->nextULessThan(256),
+                                        random->nextULessThan(256),
+                                        random->nextULessThan(256));
     return GrDistanceFieldLCDTextureEffect::Create(textures[texIdx], params,
+                                                   textures[texIdx2], params2,
+                                                   textColor,
                                                    random->nextBool(), random->nextBool());
 }