Clip text geometrically when possible.

Currently when we clip text we can't batch the clipped text with non-clipped text.
By modifying the quads and texCoords we can produce the same effect, and allow batching.
Includes some minor text code cleanup.

Bug: skia:6990
Change-Id: Ibfd4bc2fdc2d7680071e2abddd4d77fc3017e3d3
Reviewed-on: https://skia-review.googlesource.com/60780
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ops/GrAtlasTextOp.cpp b/src/gpu/ops/GrAtlasTextOp.cpp
index 1c67fe7..fb041e0 100644
--- a/src/gpu/ops/GrAtlasTextOp.cpp
+++ b/src/gpu/ops/GrAtlasTextOp.cpp
@@ -76,6 +76,108 @@
     return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo;
 }
 
+static void clip_quads(const SkIRect& clipRect,
+                       unsigned char* currVertex, unsigned char* blobVertices,
+                       size_t vertexStride, int glyphCount) {
+    for (int i = 0; i < glyphCount; ++i) {
+        SkPoint* blobPositionLT = reinterpret_cast<SkPoint*>(blobVertices);
+        SkPoint* blobPositionRB = reinterpret_cast<SkPoint*>(blobVertices + 3*vertexStride);
+
+        SkRect positionRect = SkRect::MakeLTRB(blobPositionLT->fX,
+                                               blobPositionLT->fY,
+                                               blobPositionRB->fX,
+                                               blobPositionRB->fY);
+        if (clipRect.contains(positionRect)) {
+            memcpy(currVertex, blobVertices, 4 * vertexStride);
+            currVertex += 4 * vertexStride;
+        } else {
+            // Pull out some more data that we'll need.
+            // In the LCD case the color will be garbage, but we'll overwrite it with the texcoords
+            // and it avoids a lot of conditionals.
+            SkColor color = *reinterpret_cast<SkColor*>(blobVertices + sizeof(SkPoint));
+            size_t coordOffset = vertexStride - sizeof(SkIPoint16);
+            SkIPoint16* blobCoordsLT = reinterpret_cast<SkIPoint16*>(blobVertices + coordOffset);
+            SkIPoint16* blobCoordsRB = reinterpret_cast<SkIPoint16*>(blobVertices +
+                                                                     3*vertexStride +
+                                                                     coordOffset);
+            SkIRect coordsRect = SkIRect::MakeLTRB(blobCoordsLT->fX >> 1,
+                                                   blobCoordsLT->fY >> 1,
+                                                   blobCoordsRB->fX >> 1,
+                                                   blobCoordsRB->fY >> 1);
+            int pageIndexX = blobCoordsLT->fX & 0x1;
+            int pageIndexY = blobCoordsLT->fY & 0x1;
+
+            SkASSERT(positionRect.width() == coordsRect.width());
+
+            // Clip position and texCoords to the clipRect
+            if (positionRect.fLeft < clipRect.fLeft) {
+                coordsRect.fLeft += clipRect.fLeft - positionRect.fLeft;
+                positionRect.fLeft = clipRect.fLeft;
+            }
+            if (positionRect.fTop < clipRect.fTop) {
+                coordsRect.fTop += clipRect.fTop - positionRect.fTop;
+                positionRect.fTop = clipRect.fTop;
+            }
+            if (positionRect.fRight > clipRect.fRight) {
+                coordsRect.fRight += clipRect.fRight - positionRect.fRight;
+                positionRect.fRight = clipRect.fRight;
+            }
+            if (positionRect.fBottom > clipRect.fBottom) {
+                coordsRect.fBottom += clipRect.fBottom - positionRect.fBottom;
+                positionRect.fBottom = clipRect.fBottom;
+            }
+            if (positionRect.fLeft > positionRect.fRight) {
+                positionRect.fLeft = positionRect.fRight;
+            }
+            if (positionRect.fTop > positionRect.fBottom) {
+                positionRect.fTop = positionRect.fBottom;
+            }
+            coordsRect.fLeft = coordsRect.fLeft << 1 | pageIndexX;
+            coordsRect.fTop = coordsRect.fTop << 1 | pageIndexY;
+            coordsRect.fRight = coordsRect.fRight << 1 | pageIndexX;
+            coordsRect.fBottom = coordsRect.fBottom << 1 | pageIndexY;
+
+            SkPoint* currPosition = reinterpret_cast<SkPoint*>(currVertex);
+            currPosition->fX = positionRect.fLeft;
+            currPosition->fY = positionRect.fTop;
+            *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
+            SkIPoint16* currCoords = reinterpret_cast<SkIPoint16*>(currVertex + coordOffset);
+            currCoords->fX = coordsRect.fLeft;
+            currCoords->fY = coordsRect.fTop;
+            currVertex += vertexStride;
+
+            currPosition = reinterpret_cast<SkPoint*>(currVertex);
+            currPosition->fX = positionRect.fLeft;
+            currPosition->fY = positionRect.fBottom;
+            *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
+            currCoords = reinterpret_cast<SkIPoint16*>(currVertex + coordOffset);
+            currCoords->fX = coordsRect.fLeft;
+            currCoords->fY = coordsRect.fBottom;
+            currVertex += vertexStride;
+
+            currPosition = reinterpret_cast<SkPoint*>(currVertex);
+            currPosition->fX = positionRect.fRight;
+            currPosition->fY = positionRect.fTop;
+            *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
+            currCoords = reinterpret_cast<SkIPoint16*>(currVertex + coordOffset);
+            currCoords->fX = coordsRect.fRight;
+            currCoords->fY = coordsRect.fTop;
+            currVertex += vertexStride;
+
+            currPosition = reinterpret_cast<SkPoint*>(currVertex);
+            currPosition->fX = positionRect.fRight;
+            currPosition->fY = positionRect.fBottom;
+            *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
+            currCoords = reinterpret_cast<SkIPoint16*>(currVertex + coordOffset);
+            currCoords->fX = coordsRect.fRight;
+            currCoords->fY = coordsRect.fBottom;
+            currVertex += vertexStride;
+        }
+
+        blobVertices += 4 * vertexStride;
+    }
+}
+
 void GrAtlasTextOp::onPrepareDraws(Target* target) {
     // if we have RGB, then we won't have any SkShaders so no need to use a localmatrix.
     // TODO actually only invert if we don't have RGBA
@@ -98,9 +200,7 @@
     flushInfo.fPipeline =
             target->makePipeline(fSRGBFlags, std::move(fProcessors), target->detachAppliedClip());
     if (this->usesDistanceFields()) {
-        flushInfo.fGeometryProcessor =
-            this->setupDfProcessor(this->viewMatrix(),
-                                   fLuminanceColor, this->color(), proxies);
+        flushInfo.fGeometryProcessor = this->setupDfProcessor();
     } else {
         flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
             this->color(), proxies, GrSamplerState::ClampNearest(), maskFormat,
@@ -127,6 +227,7 @@
 
     GrBlobRegenHelper helper(this, target, &flushInfo);
     SkAutoGlyphCache glyphCache;
+    // each of these is a SubRun
     for (int i = 0; i < fGeoCount; i++) {
         const Geometry& args = fGeoData[i];
         Blob* blob = args.fBlob;
@@ -138,7 +239,12 @@
                         &blobVertices, &byteCount, &subRunGlyphCount);
 
         // now copy all vertices
-        memcpy(currVertex, blobVertices, byteCount);
+        if (args.fClipRect.isEmpty()) {
+            memcpy(currVertex, blobVertices, byteCount);
+        } else {
+            clip_quads(args.fClipRect, currVertex, reinterpret_cast<unsigned char*>(blobVertices),
+                       vertexStride, subRunGlyphCount);
+        }
 
         currVertex += byteCount;
     }
@@ -237,13 +343,11 @@
     return true;
 }
 
-// TODO just use class params
 // TODO trying to figure out why lcd is so whack
-sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor(
-                                                const SkMatrix& viewMatrix,
-                                                SkColor luminanceColor,
-                                                GrColor color,
-                                                const sk_sp<GrTextureProxy> p[kMaxTextures]) const {
+// (see comments in GrAtlasTextContext::ComputeCanonicalColor)
+sk_sp<GrGeometryProcessor> GrAtlasTextOp::setupDfProcessor() const {
+    const SkMatrix& viewMatrix = this->viewMatrix();
+    const sk_sp<GrTextureProxy>* p = fFontCache->getProxies(this->maskFormat());
     bool isLCD = this->isLCD();
     // set up any flags
     uint32_t flags = viewMatrix.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
@@ -257,34 +361,35 @@
         flags |= (kLCDBGRDistanceField_MaskType == fMaskType) ? kBGR_DistanceFieldEffectFlag : 0;
 
         float redCorrection = fDistanceAdjustTable->getAdjustment(
-                SkColorGetR(luminanceColor) >> kDistanceAdjustLumShift,
+                SkColorGetR(fLuminanceColor) >> kDistanceAdjustLumShift,
                 fUseGammaCorrectDistanceTable);
         float greenCorrection = fDistanceAdjustTable->getAdjustment(
-                SkColorGetG(luminanceColor) >> kDistanceAdjustLumShift,
+                SkColorGetG(fLuminanceColor) >> kDistanceAdjustLumShift,
                 fUseGammaCorrectDistanceTable);
         float blueCorrection = fDistanceAdjustTable->getAdjustment(
-                SkColorGetB(luminanceColor) >> kDistanceAdjustLumShift,
+                SkColorGetB(fLuminanceColor) >> kDistanceAdjustLumShift,
                 fUseGammaCorrectDistanceTable);
         GrDistanceFieldLCDTextGeoProc::DistanceAdjust widthAdjust =
                 GrDistanceFieldLCDTextGeoProc::DistanceAdjust::Make(
                         redCorrection, greenCorrection, blueCorrection);
 
-        return GrDistanceFieldLCDTextGeoProc::Make(color, viewMatrix, p,
+        return GrDistanceFieldLCDTextGeoProc::Make(this->color(), viewMatrix, p,
                                                    GrSamplerState::ClampBilerp(), widthAdjust,
                                                    flags, this->usesLocalCoords());
     } else {
 #ifdef SK_GAMMA_APPLY_TO_A8
         float correction = 0;
         if (kAliasedDistanceField_MaskType != fMaskType) {
-            U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT, luminanceColor);
+            U8CPU lum = SkColorSpaceLuminance::computeLuminance(SK_GAMMA_EXPONENT,
+                                                                fLuminanceColor);
             correction = fDistanceAdjustTable->getAdjustment(lum >> kDistanceAdjustLumShift,
                                                              fUseGammaCorrectDistanceTable);
         }
-        return GrDistanceFieldA8TextGeoProc::Make(color, viewMatrix, p,
+        return GrDistanceFieldA8TextGeoProc::Make(this->color(), viewMatrix, p,
                                                   GrSamplerState::ClampBilerp(), correction, flags,
                                                   this->usesLocalCoords());
 #else
-        return GrDistanceFieldA8TextGeoProc::Make(color, viewMatrix, p,
+        return GrDistanceFieldA8TextGeoProc::Make(this->color(), viewMatrix, p,
                                                   GrSamplerState::ClampBilerp(), flags,
                                                   this->usesLocalCoords());
 #endif
diff --git a/src/gpu/ops/GrAtlasTextOp.h b/src/gpu/ops/GrAtlasTextOp.h
index 0738ce7..0762736 100644
--- a/src/gpu/ops/GrAtlasTextOp.h
+++ b/src/gpu/ops/GrAtlasTextOp.h
@@ -28,12 +28,13 @@
     typedef GrAtlasTextBlob Blob;
     struct Geometry {
         SkMatrix fViewMatrix;
-        Blob* fBlob;
+        SkIRect  fClipRect;
+        Blob*    fBlob;
         SkScalar fX;
         SkScalar fY;
-        int fRun;
-        int fSubRun;
-        GrColor fColor;
+        int      fRun;
+        int      fSubRun;
+        GrColor  fColor;
     };
 
     static std::unique_ptr<GrAtlasTextOp> MakeBitmap(GrPaint&& paint, GrMaskFormat maskFormat,
@@ -173,11 +174,7 @@
 
     static constexpr auto kMaxTextures = 4;
 
-    // TODO just use class params
-    sk_sp<GrGeometryProcessor> setupDfProcessor(const SkMatrix& viewMatrix, SkColor luminanceColor,
-                                                GrColor color,
-                                                const sk_sp<GrTextureProxy> [kMaxTextures]) const;
-
+    sk_sp<GrGeometryProcessor> setupDfProcessor() const;
 
     // The minimum number of Geometry we will try to allocate.
     enum { kMinGeometryAllocated = 4 };
diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp
index 5d27b3e..339ae6d 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -7,6 +7,7 @@
 
 #include "GrAtlasTextBlob.h"
 #include "GrBlurUtils.h"
+#include "GrClip.h"
 #include "GrContext.h"
 #include "GrRenderTargetContext.h"
 #include "GrTextUtils.h"
@@ -258,8 +259,9 @@
 
 inline std::unique_ptr<GrDrawOp> GrAtlasTextBlob::makeOp(
         const Run::SubRunInfo& info, int glyphCount, int run, int subRun,
-        const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const GrTextUtils::Paint& paint,
-        const SkSurfaceProps& props, const GrDistanceFieldAdjustTable* distanceAdjustTable,
+        const SkMatrix& viewMatrix, SkScalar x, SkScalar y, const SkIRect& clipRect,
+        const GrTextUtils::Paint& paint, const SkSurfaceProps& props,
+        const GrDistanceFieldAdjustTable* distanceAdjustTable,
         GrAtlasGlyphCache* cache, GrRenderTargetContext* renderTargetContext) {
     GrMaskFormat format = info.maskFormat();
 
@@ -279,6 +281,7 @@
     }
     GrAtlasTextOp::Geometry& geometry = op->geometry();
     geometry.fViewMatrix = viewMatrix;
+    geometry.fClipRect = clipRect;
     geometry.fBlob = SkRef(this);
     geometry.fRun = run;
     geometry.fSubRun = subRun;
@@ -302,10 +305,33 @@
         if (0 == glyphCount) {
             continue;
         }
-        auto op = this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, std::move(paint),
-                               props, distanceAdjustTable, cache, rtc);
+
+        SkRect rtBounds = SkRect::MakeWH(rtc->width(), rtc->height());
+        SkRRect clipRRect;
+        GrAA aa;
+        // we can clip geometrically if we're not using SDFs,
+        // and we have an axis-aligned rectangular non-AA clip
+        bool skipClip = false;
+        SkIRect clipRect = SkIRect::MakeEmpty();
+        if (!info.drawAsDistanceFields() && clip.isRRect(rtBounds, &clipRRect, &aa) &&
+            clipRRect.isRect() && GrAA::kNo == aa) {
+            skipClip = true;
+            // we only need to do clipping work if the subrun isn't contained by the clip
+            SkRect subRunBounds;
+            this->computeSubRunBounds(&subRunBounds, run, subRun, viewMatrix, x, y);
+            if (!clipRRect.getBounds().contains(subRunBounds)) {
+                clipRRect.getBounds().round(&clipRect);
+            }
+        }
+
+        auto op = this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, clipRect,
+                               std::move(paint), props, distanceAdjustTable, cache, rtc);
         if (op) {
-            rtc->addDrawOp(clip, std::move(op));
+            if (skipClip) {
+                rtc->addDrawOp(GrNoClip(), std::move(op));
+            } else {
+                rtc->addDrawOp(clip, std::move(op));
+            }
         }
     }
 }
@@ -427,8 +453,9 @@
         const GrDistanceFieldAdjustTable* distanceAdjustTable, GrAtlasGlyphCache* cache,
         GrRenderTargetContext* rtc) {
     const GrAtlasTextBlob::Run::SubRunInfo& info = fRuns[run].fSubRunInfo[subRun];
-    return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, paint, props,
-                        distanceAdjustTable, cache, rtc);
+    SkIRect emptyRect = SkIRect::MakeEmpty();
+    return this->makeOp(info, glyphCount, run, subRun, viewMatrix, x, y, emptyRect,
+                        paint, props, distanceAdjustTable, cache, rtc);
 }
 
 void GrAtlasTextBlob::AssertEqual(const GrAtlasTextBlob& l, const GrAtlasTextBlob& r) {
diff --git a/src/gpu/text/GrAtlasTextBlob.h b/src/gpu/text/GrAtlasTextBlob.h
index e300c66..11f8505 100644
--- a/src/gpu/text/GrAtlasTextBlob.h
+++ b/src/gpu/text/GrAtlasTextBlob.h
@@ -240,8 +240,8 @@
     // The color here is the GrPaint color, and it is used to determine whether we
     // have to regenerate LCD text blobs.
     // We use this color vs the SkPaint color because it has the colorfilter applied.
-    void initReusableBlob(SkColor luminanceColor, const SkMatrix& viewMatrix, SkScalar x,
-                          SkScalar y) {
+    void initReusableBlob(SkColor luminanceColor, const SkMatrix& viewMatrix,
+                          SkScalar x, SkScalar y) {
         fLuminanceColor = luminanceColor;
         this->setupViewMatrix(viewMatrix, x, y);
     }
@@ -463,7 +463,7 @@
             GrColor fColor;
             GrMaskFormat fMaskFormat;
             uint32_t fFlags;
-        };
+        };  // SubRunInfo
 
         SubRunInfo& push_back() {
             // Forward glyph / vertex information to seed the new sub run
@@ -490,7 +490,7 @@
         std::unique_ptr<SkAutoDescriptor> fOverrideDescriptor; // df properties
         bool fInitialized;
         bool fDrawAsPaths;
-    };
+    };  // Run
 
     template <bool regenPos, bool regenCol, bool regenTexCoords, bool regenGlyphs>
     void regenInOp(GrDrawOp::Target* target, GrAtlasGlyphCache* fontCache, GrBlobRegenHelper* helper,
@@ -498,8 +498,9 @@
                    size_t vertexStride, GrColor color, SkScalar transX, SkScalar transY) const;
 
     inline std::unique_ptr<GrDrawOp> makeOp(const Run::SubRunInfo& info, int glyphCount, int run,
-                                            int subRun, const SkMatrix& viewMatrix, SkScalar x,
-                                            SkScalar y, const GrTextUtils::Paint& paint,
+                                            int subRun, const SkMatrix& viewMatrix,
+                                            SkScalar x, SkScalar y, const SkIRect& clipRect,
+                                            const GrTextUtils::Paint& paint,
                                             const SkSurfaceProps& props,
                                             const GrDistanceFieldAdjustTable* distanceAdjustTable,
                                             GrAtlasGlyphCache* cache, GrRenderTargetContext*);
diff --git a/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp b/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp
index 2d373ad..c551a2a 100644
--- a/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp
+++ b/src/gpu/text/GrAtlasTextBlob_regenInOp.cpp
@@ -199,8 +199,8 @@
         }
 
         regen_vertices<regenPos, regenCol, regenTexCoords>(vertex, glyph, vertexStride,
-                                                           info->drawAsDistanceFields(), transX,
-                                                           transY, color);
+                                                           info->drawAsDistanceFields(),
+                                                           transX, transY, color);
         vertex += vertexStride * GrAtlasTextOp::kVerticesPerGlyph;
         helper->incGlyphCount();
     }
diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp
index 3ddf4ff..8f310d0 100644
--- a/src/gpu/text/GrAtlasTextContext.cpp
+++ b/src/gpu/text/GrAtlasTextContext.cpp
@@ -163,7 +163,8 @@
                                             GrAtlasGlyphCache* fontCache,
                                             const GrShaderCaps& shaderCaps,
                                             const GrTextUtils::Paint& paint,
-                                            uint32_t scalerContextFlags, const SkMatrix& viewMatrix,
+                                            uint32_t scalerContextFlags,
+                                            const SkMatrix& viewMatrix,
                                             const SkSurfaceProps& props, const SkTextBlob* blob,
                                             SkScalar x, SkScalar y, SkDrawFilter* drawFilter) {
     cacheBlob->initReusableBlob(paint.luminanceColor(), viewMatrix, x, y);
diff --git a/src/gpu/text/GrTextUtils.cpp b/src/gpu/text/GrTextUtils.cpp
index c6ae5b1..7ad4b4a 100644
--- a/src/gpu/text/GrTextUtils.cpp
+++ b/src/gpu/text/GrTextUtils.cpp
@@ -181,7 +181,6 @@
     int x = vx + glyph->fBounds.fLeft;
     int y = vy + glyph->fBounds.fTop;
 
-    // keep them as ints until we've done the clip-test
     int width = glyph->fBounds.width();
     int height = glyph->fBounds.height();