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/GrDistanceFieldTextContext.cpp b/src/gpu/GrDistanceFieldTextContext.cpp
index 2d2f9a1..a729939 100755
--- a/src/gpu/GrDistanceFieldTextContext.cpp
+++ b/src/gpu/GrDistanceFieldTextContext.cpp
@@ -7,10 +7,12 @@
 
 #include "GrDistanceFieldTextContext.h"
 #include "GrAtlas.h"
+#include "SkColorFilter.h"
 #include "GrDrawTarget.h"
 #include "GrDrawTargetCaps.h"
 #include "GrFontScaler.h"
 #include "SkGlyphCache.h"
+#include "GrGpu.h"
 #include "GrIndexBuffer.h"
 #include "GrTextStrike.h"
 #include "GrTextStrike_impl.h"
@@ -43,6 +45,7 @@
     fEnableDFRendering = enable;
 #endif
     fStrike = NULL;
+    fGammaTexture = NULL;
 
     fCurrTexture = NULL;
     fCurrVertex = 0;
@@ -53,6 +56,7 @@
 
 GrDistanceFieldTextContext::~GrDistanceFieldTextContext() {
     this->flushGlyphs();
+    SkSafeSetNull(fGammaTexture);
 }
 
 bool GrDistanceFieldTextContext::canDraw(const SkPaint& paint) {
@@ -107,14 +111,26 @@
         SkASSERT(SkIsAlign4(fCurrVertex));
         SkASSERT(fCurrTexture);
         GrTextureParams params(SkShader::kRepeat_TileMode, GrTextureParams::kBilerp_FilterMode);
+        GrTextureParams gammaParams(SkShader::kClamp_TileMode, GrTextureParams::kNone_FilterMode);
 
         // Effects could be stored with one of the cache objects (atlas?)
+        SkColor filteredColor;
+        SkColorFilter* colorFilter = fSkPaint.getColorFilter();
+        if (NULL != colorFilter) {
+            filteredColor = colorFilter->filterColor(fSkPaint.getColor());
+        } else {
+            filteredColor = fSkPaint.getColor();
+        }
         if (fUseLCDText) {
+            GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
             bool useBGR = SkDeviceProperties::Geometry::kBGR_Layout ==
                                                             fDeviceProperties.fGeometry.getLayout();
             drawState->addCoverageEffect(GrDistanceFieldLCDTextureEffect::Create(
                                                             fCurrTexture,
                                                             params,
+                                                            fGammaTexture,
+                                                            gammaParams,
+                                                            colorNoPreMul,
                                                             fContext->getMatrix().rectStaysRect() &&
                                                             fContext->getMatrix().isSimilarity(),
                                                             useBGR),
@@ -133,13 +149,24 @@
             // paintAlpha
             drawState->setColor(SkColorSetARGB(a, a, a, a));
             // paintColor
-            drawState->setBlendConstant(skcolor_to_grcolor_nopremultiply(fSkPaint.getColor()));
+            drawState->setBlendConstant(colorNoPreMul);
             drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
         } else {
-            drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(fCurrTexture, params,
+#ifdef SK_GAMMA_APPLY_TO_A8
+            U8CPU lum = SkColorSpaceLuminance::computeLuminance(fDeviceProperties.fGamma,
+                                                                filteredColor);
+            drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(
+                                                              fCurrTexture, params,
+                                                              fGammaTexture, gammaParams,
+                                                              lum/255.f,
                                                               fContext->getMatrix().isSimilarity()),
                                          kGlyphCoordsAttributeIndex)->unref();
-
+#else
+            drawState->addCoverageEffect(GrDistanceFieldTextureEffect::Create(
+                                                              fCurrTexture, params,
+                                                              fContext->getMatrix().isSimilarity()),
+                                         kGlyphCoordsAttributeIndex)->unref();
+#endif
             // set back to normal in case we took LCD path previously.
             drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
             drawState->setColor(fPaint.getColor());
@@ -356,7 +383,9 @@
 
     fSkPaint.setLCDRenderText(false);
     fSkPaint.setAutohinted(false);
+    fSkPaint.setHinting(SkPaint::kNormal_Hinting);
     fSkPaint.setSubpixelText(true);
+
 }
 
 inline void GrDistanceFieldTextContext::finish() {
@@ -365,6 +394,46 @@
     GrTextContext::finish();
 }
 
+static void setup_gamma_texture(GrContext* context, const SkGlyphCache* cache,
+                                const SkDeviceProperties& deviceProperties,
+                                GrTexture** gammaTexture) {
+    if (NULL == *gammaTexture) {
+        int width, height;
+        size_t size;
+
+#ifdef SK_GAMMA_CONTRAST
+        SkScalar contrast = SK_GAMMA_CONTRAST;
+#else
+        SkScalar contrast = 0.5f;
+#endif
+        SkScalar paintGamma = deviceProperties.fGamma;
+        SkScalar deviceGamma = deviceProperties.fGamma;
+
+        size = SkScalerContext::GetGammaLUTSize(contrast, paintGamma, deviceGamma,
+                                                &width, &height);
+
+        SkAutoTArray<uint8_t> data((int)size);
+        SkScalerContext::GetGammaLUTData(contrast, paintGamma, deviceGamma, data.get());
+
+        // TODO: Update this to use the cache rather than directly creating a texture.
+        GrTextureDesc desc;
+        desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
+        desc.fWidth = width;
+        desc.fHeight = height;
+        desc.fConfig = kAlpha_8_GrPixelConfig;
+
+        *gammaTexture = context->getGpu()->createTexture(desc, NULL, 0);
+        if (NULL == *gammaTexture) {
+            return;
+        }
+
+        context->writeTexturePixels(*gammaTexture,
+                                    0, 0, width, height,
+                                    (*gammaTexture)->config(), data.get(), 0,
+                                    GrContext::kDontFlush_PixelOpsFlag);
+    }
+}
+
 void GrDistanceFieldTextContext::drawText(const GrPaint& paint, const SkPaint& skPaint,
                                           const char text[], size_t byteLength,
                                           SkScalar x, SkScalar y) {
@@ -382,9 +451,11 @@
 
     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
 
-    SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, NULL);
-    SkGlyphCache*       cache = autoCache.getCache();
-    GrFontScaler*       fontScaler = GetGrFontScaler(cache);
+    SkAutoGlyphCacheNoGamma    autoCache(fSkPaint, &fDeviceProperties, NULL);
+    SkGlyphCache*              cache = autoCache.getCache();
+    GrFontScaler*              fontScaler = GetGrFontScaler(cache);
+
+    setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
 
     // need to measure first
     // TODO - generate positions and pre-load cache as well?
@@ -455,9 +526,11 @@
 
     SkDrawCacheProc glyphCacheProc = fSkPaint.getDrawCacheProc();
 
-    SkAutoGlyphCache    autoCache(fSkPaint, &fDeviceProperties, NULL);
-    SkGlyphCache*       cache = autoCache.getCache();
-    GrFontScaler*       fontScaler = GetGrFontScaler(cache);
+    SkAutoGlyphCacheNoGamma    autoCache(fSkPaint, &fDeviceProperties, NULL);
+    SkGlyphCache*              cache = autoCache.getCache();
+    GrFontScaler*              fontScaler = GetGrFontScaler(cache);
+
+    setup_gamma_texture(fContext, cache, fDeviceProperties, &fGammaTexture);
 
     const char*        stop = text + byteLength;