Fix GPU text colors when color spaces are involved

1) Only store GrColors in GrTextUtils::Paint. We still store
   premul and unpremul versions, but this is slightly clearer.
2) GrTextUtils::Paint also needs info from the render target
   context to linearize and transform the color to dst space.

Bug: skia:6605
Change-Id: I6e12c55eafaecd2a090c82b4f56827401305bf3a
Reviewed-on: https://skia-review.googlesource.com/16486
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 5271d42..f902cce 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -20,12 +20,14 @@
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
 
-static inline GrColor skcolor_to_grcolor_nopremultiply(SkColor c) {
-    unsigned r = SkColorGetR(c);
-    unsigned g = SkColorGetG(c);
-    unsigned b = SkColorGetB(c);
-    return GrColorPackRGBA(r, g, b, 0xff);
+#ifdef SK_GAMMA_APPLY_TO_A8
+static inline SkColor unpremultiplied_grcolor_to_skcolor(GrColor c) {
+    unsigned r = GrColorUnpackR(c);
+    unsigned g = GrColorUnpackG(c);
+    unsigned b = GrColorUnpackB(c);
+    return SkColorSetRGB(r, g, b);
 }
+#endif
 
 static const int kDistanceAdjustLumShift = 5;
 
@@ -227,7 +229,7 @@
 // TODO trying to figure out why lcd is so whack
 sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor(GrResourceProvider* resourceProvider,
                                                            const SkMatrix& viewMatrix,
-                                                           SkColor filteredColor,
+                                                           GrColor filteredColor,
                                                            GrColor color,
                                                            sk_sp<GrTextureProxy> proxy) const {
     GrSamplerParams params(SkShader::kClamp_TileMode, GrSamplerParams::kBilerp_FilterMode);
@@ -242,16 +244,14 @@
         flags |= kUseLCD_DistanceFieldEffectFlag;
         flags |= fUseBGR ? kBGR_DistanceFieldEffectFlag : 0;
 
-        GrColor colorNoPreMul = skcolor_to_grcolor_nopremultiply(filteredColor);
-
         float redCorrection = fDistanceAdjustTable->getAdjustment(
-                GrColorUnpackR(colorNoPreMul) >> kDistanceAdjustLumShift,
+                GrColorUnpackR(filteredColor) >> kDistanceAdjustLumShift,
                 fUseGammaCorrectDistanceTable);
         float greenCorrection = fDistanceAdjustTable->getAdjustment(
-                GrColorUnpackG(colorNoPreMul) >> kDistanceAdjustLumShift,
+                GrColorUnpackG(filteredColor) >> kDistanceAdjustLumShift,
                 fUseGammaCorrectDistanceTable);
         float blueCorrection = fDistanceAdjustTable->getAdjustment(
-                GrColorUnpackB(colorNoPreMul) >> kDistanceAdjustLumShift,
+                GrColorUnpackB(filteredColor) >> kDistanceAdjustLumShift,
                 fUseGammaCorrectDistanceTable);
         GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
                 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
@@ -263,7 +263,9 @@
                                                    this->usesLocalCoords());
     } else {
 #ifdef SK_GAMMA_APPLY_TO_A8
-        U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredColor);
+        SkColor filteredSkColor = unpremultiplied_grcolor_to_skcolor(filteredColor);
+
+        U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, filteredSkColor);
         float correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
                                                                fUseGammaCorrectDistanceTable);
         return GrDistanceFieldA8TextGeoProc::Make(resourceProvider, color,
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index 8b65fb9..ac59d4c 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -64,7 +64,7 @@
     static std::unique_ptr<GrAtlasTextOp> MakeDistanceField(
             int glyphCount, GrAtlasGlyphCache* fontCache,
             const GrDistanceFieldAdjustTable* distanceAdjustTable,
-            bool useGammaCorrectDistanceTable, SkColor filteredColor, bool isLCD, bool useBGR) {
+            bool useGammaCorrectDistanceTable, GrColor filteredColor, bool isLCD, bool useBGR) {
         std::unique_ptr<GrAtlasTextOp> op(new GrAtlasTextOp);
 
         op->fFontCache = fontCache;
@@ -150,7 +150,7 @@
     // TODO just use class params
     // TODO trying to figure out why lcd is so whack
     sk_sp<GrGeometryProcessor> setupDfProcessor(GrResourceProvider*,
-                                                const SkMatrix& viewMatrix, SkColor filteredColor,
+                                                const SkMatrix& viewMatrix, GrColor filteredColor,
                                                 GrColor color, sk_sp<GrTextureProxy> proxy) const;
 
     GrColor fColor;
@@ -175,7 +175,7 @@
 
     // Distance field properties
     sk_sp<const GrDistanceFieldAdjustTable> fDistanceAdjustTable;
-    SkColor fFilteredColor;
+    GrColor fFilteredColor;
     bool fUseGammaCorrectDistanceTable;
 
     friend class GrBlobRegenHelper;  // Needs to trigger flushes
diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp
index d3aa7e4..be9bb27 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -179,7 +179,7 @@
     // to regenerate the blob on any color change
     // We use the grPaint to get any color filter effects
     if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
-        fFilteredPaintColor != paint.filteredSkColor()) {
+        fFilteredPaintColor != paint.filteredUnpremulColor()) {
         return true;
     }
 
@@ -266,7 +266,7 @@
 
     std::unique_ptr<GrAtlasTextOp> op;
     if (info.drawAsDistanceFields()) {
-        SkColor filteredColor = paint.filteredSkColor();
+        GrColor filteredColor = paint.filteredUnpremulColor();
         bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
         op = GrAtlasTextOp::MakeDistanceField(glyphCount, cache, distanceAdjustTable,
                                               useGammaCorrectDistanceTable, filteredColor,
@@ -280,7 +280,7 @@
     geometry.fRun = run;
     geometry.fSubRun = subRun;
     geometry.fColor =
-            info.maskFormat() == kARGB_GrMaskFormat ? GrColor_WHITE : paint.filteredPremulGrColor();
+            info.maskFormat() == kARGB_GrMaskFormat ? GrColor_WHITE : paint.filteredPremulColor();
     geometry.fX = x;
     geometry.fY = y;
     op->init();
diff --git a/src/gpu/text/GrAtlasTextBlob.h b/src/gpu/text/GrAtlasTextBlob.h
index 855413b..4cc1028 100644
--- a/src/gpu/text/GrAtlasTextBlob.h
+++ b/src/gpu/text/GrAtlasTextBlob.h
@@ -240,7 +240,7 @@
     // The color here is the GrPaint color, and it is used to determine whether we
     // have to regenerate LCD text blobs.
     // We use this color vs the SkPaint color because it has the colorfilter applied.
-    void initReusableBlob(SkColor filteredColor, const SkMatrix& viewMatrix, SkScalar x,
+    void initReusableBlob(GrColor filteredColor, const SkMatrix& viewMatrix, SkScalar x,
                           SkScalar y) {
         fFilteredPaintColor = filteredColor;
         this->setupViewMatrix(viewMatrix, x, y);
@@ -530,7 +530,7 @@
     SkMatrix fInitialViewMatrix;
     SkMatrix fInitialViewMatrixInverse;
     size_t fSize;
-    SkColor fFilteredPaintColor;
+    GrColor fFilteredPaintColor;
     SkScalar fInitialX;
     SkScalar fInitialY;
 
diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp
index d99f282..abe8d24 100644
--- a/src/gpu/text/GrAtlasTextContext.cpp
+++ b/src/gpu/text/GrAtlasTextContext.cpp
@@ -118,7 +118,7 @@
         cacheBlob = cache->find(key);
     }
 
-    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
     if (cacheBlob) {
         if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, x, y)) {
             // We have to remake the blob because changes may invalidate our masks.
@@ -166,7 +166,7 @@
                                             uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
                                             const SkSurfaceProps& props, const SkTextBlob* blob,
                                             SkScalar x, SkScalar y, SkDrawFilter* drawFilter) {
-    cacheBlob->initReusableBlob(paint.filteredSkColor(), viewMatrix, x, y);
+    cacheBlob->initReusableBlob(paint.filteredUnpremulColor(), viewMatrix, x, y);
 
     // Regenerate textblob
     SkTextBlobRunIterator it(blob);
@@ -289,7 +289,7 @@
     if (context->abandoned()) {
         return;
     }
-    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
     if (this->canDraw(skPaint, viewMatrix, props, *context->caps()->shaderCaps())) {
         sk_sp<GrAtlasTextBlob> blob(
             MakeDrawTextBlob(context->getTextBlobCache(), context->getAtlasGlyphCache(),
@@ -313,7 +313,7 @@
                                      const char text[], size_t byteLength, const SkScalar pos[],
                                      int scalarsPerPosition, const SkPoint& offset,
                                      const SkIRect& regionClipBounds) {
-    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
     if (context->abandoned()) {
         return;
     } else if (this->canDraw(skPaint, viewMatrix, props, *context->caps()->shaderCaps())) {
@@ -352,7 +352,7 @@
     }
 
     // Setup dummy SkPaint / GrPaint / GrRenderTargetContext
-    sk_sp<GrRenderTargetContext> renderTargetContext(context->makeDeferredRenderTargetContext(
+    sk_sp<GrRenderTargetContext> rtc(context->makeDeferredRenderTargetContext(
         SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
 
     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
@@ -374,7 +374,7 @@
     SkScalar x = SkIntToScalar(xInt);
     SkScalar y = SkIntToScalar(yInt);
 
-    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
     // right now we don't handle textblobs, nor do we handle drawPosText. Since we only intend to
     // test the text op with this unit test, that is okay.
     sk_sp<GrAtlasTextBlob> blob(GrAtlasTextContext::MakeDrawTextBlob(
diff --git a/src/gpu/text/GrStencilAndCoverTextContext.cpp b/src/gpu/text/GrStencilAndCoverTextContext.cpp
index 0ae4023..65889b2 100644
--- a/src/gpu/text/GrStencilAndCoverTextContext.cpp
+++ b/src/gpu/text/GrStencilAndCoverTextContext.cpp
@@ -134,7 +134,7 @@
                                                         SkScalar x, SkScalar y,
                                                         SkDrawFilter* drawFilter,
                                                         const SkIRect& clipBounds) {
-    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::Paint paint(&skPaint, rtc->getColorSpace(), rtc->getColorXformFromSRGB());
     GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
     SkTextBlobRunIterator it(blob);
     for (;!it.done(); it.next()) {
diff --git a/src/gpu/text/GrTextUtils.cpp b/src/gpu/text/GrTextUtils.cpp
index 31d0291..9baf341 100644
--- a/src/gpu/text/GrTextUtils.cpp
+++ b/src/gpu/text/GrTextUtils.cpp
@@ -50,6 +50,27 @@
     }
 }
 
+void GrTextUtils::Paint::initFilteredColor() {
+    // This mirrors the logic in skpaint_to_grpaint_impl for handling paint colors
+    if (fDstColorSpace) {
+        GrColor4f filteredColor = SkColorToUnpremulGrColor4f(fPaint->getColor(), fDstColorSpace,
+                                                             fColorXformFromSRGB);
+        if (fPaint->getColorFilter()) {
+            filteredColor = GrColor4f::FromSkColor4f(
+                fPaint->getColorFilter()->filterColor4f(filteredColor.toSkColor4f()));
+        }
+        fFilteredPremulColor = filteredColor.premul().toGrColor();
+        fFilteredUnpremulColor = filteredColor.toGrColor();
+    } else {
+        SkColor filteredSkColor = fPaint->getColor();
+        if (fPaint->getColorFilter()) {
+            filteredSkColor = fPaint->getColorFilter()->filterColor(filteredSkColor);
+        }
+        fFilteredPremulColor = SkColorToPremulGrColor(filteredSkColor);
+        fFilteredUnpremulColor = SkColorToUnpremulGrColor(filteredSkColor);
+    }
+}
+
 bool GrTextUtils::RunPaint::modifyForRun(const SkTextBlobRunIterator& run) {
     if (!fModifiedPaint.isValid()) {
         fModifiedPaint.init(fOriginalPaint->skPaint());
@@ -99,7 +120,7 @@
              BmpAppendGlyph(
                  blob, runIndex, fontCache, &currStrike, glyph,
                  SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
-                 paint.filteredPremulGrColor(), cache);
+                 paint.filteredPremulColor(), cache);
         }
     );
 
@@ -135,7 +156,7 @@
             BmpAppendGlyph(
                 blob, runIndex, fontCache, &currStrike, glyph,
                 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
-                paint.filteredPremulGrColor(), cache);
+                paint.filteredPremulColor(), cache);
         }
     );
 
@@ -390,7 +411,7 @@
                 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
 
                 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y,
-                                   paint.filteredPremulGrColor(), cache, textRatio, viewMatrix)) {
+                                   paint.filteredPremulColor(), cache, textRatio, viewMatrix)) {
                     // couldn't append, send to fallback
                     fallbackTxt.append(SkToInt(text-lastText), lastText);
                     *fallbackPos.append() = pos[0];
@@ -417,7 +438,7 @@
                 SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
 
                 if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
-                                   y - advanceY, paint.filteredPremulGrColor(), cache, textRatio,
+                                   y - advanceY, paint.filteredPremulColor(), cache, textRatio,
                                    viewMatrix)) {
                     // couldn't append, send to fallback
                     fallbackTxt.append(SkToInt(text-lastText), lastText);
diff --git a/src/gpu/text/GrTextUtils.h b/src/gpu/text/GrTextUtils.h
index c5daa42..9f38699 100644
--- a/src/gpu/text/GrTextUtils.h
+++ b/src/gpu/text/GrTextUtils.h
@@ -19,10 +19,12 @@
 class GrAtlasTextBlob;
 class GrAtlasTextStrike;
 class GrClip;
+class GrColorSpaceXform;
 class GrContext;
 class GrPaint;
 class GrRenderTargetContext;
 class GrShaderCaps;
+class SkColorSpace;
 class SkDrawFilter;
 class SkGlyph;
 class SkMatrix;
@@ -45,12 +47,19 @@
      */
     class Paint {
     public:
-        explicit Paint(const SkPaint* paint) : fPaint(paint) { this->initFilteredColor(); }
+        explicit Paint(const SkPaint* paint,
+                       SkColorSpace* dstColorSpace,
+                       GrColorSpaceXform* colorXformFromSRGB)
+                : fPaint(paint)
+                , fDstColorSpace(dstColorSpace)
+                , fColorXformFromSRGB(colorXformFromSRGB) {
+            this->initFilteredColor();
+        }
 
         // These expose the paint's color run through its color filter (if any). This is only valid
         // when drawing grayscale/lcd glyph masks and not when drawing color glyphs.
-        SkColor filteredSkColor() const { return fFilteredSkColor; }
-        GrColor filteredPremulGrColor() const { return fFilteredGrColor; }
+        GrColor filteredPremulColor() const { return fFilteredPremulColor; }
+        GrColor filteredUnpremulColor() const { return fFilteredUnpremulColor; }
 
         const SkPaint& skPaint() const { return *fPaint; }
         operator const SkPaint&() const { return this->skPaint(); }
@@ -58,21 +67,21 @@
         bool toGrPaint(GrMaskFormat, GrRenderTargetContext*, const SkMatrix& viewMatrix,
                        GrPaint*) const;
 
+        // Just for RunPaint's constructor
+        SkColorSpace* dstColorSpace() const { return fDstColorSpace; }
+        GrColorSpaceXform* colorXformFromSRGB() const { return fColorXformFromSRGB; }
+
     protected:
-        void initFilteredColor() {
-            fFilteredSkColor = fPaint->getColor();
-            if (fPaint->getColorFilter()) {
-                fFilteredSkColor = fPaint->getColorFilter()->filterColor(fFilteredSkColor);
-            }
-            fFilteredGrColor = SkColorToPremulGrColor(fFilteredSkColor);
-        }
+        void initFilteredColor();
         Paint() = default;
         const SkPaint* fPaint;
+        SkColorSpace* fDstColorSpace;
+        GrColorSpaceXform* fColorXformFromSRGB;
         // This is the paint's color run through its color filter, if present. This color should
         // be used except when rendering bitmap text, in which case the bitmap must be filtered in
         // the fragment shader.
-        SkColor fFilteredSkColor;
-        SkColor fFilteredGrColor;
+        GrColor fFilteredUnpremulColor;
+        GrColor fFilteredPremulColor;
     };
 
     /**
@@ -86,8 +95,10 @@
                 : fOriginalPaint(paint), fFilter(filter), fProps(props) {
             // Initially we represent the original paint.
             fPaint = &fOriginalPaint->skPaint();
-            fFilteredSkColor = fOriginalPaint->filteredSkColor();
-            fFilteredGrColor = fOriginalPaint->filteredPremulGrColor();
+            fDstColorSpace = fOriginalPaint->dstColorSpace();
+            fColorXformFromSRGB = fOriginalPaint->colorXformFromSRGB();
+            fFilteredPremulColor = fOriginalPaint->filteredPremulColor();
+            fFilteredUnpremulColor = fOriginalPaint->filteredUnpremulColor();
         }
 
         bool modifyForRun(const SkTextBlobRunIterator&);