For TextureOp + Alpha texture, xform paint color to dest color space

This is an unfortunate amount of plumbing for this, but benchmarks
confirm that we're better off doing this work in the vertex shader
(with a ubyte attribute) than using a float4 attribute.

Change-Id: I358d330ee452ea0a89cdd725019c8df2686036a0
Reviewed-on: https://skia-review.googlesource.com/144351
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/gm/clockwise.cpp b/gm/clockwise.cpp
index 0ca6e0a..00bc47e 100644
--- a/gm/clockwise.cpp
+++ b/gm/clockwise.cpp
@@ -168,7 +168,7 @@
                          GrSamplerState::Filter::kNearest, 0xffffffff, {0,0,100,200},
                          {100,0,200,200}, GrAA::kNo,
                          SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, SkMatrix::I(),
-                         nullptr);
+                         nullptr, nullptr);
     }
 
     // Draw the test to an off-screen, bottom-up render target.
@@ -183,7 +183,7 @@
                          GrSamplerState::Filter::kNearest, 0xffffffff, {0,0,100,200},
                          {200,0,300,200}, GrAA::kNo,
                          SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, SkMatrix::I(),
-                         nullptr);
+                         nullptr, nullptr);
     }
 }
 
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 7d88bc4..a67e19a 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -764,7 +764,8 @@
                                         const SkRect& srcRect, const SkRect& dstRect, GrAA aa,
                                         SkCanvas::SrcRectConstraint constraint,
                                         const SkMatrix& viewMatrix,
-                                        sk_sp<GrColorSpaceXform> colorSpaceXform) {
+                                        sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+                                        sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
     ASSERT_SINGLE_OWNER
     RETURN_IF_ABANDONED
     SkDEBUGCODE(this->validate();)
@@ -790,7 +791,8 @@
     }
     this->addDrawOp(clip, GrTextureOp::Make(fContext, std::move(proxy), filter, color,
                                             clippedSrcRect, clippedDstRect, aaType, constraint,
-                                            viewMatrix, std::move(colorSpaceXform)));
+                                            viewMatrix, std::move(textureColorSpaceXform),
+                                            std::move(paintColorSpaceXform)));
 }
 
 void GrRenderTargetContext::fillRectWithLocalMatrix(const GrClip& clip,
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index 565a2b8..4e31a1a 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -148,7 +148,7 @@
     void drawTexture(const GrClip& clip, sk_sp<GrTextureProxy>, GrSamplerState::Filter, GrColor,
                      const SkRect& srcRect, const SkRect& dstRect, GrAA aa,
                      SkCanvas::SrcRectConstraint, const SkMatrix& viewMatrix,
-                     sk_sp<GrColorSpaceXform>);
+                     sk_sp<GrColorSpaceXform> texXform, sk_sp<GrColorSpaceXform> colorXform);
 
     /**
      * Draw a roundrect using a paint.
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index af397cd..9b2641e 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -99,7 +99,6 @@
 static void draw_texture(const SkPaint& paint, const SkMatrix& ctm, const SkRect* src,
                          const SkRect* dst, GrAA aa, SkCanvas::SrcRectConstraint constraint,
                          sk_sp<GrTextureProxy> proxy,
-
                          SkColorSpace* colorSpace, const GrClip& clip, GrRenderTargetContext* rtc) {
     SkASSERT(!(SkToBool(src) && !SkToBool(dst)));
     SkRect srcRect = src ? *src : SkRect::MakeWH(proxy->width(), proxy->height());
@@ -111,7 +110,7 @@
         SkAssertResult(srcRect.intersect(SkRect::MakeIWH(proxy->width(), proxy->height())));
         srcToDst.mapRect(&dstRect, srcRect);
     }
-    auto csxf = GrColorSpaceXform::Make(colorSpace, rtc->colorSpaceInfo().colorSpace());
+    auto textureXform = GrColorSpaceXform::Make(colorSpace, rtc->colorSpaceInfo().colorSpace());
     GrSamplerState::Filter filter;
     switch (paint.getFilterQuality()) {
         case kNone_SkFilterQuality:
@@ -124,11 +123,18 @@
         case kHigh_SkFilterQuality:
             SK_ABORT("Quality level not allowed.");
     }
-    GrColor color = GrPixelConfigIsAlphaOnly(proxy->config())
-                            ? SkColorToPremulGrColor(paint.getColor())
-                            : SkColorAlphaToGrColor(paint.getColor());
+    GrColor color;
+    sk_sp<GrColorSpaceXform> paintColorXform = nullptr;
+    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());
+    } else {
+        color = SkColorAlphaToGrColor(paint.getColor());
+    }
     rtc->drawTexture(clip, std::move(proxy), filter, color, srcRect, dstRect, aa, constraint, ctm,
-                     std::move(csxf));
+                     std::move(textureXform), std::move(paintColorXform));
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 99ecdf8..516cee6 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -105,7 +105,9 @@
     }
 
     static sk_sp<GrGeometryProcessor> Make(sk_sp<GrTextureProxy> proxies[], int proxyCnt,
-                                           sk_sp<GrColorSpaceXform> csxf, bool coverageAA,
+                                           sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+                                           sk_sp<GrColorSpaceXform> paintColorSpaceXform,
+                                           bool coverageAA,
                                            bool perspective, Domain domain,
                                            const GrSamplerState::Filter filters[],
                                            const GrShaderCaps& caps) {
@@ -115,7 +117,9 @@
         size_t size = sizeof(TextureGeometryProcessor) + sizeof(TextureSampler) * (samplerCnt - 1);
         void* mem = GrGeometryProcessor::operator new(size);
         return sk_sp<TextureGeometryProcessor>(
-                new (mem) TextureGeometryProcessor(proxies, proxyCnt, samplerCnt, std::move(csxf),
+                new (mem) TextureGeometryProcessor(proxies, proxyCnt, samplerCnt,
+                                                   std::move(textureColorSpaceXform),
+                                                   std::move(paintColorSpaceXform),
                                                    coverageAA, perspective, domain, filters, caps));
     }
 
@@ -129,7 +133,8 @@
     const char* name() const override { return "TextureGeometryProcessor"; }
 
     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
-        b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
+        b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
+        b->add32(GrColorSpaceXform::XformKey(fPaintColorSpaceXform.get()));
         uint32_t x = this->usesCoverageEdgeAA() ? 0 : 1;
         x |= kFloat3_GrVertexAttribType == fPositions.type() ? 0 : 2;
         x |= fDomain.isInitialized() ? 4 : 0;
@@ -143,15 +148,20 @@
                          FPCoordTransformIter&& transformIter) override {
                 const auto& textureGP = proc.cast<TextureGeometryProcessor>();
                 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
-                fColorSpaceXformHelper.setData(pdman, textureGP.fColorSpaceXform.get());
+                fTextureColorSpaceXformHelper.setData(
+                        pdman, textureGP.fTextureColorSpaceXform.get());
+                fPaintColorSpaceXformHelper.setData(pdman, textureGP.fPaintColorSpaceXform.get());
             }
 
         private:
             void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
                 using Interpolation = GrGLSLVaryingHandler::Interpolation;
                 const auto& textureGP = args.fGP.cast<TextureGeometryProcessor>();
-                fColorSpaceXformHelper.emitCode(
-                        args.fUniformHandler, textureGP.fColorSpaceXform.get());
+                fTextureColorSpaceXformHelper.emitCode(
+                        args.fUniformHandler, textureGP.fTextureColorSpaceXform.get());
+                fPaintColorSpaceXformHelper.emitCode(
+                        args.fUniformHandler, textureGP.fPaintColorSpaceXform.get(),
+                        kVertex_GrShaderFlag);
                 if (kFloat2_GrVertexAttribType == textureGP.fPositions.type()) {
                     args.fVaryingHandler->setNoPerspective();
                 }
@@ -163,8 +173,20 @@
                                      args.fUniformHandler,
                                      textureGP.fTextureCoords.asShaderVar(),
                                      args.fFPCoordTransformHandler);
-                args.fVaryingHandler->addPassThroughAttribute(
-                        textureGP.fColors, args.fOutputColor, Interpolation::kCanBeFlat);
+                if (fPaintColorSpaceXformHelper.isNoop()) {
+                    args.fVaryingHandler->addPassThroughAttribute(
+                            textureGP.fColors, args.fOutputColor, Interpolation::kCanBeFlat);
+                } else {
+                    GrGLSLVarying varying(kHalf4_GrSLType);
+                    args.fVaryingHandler->addVarying("color", &varying);
+                    args.fVertBuilder->codeAppend("half4 color = ");
+                    args.fVertBuilder->appendColorGamutXform(textureGP.fColors.name(),
+                                                             &fPaintColorSpaceXformHelper);
+                    args.fVertBuilder->codeAppend(";");
+                    args.fVertBuilder->codeAppendf("%s = half4(color.rgb * color.a, color.a);",
+                                                   varying.vsOut());
+                    args.fFragBuilder->codeAppendf("%s = %s;", args.fOutputColor, varying.fsIn());
+                }
                 args.fFragBuilder->codeAppend("float2 texCoord;");
                 args.fVaryingHandler->addPassThroughAttribute(textureGP.fTextureCoords, "texCoord");
                 if (textureGP.fDomain.isInitialized()) {
@@ -185,21 +207,17 @@
                     args.fFragBuilder->codeAppend("switch (texIdx) {");
                     for (int i = 0; i < textureGP.numTextureSamplers(); ++i) {
                         args.fFragBuilder->codeAppendf("case %d: %s = ", i, args.fOutputColor);
-                        args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
-                                                                          args.fTexSamplers[i],
-                                                                          "texCoord",
-                                                                          kFloat2_GrSLType,
-                                                                          &fColorSpaceXformHelper);
+                        args.fFragBuilder->appendTextureLookupAndModulate(
+                                args.fOutputColor, args.fTexSamplers[i], "texCoord",
+                                kFloat2_GrSLType, &fTextureColorSpaceXformHelper);
                         args.fFragBuilder->codeAppend("; break;");
                     }
                     args.fFragBuilder->codeAppend("}");
                 } else {
                     args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
-                    args.fFragBuilder->appendTextureLookupAndModulate(args.fOutputColor,
-                                                                      args.fTexSamplers[0],
-                                                                      "texCoord",
-                                                                      kFloat2_GrSLType,
-                                                                      &fColorSpaceXformHelper);
+                    args.fFragBuilder->appendTextureLookupAndModulate(
+                            args.fOutputColor, args.fTexSamplers[0], "texCoord",
+                            kFloat2_GrSLType, &fTextureColorSpaceXformHelper);
                 }
                 args.fFragBuilder->codeAppend(";");
                 if (textureGP.usesCoverageEdgeAA()) {
@@ -244,7 +262,8 @@
                     args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
                 }
             }
-            GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
+            GrGLSLColorSpaceXformHelper fTextureColorSpaceXformHelper;
+            GrGLSLColorSpaceXformHelper fPaintColorSpaceXformHelper;
         };
         return new GLSLProcessor;
     }
@@ -268,10 +287,13 @@
     }
 
     TextureGeometryProcessor(sk_sp<GrTextureProxy> proxies[], int proxyCnt, int samplerCnt,
-                             sk_sp<GrColorSpaceXform> csxf, bool coverageAA, bool perspective,
-                             Domain domain, const GrSamplerState::Filter filters[],
-                             const GrShaderCaps& caps)
-            : INHERITED(kTextureGeometryProcessor_ClassID), fColorSpaceXform(std::move(csxf)) {
+                             sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+                             sk_sp<GrColorSpaceXform> paintColorSpaceXform,
+                             bool coverageAA, bool perspective, Domain domain,
+                             const GrSamplerState::Filter filters[], const GrShaderCaps& caps)
+            : INHERITED(kTextureGeometryProcessor_ClassID)
+            , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
+            , fPaintColorSpaceXform(std::move(paintColorSpaceXform)) {
         SkASSERT(proxyCnt > 0 && samplerCnt >= proxyCnt);
         fSamplers[0].reset(std::move(proxies[0]), filters[0]);
         this->addTextureSampler(&fSamplers[0]);
@@ -328,7 +350,8 @@
     Attribute fTextureIdx;
     Attribute fDomain;
     Attribute fAAEdges[4];
-    sk_sp<GrColorSpaceXform> fColorSpaceXform;
+    sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
+    sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
     TextureSampler fSamplers[1];
 
     typedef GrGeometryProcessor INHERITED;
@@ -597,12 +620,14 @@
                                           GrAAType aaType,
                                           SkCanvas::SrcRectConstraint constraint,
                                           const SkMatrix& viewMatrix,
-                                          sk_sp<GrColorSpaceXform> csxf) {
+                                          sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+                                          sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
         GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
 
         return pool->allocate<TextureOp>(std::move(proxy), filter, color,
                                          srcRect, dstRect, aaType, constraint,
-                                         viewMatrix, std::move(csxf));
+                                         viewMatrix, std::move(textureColorSpaceXform),
+                                         std::move(paintColorSpaceXform));
     }
 
     ~TextureOp() override {
@@ -685,9 +710,11 @@
     TextureOp(sk_sp<GrTextureProxy> proxy, GrSamplerState::Filter filter, GrColor color,
               const SkRect& srcRect, const SkRect& dstRect, GrAAType aaType,
               SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
-              sk_sp<GrColorSpaceXform> csxf)
+              sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+              sk_sp<GrColorSpaceXform> paintColorSpaceXform)
             : INHERITED(ClassID())
-            , fColorSpaceXform(std::move(csxf))
+            , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
+            , fPaintColorSpaceXform(std::move(paintColorSpaceXform))
             , fProxy0(proxy.release())
             , fFilter0(filter)
             , fProxyCnt(1)
@@ -750,7 +777,8 @@
         Domain domain = fDomain ? Domain::kYes : Domain::kNo;
         bool coverageAA = GrAAType::kCoverage == this->aaType();
         sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
-                proxiesSPs, fProxyCnt, std::move(fColorSpaceXform), coverageAA, fPerspective,
+                proxiesSPs, fProxyCnt, std::move(fTextureColorSpaceXform),
+                std::move(fPaintColorSpaceXform), coverageAA, fPerspective,
                 domain, filters, *target->caps().shaderCaps());
         GrPipeline::InitArgs args;
         args.fProxy = target->proxy();
@@ -842,7 +870,12 @@
     bool onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
         const auto* that = t->cast<TextureOp>();
         const auto& shaderCaps = *caps.shaderCaps();
-        if (!GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
+        if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
+                                       that->fTextureColorSpaceXform.get())) {
+            return false;
+        }
+        if (!GrColorSpaceXform::Equals(fPaintColorSpaceXform.get(),
+                                       that->fPaintColorSpaceXform.get())) {
             return false;
         }
         if (this->aaType() != that->aaType()) {
@@ -850,7 +883,8 @@
         }
         // Because of an issue where GrColorSpaceXform adds the same function every time it is used
         // in a texture lookup, we only allow multiple textures when there is no transform.
-        if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) && !fColorSpaceXform &&
+        if (TextureGeometryProcessor::SupportsMultitexture(shaderCaps) &&
+            !fTextureColorSpaceXform &&
             fMaxApproxDstPixelArea <= shaderCaps.disableImageMultitexturingDstRectAreaThreshold() &&
             that->fMaxApproxDstPixelArea <=
                     shaderCaps.disableImageMultitexturingDstRectAreaThreshold()) {
@@ -999,7 +1033,8 @@
         GrColor fColor;
     };
     SkSTArray<1, Draw, true> fDraws;
-    sk_sp<GrColorSpaceXform> fColorSpaceXform;
+    sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
+    sk_sp<GrColorSpaceXform> fPaintColorSpaceXform;
     // Initially we store a single proxy ptr and a single filter. If we grow to have more than
     // one proxy we instead store pointers to dynamically allocated arrays of size kMaxTextures
     // followed by kMaxTextures filters.
@@ -1035,9 +1070,11 @@
                                GrAAType aaType,
                                SkCanvas::SrcRectConstraint constraint,
                                const SkMatrix& viewMatrix,
-                               sk_sp<GrColorSpaceXform> csxf) {
+                               sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+                               sk_sp<GrColorSpaceXform> paintColorSpaceXform) {
     return TextureOp::Make(context, std::move(proxy), filter, color, srcRect, dstRect, aaType,
-                           constraint, viewMatrix, std::move(csxf));
+                           constraint, viewMatrix, std::move(textureColorSpaceXform),
+                           std::move(paintColorSpaceXform));
 }
 
 }  // namespace GrTextureOp
@@ -1078,7 +1115,8 @@
         filter = (GrSamplerState::Filter)random->nextULessThan(
                 static_cast<uint32_t>(GrSamplerState::Filter::kMipMap) + 1);
     }
-    auto csxf = GrTest::TestColorXform(random);
+    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;
@@ -1086,7 +1124,7 @@
     auto constraint = random->nextBool() ? SkCanvas::kStrict_SrcRectConstraint
                                          : SkCanvas::kFast_SrcRectConstraint;
     return GrTextureOp::Make(context, std::move(proxy), filter, color, srcRect, rect, aaType,
-                             constraint, viewMatrix, std::move(csxf));
+                             constraint, viewMatrix, std::move(texXform), std::move(paintXform));
 }
 
 #endif
diff --git a/src/gpu/ops/GrTextureOp.h b/src/gpu/ops/GrTextureOp.h
index 7211b5e..96cbc70 100644
--- a/src/gpu/ops/GrTextureOp.h
+++ b/src/gpu/ops/GrTextureOp.h
@@ -34,5 +34,6 @@
                                GrAAType,
                                SkCanvas::SrcRectConstraint,
                                const SkMatrix& viewMatrix,
-                               sk_sp<GrColorSpaceXform>);
+                               sk_sp<GrColorSpaceXform> textureXform,
+                               sk_sp<GrColorSpaceXform> paintXform);
 }