Add blendmode to GrRTC::drawTexture

This makes drawTexture handle blend modes with a fallback to be consistent
with drawTextureSet. Simple draws (other than non src-over) should be
faster now since they will skip the extra overhead of the GrTextureAdjuster.

It also refactors the GrPaint emulation of GrTextureOp into a function,
which will be reused in the general-purpose quad APIs.

Bug: skia:
Change-Id: Idad67ec749b82c6894df6ec2b57987130125b910
Reviewed-on: https://skia-review.googlesource.com/c/191360
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
diff --git a/gm/clockwise.cpp b/gm/clockwise.cpp
index a8a201f..a93a94e 100644
--- a/gm/clockwise.cpp
+++ b/gm/clockwise.cpp
@@ -154,7 +154,8 @@
         topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, false, 0));
         topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, true, 100));
         rtc->drawTexture(GrNoClip(), sk_ref_sp(topLeftRTC->asTextureProxy()),
-                         GrSamplerState::Filter::kNearest, SK_PMColor4fWHITE, {0, 0, 100, 200},
+                         GrSamplerState::Filter::kNearest, SkBlendMode::kSrcOver,
+                         SK_PMColor4fWHITE, {0, 0, 100, 200},
                          {100, 0, 200, 200}, GrQuadAAFlags::kNone,
                          SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, SkMatrix::I(),
                          nullptr);
@@ -170,7 +171,8 @@
         topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, false, 0));
         topLeftRTC->priv().testingOnly_addDrawOp(ClockwiseTestOp::Make(ctx, true, 100));
         rtc->drawTexture(GrNoClip(), sk_ref_sp(topLeftRTC->asTextureProxy()),
-                         GrSamplerState::Filter::kNearest, SK_PMColor4fWHITE, {0, 0, 100, 200},
+                         GrSamplerState::Filter::kNearest, SkBlendMode::kSrcOver,
+                         SK_PMColor4fWHITE, {0, 0, 100, 200},
                          {200, 0, 300, 200}, GrQuadAAFlags::kNone,
                          SkCanvas::SrcRectConstraint::kStrict_SrcRectConstraint, SkMatrix::I(),
                          nullptr);
diff --git a/src/gpu/GrRenderTargetContext.cpp b/src/gpu/GrRenderTargetContext.cpp
index 21bfebd..b66d0ad 100644
--- a/src/gpu/GrRenderTargetContext.cpp
+++ b/src/gpu/GrRenderTargetContext.cpp
@@ -37,6 +37,7 @@
 #include "SkShadowUtils.h"
 #include "SkSurfacePriv.h"
 #include "effects/GrRRectEffect.h"
+#include "effects/GrTextureDomain.h"
 #include "ops/GrAtlasTextOp.h"
 #include "ops/GrClearOp.h"
 #include "ops/GrClearStencilClipOp.h"
@@ -937,10 +938,30 @@
                                                     viewMatrix, croppedRect));
 }
 
+// Creates a paint for GrFillRectOp that matches behavior of GrTextureOp
+static void draw_texture_to_grpaint(sk_sp<GrTextureProxy> proxy, const SkRect* domain,
+                                    GrSamplerState::Filter filter, SkBlendMode mode,
+                                    const SkPMColor4f& color, sk_sp<GrColorSpaceXform> csXform,
+                                    GrPaint* paint) {
+    paint->setColor4f(color);
+    paint->setXPFactory(SkBlendMode_AsXPFactory(mode));
+
+    std::unique_ptr<GrFragmentProcessor> fp;
+    if (domain) {
+        fp = GrTextureDomainEffect::Make(std::move(proxy), SkMatrix::I(), *domain,
+                                         GrTextureDomain::kClamp_Mode, filter);
+    } else {
+        fp = GrSimpleTextureEffect::Make(std::move(proxy), SkMatrix::I(), filter);
+    }
+
+    fp = GrColorSpaceXformEffect::Make(std::move(fp), csXform);
+    paint->addColorFragmentProcessor(std::move(fp));
+}
+
 void GrRenderTargetContext::drawTexture(const GrClip& clip, sk_sp<GrTextureProxy> proxy,
-                                        GrSamplerState::Filter filter, const SkPMColor4f& color,
-                                        const SkRect& srcRect, const SkRect& dstRect,
-                                        GrQuadAAFlags aaFlags,
+                                        GrSamplerState::Filter filter, SkBlendMode mode,
+                                        const SkPMColor4f& color, const SkRect& srcRect,
+                                        const SkRect& dstRect, GrQuadAAFlags aaFlags,
                                         SkCanvas::SrcRectConstraint constraint,
                                         const SkMatrix& viewMatrix,
                                         sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
@@ -952,6 +973,7 @@
         srcRect.contains(proxy->getWorstCaseBoundsRect())) {
         constraint = SkCanvas::kFast_SrcRectConstraint;
     }
+
     GrAAType aaType =
             this->chooseAAType(GrAA(aaFlags != GrQuadAAFlags::kNone), GrAllowMixedSamples::kNo);
     SkRect clippedDstRect = dstRect;
@@ -960,9 +982,37 @@
                           &clippedSrcRect)) {
         return;
     }
-    auto op = GrTextureOp::Make(fContext, std::move(proxy), filter, color, clippedSrcRect,
-                                clippedDstRect, aaType, aaFlags, constraint, viewMatrix,
-                                std::move(textureColorSpaceXform));
+
+    AutoCheckFlush acf(this->drawingManager());
+
+    std::unique_ptr<GrDrawOp> op;
+    if (mode != SkBlendMode::kSrcOver) {
+        // Emulation mode with GrPaint and GrFillRectOp
+        if (filter != GrSamplerState::Filter::kNearest &&
+            !GrTextureOp::GetFilterHasEffect(viewMatrix, clippedSrcRect, clippedDstRect)) {
+            filter = GrSamplerState::Filter::kNearest;
+        }
+
+        SkRect domain = srcRect;
+        if (constraint == SkCanvas::kStrict_SrcRectConstraint &&
+            filter == GrSamplerState::Filter::kBilerp) {
+            // Inset by 1/2 pixel, which GrTextureOp and GrTextureAdjuster handle automatically
+            domain.inset(0.5f, 0.5f);
+        }
+
+        GrPaint paint;
+        draw_texture_to_grpaint(std::move(proxy),
+                constraint == SkCanvas::kStrict_SrcRectConstraint ? &domain : nullptr,
+                filter, mode, color, std::move(textureColorSpaceXform), &paint);
+        op = GrFillRectOp::MakePerEdgeWithLocalRect(fContext, std::move(paint), aaType, aaFlags,
+                                                    viewMatrix, clippedDstRect, clippedSrcRect);
+    } else {
+        // Can use a lighter weight op that can chain across proxies
+        op = GrTextureOp::Make(fContext, std::move(proxy), filter, color, clippedSrcRect,
+                               clippedDstRect, aaType, aaFlags, constraint, viewMatrix,
+                               std::move(textureColorSpaceXform));
+    }
+
     this->addDrawOp(clip, std::move(op));
 }
 
@@ -975,35 +1025,19 @@
     SkDEBUGCODE(this->validate();)
     GR_CREATE_TRACE_MARKER_CONTEXT("GrRenderTargetContext", "drawTextureSet", fContext);
 
-    AutoCheckFlush acf(this->drawingManager());
-
-    GrAAType aaType = this->chooseAAType(GrAA::kYes, GrAllowMixedSamples::kNo);
     if (mode != SkBlendMode::kSrcOver ||
         !fContext->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
         // Draw one at a time with GrFillRectOp and a GrPaint that emulates what GrTextureOp does
-        const GrXPFactory* xpFactory = SkBlendMode_AsXPFactory(mode);
         for (int i = 0; i < cnt; ++i) {
-            GrPaint paint;
-            paint.setColor4f({set[i].fAlpha, set[i].fAlpha, set[i].fAlpha, set[i].fAlpha});
-            paint.setXPFactory(xpFactory);
-
-            // See if we can disable bilerp filtering when the src and dst rects line up
-            if (filter != GrSamplerState::Filter::kNearest &&
-                !GrTextureOp::GetFilterHasEffect(viewMatrix, set[i].fSrcRect, set[i].fDstRect)) {
-                filter = GrSamplerState::Filter::kNearest;
-            }
-
-            auto fp = GrSimpleTextureEffect::Make(set[i].fProxy, SkMatrix::I(), filter);
-            fp = GrColorSpaceXformEffect::Make(std::move(fp), texXform);
-            paint.addColorFragmentProcessor(std::move(fp));
-
-            auto op = GrFillRectOp::MakePerEdgeWithLocalRect(fContext, std::move(paint), aaType,
-                                                             set[i].fAAFlags, viewMatrix,
-                                                             set[i].fDstRect, set[i].fSrcRect);
-            this->addDrawOp(clip, std::move(op));
+            float alpha = set[i].fAlpha;
+            this->drawTexture(clip, set[i].fProxy, filter, mode, {alpha, alpha, alpha, alpha},
+                              set[i].fSrcRect, set[i].fDstRect, set[i].fAAFlags,
+                              SkCanvas::kFast_SrcRectConstraint, viewMatrix, texXform);
         }
     } else {
         // Can use a single op, avoiding GrPaint creation, and can batch across proxies
+        AutoCheckFlush acf(this->drawingManager());
+        GrAAType aaType = this->chooseAAType(GrAA::kYes, GrAllowMixedSamples::kNo);
         auto op = GrTextureOp::Make(fContext, set, cnt, filter, aaType, viewMatrix,
                                     std::move(texXform));
         this->addDrawOp(clip, std::move(op));
diff --git a/src/gpu/GrRenderTargetContext.h b/src/gpu/GrRenderTargetContext.h
index a4b329e..7854ab0 100644
--- a/src/gpu/GrRenderTargetContext.h
+++ b/src/gpu/GrRenderTargetContext.h
@@ -152,9 +152,9 @@
      * device space.
      */
     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);
+                     SkBlendMode mode, const SkPMColor4f&, const SkRect& srcRect,
+                     const SkRect& dstRect, GrQuadAAFlags, SkCanvas::SrcRectConstraint,
+                     const SkMatrix& viewMatrix, sk_sp<GrColorSpaceXform> texXform);
 
     /** Used with drawTextureSet */
     struct TextureSetEntry {
diff --git a/src/gpu/SkGpuDevice_drawTexture.cpp b/src/gpu/SkGpuDevice_drawTexture.cpp
index f1a0424..da8d9ed 100644
--- a/src/gpu/SkGpuDevice_drawTexture.cpp
+++ b/src/gpu/SkGpuDevice_drawTexture.cpp
@@ -93,8 +93,7 @@
  */
 static bool can_use_draw_texture(const SkPaint& paint) {
     return (!paint.getColorFilter() && !paint.getShader() && !paint.getMaskFilter() &&
-            !paint.getImageFilter() && paint.getFilterQuality() < kMedium_SkFilterQuality &&
-            paint.getBlendMode() == SkBlendMode::kSrcOver);
+            !paint.getImageFilter() && paint.getFilterQuality() < kMedium_SkFilterQuality);
 }
 
 static void draw_texture(const SkPaint& paint, const SkMatrix& ctm, const SkRect* src,
@@ -127,6 +126,21 @@
         case kHigh_SkFilterQuality:
             SK_ABORT("Quality level not allowed.");
     }
+
+    // Must specify the strict constraint when the proxy is not functionally exact and the src
+    // rect would access pixels outside the proxy's content area without the constraint.
+    if (constraint != SkCanvas::kStrict_SrcRectConstraint &&
+        !GrProxyProvider::IsFunctionallyExact(proxy.get())) {
+        // Conservative estimate of how much a coord could be outset from src rect:
+        // 1/2 pixel for AA and 1/2 pixel for bilerp
+        float buffer = 0.5f * (aa == GrAA::kYes) +
+                       0.5f * (filter == GrSamplerState::Filter::kBilerp);
+        SkRect safeBounds = SkRect::MakeWH(proxy->width(), proxy->height());
+        safeBounds.inset(buffer, buffer);
+        if (!safeBounds.contains(srcRect)) {
+            constraint = SkCanvas::kStrict_SrcRectConstraint;
+        }
+    }
     SkPMColor4f color;
     if (GrPixelConfigIsAlphaOnly(proxy->config())) {
         color = SkColor4fPrepForDst(paint.getColor4f(), dstInfo, *rtc->caps()).premul();
@@ -135,8 +149,8 @@
         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));
+    rtc->drawTexture(clip, std::move(proxy), filter, paint.getBlendMode(), color, srcRect, dstRect,
+                     aaFlags, constraint, ctm, std::move(textureXform));
 }
 
 //////////////////////////////////////////////////////////////////////////////
diff --git a/tests/GrSurfaceTest.cpp b/tests/GrSurfaceTest.cpp
index 95f3794..39c6d76 100644
--- a/tests/GrSurfaceTest.cpp
+++ b/tests/GrSurfaceTest.cpp
@@ -472,10 +472,10 @@
                         GrSurfaceOrigin::kTopLeft_GrSurfaceOrigin, GrMipMapped::kNo,
                         GrInternalSurfaceFlags ::kNone, SkBackingFit::kExact, budgeted,
                         GrSurfaceProxy::LazyInstantiationType::kSingleUse);
-                rtc->drawTexture(GrNoClip(), proxy, GrSamplerState::Filter::kNearest, SkPMColor4f(),
-                                 SkRect::MakeWH(kS, kS), SkRect::MakeWH(kS, kS),
-                                 GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
-                                 SkMatrix::I(), nullptr);
+                rtc->drawTexture(GrNoClip(), proxy, GrSamplerState::Filter::kNearest,
+                                 SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(kS, kS),
+                                 SkRect::MakeWH(kS, kS), GrQuadAAFlags::kNone,
+                                 SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
                 // We still have the proxy, which should remain instantiated, thereby keeping the
                 // texture not purgeable.
                 REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
@@ -486,9 +486,9 @@
 
                 // This time we move the proxy into the draw.
                 rtc->drawTexture(GrNoClip(), std::move(proxy), GrSamplerState::Filter::kNearest,
-                                 SkPMColor4f(), SkRect::MakeWH(kS, kS), SkRect::MakeWH(kS, kS),
-                                 GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
-                                 SkMatrix::I(), nullptr);
+                                 SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(kS, kS),
+                                 SkRect::MakeWH(kS, kS), GrQuadAAFlags::kNone,
+                                 SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
                 REPORTER_ASSERT(reporter, idleIDs.find(2) == idleIDs.end());
                 context->flush();
                 context->priv().getGpu()->testingOnly_flushGpuAndSync();
@@ -505,9 +505,9 @@
                         GrInternalSurfaceFlags ::kNone, SkBackingFit::kExact, budgeted,
                         GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
                 rtc->drawTexture(GrNoClip(), std::move(proxy), GrSamplerState::Filter::kNearest,
-                                 SkPMColor4f(), SkRect::MakeWH(kS, kS), SkRect::MakeWH(kS, kS),
-                                 GrQuadAAFlags::kNone, SkCanvas::kFast_SrcRectConstraint,
-                                 SkMatrix::I(), nullptr);
+                                 SkBlendMode::kSrcOver, SkPMColor4f(), SkRect::MakeWH(kS, kS),
+                                 SkRect::MakeWH(kS, kS), GrQuadAAFlags::kNone,
+                                 SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(), nullptr);
                 // At this point the proxy shouldn't even be instantiated, there is no texture with
                 // id 3.
                 REPORTER_ASSERT(reporter, idleIDs.find(3) == idleIDs.end());
@@ -552,8 +552,9 @@
                             auto proxy = context->priv().proxyProvider()->testingOnly_createWrapped(
                                                          texture, kTopLeft_GrSurfaceOrigin);
                             rtc->drawTexture(GrNoClip(), proxy, GrSamplerState::Filter::kNearest,
-                                             SkPMColor4f(), SkRect::MakeWH(kS, kS),
-                                             SkRect::MakeWH(kS, kS), GrQuadAAFlags::kNone,
+                                             SkBlendMode::kSrcOver, SkPMColor4f(),
+                                             SkRect::MakeWH(kS, kS), SkRect::MakeWH(kS, kS),
+                                             GrQuadAAFlags::kNone,
                                              SkCanvas::kFast_SrcRectConstraint, SkMatrix::I(),
                                              nullptr);
                             if (drawType == DrawType::kDrawAndFlush) {