Delay SkPaint->GrPaint conversion in text rendering.

This fixes an issue where color filters aren't correctly applied to color glyphs. Instead we apply the filter to the SkPaint's color which is correct for mask glyphs only.

Add color filter and alpha + various effects to coloremoji gm

Change-Id: If77dece71d43468fec65499857eaaaedb56428e9
Reviewed-on: https://skia-review.googlesource.com/6891
Commit-Queue: Brian Salomon <bsalomon@google.com>
Reviewed-by: Jim Van Verth <jvanverth@google.com>
diff --git a/gm/coloremoji.cpp b/gm/coloremoji.cpp
index c1ebef6..c139bc9f 100644
--- a/gm/coloremoji.cpp
+++ b/gm/coloremoji.cpp
@@ -9,9 +9,9 @@
 
 #include "Resources.h"
 #include "SkBlurImageFilter.h"
+#include "SkCanvas.h"
 #include "SkColorFilterImageFilter.h"
 #include "SkColorMatrixFilter.h"
-#include "SkCanvas.h"
 #include "SkGradientShader.h"
 #include "SkStream.h"
 #include "SkTypeface.h"
@@ -42,6 +42,11 @@
     return SkBlurImageFilter::Make(amount, amount, std::move(input));
 }
 
+static sk_sp<SkColorFilter> make_color_filter() {
+    return SkColorMatrixFilter::MakeLightingFilter(SkColorSetRGB(0x00, 0x80, 0xFF),
+                                                   SkColorSetRGB(0xFF, 0x20, 0x00));
+}
+
 namespace skiagm {
 
 class ColorEmojiGM : public GM {
@@ -64,9 +69,7 @@
         return name;
     }
 
-    SkISize onISize() override {
-        return SkISize::Make(650, 900);
-    }
+    SkISize onISize() override { return SkISize::Make(650, 1200); }
 
     void onDraw(SkCanvas* canvas) override {
 
@@ -94,26 +97,36 @@
         for (int makeLinear = 0; makeLinear < 2; makeLinear++) {
             for (int makeBlur = 0; makeBlur < 2; makeBlur++) {
                 for (int makeGray = 0; makeGray < 2; makeGray++) {
-                    SkPaint shaderPaint;
-                    shaderPaint.setTypeface(paint.refTypeface());
-                    if (SkToBool(makeLinear)) {
-                        shaderPaint.setShader(MakeLinear());
-                    }
+                    for (int makeMode = 0; makeMode < 2; ++makeMode) {
+                        for (int alpha = 0; alpha < 2; ++alpha) {
+                            SkPaint shaderPaint;
+                            shaderPaint.setTypeface(sk_ref_sp(paint.getTypeface()));
+                            if (SkToBool(makeLinear)) {
+                                shaderPaint.setShader(MakeLinear());
+                            }
 
-                    if (SkToBool(makeBlur) && SkToBool(makeGray)) {
-                        sk_sp<SkImageFilter> grayScale(make_grayscale(nullptr));
-                        sk_sp<SkImageFilter> blur(make_blur(3.0f, std::move(grayScale)));
-                        shaderPaint.setImageFilter(std::move(blur));
-                    } else if (SkToBool(makeBlur)) {
-                        shaderPaint.setImageFilter(make_blur(3.0f, nullptr));
-                    } else if (SkToBool(makeGray)) {
-                        shaderPaint.setImageFilter(make_grayscale(nullptr));
+                            if (SkToBool(makeBlur) && SkToBool(makeGray)) {
+                                sk_sp<SkImageFilter> grayScale(make_grayscale(nullptr));
+                                sk_sp<SkImageFilter> blur(make_blur(3.0f, std::move(grayScale)));
+                                shaderPaint.setImageFilter(std::move(blur));
+                            } else if (SkToBool(makeBlur)) {
+                                shaderPaint.setImageFilter(make_blur(3.0f, nullptr));
+                            } else if (SkToBool(makeGray)) {
+                                shaderPaint.setImageFilter(make_grayscale(nullptr));
+                            }
+                            if (makeMode) {
+                                shaderPaint.setColorFilter(make_color_filter());
+                            }
+                            if (alpha) {
+                                shaderPaint.setAlpha(0x80);
+                            }
+                            shaderPaint.setTextSize(30);
+                            shaderPaint.getFontMetrics(&metrics);
+                            y += -metrics.fAscent;
+                            canvas->drawText(text, strlen(text), 380, y, shaderPaint);
+                            y += metrics.fDescent + metrics.fLeading;
+                        }
                     }
-                    shaderPaint.setTextSize(30);
-                    shaderPaint.getFontMetrics(&metrics);
-                    y += -metrics.fAscent;
-                    canvas->drawText(text, strlen(text), 380, y, shaderPaint);
-                    y += metrics.fDescent + metrics.fLeading;
                 }
             }
         }
diff --git a/include/gpu/GrRenderTargetContext.h b/include/gpu/GrRenderTargetContext.h
index 0f6662d..3636d5c 100644
--- a/include/gpu/GrRenderTargetContext.h
+++ b/include/gpu/GrRenderTargetContext.h
@@ -50,13 +50,15 @@
 public:
     ~GrRenderTargetContext() override;
 
-    // TODO: it is odd that we need both the SkPaint in the following 3 methods.
-    // We should extract the text parameters from SkPaint and pass them separately
-    // akin to GrStyle (GrTextInfo?)
-    virtual void drawText(const GrClip&, GrPaint&&, const SkPaint&, const SkMatrix& viewMatrix,
+    // We use SkPaint rather than GrPaint here for two reasons:
+    //    * The SkPaint carries extra text settings. If these were extracted to a lighter object
+    //      we could use GrPaint except that
+    //    * SkPaint->GrPaint conversion depends upon whether the glyphs are color or grayscale and
+    //      this can vary within a text run.
+    virtual void drawText(const GrClip&, const SkPaint&, const SkMatrix& viewMatrix,
                           const char text[], size_t byteLength, SkScalar x, SkScalar y,
                           const SkIRect& clipBounds);
-    virtual void drawPosText(const GrClip&, GrPaint&&, const SkPaint&, const SkMatrix& viewMatrix,
+    virtual void drawPosText(const GrClip&, const SkPaint&, const SkMatrix& viewMatrix,
                              const char text[], size_t byteLength, const SkScalar pos[],
                              int scalarsPerPosition, const SkPoint& offset,
                              const SkIRect& clipBounds);
diff --git a/src/gpu/GrPathRenderingRenderTargetContext.cpp b/src/gpu/GrPathRenderingRenderTargetContext.cpp
index 3319a05..90966a8 100644
--- a/src/gpu/GrPathRenderingRenderTargetContext.cpp
+++ b/src/gpu/GrPathRenderingRenderTargetContext.cpp
@@ -15,8 +15,7 @@
     SkDEBUGCODE(GrSingleOwner::AutoEnforce debug_SingleOwner(this->singleOwner());)
 #define RETURN_IF_ABANDONED        if (this->drawingManager()->wasAbandoned()) { return; }
 
-void GrPathRenderingRenderTargetContext::drawText(const GrClip& clip, GrPaint&& grPaint,
-                                                  const SkPaint& skPaint,
+void GrPathRenderingRenderTargetContext::drawText(const GrClip& clip, const SkPaint& skPaint,
                                                   const SkMatrix& viewMatrix, const char text[],
                                                   size_t byteLength, SkScalar x, SkScalar y,
                                                   const SkIRect& clipBounds) {
@@ -30,13 +29,12 @@
         fStencilAndCoverTextContext.reset(GrStencilAndCoverTextContext::Create(fallbackContext));
     }
 
-    fStencilAndCoverTextContext->drawText(this->drawingManager()->getContext(), this, clip,
-                                          std::move(grPaint), skPaint, viewMatrix,
-                                          this->surfaceProps(), text, byteLength, x, y, clipBounds);
+    fStencilAndCoverTextContext->drawText(this->drawingManager()->getContext(), this, clip, skPaint,
+                                          viewMatrix, this->surfaceProps(), text, byteLength, x, y,
+                                          clipBounds);
 }
 
-void GrPathRenderingRenderTargetContext::drawPosText(const GrClip& clip, GrPaint&& grPaint,
-                                                     const SkPaint& skPaint,
+void GrPathRenderingRenderTargetContext::drawPosText(const GrClip& clip, const SkPaint& skPaint,
                                                      const SkMatrix& viewMatrix, const char text[],
                                                      size_t byteLength, const SkScalar pos[],
                                                      int scalarsPerPosition, const SkPoint& offset,
@@ -52,10 +50,9 @@
         fStencilAndCoverTextContext.reset(GrStencilAndCoverTextContext::Create(fallbackContext));
     }
 
-    fStencilAndCoverTextContext->drawPosText(this->drawingManager()->getContext(), this, clip,
-                                             std::move(grPaint), skPaint, viewMatrix,
-                                             this->surfaceProps(), text, byteLength, pos,
-                                             scalarsPerPosition, offset, clipBounds);
+    fStencilAndCoverTextContext->drawPosText(
+            this->drawingManager()->getContext(), this, clip, skPaint, viewMatrix,
+            this->surfaceProps(), text, byteLength, pos, scalarsPerPosition, offset, clipBounds);
 }
 
 void GrPathRenderingRenderTargetContext::drawTextBlob(const GrClip& clip, const SkPaint& skPaint,
diff --git a/src/gpu/GrPathRenderingRenderTargetContext.h b/src/gpu/GrPathRenderingRenderTargetContext.h
index 0597539..d26f08e 100644
--- a/src/gpu/GrPathRenderingRenderTargetContext.h
+++ b/src/gpu/GrPathRenderingRenderTargetContext.h
@@ -14,13 +14,11 @@
 
 class GrPathRenderingRenderTargetContext : public GrRenderTargetContext {
 public:
-    void drawText(const GrClip&, GrPaint&&, const SkPaint&, const SkMatrix& viewMatrix,
-                  const char text[], size_t byteLength, SkScalar x, SkScalar y,
-                  const SkIRect& clipBounds) override;
-    void drawPosText(const GrClip&, GrPaint&&, const SkPaint&, const SkMatrix& viewMatrix,
-                     const char text[], size_t byteLength, const SkScalar pos[],
-                     int scalarsPerPosition, const SkPoint& offset,
-                     const SkIRect& clipBounds) override;
+    void drawText(const GrClip&, const SkPaint&, const SkMatrix& viewMatrix, const char text[],
+                  size_t byteLength, SkScalar x, SkScalar y, const SkIRect& clipBounds) override;
+    void drawPosText(const GrClip&, const SkPaint&, const SkMatrix& viewMatrix, const char text[],
+                     size_t byteLength, const SkScalar pos[], int scalarsPerPosition,
+                     const SkPoint& offset, const SkIRect& clipBounds) override;
     void drawTextBlob(const GrClip&, const SkPaint&,
                       const SkMatrix& viewMatrix, const SkTextBlob*,
                       SkScalar x, SkScalar y,
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 968663c..b0d941b 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -158,7 +158,7 @@
     return this->getOpList()->copySurface(rt.get(), src.get(), srcRect, dstPoint);
 }
 
-void GrRenderTargetContext::drawText(const GrClip& clip, GrPaint&& grPaint, const SkPaint& skPaint,
+void GrRenderTargetContext::drawText(const GrClip& clip, const SkPaint& skPaint,
                                      const SkMatrix& viewMatrix, const char text[],
                                      size_t byteLength, SkScalar x, SkScalar y,
                                      const SkIRect& clipBounds) {
@@ -168,13 +168,13 @@
     GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::drawText");
 
     GrAtlasTextContext* atlasTextContext = fDrawingManager->getAtlasTextContext();
-    atlasTextContext->drawText(fContext, this, clip, std::move(grPaint), skPaint, viewMatrix,
-                               fSurfaceProps, text, byteLength, x, y, clipBounds);
+    atlasTextContext->drawText(fContext, this, clip, skPaint, viewMatrix, fSurfaceProps, text,
+                               byteLength, x, y, clipBounds);
 }
 
-void GrRenderTargetContext::drawPosText(const GrClip& clip, GrPaint&& grPaint,
-                                        const SkPaint& skPaint, const SkMatrix& viewMatrix,
-                                        const char text[], size_t byteLength, const SkScalar pos[],
+void GrRenderTargetContext::drawPosText(const GrClip& clip, const SkPaint& paint,
+                                        const SkMatrix& viewMatrix, const char text[],
+                                        size_t byteLength, const SkScalar pos[],
                                         int scalarsPerPosition, const SkPoint& offset,
                                         const SkIRect& clipBounds) {
     ASSERT_SINGLE_OWNER
@@ -183,23 +183,22 @@
     GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::drawPosText");
 
     GrAtlasTextContext* atlasTextContext = fDrawingManager->getAtlasTextContext();
-    atlasTextContext->drawPosText(fContext, this, clip, std::move(grPaint), skPaint, viewMatrix,
-                                  fSurfaceProps, text, byteLength, pos, scalarsPerPosition, offset,
-                                  clipBounds);
+    atlasTextContext->drawPosText(fContext, this, clip, paint, viewMatrix, fSurfaceProps, text,
+                                  byteLength, pos, scalarsPerPosition, offset, clipBounds);
 }
 
-void GrRenderTargetContext::drawTextBlob(const GrClip& clip, const SkPaint& skPaint,
+void GrRenderTargetContext::drawTextBlob(const GrClip& clip, const SkPaint& paint,
                                          const SkMatrix& viewMatrix, const SkTextBlob* blob,
-                                         SkScalar x, SkScalar y,
-                                         SkDrawFilter* filter, const SkIRect& clipBounds) {
+                                         SkScalar x, SkScalar y, SkDrawFilter* filter,
+                                         const SkIRect& clipBounds) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
     GR_AUDIT_TRAIL_AUTO_FRAME(fAuditTrail, "GrRenderTargetContext::drawTextBlob");
 
     GrAtlasTextContext* atlasTextContext = fDrawingManager->getAtlasTextContext();
-    atlasTextContext->drawTextBlob(fContext, this, clip, skPaint, viewMatrix, fSurfaceProps, blob,
-                                   x, y, filter, clipBounds);
+    atlasTextContext->drawTextBlob(fContext, this, clip, paint, viewMatrix, fSurfaceProps, blob, x,
+                                   y, filter, clipBounds);
 }
 
 void GrRenderTargetContext::discard() {
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index bf9341f..258713c 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1727,16 +1727,9 @@
     ASSERT_SINGLE_OWNER
     CHECK_SHOULD_DRAW(draw);
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawText", fContext.get());
-
-    GrPaint grPaint;
-    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext.get(), paint, *draw.fMatrix,
-                          &grPaint)) {
-        return;
-    }
-
     SkDEBUGCODE(this->validate();)
 
-    fRenderTargetContext->drawText(fClip, std::move(grPaint), paint, *draw.fMatrix,
+    fRenderTargetContext->drawText(fClip, paint, *draw.fMatrix,
                                    (const char*)text, byteLength, x, y, draw.fRC->getBounds());
 }
 
@@ -1746,16 +1739,9 @@
     ASSERT_SINGLE_OWNER
     GR_CREATE_TRACE_MARKER_CONTEXT("SkGpuDevice", "drawPosText", fContext.get());
     CHECK_SHOULD_DRAW(draw);
-
-    GrPaint grPaint;
-    if (!SkPaintToGrPaint(this->context(), fRenderTargetContext.get(), paint, *draw.fMatrix,
-                          &grPaint)) {
-        return;
-    }
-
     SkDEBUGCODE(this->validate();)
 
-    fRenderTargetContext->drawPosText(fClip, std::move(grPaint), paint, *draw.fMatrix,
+    fRenderTargetContext->drawPosText(fClip, paint, *draw.fMatrix,
                                       (const char*)text, byteLength, pos, scalarsPerPos, offset,
                                       draw.fRC->getBounds());
 }
diff --git a/src/gpu/SkGrPriv.h b/src/gpu/SkGrPriv.h
index c64646b..a4e3976 100644
--- a/src/gpu/SkGrPriv.h
+++ b/src/gpu/SkGrPriv.h
@@ -21,9 +21,11 @@
 class GrPaint;
 class GrTexture;
 class GrUniqueKey;
+class SkBitmap;
 class SkData;
 class SkPaint;
 class SkPixelRef;
+class SkPixmap;
 struct SkIRect;
 
 /**
diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp
index 5e3b4bf..321c4a6 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -6,7 +6,6 @@
  */
 
 #include "GrAtlasTextBlob.h"
-
 #include "GrBlurUtils.h"
 #include "GrContext.h"
 #include "GrPipelineBuilder.h"
@@ -169,14 +168,14 @@
     fBigGlyphs.push_back(GrAtlasTextBlob::BigGlyph(*glyph->fPath, x, y, scale, treatAsBMP));
 }
 
-bool GrAtlasTextBlob::mustRegenerate(const SkPaint& paint,
-                                     GrColor color, const SkMaskFilter::BlurRec& blurRec,
+bool GrAtlasTextBlob::mustRegenerate(const GrTextUtils::Paint& paint,
+                                     const SkMaskFilter::BlurRec& blurRec,
                                      const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
     // If we have LCD text then our canonical color will be set to transparent, in this case we have
     // to regenerate the blob on any color change
     // We use the grPaint to get any color filter effects
     if (fKey.fCanonicalColor == SK_ColorTRANSPARENT &&
-        fPaintColor != color) {
+        fFilteredPaintColor != paint.filteredSkColor()) {
         return true;
     }
 
@@ -198,9 +197,9 @@
 
     // Similarly, we only cache one version for each style
     if (fKey.fStyle != SkPaint::kFill_Style &&
-        (fStrokeInfo.fFrameWidth != paint.getStrokeWidth() ||
-         fStrokeInfo.fMiterLimit != paint.getStrokeMiter() ||
-         fStrokeInfo.fJoin != paint.getStrokeJoin())) {
+        (fStrokeInfo.fFrameWidth != paint.skPaint().getStrokeWidth() ||
+         fStrokeInfo.fMiterLimit != paint.skPaint().getStrokeMiter() ||
+         fStrokeInfo.fJoin != paint.skPaint().getStrokeJoin())) {
         return true;
     }
 
@@ -256,27 +255,14 @@
 
 inline std::unique_ptr<GrDrawOp> GrAtlasTextBlob::makeOp(
         const Run::SubRunInfo& info, int glyphCount, int run, int subRun,
-        const SkMatrix& viewMatrix, SkScalar x, SkScalar y, GrColor color, const SkPaint& skPaint,
+        const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const GrTextUtils::Paint& paint,
         const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
         bool useGammaCorrectDistanceTable, GrAtlasGlyphCache* cache) {
     GrMaskFormat format = info.maskFormat();
-    GrColor subRunColor;
-    if (kARGB_GrMaskFormat == format) {
-        uint8_t paintAlpha = skPaint.getAlpha();
-        subRunColor = SkColorSetARGB(paintAlpha, paintAlpha, paintAlpha, paintAlpha);
-    } else {
-        subRunColor = color;
-    }
 
     std::unique_ptr<GrAtlasTextOp> op;
     if (info.drawAsDistanceFields()) {
-        SkColor filteredColor;
-        SkColorFilter* colorFilter = skPaint.getColorFilter();
-        if (colorFilter) {
-            filteredColor = colorFilter->filterColor(skPaint.getColor());
-        } else {
-            filteredColor = skPaint.getColor();
-        }
+        SkColor filteredColor = paint.filteredSkColor();
         bool useBGR = SkPixelGeometryIsBGR(props.pixelGeometry());
         op = GrAtlasTextOp::MakeDistanceField(glyphCount, cache, distanceAdjustTable,
                                               useGammaCorrectDistanceTable, filteredColor,
@@ -289,7 +275,8 @@
     geometry.fBlob = SkRef(this);
     geometry.fRun = run;
     geometry.fSubRun = subRun;
-    geometry.fColor = subRunColor;
+    geometry.fColor =
+            info.maskFormat() == kARGB_GrMaskFormat ? GrColor_WHITE : paint.filteredPremulGrColor();
     geometry.fX = x;
     geometry.fY = y;
     op->init();
@@ -297,27 +284,27 @@
     return std::move(op);
 }
 
-inline void GrAtlasTextBlob::flushRun(GrRenderTargetContext* rtc, GrPaint&& grPaint,
-                                      const GrClip& clip, int run, const SkMatrix& viewMatrix,
-                                      SkScalar x, SkScalar y, const SkPaint& skPaint,
-                                      const SkSurfaceProps& props,
+inline void GrAtlasTextBlob::flushRun(GrRenderTargetContext* rtc, const GrClip& clip, int run,
+                                      const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
+                                      const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
                                       const GrDistanceFieldAdjustTable* distanceAdjustTable,
                                       GrAtlasGlyphCache* cache) {
     int lastRun = fRuns[run].fSubRunInfo.count() - 1;
     for (int subRun = 0; subRun <= lastRun; subRun++) {
         const Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
+        GrPaint grPaint;
+        if (!paint.toGrPaint(info.maskFormat(), rtc, viewMatrix, &grPaint)) {
+            continue;
+        }
         int glyphCount = info.glyphCount();
         if (0 == glyphCount) {
             continue;
         }
 
-        GrColor color = grPaint.getColor();
-
         std::unique_ptr<GrDrawOp> op(this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y,
-                                                  color, skPaint, props, distanceAdjustTable,
+                                                  paint, props, distanceAdjustTable,
                                                   rtc->isGammaCorrect(), cache));
-        GrPipelineBuilder pipelineBuilder(GrPaint::MoveOrClone(grPaint, subRun < lastRun),
-                                          GrAAType::kNone);
+        GrPipelineBuilder pipelineBuilder(std::move(grPaint), GrAAType::kNone);
 
         rtc->addDrawOp(pipelineBuilder, clip, std::move(op));
     }
@@ -343,9 +330,8 @@
     }
 }
 
-
 void GrAtlasTextBlob::flushBigGlyphs(GrContext* context, GrRenderTargetContext* rtc,
-                                     const GrClip& clip, const SkPaint& skPaint,
+                                     const GrClip& clip, const SkPaint& paint,
                                      const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
                                      const SkIRect& clipBounds) {
     SkScalar transX, transY;
@@ -360,46 +346,39 @@
             ctm.postConcat(viewMatrix);
         }
 
-        GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, bigGlyph.fPath,
-                                            skPaint, ctm, nullptr, clipBounds, false);
+        GrBlurUtils::drawPathWithMaskFilter(context, rtc, clip, bigGlyph.fPath, paint, ctm, nullptr,
+                                            clipBounds, false);
     }
 }
 
 void GrAtlasTextBlob::flushRunAsPaths(GrContext* context, GrRenderTargetContext* rtc,
-                                      const SkSurfaceProps& props,
-                                      const SkTextBlobRunIterator& it,
-                                      const GrClip& clip, const SkPaint& skPaint,
+                                      const SkSurfaceProps& props, const SkTextBlobRunIterator& it,
+                                      const GrClip& clip, const GrTextUtils::Paint& paint,
                                       SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
                                       const SkIRect& clipBounds, SkScalar x, SkScalar y) {
-    SkPaint runPaint = skPaint;
-
     size_t textLen = it.glyphCount() * sizeof(uint16_t);
     const SkPoint& offset = it.offset();
 
-    it.applyFontToPaint(&runPaint);
-
-    if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
+    GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
+    if (!runPaint.modifyForRun(it)) {
         return;
     }
 
-    runPaint.setFlags(GrTextUtils::FilterTextFlags(props, runPaint));
-
     switch (it.positioning()) {
         case SkTextBlob::kDefault_Positioning:
             GrTextUtils::DrawTextAsPath(context, rtc, clip, runPaint, viewMatrix,
-                                        (const char *)it.glyphs(),
-                                        textLen, x + offset.x(), y + offset.y(), clipBounds);
+                                        (const char*)it.glyphs(), textLen, x + offset.x(),
+                                        y + offset.y(), clipBounds);
             break;
         case SkTextBlob::kHorizontal_Positioning:
             GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, runPaint, viewMatrix,
-                                           (const char*)it.glyphs(),
-                                           textLen, it.pos(), 1, SkPoint::Make(x, y + offset.y()),
-                                           clipBounds);
+                                           (const char*)it.glyphs(), textLen, it.pos(), 1,
+                                           SkPoint::Make(x, y + offset.y()), clipBounds);
             break;
         case SkTextBlob::kFull_Positioning:
             GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, runPaint, viewMatrix,
-                                           (const char*)it.glyphs(),
-                                           textLen, it.pos(), 2, SkPoint::Make(x, y), clipBounds);
+                                           (const char*)it.glyphs(), textLen, it.pos(), 2,
+                                           SkPoint::Make(x, y), clipBounds);
             break;
     }
 }
@@ -407,49 +386,47 @@
 void GrAtlasTextBlob::flushCached(GrContext* context, GrRenderTargetContext* rtc,
                                   const SkTextBlob* blob, const SkSurfaceProps& props,
                                   const GrDistanceFieldAdjustTable* distanceAdjustTable,
-                                  const SkPaint& skPaint, GrPaint&& grPaint,
-                                  SkDrawFilter* drawFilter, const GrClip& clip,
-                                  const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x,
-                                  SkScalar y) {
+                                  const GrTextUtils::Paint& paint, SkDrawFilter* drawFilter,
+                                  const GrClip& clip, const SkMatrix& viewMatrix,
+                                  const SkIRect& clipBounds, SkScalar x, SkScalar y) {
     // We loop through the runs of the blob, flushing each.  If any run is too large, then we flush
     // it as paths
     SkTextBlobRunIterator it(blob);
     for (int run = 0; !it.done(); it.next(), run++) {
         if (fRuns[run].fDrawAsPaths) {
-            this->flushRunAsPaths(context, rtc, props, it, clip, skPaint,
-                                  drawFilter, viewMatrix, clipBounds, x, y);
+            this->flushRunAsPaths(context, rtc, props, it, clip, paint, drawFilter, viewMatrix,
+                                  clipBounds, x, y);
             continue;
         }
-        this->flushRun(rtc, GrPaint::MoveOrClone(grPaint, !it.done()), clip, run, viewMatrix, x, y,
-                       skPaint, props, distanceAdjustTable, context->getAtlasGlyphCache());
+        this->flushRun(rtc, clip, run, viewMatrix, x, y, paint, props, distanceAdjustTable,
+                       context->getAtlasGlyphCache());
     }
 
     // Now flush big glyphs
-    this->flushBigGlyphs(context, rtc, clip, skPaint, viewMatrix, x, y, clipBounds);
+    this->flushBigGlyphs(context, rtc, clip, paint, viewMatrix, x, y, clipBounds);
 }
 
 void GrAtlasTextBlob::flushThrowaway(GrContext* context, GrRenderTargetContext* rtc,
                                      const SkSurfaceProps& props,
                                      const GrDistanceFieldAdjustTable* distanceAdjustTable,
-                                     const SkPaint& skPaint, GrPaint&& grPaint, const GrClip& clip,
+                                     const GrTextUtils::Paint& paint, const GrClip& clip,
                                      const SkMatrix& viewMatrix, const SkIRect& clipBounds,
                                      SkScalar x, SkScalar y) {
     for (int run = 0; run < fRunCount; run++) {
-        this->flushRun(rtc, GrPaint::MoveOrClone(grPaint, run + 1 != fRunCount), clip, run,
-                       viewMatrix, x, y, skPaint, props, distanceAdjustTable,
+        this->flushRun(rtc, clip, run, viewMatrix, x, y, paint, props, distanceAdjustTable,
                        context->getAtlasGlyphCache());
     }
 
     // Now flush big glyphs
-    this->flushBigGlyphs(context, rtc, clip, skPaint, viewMatrix, x, y, clipBounds);
+    this->flushBigGlyphs(context, rtc, clip, paint, viewMatrix, x, y, clipBounds);
 }
 
 std::unique_ptr<GrDrawOp> GrAtlasTextBlob::test_makeOp(
         int glyphCount, int run, int subRun, const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
-        GrColor color, const SkPaint& skPaint, const SkSurfaceProps& props,
+        const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
         const GrDistanceFieldAdjustTable* distanceAdjustTable, GrAtlasGlyphCache* cache) {
     const GrAtlasTextBlob::Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
-    return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, color, skPaint, props,
+    return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, paint, props,
                         distanceAdjustTable, false, cache);
 }
 
diff --git a/src/gpu/text/GrAtlasTextBlob.h b/src/gpu/text/GrAtlasTextBlob.h
index f4e149f..787518d 100644
--- a/src/gpu/text/GrAtlasTextBlob.h
+++ b/src/gpu/text/GrAtlasTextBlob.h
@@ -12,6 +12,7 @@
 #include "GrColor.h"
 #include "GrDrawOpAtlas.h"
 #include "GrMemoryPool.h"
+#include "GrTextUtils.h"
 #include "SkDescriptor.h"
 #include "SkMaskFilter.h"
 #include "SkOpts.h"
@@ -177,20 +178,20 @@
         }
     }
 
-    bool mustRegenerate(const SkPaint& paint, GrColor color, const SkMaskFilter::BlurRec& blurRec,
+    bool mustRegenerate(const GrTextUtils::Paint&, const SkMaskFilter::BlurRec& blurRec,
                         const SkMatrix& viewMatrix, SkScalar x, SkScalar y);
 
     // flush a GrAtlasTextBlob associated with a SkTextBlob
     void flushCached(GrContext* context, GrRenderTargetContext* rtc, const SkTextBlob* blob,
                      const SkSurfaceProps& props,
-                     const GrDistanceFieldAdjustTable* distanceAdjustTable, const SkPaint& skPaint,
-                     GrPaint&& grPaint, SkDrawFilter* drawFilter, const GrClip& clip,
+                     const GrDistanceFieldAdjustTable* distanceAdjustTable,
+                     const GrTextUtils::Paint&, SkDrawFilter* drawFilter, const GrClip& clip,
                      const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x, SkScalar y);
 
     // flush a throwaway GrAtlasTextBlob *not* associated with an SkTextBlob
     void flushThrowaway(GrContext* context, GrRenderTargetContext* rtc, const SkSurfaceProps& props,
                         const GrDistanceFieldAdjustTable* distanceAdjustTable,
-                        const SkPaint& skPaint, GrPaint&& grPaint, const GrClip& clip,
+                        const GrTextUtils::Paint& paint, const GrClip& clip,
                         const SkMatrix& viewMatrix, const SkIRect& clipBounds, SkScalar x,
                         SkScalar y);
 
@@ -238,8 +239,9 @@
     // 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(GrColor color, const SkMatrix& viewMatrix, SkScalar x, SkScalar y) {
-        fPaintColor = color;
+    void initReusableBlob(SkColor filteredColor, const SkMatrix& viewMatrix, SkScalar x,
+                          SkScalar y) {
+        fFilteredPaintColor = filteredColor;
         this->setupViewMatrix(viewMatrix, x, y);
     }
 
@@ -269,7 +271,7 @@
     // Internal test methods
     std::unique_ptr<GrDrawOp> test_makeOp(int glyphCount, int run, int subRun,
                                           const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
-                                          GrColor color, const SkPaint& skPaint,
+                                          const GrTextUtils::Paint& paint,
                                           const SkSurfaceProps& props,
                                           const GrDistanceFieldAdjustTable* distanceAdjustTable,
                                           GrAtlasGlyphCache* cache);
@@ -283,22 +285,19 @@
     void appendLargeGlyph(GrGlyph* glyph, SkGlyphCache* cache, const SkGlyph& skGlyph,
                           SkScalar x, SkScalar y, SkScalar scale, bool treatAsBMP);
 
-    inline void flushRun(GrRenderTargetContext* rtc, GrPaint&&, const GrClip&, int run,
-                         const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkPaint& skPaint,
-                         const SkSurfaceProps& props,
+    inline void flushRun(GrRenderTargetContext* rtc, const GrClip&, int run,
+                         const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
+                         const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
                          const GrDistanceFieldAdjustTable* distanceAdjustTable,
                          GrAtlasGlyphCache* cache);
 
-    void flushBigGlyphs(GrContext* context, GrRenderTargetContext* rtc,
-                        const GrClip& clip, const SkPaint& skPaint,
-                        const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
+    void flushBigGlyphs(GrContext* context, GrRenderTargetContext* rtc, const GrClip& clip,
+                        const SkPaint& paint, const SkMatrix& viewMatrix, SkScalar x, SkScalar y,
                         const SkIRect& clipBounds);
 
-    void flushRunAsPaths(GrContext* context,
-                         GrRenderTargetContext* rtc,
-                         const SkSurfaceProps& props,
-                         const SkTextBlobRunIterator& it,
-                         const GrClip& clip, const SkPaint& skPaint,
+    void flushRunAsPaths(GrContext* context, GrRenderTargetContext* rtc,
+                         const SkSurfaceProps& props, const SkTextBlobRunIterator& it,
+                         const GrClip& clip, const GrTextUtils::Paint& paint,
                          SkDrawFilter* drawFilter, const SkMatrix& viewMatrix,
                          const SkIRect& clipBounds, SkScalar x, SkScalar y);
 
@@ -491,7 +490,7 @@
 
     inline std::unique_ptr<GrDrawOp> makeOp(const Run::SubRunInfo& info, int glyphCount, int run,
                                             int subRun, const SkMatrix& viewMatrix, SkScalar x,
-                                            SkScalar y, GrColor color, const SkPaint& skPaint,
+                                            SkScalar y, const GrTextUtils::Paint& paint,
                                             const SkSurfaceProps& props,
                                             const GrDistanceFieldAdjustTable* distanceAdjustTable,
                                             bool useGammaCorrectDistanceTable,
@@ -534,7 +533,7 @@
     SkMatrix fInitialViewMatrix;
     SkMatrix fInitialViewMatrixInverse;
     size_t fSize;
-    GrColor fPaintColor;
+    SkColor fFilteredPaintColor;
     SkScalar fInitialX;
     SkScalar fInitialY;
 
diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp
index 1d8571c..07a653c 100644
--- a/src/gpu/text/GrAtlasTextContext.cpp
+++ b/src/gpu/text/GrAtlasTextContext.cpp
@@ -9,8 +9,6 @@
 #include "GrContext.h"
 #include "GrRenderTargetContext.h"
 #include "GrTextBlobCache.h"
-#include "GrTextUtils.h"
-
 #include "SkDraw.h"
 #include "SkDrawFilter.h"
 #include "SkGrPriv.h"
@@ -19,7 +17,6 @@
     : fDistanceAdjustTable(new GrDistanceFieldAdjustTable) {
 }
 
-
 GrAtlasTextContext* GrAtlasTextContext::Create() {
     return new GrAtlasTextContext();
 }
@@ -32,8 +29,8 @@
            !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix);
 }
 
-GrColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
-    GrColor canonicalColor = paint.computeLuminanceColor();
+SkColor GrAtlasTextContext::ComputeCanonicalColor(const SkPaint& paint, bool lcd) {
+    SkColor canonicalColor = paint.computeLuminanceColor();
     if (lcd) {
         // This is the correct computation, but there are tons of cases where LCD can be overridden.
         // For now we just regenerate if any run in a textblob has LCD.
@@ -121,24 +118,17 @@
         cacheBlob.reset(SkSafeRef(cache->find(key)));
     }
 
-    // Though for the time being runs in the textblob can override the paint, they only touch font
-    // info.
-    GrPaint grPaint;
-    if (!SkPaintToGrPaint(context, rtc, skPaint, viewMatrix, &grPaint)) {
-        return;
-    }
-
+    GrTextUtils::Paint paint(&skPaint);
     if (cacheBlob) {
-        if (cacheBlob->mustRegenerate(skPaint, grPaint.getColor(), blurRec, viewMatrix, x, y)) {
+        if (cacheBlob->mustRegenerate(paint, blurRec, viewMatrix, x, y)) {
             // We have to remake the blob because changes may invalidate our masks.
             // TODO we could probably get away reuse most of the time if the pointer is unique,
             // but we'd have to clear the subrun information
             cache->remove(cacheBlob.get());
             cacheBlob.reset(SkRef(cache->createCachedBlob(blob, key, blurRec, skPaint)));
             RegenerateTextBlob(cacheBlob.get(), context->getAtlasGlyphCache(),
-                               *context->caps()->shaderCaps(), skPaint, grPaint.getColor(),
-                               scalerContextFlags, viewMatrix, props,
-                               blob, x, y, drawFilter);
+                               *context->caps()->shaderCaps(), paint, scalerContextFlags,
+                               viewMatrix, props, blob, x, y, drawFilter);
         } else {
             cache->makeMRU(cacheBlob.get());
 
@@ -149,9 +139,8 @@
                 sk_sp<GrAtlasTextBlob> sanityBlob(cache->createBlob(glyphCount, runCount));
                 sanityBlob->setupKey(key, blurRec, skPaint);
                 RegenerateTextBlob(sanityBlob.get(), context->getAtlasGlyphCache(),
-                                   *context->caps()->shaderCaps(), skPaint,
-                                   grPaint.getColor(), scalerContextFlags, viewMatrix, props,
-                                   blob, x, y, drawFilter);
+                                   *context->caps()->shaderCaps(), paint, scalerContextFlags,
+                                   viewMatrix, props, blob, x, y, drawFilter);
                 GrAtlasTextBlob::AssertEqual(*sanityBlob, *cacheBlob);
             }
         }
@@ -162,70 +151,55 @@
             cacheBlob.reset(cache->createBlob(blob));
         }
         RegenerateTextBlob(cacheBlob.get(), context->getAtlasGlyphCache(),
-                           *context->caps()->shaderCaps(), skPaint, grPaint.getColor(),
-                           scalerContextFlags, viewMatrix, props,
-                           blob, x, y, drawFilter);
+                           *context->caps()->shaderCaps(), paint, scalerContextFlags, viewMatrix,
+                           props, blob, x, y, drawFilter);
     }
 
-    cacheBlob->flushCached(context, rtc, blob, props, fDistanceAdjustTable.get(), skPaint,
-                           std::move(grPaint), drawFilter, clip, viewMatrix, clipBounds, x, y);
+    cacheBlob->flushCached(context, rtc, blob, props, fDistanceAdjustTable.get(), paint, drawFilter,
+                           clip, viewMatrix, clipBounds, x, y);
 }
 
 void GrAtlasTextContext::RegenerateTextBlob(GrAtlasTextBlob* cacheBlob,
                                             GrAtlasGlyphCache* fontCache,
                                             const GrShaderCaps& shaderCaps,
-                                            const SkPaint& skPaint, GrColor color,
-                                            uint32_t scalerContextFlags,
-                                            const SkMatrix& viewMatrix,
-                                            const SkSurfaceProps& props,
-                                            const SkTextBlob* blob, SkScalar x, SkScalar y,
-                                            SkDrawFilter* drawFilter) {
-    cacheBlob->initReusableBlob(color, viewMatrix, x, y);
+                                            const GrTextUtils::Paint& paint,
+                                            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);
 
     // Regenerate textblob
-    SkPaint runPaint = skPaint;
     SkTextBlobRunIterator it(blob);
+    GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
     for (int run = 0; !it.done(); it.next(), run++) {
         int glyphCount = it.glyphCount();
         size_t textLen = glyphCount * sizeof(uint16_t);
         const SkPoint& offset = it.offset();
-        // applyFontToPaint() always overwrites the exact same attributes,
-        // so it is safe to not re-seed the paint for this reason.
-        it.applyFontToPaint(&runPaint);
-
-        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
-            // A false return from filter() means we should abort the current draw.
-            runPaint = skPaint;
+        cacheBlob->push_back_run(run);
+        if (!runPaint.modifyForRun(it)) {
             continue;
         }
-
-        runPaint.setFlags(GrTextUtils::FilterTextFlags(props, runPaint));
-
-        cacheBlob->push_back_run(run);
-
         if (GrTextUtils::CanDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) {
             switch (it.positioning()) {
                 case SkTextBlob::kDefault_Positioning: {
-                    GrTextUtils::DrawDFText(cacheBlob, run, fontCache,
-                                            props, runPaint, color, scalerContextFlags,
-                                            viewMatrix, (const char *)it.glyphs(), textLen,
-                                            x + offset.x(), y + offset.y());
+                    GrTextUtils::DrawDFText(cacheBlob, run, fontCache, props, runPaint,
+                                            scalerContextFlags, viewMatrix,
+                                            (const char*)it.glyphs(), textLen, x + offset.x(),
+                                            y + offset.y());
                     break;
                 }
                 case SkTextBlob::kHorizontal_Positioning: {
                     SkPoint dfOffset = SkPoint::Make(x, y + offset.y());
-                    GrTextUtils::DrawDFPosText(cacheBlob, run, fontCache,
-                                               props, runPaint, color, scalerContextFlags,
-                                               viewMatrix, (const char*)it.glyphs(), textLen,
-                                               it.pos(), 1, dfOffset);
+                    GrTextUtils::DrawDFPosText(
+                            cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
+                            viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1, dfOffset);
                     break;
                 }
                 case SkTextBlob::kFull_Positioning: {
                     SkPoint dfOffset = SkPoint::Make(x, y);
-                    GrTextUtils::DrawDFPosText(cacheBlob, run,  fontCache,
-                                               props, runPaint, color, scalerContextFlags,
-                                               viewMatrix, (const char*)it.glyphs(), textLen,
-                                               it.pos(), 2, dfOffset);
+                    GrTextUtils::DrawDFPosText(
+                            cacheBlob, run, fontCache, props, runPaint, scalerContextFlags,
+                            viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, dfOffset);
                     break;
                 }
             }
@@ -234,30 +208,25 @@
         } else {
             switch (it.positioning()) {
                 case SkTextBlob::kDefault_Positioning:
-                    GrTextUtils::DrawBmpText(cacheBlob, run, fontCache,
-                                             props, runPaint, color, scalerContextFlags,
-                                             viewMatrix, (const char *)it.glyphs(), textLen,
-                                             x + offset.x(), y + offset.y());
+                    GrTextUtils::DrawBmpText(cacheBlob, run, fontCache, props, runPaint,
+                                             scalerContextFlags, viewMatrix,
+                                             (const char*)it.glyphs(), textLen, x + offset.x(),
+                                             y + offset.y());
                     break;
                 case SkTextBlob::kHorizontal_Positioning:
-                    GrTextUtils::DrawBmpPosText(cacheBlob, run, fontCache,
-                                                props, runPaint, color, scalerContextFlags,
-                                                viewMatrix, (const char*)it.glyphs(), textLen,
-                                                it.pos(), 1, SkPoint::Make(x, y + offset.y()));
+                    GrTextUtils::DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint,
+                                                scalerContextFlags, viewMatrix,
+                                                (const char*)it.glyphs(), textLen, it.pos(), 1,
+                                                SkPoint::Make(x, y + offset.y()));
                     break;
                 case SkTextBlob::kFull_Positioning:
-                    GrTextUtils::DrawBmpPosText(cacheBlob, run, fontCache,
-                                                props, runPaint, color, scalerContextFlags,
-                                                viewMatrix, (const char*)it.glyphs(), textLen,
-                                                it.pos(), 2, SkPoint::Make(x, y));
+                    GrTextUtils::DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint,
+                                                scalerContextFlags, viewMatrix,
+                                                (const char*)it.glyphs(), textLen, it.pos(), 2,
+                                                SkPoint::Make(x, y));
                     break;
             }
         }
-
-        if (drawFilter) {
-            // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
-            runPaint = skPaint;
-        }
     }
 }
 
@@ -265,106 +234,105 @@
 GrAtlasTextContext::CreateDrawTextBlob(GrTextBlobCache* blobCache,
                                        GrAtlasGlyphCache* fontCache,
                                        const GrShaderCaps& shaderCaps,
-                                       const GrPaint& paint,
-                                       const SkPaint& skPaint,
+                                       const GrTextUtils::Paint& paint,
                                        uint32_t scalerContextFlags,
                                        const SkMatrix& viewMatrix,
                                        const SkSurfaceProps& props,
                                        const char text[], size_t byteLength,
                                        SkScalar x, SkScalar y) {
-    int glyphCount = skPaint.countText(text, byteLength);
+    int glyphCount = paint.skPaint().countText(text, byteLength);
 
     GrAtlasTextBlob* blob = blobCache->createBlob(glyphCount, 1);
     blob->initThrowawayBlob(viewMatrix, x, y);
 
-    if (GrTextUtils::CanDrawAsDistanceFields(skPaint, viewMatrix, props, shaderCaps)) {
-        GrTextUtils::DrawDFText(blob, 0, fontCache, props, skPaint, paint.getColor(),
-                                scalerContextFlags, viewMatrix, text, byteLength, x, y);
+    if (GrTextUtils::CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
+        GrTextUtils::DrawDFText(blob, 0, fontCache, props, paint, scalerContextFlags, viewMatrix,
+                                text, byteLength, x, y);
     } else {
-        GrTextUtils::DrawBmpText(blob, 0, fontCache, props, skPaint, paint.getColor(),
-                                 scalerContextFlags, viewMatrix, text, byteLength, x, y);
+        GrTextUtils::DrawBmpText(blob, 0, fontCache, props, paint, scalerContextFlags, viewMatrix,
+                                 text, byteLength, x, y);
     }
     return blob;
 }
 
 inline GrAtlasTextBlob*
-GrAtlasTextContext::CreateDrawPosTextBlob(GrTextBlobCache* blobCache, GrAtlasGlyphCache* fontCache,
-                                          const GrShaderCaps& shaderCaps, const GrPaint& paint,
-                                          const SkPaint& skPaint, uint32_t scalerContextFlags,
-                                          const SkMatrix& viewMatrix, const SkSurfaceProps& props,
+GrAtlasTextContext::CreateDrawPosTextBlob(GrTextBlobCache* blobCache,
+                                          GrAtlasGlyphCache* fontCache,
+                                          const GrShaderCaps& shaderCaps,
+                                          const GrTextUtils::Paint& paint,
+                                          uint32_t scalerContextFlags,
+                                          const SkMatrix& viewMatrix,
+                                          const SkSurfaceProps& props,
                                           const char text[], size_t byteLength,
-                                          const SkScalar pos[], int scalarsPerPosition,
-                                          const SkPoint& offset) {
-    int glyphCount = skPaint.countText(text, byteLength);
+                                          const SkScalar pos[], int scalarsPerPosition, const
+                                          SkPoint& offset) {
+    int glyphCount = paint.skPaint().countText(text, byteLength);
 
     GrAtlasTextBlob* blob = blobCache->createBlob(glyphCount, 1);
     blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y());
 
-    if (GrTextUtils::CanDrawAsDistanceFields(skPaint, viewMatrix, props, shaderCaps)) {
-        GrTextUtils::DrawDFPosText(blob, 0, fontCache, props,
-                                   skPaint, paint.getColor(), scalerContextFlags, viewMatrix, text,
-                                   byteLength, pos, scalarsPerPosition, offset);
+    if (GrTextUtils::CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) {
+        GrTextUtils::DrawDFPosText(blob, 0, fontCache, props, paint, scalerContextFlags, viewMatrix,
+                                   text, byteLength, pos, scalarsPerPosition, offset);
     } else {
-        GrTextUtils::DrawBmpPosText(blob, 0, fontCache, props, skPaint,
-                                    paint.getColor(), scalerContextFlags, viewMatrix, text,
-                                    byteLength, pos, scalarsPerPosition, offset);
+        GrTextUtils::DrawBmpPosText(blob, 0, fontCache, props, paint, scalerContextFlags,
+                                    viewMatrix, text, byteLength, pos, scalarsPerPosition, offset);
     }
     return blob;
 }
 
 void GrAtlasTextContext::drawText(GrContext* context, GrRenderTargetContext* rtc,
-                                  const GrClip& clip, GrPaint&& paint, const SkPaint& skPaint,
+                                  const GrClip& clip, const SkPaint& skPaint,
                                   const SkMatrix& viewMatrix, const SkSurfaceProps& props,
                                   const char text[], size_t byteLength, SkScalar x, SkScalar y,
                                   const SkIRect& regionClipBounds) {
     if (context->abandoned()) {
         return;
-    } else if (this->canDraw(skPaint, viewMatrix, props, *context->caps()->shaderCaps())) {
+    }
+    GrTextUtils::Paint paint(&skPaint);
+    if (this->canDraw(skPaint, viewMatrix, props, *context->caps()->shaderCaps())) {
         sk_sp<GrAtlasTextBlob> blob(
             CreateDrawTextBlob(context->getTextBlobCache(), context->getAtlasGlyphCache(),
                                *context->caps()->shaderCaps(),
-                               paint, skPaint,
-                               ComputeScalerContextFlags(rtc),
+                               paint, ComputeScalerContextFlags(rtc),
                                viewMatrix, props,
                                text, byteLength, x, y));
-        blob->flushThrowaway(context, rtc, props, fDistanceAdjustTable.get(), skPaint,
-                             std::move(paint), clip, viewMatrix, regionClipBounds, x, y);
+        blob->flushThrowaway(context, rtc, props, fDistanceAdjustTable.get(), paint, clip,
+                             viewMatrix, regionClipBounds, x, y);
         return;
     }
 
     // fall back to drawing as a path
-    GrTextUtils::DrawTextAsPath(context, rtc, clip, skPaint, viewMatrix, text, byteLength, x, y,
+    GrTextUtils::DrawTextAsPath(context, rtc, clip, paint, viewMatrix, text, byteLength, x, y,
                                 regionClipBounds);
 }
 
 void GrAtlasTextContext::drawPosText(GrContext* context, GrRenderTargetContext* rtc,
-                                     const GrClip& clip, GrPaint&& paint, const SkPaint& skPaint,
+                                     const GrClip& clip, const SkPaint& skPaint,
                                      const SkMatrix& viewMatrix, const SkSurfaceProps& props,
                                      const char text[], size_t byteLength, const SkScalar pos[],
                                      int scalarsPerPosition, const SkPoint& offset,
                                      const SkIRect& regionClipBounds) {
+    GrTextUtils::Paint paint(&skPaint);
     if (context->abandoned()) {
         return;
     } else if (this->canDraw(skPaint, viewMatrix, props, *context->caps()->shaderCaps())) {
         sk_sp<GrAtlasTextBlob> blob(
-            CreateDrawPosTextBlob(context->getTextBlobCache(),
-                                  context->getAtlasGlyphCache(),
+            CreateDrawPosTextBlob(context->getTextBlobCache(), context->getAtlasGlyphCache(),
                                   *context->caps()->shaderCaps(),
-                                  paint, skPaint,
-                                  ComputeScalerContextFlags(rtc),
+                                  paint, ComputeScalerContextFlags(rtc),
                                   viewMatrix, props,
                                   text, byteLength,
                                   pos, scalarsPerPosition,
                                   offset));
-        blob->flushThrowaway(context, rtc, props, fDistanceAdjustTable.get(), skPaint,
-                             std::move(paint), clip, viewMatrix, regionClipBounds, offset.fX,
-                             offset.fY);
+        blob->flushThrowaway(context, rtc, props, fDistanceAdjustTable.get(), paint, clip,
+                             viewMatrix, regionClipBounds, offset.fX, offset.fY);
         return;
     }
 
     // fall back to drawing as a path
-    GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, skPaint, viewMatrix, text,
-                                   byteLength, pos, scalarsPerPosition, offset, regionClipBounds);
+    GrTextUtils::DrawPosTextAsPath(context, rtc, props, clip, paint, viewMatrix, text, byteLength,
+                                   pos, scalarsPerPosition, offset, regionClipBounds);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////
@@ -387,19 +355,13 @@
     sk_sp<GrRenderTargetContext> renderTargetContext(context->makeRenderTargetContext(
         SkBackingFit::kApprox, 1024, 1024, kRGBA_8888_GrPixelConfig, nullptr));
 
-    GrColor color = GrRandomColor(random);
     SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
     SkPaint skPaint;
-    skPaint.setColor(color);
+    skPaint.setColor(random->nextU());
     skPaint.setLCDRenderText(random->nextBool());
     skPaint.setAntiAlias(skPaint.isLCDRenderText() ? true : random->nextBool());
     skPaint.setSubpixelText(random->nextBool());
 
-    GrPaint grPaint;
-    if (!SkPaintToGrPaint(context, renderTargetContext.get(), skPaint, viewMatrix, &grPaint)) {
-        SkFAIL("couldn't convert paint\n");
-    }
-
     const char* text = "The quick brown fox jumps over the lazy dog.";
     int textLen = (int)strlen(text);
 
@@ -412,15 +374,16 @@
     SkScalar x = SkIntToScalar(xInt);
     SkScalar y = SkIntToScalar(yInt);
 
+    GrTextUtils::Paint paint(&skPaint);
     // 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::CreateDrawTextBlob(
             context->getTextBlobCache(), context->getAtlasGlyphCache(),
-            *context->caps()->shaderCaps(), grPaint, skPaint,
+            *context->caps()->shaderCaps(), paint,
             GrAtlasTextContext::kTextBlobOpScalerContextFlags, viewMatrix, gSurfaceProps, text,
             static_cast<size_t>(textLen), x, y));
 
-    return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, color, skPaint, gSurfaceProps,
+    return blob->test_makeOp(textLen, 0, 0, viewMatrix, x, y, paint, gSurfaceProps,
                              gTextContext->dfAdjustTable(), context->getAtlasGlyphCache());
 }
 
diff --git a/src/gpu/text/GrAtlasTextContext.h b/src/gpu/text/GrAtlasTextContext.h
index 27560d4..11e22d3 100644
--- a/src/gpu/text/GrAtlasTextContext.h
+++ b/src/gpu/text/GrAtlasTextContext.h
@@ -11,6 +11,7 @@
 #include "GrAtlasTextBlob.h"
 #include "GrDistanceFieldAdjustTable.h"
 #include "GrGeometryProcessor.h"
+#include "GrTextUtils.h"
 #include "SkTextBlobRunIterator.h"
 
 #ifdef GR_TEST_UTILS
@@ -32,10 +33,11 @@
 
     bool canDraw(const SkPaint&, const SkMatrix& viewMatrix, const SkSurfaceProps&,
                  const GrShaderCaps&);
-    void drawText(GrContext*, GrRenderTargetContext*, const GrClip&, GrPaint&&, const SkPaint&,
+
+    void drawText(GrContext*, GrRenderTargetContext*, const GrClip&, const SkPaint&,
                   const SkMatrix& viewMatrix, const SkSurfaceProps&, const char text[],
                   size_t byteLength, SkScalar x, SkScalar y, const SkIRect& regionClipBounds);
-    void drawPosText(GrContext*, GrRenderTargetContext*, const GrClip&, GrPaint&&, const SkPaint&,
+    void drawPosText(GrContext*, GrRenderTargetContext*, const GrClip&, const SkPaint&,
                      const SkMatrix& viewMatrix, const SkSurfaceProps&, const char text[],
                      size_t byteLength, const SkScalar pos[], int scalarsPerPosition,
                      const SkPoint& offset, const SkIRect& regionClipBounds);
@@ -48,13 +50,13 @@
     GrAtlasTextContext();
 
     // sets up the descriptor on the blob and returns a detached cache.  Client must attach
-    inline static GrColor ComputeCanonicalColor(const SkPaint&, bool lcd);
+    inline static SkColor ComputeCanonicalColor(const SkPaint&, bool lcd);
     // Determines if we need to use fake gamma (and contrast boost):
     inline static uint32_t ComputeScalerContextFlags(GrRenderTargetContext*);
     static void RegenerateTextBlob(GrAtlasTextBlob* bmp,
                                    GrAtlasGlyphCache*,
                                    const GrShaderCaps&,
-                                   const SkPaint& skPaint, GrColor,
+                                   const GrTextUtils::Paint&,
                                    uint32_t scalerContextFlags,
                                    const SkMatrix& viewMatrix,
                                    const SkSurfaceProps&,
@@ -62,10 +64,9 @@
                                    SkDrawFilter* drawFilter);
     inline static bool HasLCD(const SkTextBlob*);
 
-    static inline GrAtlasTextBlob* CreateDrawTextBlob(GrTextBlobCache*,
-                                                      GrAtlasGlyphCache*, const GrShaderCaps&,
-                                                      const GrPaint&,
-                                                      const SkPaint&,
+    static inline GrAtlasTextBlob* CreateDrawTextBlob(GrTextBlobCache*, GrAtlasGlyphCache*,
+                                                      const GrShaderCaps&,
+                                                      const GrTextUtils::Paint&,
                                                       uint32_t scalerContextFlags,
                                                       const SkMatrix& viewMatrix,
                                                       const SkSurfaceProps&,
@@ -73,8 +74,7 @@
                                                       SkScalar x, SkScalar y);
     static inline GrAtlasTextBlob* CreateDrawPosTextBlob(GrTextBlobCache*, GrAtlasGlyphCache*,
                                                          const GrShaderCaps&,
-                                                         const GrPaint&,
-                                                         const SkPaint&,
+                                                         const GrTextUtils::Paint&,
                                                          uint32_t scalerContextFlags,
                                                          const SkMatrix& viewMatrix,
                                                          const SkSurfaceProps&,
diff --git a/src/gpu/text/GrStencilAndCoverTextContext.cpp b/src/gpu/text/GrStencilAndCoverTextContext.cpp
index e5ca7ca..5c3b8b9 100644
--- a/src/gpu/text/GrStencilAndCoverTextContext.cpp
+++ b/src/gpu/text/GrStencilAndCoverTextContext.cpp
@@ -8,22 +8,23 @@
 #include "GrStencilAndCoverTextContext.h"
 #include "GrAtlasTextContext.h"
 #include "GrContext.h"
-#include "GrRenderTargetContext.h"
 #include "GrPath.h"
 #include "GrPathRange.h"
 #include "GrPipelineBuilder.h"
+#include "GrRenderTargetContext.h"
 #include "GrResourceProvider.h"
+#include "GrSurfaceContextPriv.h"
 #include "GrTextUtils.h"
 #include "SkAutoKern.h"
 #include "SkDraw.h"
+#include "SkDrawFilter.h"
 #include "SkDrawProcs.h"
 #include "SkGlyphCache.h"
 #include "SkGrPriv.h"
-#include "SkDrawFilter.h"
 #include "SkPath.h"
 #include "SkTextBlobRunIterator.h"
-#include "SkTextMapStateProc.h"
 #include "SkTextFormatParams.h"
+#include "SkTextMapStateProc.h"
 
 #include "ops/GrDrawPathOp.h"
 
@@ -69,25 +70,24 @@
 }
 
 void GrStencilAndCoverTextContext::drawText(GrContext* context, GrRenderTargetContext* rtc,
-                                            const GrClip& clip, GrPaint&& paint,
-                                            const SkPaint& skPaint, const SkMatrix& viewMatrix,
-                                            const SkSurfaceProps& props, const char text[],
-                                            size_t byteLength, SkScalar x, SkScalar y,
-                                            const SkIRect& clipBounds) {
+                                            const GrClip& clip, const SkPaint& skPaint,
+                                            const SkMatrix& viewMatrix, const SkSurfaceProps& props,
+                                            const char text[], size_t byteLength, SkScalar x,
+                                            SkScalar y, const SkIRect& clipBounds) {
     if (context->abandoned()) {
         return;
     } else if (this->canDraw(skPaint, viewMatrix)) {
         if (skPaint.getTextSize() > 0) {
             TextRun run(skPaint);
             run.setText(text, byteLength, x, y);
-            run.draw(context, rtc, std::move(paint), clip, viewMatrix, props, 0, 0, clipBounds,
-                     fFallbackTextContext, skPaint);
+            run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
+                     skPaint);
         }
         return;
     } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
                                              *context->caps()->shaderCaps())) {
-        fFallbackTextContext->drawText(context, rtc, clip, std::move(paint), skPaint, viewMatrix,
-                                       props, text, byteLength, x, y, clipBounds);
+        fFallbackTextContext->drawText(context, rtc, clip, skPaint, viewMatrix, props, text,
+                                       byteLength, x, y, clipBounds);
         return;
     }
 
@@ -97,8 +97,8 @@
 }
 
 void GrStencilAndCoverTextContext::drawPosText(GrContext* context, GrRenderTargetContext* rtc,
-                                               const GrClip& clip, GrPaint&& paint,
-                                               const SkPaint& skPaint, const SkMatrix& viewMatrix,
+                                               const GrClip& clip, const SkPaint& skPaint,
+                                               const SkMatrix& viewMatrix,
                                                const SkSurfaceProps& props, const char text[],
                                                size_t byteLength, const SkScalar pos[],
                                                int scalarsPerPosition, const SkPoint& offset,
@@ -109,15 +109,14 @@
         if (skPaint.getTextSize() > 0) {
             TextRun run(skPaint);
             run.setPosText(text, byteLength, pos, scalarsPerPosition, offset);
-            run.draw(context, rtc, std::move(paint), clip, viewMatrix, props, 0, 0, clipBounds,
-                     fFallbackTextContext, skPaint);
+            run.draw(context, rtc, clip, viewMatrix, props, 0, 0, clipBounds, fFallbackTextContext,
+                     skPaint);
         }
         return;
     } else if (fFallbackTextContext->canDraw(skPaint, viewMatrix, props,
                                              *context->caps()->shaderCaps())) {
-        fFallbackTextContext->drawPosText(context, rtc, clip, std::move(paint), skPaint, viewMatrix,
-                                          props, text, byteLength, pos, scalarsPerPosition, offset,
-                                          clipBounds);
+        fFallbackTextContext->drawPosText(context, rtc, clip, skPaint, viewMatrix, props, text,
+                                          byteLength, pos, scalarsPerPosition, offset, clipBounds);
         return;
     }
 
@@ -136,52 +135,33 @@
                                                         SkScalar x, SkScalar y,
                                                         SkDrawFilter* drawFilter,
                                                         const SkIRect& clipBounds) {
-    SkPaint runPaint = skPaint;
-
+    GrTextUtils::Paint paint(&skPaint);
+    GrTextUtils::RunPaint runPaint(&paint, drawFilter, props);
     SkTextBlobRunIterator it(blob);
     for (;!it.done(); it.next()) {
+        if (!runPaint.modifyForRun(it)) {
+            continue;
+        }
         size_t textLen = it.glyphCount() * sizeof(uint16_t);
         const SkPoint& offset = it.offset();
 
-        // applyFontToPaint() always overwrites the exact same attributes,
-        // so it is safe to not re-seed the paint for this reason.
-        it.applyFontToPaint(&runPaint);
-
-        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
-            // A false return from filter() means we should abort the current draw.
-            runPaint = skPaint;
-            continue;
-        }
-
-        runPaint.setFlags(GrTextUtils::FilterTextFlags(props, runPaint));
-
-        GrPaint grPaint;
-        if (!SkPaintToGrPaint(context, rtc, runPaint, viewMatrix, &grPaint)) {
-            return;
-        }
-
         switch (it.positioning()) {
             case SkTextBlob::kDefault_Positioning:
-                this->drawText(context, rtc, clip, std::move(grPaint), runPaint, viewMatrix, props,
+                this->drawText(context, rtc, clip, runPaint, viewMatrix, props,
                                (const char*)it.glyphs(), textLen, x + offset.x(), y + offset.y(),
                                clipBounds);
                 break;
             case SkTextBlob::kHorizontal_Positioning:
-                this->drawPosText(context, rtc, clip, std::move(grPaint), runPaint, viewMatrix,
-                                  props, (const char*)it.glyphs(), textLen, it.pos(), 1,
+                this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
+                                  (const char*)it.glyphs(), textLen, it.pos(), 1,
                                   SkPoint::Make(x, y + offset.y()), clipBounds);
                 break;
             case SkTextBlob::kFull_Positioning:
-                this->drawPosText(context, rtc, clip, std::move(grPaint), runPaint, viewMatrix,
-                                  props, (const char*)it.glyphs(), textLen, it.pos(), 2,
+                this->drawPosText(context, rtc, clip, runPaint, viewMatrix, props,
+                                  (const char*)it.glyphs(), textLen, it.pos(), 2,
                                   SkPoint::Make(x, y), clipBounds);
                 break;
         }
-
-        if (drawFilter) {
-            // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
-            runPaint = skPaint;
-        }
     }
 }
 
@@ -209,18 +189,13 @@
         return;
     }
 
-    GrPaint paint;
-    if (!SkPaintToGrPaint(context, rtc, skPaint, viewMatrix, &paint)) {
-        return;
-    }
-
     const TextBlob& blob = this->findOrCreateTextBlob(skBlob, skPaint);
 
     TextBlob::Iter iter(blob);
     for (TextRun *run = iter.get(), *nextRun; run; run = nextRun) {
         nextRun = iter.next();
-        run->draw(context, rtc, GrPaint::MoveOrClone(paint, nextRun), clip, viewMatrix, props, x, y,
-                  clipBounds, fFallbackTextContext, skPaint);
+        run->draw(context, rtc, clip, viewMatrix, props, x, y, clipBounds, fFallbackTextContext,
+                  skPaint);
         run->releaseGlyphCache();
     }
 }
@@ -588,11 +563,13 @@
     }
 }
 
-void GrStencilAndCoverTextContext::TextRun::draw(
-        GrContext* ctx, GrRenderTargetContext* renderTargetContext, GrPaint&& grPaint,
-        const GrClip& clip, const SkMatrix& viewMatrix, const SkSurfaceProps& props, SkScalar x,
-        SkScalar y, const SkIRect& clipBounds, GrAtlasTextContext* fallbackTextContext,
-        const SkPaint& originalSkPaint) const {
+void GrStencilAndCoverTextContext::TextRun::draw(GrContext* ctx,
+                                                 GrRenderTargetContext* renderTargetContext,
+                                                 const GrClip& clip, const SkMatrix& viewMatrix,
+                                                 const SkSurfaceProps& props, SkScalar x,
+                                                 SkScalar y, const SkIRect& clipBounds,
+                                                 GrAtlasTextContext* fallbackTextContext,
+                                                 const SkPaint& originalSkPaint) const {
     GrAA runAA = this->isAntiAlias();
     SkASSERT(fInstanceData);
     SkASSERT(renderTargetContext->isStencilBufferMultisampled() || GrAA::kNo == runAA);
@@ -615,6 +592,13 @@
             fLastDrawnGlyphsID = glyphs->uniqueID();
         }
 
+        GrPaint grPaint;
+        GrContext* context = renderTargetContext->surfPriv().getContext();
+        if (!SkPaintToGrPaint(context, renderTargetContext, originalSkPaint, viewMatrix,
+                              &grPaint)) {
+            return;
+        }
+
         // Don't compute a bounding box. For dst copy texture, we'll opt instead for it to just copy
         // the entire dst. Realistically this is a moot point, because any context that supports
         // NV_path_rendering will also support NV_blend_equation_advanced.
diff --git a/src/gpu/text/GrStencilAndCoverTextContext.h b/src/gpu/text/GrStencilAndCoverTextContext.h
index 0597225..a99c264 100644
--- a/src/gpu/text/GrStencilAndCoverTextContext.h
+++ b/src/gpu/text/GrStencilAndCoverTextContext.h
@@ -31,10 +31,10 @@
 public:
     static GrStencilAndCoverTextContext* Create(GrAtlasTextContext* fallbackTextContext);
 
-    void drawText(GrContext*, GrRenderTargetContext* rtc, const GrClip&, GrPaint&&, const SkPaint&,
+    void drawText(GrContext*, GrRenderTargetContext* rtc, const GrClip&, const SkPaint&,
                   const SkMatrix& viewMatrix, const SkSurfaceProps&, const char text[],
                   size_t byteLength, SkScalar x, SkScalar y, const SkIRect& clipBounds);
-    void drawPosText(GrContext*, GrRenderTargetContext*, const GrClip&, GrPaint&&, const SkPaint&,
+    void drawPosText(GrContext*, GrRenderTargetContext*, const GrClip&, const SkPaint&,
                      const SkMatrix& viewMatrix, const SkSurfaceProps&, const char text[],
                      size_t byteLength, const SkScalar pos[], int scalarsPerPosition,
                      const SkPoint& offset, const SkIRect& clipBounds);
@@ -75,7 +75,7 @@
         void setPosText(const char text[], size_t byteLength, const SkScalar pos[],
                         int scalarsPerPosition, const SkPoint& offset);
 
-        void draw(GrContext*, GrRenderTargetContext*, GrPaint&&, const GrClip&, const SkMatrix&,
+        void draw(GrContext*, GrRenderTargetContext*, const GrClip&, const SkMatrix&,
                   const SkSurfaceProps&, SkScalar x, SkScalar y, const SkIRect& clipBounds,
                   GrAtlasTextContext* fallbackTextContext, const SkPaint& originalSkPaint) const;
 
diff --git a/src/gpu/text/GrTextUtils.cpp b/src/gpu/text/GrTextUtils.cpp
index 548d4aa..a199d8c 100644
--- a/src/gpu/text/GrTextUtils.cpp
+++ b/src/gpu/text/GrTextUtils.cpp
@@ -6,20 +6,22 @@
  */
 
 #include "GrTextUtils.h"
-
 #include "GrAtlasGlyphCache.h"
 #include "GrAtlasTextBlob.h"
 #include "GrBlurUtils.h"
 #include "GrCaps.h"
 #include "GrContext.h"
 #include "GrRenderTargetContext.h"
-
+#include "GrSurfaceContextPriv.h"
 #include "SkDistanceFieldGen.h"
+#include "SkDrawFilter.h"
 #include "SkDrawProcs.h"
 #include "SkFindAndPlaceGlyph.h"
 #include "SkGlyphCache.h"
+#include "SkGrPriv.h"
 #include "SkPaint.h"
 #include "SkRect.h"
+#include "SkTextBlobRunIterator.h"
 #include "SkTextMapStateProc.h"
 #include "SkTextToPathIter.h"
 
@@ -37,13 +39,43 @@
 #endif
 };
 
-void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex,
-                              GrAtlasGlyphCache* fontCache,
-                              const SkSurfaceProps& props, const SkPaint& skPaint,
-                              GrColor color, uint32_t scalerContextFlags,
-                              const SkMatrix& viewMatrix,
-                              const char text[], size_t byteLength,
-                              SkScalar x, SkScalar y) {
+bool GrTextUtils::Paint::toGrPaint(GrMaskFormat maskFormat, GrRenderTargetContext* rtc,
+                                   const SkMatrix& viewMatrix, GrPaint* grPaint) const {
+    GrContext* context = rtc->surfPriv().getContext();
+    if (kARGB_GrMaskFormat == maskFormat) {
+        return SkPaintToGrPaintWithPrimitiveColor(context, rtc, this->skPaint(), grPaint);
+    } else {
+        return SkPaintToGrPaint(context, rtc, this->skPaint(), viewMatrix, grPaint);
+    }
+}
+
+bool GrTextUtils::RunPaint::modifyForRun(const SkTextBlobRunIterator& run) {
+    if (!fModifiedPaint.isValid()) {
+        fModifiedPaint.init(fOriginalPaint->skPaint());
+        fPaint = fModifiedPaint.get();
+    } else if (fFilter) {
+        // We have to reset before applying the run because the filter could have arbitrary
+        // changed the paint.
+        *fModifiedPaint.get() = fOriginalPaint->skPaint();
+    }
+    run.applyFontToPaint(fModifiedPaint.get());
+
+    if (fFilter) {
+        if (!fFilter->filter(fModifiedPaint.get(), SkDrawFilter::kText_Type)) {
+            // A false return from filter() means we should abort the current draw.
+            return false;
+        }
+        // The draw filter could have changed either the paint color or color filter.
+        this->initFilteredColor();
+    }
+    fModifiedPaint.get()->setFlags(FilterTextFlags(fProps, *fModifiedPaint.get()));
+    return true;
+}
+
+void GrTextUtils::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
+                              const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
+                              uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
+                              const char text[], size_t byteLength, SkScalar x, SkScalar y) {
     SkASSERT(byteLength == 0 || text != nullptr);
 
     // nothing to draw
@@ -56,32 +88,28 @@
 
     GrAtlasTextStrike* currStrike = nullptr;
 
-    SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, skPaint,
-                                           &viewMatrix);
+    SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
     SkFindAndPlaceGlyph::ProcessText(
-        skPaint.getTextEncoding(), text, byteLength,
-        {x, y}, viewMatrix, skPaint.getTextAlign(),
+        paint.skPaint().getTextEncoding(), text, byteLength,
+        {x, y}, viewMatrix, paint.skPaint().getTextAlign(),
         cache,
         [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
-            position += rounding;
-            BmpAppendGlyph(
-                blob, runIndex, fontCache, &currStrike, glyph,
-                SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
-                color, cache);
+             position += rounding;
+             BmpAppendGlyph(
+                 blob, runIndex, fontCache, &currStrike, glyph,
+                 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
+                 paint.filteredPremulGrColor(), cache);
         }
     );
 
     SkGlyphCache::AttachCache(cache);
 }
 
-void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex,
-                                 GrAtlasGlyphCache* fontCache,
-                                 const SkSurfaceProps& props, const SkPaint& skPaint,
-                                 GrColor color, uint32_t scalerContextFlags,
-                                 const SkMatrix& viewMatrix,
-                                 const char text[], size_t byteLength,
-                                 const SkScalar pos[], int scalarsPerPosition,
-                                 const SkPoint& offset) {
+void GrTextUtils::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
+                                 const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
+                                 uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
+                                 const char text[], size_t byteLength, const SkScalar pos[],
+                                 int scalarsPerPosition, const SkPoint& offset) {
     SkASSERT(byteLength == 0 || text != nullptr);
     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
 
@@ -95,19 +123,18 @@
 
     GrAtlasTextStrike* currStrike = nullptr;
 
-    SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, skPaint,
-                                           &viewMatrix);
+    SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix);
 
     SkFindAndPlaceGlyph::ProcessPosText(
-        skPaint.getTextEncoding(), text, byteLength,
+        paint.skPaint().getTextEncoding(), text, byteLength,
         offset, viewMatrix, pos, scalarsPerPosition,
-        skPaint.getTextAlign(), cache,
+        paint.skPaint().getTextAlign(), cache,
         [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) {
             position += rounding;
             BmpAppendGlyph(
                 blob, runIndex, fontCache, &currStrike, glyph,
                 SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY),
-                color, cache);
+                paint.filteredPremulGrColor(), cache);
         }
     );
 
@@ -156,7 +183,7 @@
     }
 
     SkScalar maxScale = viewMatrix.getMaxScale();
-    SkScalar scaledTextSize = maxScale*skPaint.getTextSize();
+    SkScalar scaledTextSize = maxScale * skPaint.getTextSize();
     // Hinted text looks far better at small resolutions
     // Scaling up beyond 2x yields undesireable artifacts
     if (scaledTextSize < kMinDFFontSize ||
@@ -242,7 +269,7 @@
 
 void GrTextUtils::DrawDFText(GrAtlasTextBlob* blob, int runIndex,
                              GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
-                             const SkPaint& skPaint, GrColor color, uint32_t scalerContextFlags,
+                             const GrTextUtils::Paint& paint, uint32_t scalerContextFlags,
                              const SkMatrix& viewMatrix,
                              const char text[], size_t byteLength,
                              SkScalar x, SkScalar y) {
@@ -253,6 +280,7 @@
         return;
     }
 
+    const SkPaint& skPaint = paint.skPaint();
     SkPaint::GlyphCacheProc glyphCacheProc = SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(),
                                                                         skPaint.isDevKernText(),
                                                                         true);
@@ -311,18 +339,15 @@
     y -= alignY;
     SkPoint offset = SkPoint::Make(x, y);
 
-    DrawDFPosText(blob, runIndex, fontCache, props, skPaint, color, scalerContextFlags, viewMatrix,
-                  text, byteLength, positions.begin(), 2, offset);
+    DrawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, text,
+                  byteLength, positions.begin(), 2, offset);
 }
 
-void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
-                                GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props,
-                                const SkPaint& origPaint,
-                                GrColor color, uint32_t scalerContextFlags,
-                                const SkMatrix& viewMatrix,
-                                const char text[], size_t byteLength,
-                                const SkScalar pos[], int scalarsPerPosition,
-                                const SkPoint& offset) {
+void GrTextUtils::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache* fontCache,
+                                const SkSurfaceProps& props, const GrTextUtils::Paint& paint,
+                                uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
+                                const char text[], size_t byteLength, const SkScalar pos[],
+                                int scalarsPerPosition, const SkPoint& offset) {
     SkASSERT(byteLength == 0 || text != nullptr);
     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
 
@@ -336,10 +361,10 @@
 
     // Setup distance field paint and text ratio
     SkScalar textRatio;
-    SkPaint dfPaint(origPaint);
+    SkPaint dfPaint(paint);
     GrTextUtils::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix);
     blob->setHasDistanceField();
-    blob->setSubRunHasDistanceFields(runIndex, origPaint.isLCDRenderText());
+    blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText());
 
     GrAtlasTextStrike* currStrike = nullptr;
 
@@ -363,13 +388,8 @@
                 SkScalar x = offset.x() + pos[0];
                 SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0);
 
-                if (!DfAppendGlyph(blob,
-                                   runIndex,
-                                   fontCache,
-                                   &currStrike,
-                                   glyph,
-                                   x, y, color, cache,
-                                   textRatio, viewMatrix)) {
+                if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y,
+                                   paint.filteredPremulGrColor(), cache, textRatio, viewMatrix)) {
                     // couldn't append, send to fallback
                     fallbackTxt.append(SkToInt(text-lastText), lastText);
                     *fallbackPos.append() = pos[0];
@@ -395,14 +415,8 @@
                 SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio;
                 SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio;
 
-                if (!DfAppendGlyph(blob,
-                                   runIndex,
-                                   fontCache,
-                                   &currStrike,
-                                   glyph,
-                                   x - advanceX, y - advanceY, color,
-                                   cache,
-                                   textRatio,
+                if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX,
+                                   y - advanceY, paint.filteredPremulGrColor(), cache, textRatio,
                                    viewMatrix)) {
                     // couldn't append, send to fallback
                     fallbackTxt.append(SkToInt(text-lastText), lastText);
@@ -419,9 +433,8 @@
     SkGlyphCache::AttachCache(cache);
     if (fallbackTxt.count()) {
         blob->initOverride(runIndex);
-        GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props,
-                                    origPaint, origPaint.getColor(), scalerContextFlags, viewMatrix,
-                                    fallbackTxt.begin(), fallbackTxt.count(),
+        GrTextUtils::DrawBmpPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags,
+                                    viewMatrix, fallbackTxt.begin(), fallbackTxt.count(),
                                     fallbackPos.begin(), scalarsPerPosition, offset);
     }
 }
@@ -468,12 +481,11 @@
     return true;
 }
 
-void GrTextUtils::DrawTextAsPath(GrContext* context, GrRenderTargetContext* rtc,
-                                 const GrClip& clip,
-                                 const SkPaint& skPaint, const SkMatrix& viewMatrix,
+void GrTextUtils::DrawTextAsPath(GrContext* context, GrRenderTargetContext* rtc, const GrClip& clip,
+                                 const SkPaint& paint, const SkMatrix& viewMatrix,
                                  const char text[], size_t byteLength, SkScalar x, SkScalar y,
                                  const SkIRect& clipBounds) {
-    SkTextToPathIter iter(text, byteLength, skPaint, true);
+    SkTextToPathIter iter(text, byteLength, paint, true);
 
     SkMatrix    matrix;
     matrix.setScale(iter.getPathScale(), iter.getPathScale());
diff --git a/src/gpu/text/GrTextUtils.h b/src/gpu/text/GrTextUtils.h
index 93a0c1f..c5daa42 100644
--- a/src/gpu/text/GrTextUtils.h
+++ b/src/gpu/text/GrTextUtils.h
@@ -9,84 +9,142 @@
 #define GrTextUtils_DEFINED
 
 #include "GrColor.h"
+#include "SkColorFilter.h"
+#include "SkGr.h"
 #include "SkPaint.h"
 #include "SkScalar.h"
+#include "SkTLazy.h"
 
 class GrAtlasGlyphCache;
 class GrAtlasTextBlob;
 class GrAtlasTextStrike;
 class GrClip;
 class GrContext;
+class GrPaint;
 class GrRenderTargetContext;
 class GrShaderCaps;
+class SkDrawFilter;
 class SkGlyph;
 class SkMatrix;
 struct SkIRect;
 struct SkPoint;
 class SkGlyphCache;
+class SkTextBlobRunIterator;
 class SkSurfaceProps;
 
-/*
+/**
  * A class to house a bunch of common text utilities.  This class should *ONLY* have static
  * functions.  It is not a namespace only because we wish to friend SkPaint
- *
  */
 class GrTextUtils {
 public:
+    /**
+     *  This is used to wrap a SkPaint and its post-color filter color. It is also used by RunPaint
+     *  (below). This keeps a pointer to the SkPaint it is initialized with and expects it to remain
+     *  const. It is also used to transform to GrPaint.
+     */
+    class Paint {
+    public:
+        explicit Paint(const SkPaint* paint) : fPaint(paint) { 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; }
+
+        const SkPaint& skPaint() const { return *fPaint; }
+        operator const SkPaint&() const { return this->skPaint(); }
+
+        bool toGrPaint(GrMaskFormat, GrRenderTargetContext*, const SkMatrix& viewMatrix,
+                       GrPaint*) const;
+
+    protected:
+        void initFilteredColor() {
+            fFilteredSkColor = fPaint->getColor();
+            if (fPaint->getColorFilter()) {
+                fFilteredSkColor = fPaint->getColorFilter()->filterColor(fFilteredSkColor);
+            }
+            fFilteredGrColor = SkColorToPremulGrColor(fFilteredSkColor);
+        }
+        Paint() = default;
+        const SkPaint* fPaint;
+        // 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;
+    };
+
+    /**
+     *  An extension of Paint that incorporated per-run modifications to the paint text settings and
+     *  application of a draw filter. It expects its constructor arguments to remain alive and const
+     *  during its lifetime.
+     */
+    class RunPaint : public Paint {
+    public:
+        RunPaint(const Paint* paint, SkDrawFilter* filter, const SkSurfaceProps& props)
+                : fOriginalPaint(paint), fFilter(filter), fProps(props) {
+            // Initially we represent the original paint.
+            fPaint = &fOriginalPaint->skPaint();
+            fFilteredSkColor = fOriginalPaint->filteredSkColor();
+            fFilteredGrColor = fOriginalPaint->filteredPremulGrColor();
+        }
+
+        bool modifyForRun(const SkTextBlobRunIterator&);
+
+    private:
+        SkTLazy<SkPaint> fModifiedPaint;
+        const Paint* fOriginalPaint;
+        SkDrawFilter* fFilter;
+        const SkSurfaceProps& fProps;
+    };
+
     // Functions for appending BMP text to GrAtlasTextBlob
-    static void DrawBmpText(GrAtlasTextBlob*, int runIndex,
-                            GrAtlasGlyphCache*, const SkSurfaceProps&,
-                            const SkPaint&,
-                            GrColor, uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
-                            const char text[], size_t byteLength,
+    static void DrawBmpText(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
+                            const SkSurfaceProps&, const Paint& paint, uint32_t scalerContextFlags,
+                            const SkMatrix& viewMatrix, const char text[], size_t byteLength,
                             SkScalar x, SkScalar y);
 
-    static void DrawBmpPosText(GrAtlasTextBlob*, int runIndex,
-                               GrAtlasGlyphCache*, const SkSurfaceProps&, const SkPaint&,
-                               GrColor, uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
-                               const char text[], size_t byteLength,
-                               const SkScalar pos[], int scalarsPerPosition,
-                               const SkPoint& offset);
+    static void DrawBmpPosText(GrAtlasTextBlob*, int runIndex, GrAtlasGlyphCache*,
+                               const SkSurfaceProps&, const Paint& paint,
+                               uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
+                               const char text[], size_t byteLength, const SkScalar pos[],
+                               int scalarsPerPosition, const SkPoint& offset);
 
     // functions for appending distance field text
     static bool CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix,
                                         const SkSurfaceProps& props, const GrShaderCaps& caps);
 
-    static void DrawDFText(GrAtlasTextBlob* blob, int runIndex,
-                           GrAtlasGlyphCache*, const SkSurfaceProps&,
-                           const SkPaint& skPaint, GrColor color, uint32_t scalerContextFlags,
-                           const SkMatrix& viewMatrix,
-                           const char text[], size_t byteLength,
+    static void DrawDFText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache*,
+                           const SkSurfaceProps&, const Paint& paint, uint32_t scalerContextFlags,
+                           const SkMatrix& viewMatrix, const char text[], size_t byteLength,
                            SkScalar x, SkScalar y);
 
-    static void DrawDFPosText(GrAtlasTextBlob* blob, int runIndex,
-                              GrAtlasGlyphCache*, const SkSurfaceProps&, const SkPaint&,
-                              GrColor color, uint32_t scalerContextFlags,
-                              const SkMatrix& viewMatrix,
-                              const char text[], size_t byteLength,
-                              const SkScalar pos[], int scalarsPerPosition,
-                              const SkPoint& offset);
+    static void DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, GrAtlasGlyphCache*,
+                              const SkSurfaceProps&, const Paint& paint,
+                              uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
+                              const char text[], size_t byteLength, const SkScalar pos[],
+                              int scalarsPerPosition, const SkPoint& offset);
 
     // Functions for drawing text as paths
     static void DrawTextAsPath(GrContext*, GrRenderTargetContext*, const GrClip& clip,
-                               const SkPaint& origPaint, const SkMatrix& viewMatrix,
-                               const char text[], size_t byteLength, SkScalar x, SkScalar y,
+                               const SkPaint& paint, const SkMatrix& viewMatrix, const char text[],
+                               size_t byteLength, SkScalar x, SkScalar y,
                                const SkIRect& clipBounds);
 
-    static void DrawPosTextAsPath(GrContext* context,
-                                  GrRenderTargetContext* rtc,
-                                  const SkSurfaceProps& props,
-                                  const GrClip& clip,
-                                  const SkPaint& origPaint, const SkMatrix& viewMatrix,
-                                  const char text[], size_t byteLength,
-                                  const SkScalar pos[], int scalarsPerPosition,
-                                  const SkPoint& offset, const SkIRect& clipBounds);
+    static void DrawPosTextAsPath(GrContext* context, GrRenderTargetContext* rtc,
+                                  const SkSurfaceProps& props, const GrClip& clip,
+                                  const SkPaint& paint, const SkMatrix& viewMatrix,
+                                  const char text[], size_t byteLength, const SkScalar pos[],
+                                  int scalarsPerPosition, const SkPoint& offset,
+                                  const SkIRect& clipBounds);
 
     static bool ShouldDisableLCD(const SkPaint& paint);
 
-    static uint32_t FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint);
 
 private:
+    static uint32_t FilterTextFlags(const SkSurfaceProps& surfaceProps, const SkPaint& paint);
+
     static void InitDistanceFieldPaint(GrAtlasTextBlob* blob,
                                        SkPaint* skPaint,
                                        SkScalar* textRatio,