Use half floats for non-normalized color in GrTextureOp

Bug: skia:
Change-Id: Ie681369ef4b1d3d43c326da684afde9ce6d08486
Reviewed-on: https://skia-review.googlesource.com/c/171726
Reviewed-by: Brian Salomon <bsalomon@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 7873dd9..9e857f3 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -800,13 +800,12 @@
 }
 
 void GrRenderTargetContext::drawTexture(const GrClip& clip, sk_sp<GrTextureProxy> proxy,
-                                        GrSamplerState::Filter filter, GrColor color,
+                                        GrSamplerState::Filter filter, const SkPMColor4f& color,
                                         const SkRect& srcRect, const SkRect& dstRect,
                                         GrQuadAAFlags aaFlags,
                                         SkCanvas::SrcRectConstraint constraint,
                                         const SkMatrix& viewMatrix,
-                                        sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-                                        sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
+                                        sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
@@ -825,22 +824,21 @@
     }
     auto op = GrTextureOp::Make(fContext, std::move(proxy), filter, color, clippedSrcRect,
                                 clippedDstRect, aaType, aaFlags, constraint, viewMatrix,
-                                std::move(textureColorSpaceXform), std::move(paintColorSpaceXform));
+                                std::move(textureColorSpaceXform));
     this->addDrawOp(clip, std::move(op));
 }
 
 void GrRenderTargetContext::drawTextureSet(const GrClip& clip, const TextureSetEntry set[], int cnt,
-                                           GrSamplerState::Filter filter, GrColor color,
+                                           GrSamplerState::Filter filter, const SkPMColor4f& color,
                                            const SkMatrix& viewMatrix,
-                                           sk_sp<GrColorSpaceXform> texXform,
-                                           sk_sp<GrColorSpaceXform> colorXform) {
+                                           sk_sp<GrColorSpaceXform> texXform) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
     GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTextureSet", fContext);
     GrAAType aaType = this->chooseAAType(GrAA::kYes, GrAllowMixedSamples::kNo);
     auto op = GrTextureOp::Make(fContext, set, cnt, filter, color, aaType, viewMatrix,
-                                std::move(texXform), std::move(colorXform));
+                                std::move(texXform));
     this->addDrawOp(clip, std::move(op));
 }
 
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index f099134..da34c12 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -9,7 +9,6 @@
 #define GrRenderTargetContext_DEFINED
 
 #include "../private/GrRenderTargetProxy.h"
-#include "GrColor.h"
 #include "GrContext.h"
 #include "GrContextPriv.h"
 #include "GrPaint.h"
@@ -134,10 +133,10 @@
      * specifies the rectangle to draw in local coords which will be transformed by 'viewMatrix' to
      * device space.
      */
-    void drawTexture(const GrClip& clip, sk_sp<GrTextureProxy>, GrSamplerState::Filter, GrColor,
-                     const SkRect& srcRect, const SkRect& dstRect, GrQuadAAFlags,
-                     SkCanvas::SrcRectConstraint, const SkMatrix& viewMatrix,
-                     sk_sp<GrColorSpaceXform> texXform, sk_sp<GrColorSpaceXform> colorXform);
+    void drawTexture(const GrClip& clip, sk_sp<GrTextureProxy>, GrSamplerState::Filter,
+                     const SkPMColor4f&, const SkRect& srcRect, const SkRect& dstRect,
+                     GrQuadAAFlags, SkCanvas::SrcRectConstraint, const SkMatrix& viewMatrix,
+                     sk_sp<GrColorSpaceXform> texXform);
 
     /** Used with drawTextureSet */
     struct TextureSetEntry {
@@ -151,8 +150,8 @@
      * texture color xform. The textures must all have the same GrTextureType and GrConfig.
      */
     void drawTextureSet(const GrClip&, const TextureSetEntry[], int cnt, GrSamplerState::Filter,
-                        GrColor, const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> texXform,
-                        sk_sp<GrColorSpaceXform> colorXform);
+                        const SkPMColor4f&, const SkMatrix& viewMatrix,
+                        sk_sp<GrColorSpaceXform> texXform);
 
     /**
      * Draw a roundrect using a paint.
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index daa86fc..ff9e443 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1464,7 +1464,7 @@
     sampler.setFilterMode(kNone_SkFilterQuality == filterQuality ? GrSamplerState::Filter::kNearest
                                                                  : GrSamplerState::Filter::kBilerp);
     SkAutoTArray<GrRenderTargetContext::TextureSetEntry> textures(count);
-    GrColor color = GrColorPackA4(SkToUInt(SkTClamp(SkScalarRoundToInt(alpha * 255), 0, 255)));
+    SkPMColor4f color = { alpha, alpha, alpha, alpha };
     // We accumulate compatible proxies until we find an an incompatible one or reach the end and
     // issue the accumulated 'n' draws starting at 'base'.
     int base = 0, n = 0;
@@ -1475,7 +1475,7 @@
                     fRenderTargetContext->colorSpaceInfo().colorSpace(), kPremul_SkAlphaType);
             fRenderTargetContext->drawTextureSet(this->clip(), textures.get() + base, n,
                                                  sampler.filter(), color, this->ctm(),
-                                                 std::move(textureXform), nullptr);
+                                                 std::move(textureXform));
         }
     };
     for (int i = 0; i < count; ++i) {
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index 35dd64a..3867465 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -111,9 +111,10 @@
         SkAssertResult(srcRect.intersect(SkRect::MakeIWH(proxy->width(), proxy->height())));
         srcToDst.mapRect(&dstRect, srcRect);
     }
+    const GrColorSpaceInfo& dstInfo(rtc->colorSpaceInfo());
     auto textureXform =
-        GrColorSpaceXform::Make(colorSpace                        , alphaType,
-                                rtc->colorSpaceInfo().colorSpace(), kPremul_SkAlphaType);
+        GrColorSpaceXform::Make(colorSpace          , alphaType,
+                                dstInfo.colorSpace(), kPremul_SkAlphaType);
     GrSamplerState::Filter filter;
     switch (paint.getFilterQuality()) {
         case kNone_SkFilterQuality:
@@ -126,19 +127,16 @@
         case kHigh_SkFilterQuality:
             SK_ABORT("Quality level not allowed.");
     }
-    GrColor color;
-    sk_sp<GrColorSpaceXform> paintColorXform = nullptr;
+    SkPMColor4f color;
     if (GrPixelConfigIsAlphaOnly(proxy->config())) {
-        // Leave the color unpremul if we're going to transform it in the vertex shader
-        paintColorXform = rtc->colorSpaceInfo().refColorSpaceXformFromSRGB();
-        color = paintColorXform ? SkColorToUnpremulGrColor(paint.getColor())
-                                : SkColorToPremulGrColor(paint.getColor());
+        color = SkColor4fPrepForDst(paint.getColor4f(), dstInfo, *rtc->caps()).premul();
     } else {
-        color = GrColorPackA4(paint.getAlpha());
+        float paintAlpha = paint.getColor4f().fA;
+        color = { paintAlpha, paintAlpha, paintAlpha, paintAlpha };
     }
     GrQuadAAFlags aaFlags = aa == GrAA::kYes ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone;
     rtc->drawTexture(clip, std::move(proxy), filter, color, srcRect, dstRect, aaFlags, constraint,
-                     ctm, std::move(textureXform), std::move(paintColorXform));
+                     ctm, std::move(textureXform));
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.cpp b/src/gpu/ops/GrQuadPerEdgeAA.cpp
index d27a503..995c0e5 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.cpp
+++ b/src/gpu/ops/GrQuadPerEdgeAA.cpp
@@ -242,10 +242,11 @@
 ////////////////// Tessellate Implementation
 
 void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
-                 const GrColor& color, const GrPerspQuad& localQuad, const SkRect& domain,
+                 const SkPMColor4f& color4f, const GrPerspQuad& localQuad, const SkRect& domain,
                  GrQuadAAFlags aaFlags) {
     bool deviceHasPerspective = spec.deviceQuadType() == GrQuadType::kPerspective;
     bool localHasPerspective = spec.localQuadType() == GrQuadType::kPerspective;
+    GrVertexColor color(color4f, GrQuadPerEdgeAA::ColorType::kHalf == spec.colorType());
 
     // Load position data into Sk4fs (always x, y and maybe w)
     Sk4f x = deviceQuad.x4f();
@@ -307,7 +308,7 @@
 
         // save color
         if (spec.hasVertexColors()) {
-            vb.write<GrColor>(color);
+            vb.write(color);
         }
 
         // save local position
@@ -361,8 +362,10 @@
         fLocalCoords = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
     } // else localDim == 0 and attribute remains uninitialized
 
-    if (spec.hasVertexColors()) {
+    if (ColorType::kByte == spec.colorType()) {
         fColors = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
+    } else if (ColorType::kHalf == spec.colorType()) {
+        fColors = {"color", kHalf4_GrVertexAttribType, kHalf4_GrSLType};
     }
 
     if (spec.hasDomain()) {
@@ -384,40 +387,28 @@
 }
 
 uint32_t GPAttributes::getKey() const {
-    // aa, color, domain are single bit flags
+    // aa, domain are single bit flags
     uint32_t x = this->usesCoverageAA() ? 0 : 1;
-    x |= this->hasVertexColors() ? 0 : 2;
-    x |= this->hasDomain() ? 0 : 4;
+    x |= this->hasDomain() ? 0 : 2;
     // regular position has two options as well
-    x |= kFloat3_GrVertexAttribType == fPositions.cpuType() ? 0 : 8;
+    x |= kFloat3_GrVertexAttribType == fPositions.cpuType() ? 0 : 4;
     // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
     if (this->hasLocalCoords()) {
-        x |= kFloat3_GrVertexAttribType == fLocalCoords.cpuType() ? 16 : 32;
+        x |= kFloat3_GrVertexAttribType == fLocalCoords.cpuType() ? 8 : 16;
+    }
+    // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
+    if (this->hasVertexColors()) {
+        x |= kUByte4_norm_GrVertexAttribType == fColors.cpuType() ? 32 : 64;
     }
     return x;
 }
 
 void GPAttributes::emitColor(GrGLSLPrimitiveProcessor::EmitArgs& args,
-                             GrGLSLColorSpaceXformHelper* csXformHelper,
                              const char* colorVarName) const {
     using Interpolation = GrGLSLVaryingHandler::Interpolation;
-
-    if (!fColors.isInitialized()) {
-        return;
-    }
-
-    if (csXformHelper == nullptr || csXformHelper->isNoop()) {
-        args.fVaryingHandler->addPassThroughAttribute(
-                fColors, args.fOutputColor, Interpolation::kCanBeFlat);
-    } else {
-        GrGLSLVarying varying(kHalf4_GrSLType);
-        args.fVaryingHandler->addVarying(colorVarName, &varying);
-        args.fVertBuilder->codeAppendf("half4 %s = ", colorVarName);
-        args.fVertBuilder->appendColorGamutXform(fColors.name(), csXformHelper);
-        args.fVertBuilder->codeAppend(";");
-        args.fVertBuilder->codeAppendf("%s = half4(%s.rgb * %s.a, %s.a);",
-                                       varying.vsOut(), colorVarName, colorVarName, colorVarName);
-        args.fFragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
+    if (fColors.isInitialized()) {
+        args.fVaryingHandler->addPassThroughAttribute(fColors, args.fOutputColor,
+                                                      Interpolation::kCanBeFlat);
     }
 }
 
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.h b/src/gpu/ops/GrQuadPerEdgeAA.h
index 578e431..e8b96a0 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.h
+++ b/src/gpu/ops/GrQuadPerEdgeAA.h
@@ -22,6 +22,8 @@
 namespace GrQuadPerEdgeAA {
 
     enum class Domain : bool { kNo = false, kYes = true };
+    enum class ColorType { kNone, kByte, kHalf, kLast = kHalf };
+    static const int kColorTypeCount = static_cast<int>(ColorType::kLast) + 1;
 
     // Specifies the vertex configuration for an op that renders per-edge AA quads. The vertex
     // order (when enabled) is device position, color, local position, domain, aa edge equations.
@@ -29,19 +31,20 @@
     // GPAttributes maintains. If hasLocalCoords is false, then the local quad type can be ignored.
     struct VertexSpec {
     public:
-        VertexSpec(GrQuadType deviceQuadType, bool hasColor, GrQuadType localQuadType,
+        VertexSpec(GrQuadType deviceQuadType, ColorType colorType, GrQuadType localQuadType,
                    bool hasLocalCoords, Domain domain, GrAAType aa)
                 : fDeviceQuadType(static_cast<unsigned>(deviceQuadType))
                 , fLocalQuadType(static_cast<unsigned>(localQuadType))
                 , fHasLocalCoords(hasLocalCoords)
-                , fHasColor(hasColor)
+                , fColorType(static_cast<unsigned>(colorType))
                 , fHasDomain(static_cast<unsigned>(domain))
                 , fUsesCoverageAA(aa == GrAAType::kCoverage) { }
 
         GrQuadType deviceQuadType() const { return static_cast<GrQuadType>(fDeviceQuadType); }
         GrQuadType localQuadType() const { return static_cast<GrQuadType>(fLocalQuadType); }
         bool hasLocalCoords() const { return fHasLocalCoords; }
-        bool hasVertexColors() const { return fHasColor; }
+        ColorType colorType() const { return static_cast<ColorType>(fColorType); }
+        bool hasVertexColors() const { return ColorType::kNone != this->colorType(); }
         bool hasDomain() const { return fHasDomain; }
         bool usesCoverageAA() const { return fUsesCoverageAA; }
 
@@ -52,11 +55,12 @@
 
     private:
         static_assert(kGrQuadTypeCount <= 4, "GrQuadType doesn't fit in 2 bits");
+        static_assert(kColorTypeCount <= 4, "Color doesn't fit in 2 bits");
 
         unsigned fDeviceQuadType: 2;
         unsigned fLocalQuadType: 2;
         unsigned fHasLocalCoords: 1;
-        unsigned fHasColor: 1;
+        unsigned fColorType : 2;
         unsigned fHasDomain: 1;
         unsigned fUsesCoverageAA: 1;
     };
@@ -107,9 +111,7 @@
         // variables the emitted code must declare, so that the calling GP can ensure there's no
         // naming conflicts with their own code.
 
-        void emitColor(GrGLSLPrimitiveProcessor::EmitArgs& args,
-                       GrGLSLColorSpaceXformHelper* colorSpaceXformHelper,
-                       const char* colorVarName) const;
+        void emitColor(GrGLSLPrimitiveProcessor::EmitArgs& args, const char* colorVarName) const;
 
         // localCoordName will be declared as a float2, with any domain applied after any
         // perspective division is performed.
@@ -141,7 +143,7 @@
     // Returns the advanced pointer in vertices.
     // TODO4F: Switch GrColor to GrVertexColor
     void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
-                     const GrColor& color, const GrPerspQuad& localQuad, const SkRect& domain,
+                     const SkPMColor4f& color, const GrPerspQuad& localQuad, const SkRect& domain,
                      GrQuadAAFlags aa);
 
 } // namespace GrQuadPerEdgeAA
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 7f92a1c..dd03f9d 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -38,6 +38,7 @@
 
 using Domain = GrQuadPerEdgeAA::Domain;
 using VertexSpec = GrQuadPerEdgeAA::VertexSpec;
+using ColorType = GrQuadPerEdgeAA::ColorType;
 
 /**
  * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be
@@ -50,18 +51,16 @@
     static sk_sp<GrGeometryProcessor> Make(GrTextureType textureType, GrPixelConfig textureConfig,
                                            const GrSamplerState::Filter filter,
                                            sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-                                           sk_sp<GrColorSpaceXform> paintColorSpaceXform,
                                            const VertexSpec& vertexSpec, const GrShaderCaps& caps) {
         return sk_sp<TextureGeometryProcessor>(new TextureGeometryProcessor(
-                textureType, textureConfig, filter, std::move(textureColorSpaceXform),
-                std::move(paintColorSpaceXform), vertexSpec, caps));
+                textureType, textureConfig, filter, std::move(textureColorSpaceXform), vertexSpec,
+                caps));
     }
 
     const char* name() const override { return "TextureGeometryProcessor"; }
 
     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
         b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
-        b->add32(GrColorSpaceXform::XformKey(fPaintColorSpaceXform.get()));
         b->add32(fAttrs.getKey());
     }
 
@@ -74,7 +73,6 @@
                 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
                 fTextureColorSpaceXformHelper.setData(
                         pdman, textureGP.fTextureColorSpaceXform.get());
-                fPaintColorSpaceXformHelper.setData(pdman, textureGP.fPaintColorSpaceXform.get());
             }
 
         private:
@@ -82,9 +80,6 @@
                 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
                 fTextureColorSpaceXformHelper.emitCode(
                         args.fUniformHandler, textureGP.fTextureColorSpaceXform.get());
-                fPaintColorSpaceXformHelper.emitCode(
-                        args.fUniformHandler, textureGP.fPaintColorSpaceXform.get(),
-                        kVertex_GrShaderFlag);
                 if (!textureGP.fAttrs.needsPerspectiveInterpolation()) {
                     args.fVaryingHandler->setNoPerspective();
                 }
@@ -96,7 +91,7 @@
                                      args.fUniformHandler,
                                      textureGP.fAttrs.localCoords().asShaderVar(),
                                      args.fFPCoordTransformHandler);
-                textureGP.fAttrs.emitColor(args, &fPaintColorSpaceXformHelper, "paintColor");
+                textureGP.fAttrs.emitColor(args, "paintColor");
                 textureGP.fAttrs.emitExplicitLocalCoords(args, "texCoord", "domain");
 
                 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
@@ -108,7 +103,6 @@
                 textureGP.fAttrs.emitCoverage(args, "aaDist");
             }
             GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
-            GrGLSLColorSpaceXformHelper fPaintColorSpaceXformHelper;
         };
         return new GLSLProcessor;
     }
@@ -117,12 +111,10 @@
     TextureGeometryProcessor(GrTextureType textureType, GrPixelConfig textureConfig,
                              GrSamplerState::Filter filter,
                              sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-                             sk_sp<GrColorSpaceXform> paintColorSpaceXform,
                              const VertexSpec& vertexSpec, const GrShaderCaps& caps)
             : INHERITED(kTextureGeometryProcessor_ClassID)
             , fAttrs(vertexSpec)
             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
-            , fPaintColorSpaceXform(std::move(paintColorSpaceXform))
             , fSampler(textureType, textureConfig, filter) {
         SkASSERT(vertexSpec.hasVertexColors() && vertexSpec.localDimensionality() == 2);
         this->setTextureSamplerCnt(1);
@@ -133,7 +125,6 @@
 
     GrQuadPerEdgeAA::GPAttributes fAttrs;
     sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
-    sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
     TextureSampler fSampler;
 
     typedef GrGeometryProcessor INHERITED;
@@ -208,33 +199,31 @@
     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
                                           sk_sp<GrTextureProxy> proxy,
                                           GrSamplerState::Filter filter,
-                                          GrColor color,
+                                          const SkPMColor4f& color,
                                           const SkRect& srcRect,
                                           const SkRect& dstRect,
                                           GrAAType aaType,
                                           GrQuadAAFlags aaFlags,
                                           SkCanvas::SrcRectConstraint constraint,
                                           const SkMatrix& viewMatrix,
-                                          sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-                                          sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
+                                          sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
         GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
 
         return pool->allocate<TextureOp>(
                 std::move(proxy), filter, color, srcRect, dstRect, aaType, aaFlags, constraint,
-                viewMatrix, std::move(textureColorSpaceXform), std::move(paintColorSpaceXform));
+                viewMatrix, std::move(textureColorSpaceXform));
     }
     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
                                           const GrRenderTargetContext::TextureSetEntry set[],
-                                          int cnt, GrSamplerState::Filter filter, GrColor color,
-                                          GrAAType aaType, const SkMatrix& viewMatrix,
-                                          sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-                                          sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
+                                          int cnt, GrSamplerState::Filter filter,
+                                          const SkPMColor4f& color, GrAAType aaType,
+                                          const SkMatrix& viewMatrix,
+                                          sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
         size_t size = sizeof(TextureOp) + sizeof(Proxy) * (cnt - 1);
         GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
         void* mem = pool->allocate(size);
         return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(
-                set, cnt, filter, color, aaType, viewMatrix, std::move(textureColorSpaceXform),
-                std::move(paintColorSpaceXform)));
+                set, cnt, filter, color, aaType, viewMatrix, std::move(textureColorSpaceXform)));
     }
 
     ~TextureOp() override {
@@ -271,7 +260,7 @@
                 str.appendf(
                         "%d: Color: 0x%08x, TexRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f] "
                         "Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
-                        i, quad.color(), quad.srcRect().fLeft, quad.srcRect().fTop,
+                        i, quad.color().toBytes_RGBA(), quad.srcRect().fLeft, quad.srcRect().fTop,
                         quad.srcRect().fRight, quad.srcRect().fBottom, quad.quad().point(0).fX,
                         quad.quad().point(0).fY, quad.quad().point(1).fX, quad.quad().point(1).fY,
                         quad.quad().point(2).fX, quad.quad().point(2).fY, quad.quad().point(3).fX,
@@ -303,14 +292,12 @@
 private:
     friend class ::GrOpMemoryPool;
 
-    TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
+    TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, const SkPMColor4f& color,
               const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType, GrQuadAAFlags aaFlags,
               SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
-              sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-              sk_sp<GrColorSpaceXform> paintColorSpaceXform)
+              sk_sp<GrColorSpaceXform> textureColorSpaceXform)
             : INHERITED(ClassID())
             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
-            , fPaintColorSpaceXform(std::move(paintColorSpaceXform))
             , fFilter(static_cast<unsigned>(filter))
             , fFinalized(0) {
         GrQuadType quadType = GrQuadTypeForTransformedRect(viewMatrix);
@@ -347,16 +334,15 @@
         auto bounds = quad.bounds();
         this->setBounds(bounds, HasAABloat(aaType == GrAAType::kCoverage), IsZeroArea::kNo);
         fDomain = static_cast<unsigned>(draw.domain());
+        fWideColor = !SkPMColor4fFitsInBytes(color);
         fCanSkipAllocatorGather =
                 static_cast<unsigned>(fProxies[0].fProxy->canSkipResourceAllocator());
     }
     TextureOp(const GrRenderTargetContext::TextureSetEntry set[], int cnt,
-              GrSamplerState::Filter filter, GrColor color, GrAAType aaType,
-              const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-              sk_sp<GrColorSpaceXform> paintColorSpaceXform)
+              GrSamplerState::Filter filter, const SkPMColor4f& color, GrAAType aaType,
+              const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> textureColorSpaceXform)
             : INHERITED(ClassID())
             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
-            , fPaintColorSpaceXform(std::move(paintColorSpaceXform))
             , fFilter(static_cast<unsigned>(filter))
             , fFinalized(0) {
         fQuads.reserve(cnt);
@@ -400,6 +386,7 @@
         this->setBounds(bounds, HasAABloat(this->aaType() == GrAAType::kCoverage), IsZeroArea::kNo);
         fPerspective = static_cast<unsigned>(viewMatrix.hasPerspective());
         fDomain = static_cast<unsigned>(false);
+        fWideColor = !SkPMColor4fFitsInBytes(color);
     }
 
     void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy,
@@ -423,6 +410,7 @@
         TRACE_EVENT0("skia", TRACE_FUNC);
         bool hasPerspective = false;
         Domain domain = Domain::kNo;
+        bool wideColor = false;
         int numProxies = 0;
         int numTotalQuads = 0;
         auto textureType = fProxies[0].fProxy->textureType();
@@ -433,6 +421,7 @@
             if (op.fDomain) {
                 domain = Domain::kYes;
             }
+            wideColor |= op.fWideColor;
             numProxies += op.fProxyCnt;
             for (unsigned p = 0; p < op.fProxyCnt; ++p) {
                 numTotalQuads += op.fProxies[p].fQuadCnt;
@@ -450,12 +439,12 @@
         }
 
         VertexSpec vertexSpec(hasPerspective ? GrQuadType::kPerspective : GrQuadType::kStandard,
-                              /* hasColor */ true, GrQuadType::kRect, /* hasLocal */ true,
-                              domain, aaType);
+                              wideColor ? ColorType::kHalf : ColorType::kByte, GrQuadType::kRect,
+                              /* hasLocal */ true, domain, aaType);
 
         sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
                 textureType, config, this->filter(), std::move(fTextureColorSpaceXform),
-                std::move(fPaintColorSpaceXform), vertexSpec, *target->caps().shaderCaps());
+                vertexSpec, *target->caps().shaderCaps());
         GrPipeline::InitArgs args;
         args.fProxy = target->proxy();
         args.fCaps = &target->caps();
@@ -549,10 +538,6 @@
                                        that->fTextureColorSpaceXform.get())) {
             return CombineResult::kCannotCombine;
         }
-        if (!GrColorSpaceXform::Equals(fPaintColorSpaceXform.get(),
-                                       that->fPaintColorSpaceXform.get())) {
-            return CombineResult::kCannotCombine;
-        }
         bool upgradeToCoverageAAOnMerge = false;
         if (this->aaType() != that->aaType()) {
             if (!((this->aaType() == GrAAType::kCoverage && that->aaType() == GrAAType::kNone) ||
@@ -580,6 +565,7 @@
         fQuads.push_back_n(that->fQuads.count(), that->fQuads.begin());
         fPerspective |= that->fPerspective;
         fDomain |= that->fDomain;
+        fWideColor |= that->fWideColor;
         if (upgradeToCoverageAAOnMerge) {
             fAAType = static_cast<unsigned>(GrAAType::kCoverage);
         }
@@ -592,7 +578,7 @@
     class Quad {
     public:
         Quad(const SkRect& srcRect, const GrPerspQuad& quad, GrQuadAAFlags aaFlags,
-             SkCanvas::SrcRectConstraint constraint, GrColor color)
+             SkCanvas::SrcRectConstraint constraint, const SkPMColor4f& color)
                 : fSrcRect(srcRect)
                 , fQuad(quad)
                 , fColor(color)
@@ -602,14 +588,14 @@
         }
         const GrPerspQuad& quad() const { return fQuad; }
         const SkRect& srcRect() const { return fSrcRect; }
-        GrColor color() const { return fColor; }
+        SkPMColor4f color() const { return fColor; }
         Domain domain() const { return Domain(fHasDomain); }
         GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); }
 
     private:
         SkRect fSrcRect;
         GrPerspQuad fQuad;
-        GrColor fColor;
+        SkPMColor4f fColor;
         unsigned fHasDomain : 1;
         unsigned fAAFlags : 4;
     };
@@ -619,11 +605,11 @@
     };
     SkSTArray<1, Quad, true> fQuads;
     sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
-    sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
     unsigned fFilter : 2;
     unsigned fAAType : 2;
     unsigned fPerspective : 1;
     unsigned fDomain : 1;
+    unsigned fWideColor : 1;
     // Used to track whether fProxy is ref'ed or has a pending IO after finalize() is called.
     unsigned fFinalized : 1;
     unsigned fCanSkipAllocatorGather : 1;
@@ -640,31 +626,28 @@
 std::unique_ptr<GrDrawOp> Make(GrContext* context,
                                sk_sp<GrTextureProxy> proxy,
                                GrSamplerState::Filter filter,
-                               GrColor color,
+                               const SkPMColor4f& color,
                                const SkRect& srcRect,
                                const SkRect& dstRect,
                                GrAAType aaType,
                                GrQuadAAFlags aaFlags,
                                SkCanvas::SrcRectConstraint constraint,
                                const SkMatrix& viewMatrix,
-                               sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-                               sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
+                               sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
     return TextureOp::Make(context, std::move(proxy), filter, color, srcRect, dstRect, aaType,
-                           aaFlags, constraint, viewMatrix, std::move(textureColorSpaceXform),
-                           std::move(paintColorSpaceXform));
+                           aaFlags, constraint, viewMatrix, std::move(textureColorSpaceXform));
 }
 
 std::unique_ptr<GrDrawOp> Make(GrContext* context,
                                const GrRenderTargetContext::TextureSetEntry set[],
                                int cnt,
                                GrSamplerState::Filter filter,
-                               GrColor color,
+                               const SkPMColor4f& color,
                                GrAAType aaType,
                                const SkMatrix& viewMatrix,
-                               sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-                               sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
+                               sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
     return TextureOp::Make(context, set, cnt, filter, color, aaType, viewMatrix,
-                           std::move(textureColorSpaceXform), std::move(paintColorSpaceXform));
+                           std::move(textureColorSpaceXform));
 }
 
 }  // namespace GrTextureOp
@@ -698,7 +681,7 @@
     srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
     srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
     SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
-    GrColor color = SkColorToPremulGrColor(random->nextU());
+    SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU()));
     GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
             static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
     while (mipMapped == GrMipMapped::kNo && filter == GrSamplerState::Filter::kMipMap) {
@@ -706,7 +689,6 @@
                 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
     }
     auto texXform = GrTest::TestColorXform(random);
-    auto paintXform = GrTest::TestColorXform(random);
     GrAAType aaType = GrAAType::kNone;
     if (random->nextBool()) {
         aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage;
@@ -719,8 +701,7 @@
     auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
                                          : SkCanvas::kFast_SrcRectConstraint;
     return GrTextureOp::Make(context, std::move(proxy), filter, color, srcRect, rect, aaType,
-                             aaFlags, constraint, viewMatrix, std::move(texXform),
-                             std::move(paintXform));
+                             aaFlags, constraint, viewMatrix, std::move(texXform));
 }
 
 #endif
diff --git a/src/gpu/ops/GrTextureOp.h b/src/gpu/ops/GrTextureOp.h
index 2cf263b..69cb2a7 100644
--- a/src/gpu/ops/GrTextureOp.h
+++ b/src/gpu/ops/GrTextureOp.h
@@ -30,23 +30,21 @@
 std::unique_ptr<GrDrawOp> Make(GrContext*,
                                sk_sp<GrTextureProxy>,
                                GrSamplerState::Filter,
-                               GrColor,
+                               const SkPMColor4f&,
                                const SkRect& srcRect,
                                const SkRect& dstRect,
                                GrAAType,
                                GrQuadAAFlags,
                                SkCanvas::SrcRectConstraint,
                                const SkMatrix& viewMatrix,
-                               sk_sp<GrColorSpaceXform> textureXform,
-                               sk_sp<GrColorSpaceXform> paintXform);
+                               sk_sp<GrColorSpaceXform> textureXform);
 
 std::unique_ptr<GrDrawOp> Make(GrContext*,
                                const GrRenderTargetContext::TextureSetEntry[],
                                int cnt,
                                GrSamplerState::Filter,
-                               GrColor,
+                               const SkPMColor4f&,
                                GrAAType,
                                const SkMatrix& viewMatrix,
-                               sk_sp<GrColorSpaceXform> textureXform,
-                               sk_sp<GrColorSpaceXform> paintXform);
+                               sk_sp<GrColorSpaceXform> textureXform);
 }