Clamp in GrBicubicEffect (to match SW and for general sanity)

Bug: skia:8809
Change-Id: I9f5b6428113602929b3a1b45c25b623e8da0e264
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/200512
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrTextureProducer.cpp b/src/gpu/GrTextureProducer.cpp
index a5affe9..6587174 100644
--- a/src/gpu/GrTextureProducer.cpp
+++ b/src/gpu/GrTextureProducer.cpp
@@ -229,11 +229,12 @@
             GrTextureDomain::Mode wrapMode = fDomainNeedsDecal ? GrTextureDomain::kDecal_Mode
                                          : GrTextureDomain::kClamp_Mode;
             return GrBicubicEffect::Make(std::move(proxy), textureMatrix, kClampClamp,
-                                         wrapMode, wrapMode,
+                                         wrapMode, wrapMode, this->alphaType(),
                                          kDomain_DomainMode == domainMode ? &domain : nullptr);
         } else {
             return GrBicubicEffect::Make(std::move(proxy), textureMatrix,
-                                         fDomainNeedsDecal ? kDecalDecal : kClampClamp);
+                                         fDomainNeedsDecal ? kDecalDecal : kClampClamp,
+                                         this->alphaType());
         }
     }
 }
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index eb8b84a..0e0efb4 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -981,7 +981,7 @@
             domain.fTop = domain.fBottom = srcRect.centerY();
         }
         if (bicubic) {
-            fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, domain);
+            fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, domain, bitmap.alphaType());
         } else {
             fp = GrTextureDomainEffect::Make(std::move(proxy), texMatrix, domain,
                                              GrTextureDomain::kClamp_Mode, samplerState.filter());
@@ -989,7 +989,7 @@
     } else if (bicubic) {
         SkASSERT(GrSamplerState::Filter::kNearest == samplerState.filter());
         GrSamplerState::WrapMode wrapMode[2] = {samplerState.wrapModeX(), samplerState.wrapModeY()};
-        fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, wrapMode);
+        fp = GrBicubicEffect::Make(std::move(proxy), texMatrix, wrapMode, bitmap.alphaType());
     } else {
         fp = GrSimpleTextureEffect::Make(std::move(proxy), texMatrix, samplerState);
     }
diff --git a/src/gpu/effects/GrBicubicEffect.cpp b/src/gpu/effects/GrBicubicEffect.cpp
index 8de347c..d804021 100644
--- a/src/gpu/effects/GrBicubicEffect.cpp
+++ b/src/gpu/effects/GrBicubicEffect.cpp
@@ -20,6 +20,7 @@
                               GrProcessorKeyBuilder* b) {
         const GrBicubicEffect& bicubicEffect = effect.cast<GrBicubicEffect>();
         b->add32(GrTextureDomain::GLDomain::DomainKey(bicubicEffect.domain()));
+        b->add32(bicubicEffect.alphaType());
     }
 
 protected:
@@ -98,9 +99,17 @@
             "half4 s%d = wx.x * rowColors[0] + wx.y * rowColors[1] + wx.z * rowColors[2] + wx.w * rowColors[3];",
             y);
     }
-    SkString bicubicColor("(wy.x * s0 + wy.y * s1 + wy.z * s2 + wy.w * s3)");
-    fragBuilder->codeAppendf("%s = %s * %s;", args.fOutputColor, bicubicColor.c_str(),
-                             args.fInputColor);
+    fragBuilder->codeAppend("half4 bicubicColor = wy.x * s0 + wy.y * s1 + wy.z * s2 + wy.w * s3;");
+    // Bicubic can send colors out of range, so clamp to get them back in (source) gamut.
+    // The kind of clamp we have to do depends on the alpha type.
+    if (kPremul_SkAlphaType == bicubicEffect.alphaType()) {
+        fragBuilder->codeAppend("bicubicColor.a = saturate(bicubicColor.a);");
+        fragBuilder->codeAppend(
+                "bicubicColor.rgb = max(half3(0.0), min(bicubicColor.rgb, bicubicColor.aaa));");
+    } else {
+        fragBuilder->codeAppend("bicubicColor = saturate(bicubicColor);");
+    }
+    fragBuilder->codeAppendf("%s = bicubicColor * %s;", args.fOutputColor, args.fInputColor);
 }
 
 void GrGLBicubicEffect::onSetData(const GrGLSLProgramDataManager& pdman,
@@ -120,14 +129,16 @@
 GrBicubicEffect::GrBicubicEffect(sk_sp<GrTextureProxy> proxy,
                                  const SkMatrix& matrix, const SkRect& domain,
                                  const GrSamplerState::WrapMode wrapModes[2],
-                                 GrTextureDomain::Mode modeX, GrTextureDomain::Mode modeY)
+                                 GrTextureDomain::Mode modeX, GrTextureDomain::Mode modeY,
+                                 SkAlphaType alphaType)
         : INHERITED{kGrBicubicEffect_ClassID,
                     ModulateForSamplerOptFlags(proxy->config(),
                             GrTextureDomain::IsDecalSampled(wrapModes, modeX,modeY))}
         , fCoordTransform(matrix, proxy.get())
         , fDomain(proxy.get(), domain, modeX, modeY)
         , fTextureSampler(std::move(proxy),
-                          GrSamplerState(wrapModes, GrSamplerState::Filter::kNearest)) {
+                          GrSamplerState(wrapModes, GrSamplerState::Filter::kNearest))
+        , fAlphaType(alphaType) {
     this->addCoordTransform(&fCoordTransform);
     this->setTextureSamplerCnt(1);
 }
@@ -136,7 +147,8 @@
         : INHERITED(kGrBicubicEffect_ClassID, that.optimizationFlags())
         , fCoordTransform(that.fCoordTransform)
         , fDomain(that.fDomain)
-        , fTextureSampler(that.fTextureSampler) {
+        , fTextureSampler(that.fTextureSampler)
+        , fAlphaType(that.fAlphaType) {
     this->addCoordTransform(&fCoordTransform);
     this->setTextureSamplerCnt(1);
 }
@@ -163,7 +175,8 @@
                                         : GrProcessorUnitTest::kAlphaTextureIdx;
     static const GrSamplerState::WrapMode kClampClamp[] = {GrSamplerState::WrapMode::kClamp,
                                                            GrSamplerState::WrapMode::kClamp};
-    return GrBicubicEffect::Make(d->textureProxy(texIdx), SkMatrix::I(), kClampClamp);
+    SkAlphaType alphaType = d->fRandom->nextBool() ? kPremul_SkAlphaType : kUnpremul_SkAlphaType;
+    return GrBicubicEffect::Make(d->textureProxy(texIdx), SkMatrix::I(), kClampClamp, alphaType);
 }
 #endif
 
diff --git a/src/gpu/effects/GrBicubicEffect.h b/src/gpu/effects/GrBicubicEffect.h
index be2e44c..9086b5a 100644
--- a/src/gpu/effects/GrBicubicEffect.h
+++ b/src/gpu/effects/GrBicubicEffect.h
@@ -28,16 +28,19 @@
 
     const GrTextureDomain& domain() const { return fDomain; }
 
+    SkAlphaType alphaType() const { return fAlphaType; }
+
     /**
      * Create a Mitchell filter effect with specified texture matrix and x/y tile modes.
      */
     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
                                                      const SkMatrix& matrix,
-                                                     const GrSamplerState::WrapMode wrapModes[2]) {
+                                                     const GrSamplerState::WrapMode wrapModes[2],
+                                                     SkAlphaType alphaType) {
         // Ignore the domain on x and y, since this factory relies solely on the wrap mode of the
         // sampler to constrain texture coordinates
         return Make(std::move(proxy), matrix, wrapModes, GrTextureDomain::kIgnore_Mode,
-                    GrTextureDomain::kIgnore_Mode);
+                    GrTextureDomain::kIgnore_Mode, alphaType);
     }
 
     /**
@@ -50,11 +53,12 @@
                                                      const GrSamplerState::WrapMode wrapModes[2],
                                                      GrTextureDomain::Mode modeX,
                                                      GrTextureDomain::Mode modeY,
+                                                     SkAlphaType alphaType,
                                                      const SkRect* domain = nullptr) {
         SkRect resolvedDomain = domain ? *domain : GrTextureDomain::MakeTexelDomain(
                 SkIRect::MakeWH(proxy->width(), proxy->height()), modeX, modeY);
         return std::unique_ptr<GrFragmentProcessor>(new GrBicubicEffect(
-                std::move(proxy), matrix, resolvedDomain, wrapModes, modeX, modeY));
+                std::move(proxy), matrix, resolvedDomain, wrapModes, modeX, modeY, alphaType));
     }
 
     /**
@@ -62,11 +66,12 @@
      */
     static std::unique_ptr<GrFragmentProcessor> Make(sk_sp<GrTextureProxy> proxy,
                                                      const SkMatrix& matrix,
-                                                     const SkRect& domain) {
+                                                     const SkRect& domain,
+                                                     SkAlphaType alphaType) {
         static const GrSamplerState::WrapMode kClampClamp[] = {
                 GrSamplerState::WrapMode::kClamp, GrSamplerState::WrapMode::kClamp};
         return Make(std::move(proxy), matrix, kClampClamp, GrTextureDomain::kClamp_Mode,
-                GrTextureDomain::kClamp_Mode, &domain);
+                GrTextureDomain::kClamp_Mode, alphaType, &domain);
     }
 
     /**
@@ -82,7 +87,8 @@
 private:
     GrBicubicEffect(sk_sp<GrTextureProxy>, const SkMatrix& matrix, const SkRect& domain,
                     const GrSamplerState::WrapMode wrapModes[2],
-                    GrTextureDomain::Mode modeX, GrTextureDomain::Mode modeY);
+                    GrTextureDomain::Mode modeX, GrTextureDomain::Mode modeY,
+                    SkAlphaType alphaType);
     explicit GrBicubicEffect(const GrBicubicEffect&);
 
     GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
@@ -96,6 +102,7 @@
     GrCoordTransform fCoordTransform;
     GrTextureDomain fDomain;
     TextureSampler fTextureSampler;
+    SkAlphaType fAlphaType;
 
     GR_DECLARE_FRAGMENT_PROCESSOR_TEST
 
diff --git a/src/shaders/SkImageShader.cpp b/src/shaders/SkImageShader.cpp
index 2195019..2065b7b 100644
--- a/src/shaders/SkImageShader.cpp
+++ b/src/shaders/SkImageShader.cpp
@@ -237,7 +237,8 @@
     if (doBicubic) {
         // domainX and domainY will properly apply the decal effect with the texture domain used in
         // the bicubic filter if clamp to border was unsupported in hardware
-        inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes, domainX, domainY);
+        inner = GrBicubicEffect::Make(std::move(proxy), lmInverse, wrapModes, domainX, domainY,
+                                      fImage->alphaType());
     } else {
         if (domainX != GrTextureDomain::kIgnore_Mode || domainY != GrTextureDomain::kIgnore_Mode) {
             SkRect domain = GrTextureDomain::MakeTexelDomain(