Handle F16Norm clamping in SkPaint->GrPaint conversion.

Previously this clamping was inserted by the program builder.

Adds GrSaturateProcessor to handle saturating in the fragment shader.

Clamp the GrPaint color rather than using a fp when possible.

Has to be plumbed through GrTextureOp because that skips SkPaint
conversion.

Removes a usage of GrPixelConfig.

Bug: skia:6718
Change-Id: Ifa6544496d34677f17e797433e6ef3a97be5c2b2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/242558
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.cpp b/src/gpu/ops/GrQuadPerEdgeAA.cpp
index 6c4211c..8b9aa34 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.cpp
+++ b/src/gpu/ops/GrQuadPerEdgeAA.cpp
@@ -850,6 +850,7 @@
 
 class QuadPerEdgeAAGeometryProcessor : public GrGeometryProcessor {
 public:
+    using Saturate = GrTextureOp::Saturate;
 
     static sk_sp<GrGeometryProcessor> Make(const VertexSpec& spec) {
         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(spec));
@@ -859,33 +860,36 @@
                                            GrTextureType textureType,
                                            const GrSamplerState& samplerState,
                                            const GrSwizzle& swizzle, uint32_t extraSamplerKey,
-                                           sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
+                                           sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+                                           Saturate saturate) {
         return sk_sp<QuadPerEdgeAAGeometryProcessor>(new QuadPerEdgeAAGeometryProcessor(
                 vertexSpec, caps, textureType, samplerState, swizzle, extraSamplerKey,
-                std::move(textureColorSpaceXform)));
+                std::move(textureColorSpaceXform), saturate));
     }
 
     const char* name() const override { return "QuadPerEdgeAAGeometryProcessor"; }
 
     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
         // texturing, device-dimensions are single bit flags
-        uint32_t x = fTexDomain.isInitialized() ? 0 : 1;
-        x |= fSampler.isInitialized() ? 0 : 2;
-        x |= fNeedsPerspective ? 0 : 4;
+        uint32_t x = (fTexDomain.isInitialized() ? 0 : 0x1)
+                   | (fSampler.isInitialized()   ? 0 : 0x2)
+                   | (fNeedsPerspective          ? 0 : 0x4)
+                   | (fSaturate == Saturate::kNo ? 0 : 0x8);
         // local coords require 2 bits (3 choices), 00 for none, 01 for 2d, 10 for 3d
         if (fLocalCoord.isInitialized()) {
-            x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 8 : 16;
+            x |= kFloat3_GrVertexAttribType == fLocalCoord.cpuType() ? 0x10 : 0x20;
         }
         // similar for colors, 00 for none, 01 for bytes, 10 for half-floats
         if (fColor.isInitialized()) {
-            x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 32 : 64;
+            x |= kUByte4_norm_GrVertexAttribType == fColor.cpuType() ? 0x40 : 0x80;
         }
         // and coverage mode, 00 for none, 01 for withposition, 10 for withcolor, 11 for
         // position+geomdomain
         SkASSERT(!fGeomDomain.isInitialized() || fCoverageMode == CoverageMode::kWithPosition);
         if (fCoverageMode != CoverageMode::kNone) {
-            x |= fGeomDomain.isInitialized() ?
-                    384 : (CoverageMode::kWithPosition == fCoverageMode ? 128 : 256);
+            x |= fGeomDomain.isInitialized()
+                         ? 0x300
+                         : (CoverageMode::kWithPosition == fCoverageMode ? 0x100 : 0x200);
         }
 
         b->add32(GrColorSpaceXform::XformKey(fTextureColorSpaceXform.get()));
@@ -990,6 +994,14 @@
                         args.fOutputColor, args.fTexSamplers[0], "texCoord", kFloat2_GrSLType,
                         &fTextureColorSpaceXformHelper);
                     args.fFragBuilder->codeAppend(";");
+                    if (gp.fSaturate == Saturate::kYes) {
+                        args.fFragBuilder->codeAppendf("%s = saturate(%s);",
+                                                       args.fOutputColor, args.fOutputColor);
+                    }
+                } else {
+                    // Saturate is only intended for use with a proxy to account for the fact
+                    // that GrTextureOp skips SkPaint conversion, which normally handles this.
+                    SkASSERT(gp.fSaturate == Saturate::kNo);
                 }
 
                 // And lastly, output the coverage calculation code
@@ -1056,8 +1068,10 @@
                                    const GrSamplerState& samplerState,
                                    const GrSwizzle& swizzle,
                                    uint32_t extraSamplerKey,
-                                   sk_sp<GrColorSpaceXform> textureColorSpaceXform)
+                                   sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+                                   Saturate saturate)
             : INHERITED(kQuadPerEdgeAAGeometryProcessor_ClassID)
+            , fSaturate(saturate)
             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
             , fSampler(textureType, samplerState, swizzle, extraSamplerKey) {
         SkASSERT(spec.hasLocalCoords());
@@ -1122,6 +1136,8 @@
     // The positions attribute may have coverage built into it, so float3 is an ambiguous type
     // and may mean 2d with coverage, or 3d with no coverage
     bool fNeedsPerspective;
+    // Should saturate() be called on the color? Only relevant when created with a texture.
+    Saturate fSaturate = Saturate::kNo;
     CoverageMode fCoverageMode;
 
     // Color space will be null and fSampler.isInitialized() returns false when the GP is configured
@@ -1140,9 +1156,11 @@
                                                  GrTextureType textureType,
                                                  const GrSamplerState& samplerState,
                                                  const GrSwizzle& swizzle, uint32_t extraSamplerKey,
-                                                 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
+                                                 sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+                                                 Saturate saturate) {
     return QuadPerEdgeAAGeometryProcessor::Make(spec, caps, textureType, samplerState, swizzle,
-                                                extraSamplerKey, std::move(textureColorSpaceXform));
+                                                extraSamplerKey, std::move(textureColorSpaceXform),
+                                                saturate);
 }
 
 } // namespace GrQuadPerEdgeAA
diff --git a/src/gpu/ops/GrQuadPerEdgeAA.h b/src/gpu/ops/GrQuadPerEdgeAA.h
index ca92859..3558218 100644
--- a/src/gpu/ops/GrQuadPerEdgeAA.h
+++ b/src/gpu/ops/GrQuadPerEdgeAA.h
@@ -16,12 +16,14 @@
 #include "src/gpu/GrSamplerState.h"
 #include "src/gpu/geometry/GrQuad.h"
 #include "src/gpu/ops/GrMeshDrawOp.h"
+#include "src/gpu/ops/GrTextureOp.h"
 
 class GrCaps;
 class GrColorSpaceXform;
 class GrShaderCaps;
 
 namespace GrQuadPerEdgeAA {
+    using Saturate = GrTextureOp::Saturate;
 
     enum class Domain : bool { kNo = false, kYes = true };
     enum class ColorType { kNone, kByte, kHalf, kLast = kHalf };
@@ -84,7 +86,7 @@
     sk_sp<GrGeometryProcessor> MakeTexturedProcessor(
             const VertexSpec& spec, const GrShaderCaps& caps, GrTextureType textureType,
             const GrSamplerState& samplerState, const GrSwizzle& swizzle, uint32_t extraSamplerKey,
-            sk_sp<GrColorSpaceXform> textureColorSpaceXform);
+            sk_sp<GrColorSpaceXform> textureColorSpaceXform, Saturate saturate);
 
     // Fill vertices with the vertex data needed to represent the given quad. The device position,
     // local coords, vertex color, domain, and edge coefficients will be written and/or computed
diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp
index 4c04ce0..c77b250 100644
--- a/src/gpu/ops/GrTextureOp.cpp
+++ b/src/gpu/ops/GrTextureOp.cpp
@@ -5,8 +5,8 @@
  * found in the LICENSE file.
  */
 
-#include "src/gpu/ops/GrTextureOp.h"
 #include <new>
+
 #include "include/core/SkPoint.h"
 #include "include/core/SkPoint3.h"
 #include "include/gpu/GrTexture.h"
@@ -31,6 +31,7 @@
 #include "src/gpu/GrTextureProxy.h"
 #include "src/gpu/SkGr.h"
 #include "src/gpu/effects/GrTextureDomain.h"
+#include "src/gpu/effects/generated/GrSaturateProcessor.h"
 #include "src/gpu/geometry/GrQuad.h"
 #include "src/gpu/geometry/GrQuadBuffer.h"
 #include "src/gpu/geometry/GrQuadUtils.h"
@@ -38,6 +39,7 @@
 #include "src/gpu/ops/GrFillRectOp.h"
 #include "src/gpu/ops/GrMeshDrawOp.h"
 #include "src/gpu/ops/GrQuadPerEdgeAA.h"
+#include "src/gpu/ops/GrTextureOp.h"
 
 namespace {
 
@@ -146,28 +148,31 @@
                                           sk_sp<GrColorSpaceXform> textureXform,
                                           GrSamplerState::Filter filter,
                                           const SkPMColor4f& color,
+                                          GrTextureOp::Saturate saturate,
                                           GrAAType aaType,
                                           GrQuadAAFlags aaFlags,
                                           const GrQuad& deviceQuad,
                                           const GrQuad& localQuad,
                                           const SkRect* domain) {
         GrOpMemoryPool* pool = context->priv().opMemoryPool();
-        return pool->allocate<TextureOp>(
-                std::move(proxy), std::move(textureXform), filter, color, aaType, aaFlags,
-                deviceQuad, localQuad, domain);
+        return pool->allocate<TextureOp>(std::move(proxy), std::move(textureXform), filter, color,
+                                         saturate, aaType, aaFlags, deviceQuad, localQuad, domain);
     }
     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
                                           const GrRenderTargetContext::TextureSetEntry set[],
-                                          int cnt, GrSamplerState::Filter filter, GrAAType aaType,
+                                          int cnt,
+                                          GrSamplerState::Filter filter,
+                                          GrTextureOp::Saturate saturate,
+                                          GrAAType aaType,
                                           SkCanvas::SrcRectConstraint constraint,
                                           const SkMatrix& viewMatrix,
                                           sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
         size_t size = sizeof(TextureOp) + sizeof(Proxy) * (cnt - 1);
         GrOpMemoryPool* pool = context->priv().opMemoryPool();
         void* mem = pool->allocate(size);
-        return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(
-                set, cnt, filter, aaType, constraint, viewMatrix,
-                std::move(textureColorSpaceXform)));
+        return std::unique_ptr<GrDrawOp>(new (mem) TextureOp(set, cnt, filter, saturate, aaType,
+                                                             constraint, viewMatrix,
+                                                             std::move(textureColorSpaceXform)));
     }
 
     ~TextureOp() override {
@@ -263,13 +268,20 @@
 
     // dstQuad should be the geometry transformed by the view matrix. If domainRect
     // is not null it will be used to apply the strict src rect constraint.
-    TextureOp(sk_sp<GrTextureProxy> proxy, sk_sp<GrColorSpaceXform> textureColorSpaceXform,
-              GrSamplerState::Filter filter, const SkPMColor4f& color,
-              GrAAType aaType, GrQuadAAFlags aaFlags,
-              const GrQuad& dstQuad, const GrQuad& srcQuad, const SkRect* domainRect)
+    TextureOp(sk_sp<GrTextureProxy> proxy,
+              sk_sp<GrColorSpaceXform> textureColorSpaceXform,
+              GrSamplerState::Filter filter,
+              const SkPMColor4f& color,
+              GrTextureOp::Saturate saturate,
+              GrAAType aaType,
+              GrQuadAAFlags aaFlags,
+              const GrQuad& dstQuad,
+              const GrQuad& srcQuad,
+              const SkRect* domainRect)
             : INHERITED(ClassID())
             , fQuads(1, true /* includes locals */)
             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
+            , fSaturate(static_cast<unsigned>(saturate))
             , fFilter(static_cast<unsigned>(filter)) {
         // Clean up disparities between the overall aa type and edge configuration and apply
         // optimizations based on the rect and matrix when appropriate
@@ -295,13 +307,18 @@
                         IsZeroArea::kNo);
         fDomain = static_cast<unsigned>(domainRect != nullptr);
     }
-    TextureOp(const GrRenderTargetContext::TextureSetEntry set[], int cnt,
-              GrSamplerState::Filter filter, GrAAType aaType,
-              SkCanvas::SrcRectConstraint constraint, const SkMatrix& viewMatrix,
+    TextureOp(const GrRenderTargetContext::TextureSetEntry set[],
+              int cnt,
+              GrSamplerState::Filter filter,
+              GrTextureOp::Saturate saturate,
+              GrAAType aaType,
+              SkCanvas::SrcRectConstraint constraint,
+              const SkMatrix& viewMatrix,
               sk_sp<GrColorSpaceXform> textureColorSpaceXform)
             : INHERITED(ClassID())
             , fQuads(cnt, true /* includes locals */)
             , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
+            , fSaturate(static_cast<unsigned>(saturate))
             , fFilter(static_cast<unsigned>(filter)) {
         fProxyCnt = SkToUInt(cnt);
         SkRect bounds = SkRectPriv::MakeLargestInverted();
@@ -454,9 +471,10 @@
         uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(
                 samplerState, fProxies[0].fProxy->backendFormat());
 
+        auto saturate = static_cast<GrTextureOp::Saturate>(fSaturate);
         sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeTexturedProcessor(
                 vertexSpec, *target->caps().shaderCaps(), textureType, samplerState, swizzle,
-                extraSamplerKey, std::move(fTextureColorSpaceXform));
+                extraSamplerKey, std::move(fTextureColorSpaceXform), saturate);
 
         // We'll use a dynamic state array for the GP textures when there are multiple ops.
         // Otherwise, we use fixed dynamic state to specify the single op's proxy.
@@ -554,6 +572,9 @@
             }
             upgradeToCoverageAAOnMerge = true;
         }
+        if (fSaturate != that->fSaturate) {
+            return CombineResult::kCannotCombine;
+        }
         if (fFilter != that->fFilter) {
             return CombineResult::kCannotCombine;
         }
@@ -587,12 +608,13 @@
 
     GrQuadBuffer<ColorDomainAndAA> fQuads;
     sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
+    unsigned fSaturate : 1;
     unsigned fFilter : 2;
     unsigned fAAType : 2;
     unsigned fDomain : 1;
     unsigned fColorType : 2;
     GR_STATIC_ASSERT(GrQuadPerEdgeAA::kColorTypeCount <= 4);
-    unsigned fProxyCnt : 32 - 7;
+    unsigned fProxyCnt : 32 - 8;
     Proxy fProxies[1];
 
     static_assert(GrQuad::kTypeCount <= 4, "GrQuad::Type does not fit in 2 bits");
@@ -609,6 +631,7 @@
                                sk_sp<GrColorSpaceXform> textureXform,
                                GrSamplerState::Filter filter,
                                const SkPMColor4f& color,
+                               Saturate saturate,
                                SkBlendMode blendMode,
                                GrAAType aaType,
                                GrQuadAAFlags aaFlags,
@@ -627,7 +650,7 @@
 
     if (blendMode == SkBlendMode::kSrcOver) {
         return TextureOp::Make(context, std::move(proxy), std::move(textureXform), filter, color,
-                               aaType, aaFlags, deviceQuad, localQuad, domain);
+                               saturate, aaType, aaFlags, deviceQuad, localQuad, domain);
     } else {
         // Emulate complex blending using GrFillRectOp
         GrPaint paint;
@@ -649,6 +672,9 @@
         }
         fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(textureXform));
         paint.addColorFragmentProcessor(std::move(fp));
+        if (saturate == GrTextureOp::Saturate::kYes) {
+            paint.addColorFragmentProcessor(GrSaturateProcessor::Make());
+        }
 
         return GrFillRectOp::Make(context, std::move(paint), aaType, aaFlags,
                                   deviceQuad, localQuad);
@@ -659,11 +685,12 @@
                                   const GrRenderTargetContext::TextureSetEntry set[],
                                   int cnt,
                                   GrSamplerState::Filter filter,
+                                  Saturate saturate,
                                   GrAAType aaType,
                                   SkCanvas::SrcRectConstraint constraint,
                                   const SkMatrix& viewMatrix,
                                   sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
-    return TextureOp::Make(context, set, cnt, filter, aaType, constraint, viewMatrix,
+    return TextureOp::Make(context, set, cnt, filter, saturate, aaType, constraint, viewMatrix,
                            std::move(textureColorSpaceXform));
 }
 
@@ -719,8 +746,9 @@
     aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
     aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
     bool useDomain = random->nextBool();
+    auto saturate = random->nextBool() ? GrTextureOp::Saturate::kYes : GrTextureOp::Saturate::kNo;
     return GrTextureOp::Make(context, std::move(proxy), std::move(texXform), filter, color,
-                             SkBlendMode::kSrcOver, aaType, aaFlags,
+                             saturate, SkBlendMode::kSrcOver, aaType, aaFlags,
                              GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(srcRect),
                              useDomain ? &srcRect : nullptr);
 }
diff --git a/src/gpu/ops/GrTextureOp.h b/src/gpu/ops/GrTextureOp.h
index efd815e..1e85c29 100644
--- a/src/gpu/ops/GrTextureOp.h
+++ b/src/gpu/ops/GrTextureOp.h
@@ -23,6 +23,12 @@
 namespace GrTextureOp {
 
 /**
+ * Controls whether saturate() is called after the texture is color-converted to ensure all
+ * color values are in 0..1 range.
+ */
+enum class Saturate : bool { kNo = false, kYes = true };
+
+/**
  * Creates an op that draws a sub-quadrilateral of a texture. The passed color is modulated by the
  * texture's color. 'deviceQuad' specifies the device-space coordinates to draw, using 'localQuad'
  * to map into the proxy's texture space. If non-null, 'domain' represents the boundary for the
@@ -33,14 +39,15 @@
  * deconstructed into the texture, filter, modulating color, and blend mode. When blend mode is
  * src over, this will return a GrFillRectOp with a paint that samples the proxy.
  */
-std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
-                               sk_sp<GrTextureProxy> proxy,
-                               sk_sp<GrColorSpaceXform> textureXform,
-                               GrSamplerState::Filter filter,
-                               const SkPMColor4f& color,
-                               SkBlendMode blendMode,
-                               GrAAType aaType,
-                               GrQuadAAFlags aaFlags,
+std::unique_ptr<GrDrawOp> Make(GrRecordingContext*,
+                               sk_sp<GrTextureProxy>,
+                               sk_sp<GrColorSpaceXform>,
+                               GrSamplerState::Filter,
+                               const SkPMColor4f&,
+                               Saturate,
+                               SkBlendMode,
+                               GrAAType,
+                               GrQuadAAFlags,
                                const GrQuad& deviceQuad,
                                const GrQuad& localQuad,
                                const SkRect* domain = nullptr);
@@ -50,6 +57,7 @@
                                   const GrRenderTargetContext::TextureSetEntry[],
                                   int cnt,
                                   GrSamplerState::Filter,
+                                  Saturate,
                                   GrAAType,
                                   SkCanvas::SrcRectConstraint,
                                   const SkMatrix& viewMatrix,