Add GPU perspective support for color emoji

Will also warp orthogonal bitmaps to handle skew and rotate transforms.

Bug: skia:7985
Change-Id: Ib0d5476dd68603354be90c8d404f1e5140d63a0c
Reviewed-on: https://skia-review.googlesource.com/129480
Commit-Queue: Jim Van Verth <jvanverth@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/text/GrAtlasTextBlob.cpp b/src/gpu/text/GrAtlasTextBlob.cpp
index 611abf7..f508c1f 100644
--- a/src/gpu/text/GrAtlasTextBlob.cpp
+++ b/src/gpu/text/GrAtlasTextBlob.cpp
@@ -113,10 +113,8 @@
     run.fInitialized = true;
 
     bool hasW = subRun->hasWCoord();
-    // DF glyphs drawn in perspective must always have a w coord.
-    SkASSERT(hasW || !subRun->drawAsDistanceFields() || !fInitialViewMatrix.hasPerspective());
-    // Non-DF glyphs should never have a w coord.
-    SkASSERT(!hasW || subRun->drawAsDistanceFields());
+    // glyphs drawn in perspective must always have a w coord.
+    SkASSERT(hasW || !fInitialViewMatrix.hasPerspective());
 
     size_t vertexStride = GetVertexStride(format, hasW);
 
@@ -153,7 +151,7 @@
     subRun->appendVertices(vertexStride);
     fGlyphs[subRun->glyphEndIndex()] = glyph;
     subRun->glyphAppended();
-    subRun->setHasScaledGlyphs(SK_Scalar1 != scale);
+    subRun->setNeedsTransform(!preTransformed);
 }
 
 void GrAtlasTextBlob::appendPathGlyph(int runIndex, const SkPath& path, SkScalar x, SkScalar y,
@@ -263,7 +261,7 @@
                 props, info.isAntiAliased(), info.hasUseLCDText());
     } else {
         op = GrAtlasTextOp::MakeBitmap(std::move(grPaint), format, glyphCount,
-                                       info.hasScaledGlyphs());
+                                       info.needsTransform());
     }
     GrAtlasTextOp::Geometry& geometry = op->geometry();
     geometry.fViewMatrix = viewMatrix;
@@ -352,9 +350,9 @@
             SkRect rtBounds = SkRect::MakeWH(target->width(), target->height());
             SkRRect clipRRect;
             GrAA aa;
-            // We can clip geometrically if we're not using SDFs or scaled glyphs,
+            // We can clip geometrically if we're not using SDFs or transformed glyphs,
             // and we have an axis-aligned rectangular non-AA clip
-            if (!info.drawAsDistanceFields() && !info.hasScaledGlyphs() &&
+            if (!info.drawAsDistanceFields() && !info.needsTransform() &&
                 clip.isRRect(rtBounds, &clipRRect, &aa) &&
                 clipRRect.isRect() && GrAA::kNo == aa) {
                 skipClip = true;
diff --git a/src/gpu/text/GrAtlasTextBlob.h b/src/gpu/text/GrAtlasTextBlob.h
index e3b7d40..2c656ad 100644
--- a/src/gpu/text/GrAtlasTextBlob.h
+++ b/src/gpu/text/GrAtlasTextBlob.h
@@ -149,6 +149,13 @@
         subRun.setHasWCoord(hasWCoord);
     }
 
+    // sets the last subrun of runIndex to use w values
+    void setSubRunHasW(int runIndex, bool hasWCoord) {
+        Run& run = fRuns[runIndex];
+        Run::SubRunInfo& subRun = run.fSubRunInfo.back();
+        subRun.setHasWCoord(hasWCoord);
+    }
+
     void setRunPaintFlags(int runIndex, uint16_t paintFlags) {
         fRuns[runIndex].fPaintFlags = paintFlags & Run::kPaintFlagsMask;
     }
@@ -188,15 +195,14 @@
     void appendPathGlyph(int runIndex, const SkPath& path,
                          SkScalar x, SkScalar y, SkScalar scale, bool preTransformed);
 
-    static size_t GetVertexStride(GrMaskFormat maskFormat, bool isDistanceFieldWithWCoord) {
+    static size_t GetVertexStride(GrMaskFormat maskFormat, bool hasWCoord) {
         switch (maskFormat) {
             case kA8_GrMaskFormat:
-                return isDistanceFieldWithWCoord ? kGrayTextDFPerspectiveVASize : kGrayTextVASize;
+                return hasWCoord ? kGrayTextDFPerspectiveVASize : kGrayTextVASize;
             case kARGB_GrMaskFormat:
-                SkASSERT(!isDistanceFieldWithWCoord);
-                return kColorTextVASize;
+                return hasWCoord ? kColorTextPerspectiveVASize : kColorTextVASize;
             default:
-                SkASSERT(!isDistanceFieldWithWCoord);
+                SkASSERT(!hasWCoord);
                 return kLCDTextVASize;
         }
     }
@@ -244,6 +250,7 @@
 
     // position + local coord
     static const size_t kColorTextVASize = sizeof(SkPoint) + sizeof(SkIPoint16);
+    static const size_t kColorTextPerspectiveVASize = sizeof(SkPoint3) + sizeof(SkIPoint16);
     static const size_t kGrayTextVASize = sizeof(SkPoint) + sizeof(GrColor) + sizeof(SkIPoint16);
     static const size_t kGrayTextDFPerspectiveVASize =
             sizeof(SkPoint3) + sizeof(GrColor) + sizeof(SkIPoint16);
@@ -432,11 +439,11 @@
                 fFlags  = hasW ? (fFlags | kHasWCoord_Flag) : fFlags & ~kHasWCoord_Flag;
             }
             bool hasWCoord() const { return SkToBool(fFlags & kHasWCoord_Flag); }
-            void setHasScaledGlyphs(bool hasScaledGlyphs) {
-                fFlags  = hasScaledGlyphs ? (fFlags | kHasScaledGlyphs_Flag)
-                                          : fFlags & ~kHasScaledGlyphs_Flag;
+            void setNeedsTransform(bool needsTransform) {
+                fFlags  = needsTransform ? (fFlags | kNeedsTransform_Flag)
+                                          : fFlags & ~kNeedsTransform_Flag;
             }
-            bool hasScaledGlyphs() const { return SkToBool(fFlags & kHasScaledGlyphs_Flag); }
+            bool needsTransform() const { return SkToBool(fFlags & kNeedsTransform_Flag); }
 
         private:
             enum Flag {
@@ -444,7 +451,7 @@
                 kUseLCDText_Flag = 0x02,
                 kAntiAliased_Flag = 0x04,
                 kHasWCoord_Flag = 0x08,
-                kHasScaledGlyphs_Flag = 0x10
+                kNeedsTransform_Flag = 0x10
             };
 
             GrDrawOpAtlas::BulkUseTokenUpdater fBulkUseToken;
diff --git a/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
index e2d4fe1..a0a5f4d 100644
--- a/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
+++ b/src/gpu/text/GrAtlasTextBlobVertexRegenerator.cpp
@@ -282,7 +282,7 @@
                 code = strike->addGlyphToAtlas(fResourceProvider, fUploadTarget, fGlyphCache,
                                               fFullAtlasManager, glyph,
                                               fLazyCache->get(), fSubRun->maskFormat(),
-                                              fSubRun->hasScaledGlyphs());
+                                              fSubRun->needsTransform());
                 if (GrDrawOpAtlas::ErrorCode::kError == code) {
                     // Something horrible has happened - drop the op
                     return false;
diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp
index 1f06cfe..6d62d80 100644
--- a/src/gpu/text/GrAtlasTextContext.cpp
+++ b/src/gpu/text/GrAtlasTextContext.cpp
@@ -390,7 +390,7 @@
                                                         glyph, SkScalarFloorToScalar(position.fX),
                                                         SkScalarFloorToScalar(position.fY),
                                                         paint.filteredPremulColor(), cache.get(),
-                                                        SK_Scalar1);
+                                                        SK_Scalar1, false);
                                      });
 }
 
@@ -428,7 +428,7 @@
                 BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph,
                                SkScalarFloorToScalar(position.fX),
                                SkScalarFloorToScalar(position.fY),
-                               paint.filteredPremulColor(), cache.get(), SK_Scalar1);
+                               paint.filteredPremulColor(), cache.get(), SK_Scalar1, false);
             });
 }
 
@@ -529,7 +529,7 @@
                                         sk_sp<GrTextStrike>* strike,
                                         const SkGlyph& skGlyph, SkScalar sx, SkScalar sy,
                                         GrColor color, SkGlyphCache* skGlyphCache,
-                                        SkScalar textRatio) {
+                                        SkScalar textRatio, bool needsTransform) {
     if (!*strike) {
         *strike = grGlyphCache->getStrike(skGlyphCache);
     }
@@ -559,7 +559,7 @@
     SkRect glyphRect = SkRect::MakeXYWH(sx + dx, sy + dy, width, height);
 
     blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, skGlyphCache, skGlyph, sx, sy,
-                      textRatio, true);
+                      textRatio, !needsTransform);
 }
 
 void GrAtlasTextContext::SanitizeOptions(Options* options) {
@@ -847,38 +847,23 @@
 
 void GrAtlasTextContext::FallbackTextHelper::appendText(const SkGlyph& glyph, int count,
                                                         const char* text, SkPoint glyphPos) {
-    // can't handle perspective at the moment
-    if (fViewMatrix.hasPerspective()) {
-        return;
-    }
-
-    SkScalar maxDim;
-    if (fViewMatrix.isScaleTranslate()) {
-        maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*fTextRatio;
-    } else {
-        SkRect glyphRect;
-        glyphRect.setXYWH(glyph.fLeft, glyph.fTop, glyph.fWidth, glyph.fHeight);
-        fViewMatrix.mapRect(&glyphRect);
-        maxDim = SkTMax(glyphRect.width(), glyphRect.height());
-        maxDim *= fTextRatio/fMaxScale;
-    }
-    if (!fUseScaledFallback) {
-        SkScalar scaledGlyphSize = maxDim * fMaxScale;
-        if (!fViewMatrix.hasPerspective() && scaledGlyphSize > fMaxTextSize) {
-            fUseScaledFallback = true;
+    SkScalar maxDim = SkTMax(glyph.fWidth, glyph.fHeight)*fTextRatio;
+    if (!fUseTransformedFallback) {
+        if (!fViewMatrix.isScaleTranslate() || maxDim*fMaxScale > fMaxTextSize) {
+            fUseTransformedFallback = true;
             fMaxTextSize -= 2;    // Subtract 2 to account for the bilerp pad around the glyph
         }
     }
 
     fFallbackTxt.append(count, text);
-    if (fUseScaledFallback) {
+    if (fUseTransformedFallback) {
         // If there's a glyph in the font that's particularly large, it's possible
         // that fScaledFallbackTextSize may end up minimizing too much. We'd rather skip
         // that glyph than make the others blurry, so we set a minimum size of half the
         // maximum text size to avoid this case.
-        SkScalar glyphTextSize = SkTMax(SkScalarFloorToScalar(fMaxTextSize*fTextSize / maxDim),
+        SkScalar glyphTextSize = SkTMax(SkScalarFloorToScalar(fTextSize * fMaxTextSize/maxDim),
                                         0.5f*fMaxTextSize);
-        fScaledFallbackTextSize = SkTMin(glyphTextSize, fScaledFallbackTextSize);
+        fTransformedFallbackTextSize = SkTMin(glyphTextSize, fTransformedFallbackTextSize);
     }
     *fFallbackPos.append() = glyphPos;
 }
@@ -889,31 +874,22 @@
                                                       const GrTextUtils::Paint& paint,
                                                       SkScalerContextFlags scalerContextFlags) {
     if (fFallbackTxt.count()) {
-        if (fViewMatrix.hasPerspective()) {
-            // TODO: handle perspective
-            return;
-        }
-
         blob->initOverride(runIndex);
         blob->setHasBitmap();
+        blob->setSubRunHasW(runIndex, fViewMatrix.hasPerspective());
         SkExclusiveStrikePtr cache;
         const SkPaint& skPaint = paint.skPaint();
         SkPaint::GlyphCacheProc glyphCacheProc =
             SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(), true);
         SkColor textColor = paint.filteredPremulColor();
         SkScalar textRatio = SK_Scalar1;
-        if (fUseScaledFallback) {
+        if (fUseTransformedFallback) {
             // Set up paint and matrix to scale glyphs
             SkPaint scaledPaint(skPaint);
-            scaledPaint.setTextSize(fScaledFallbackTextSize);
-            // remove maxScale from viewMatrix and move it into textRatio
-            // this keeps the base glyph size consistent regardless of matrix scale
-            SkMatrix modMatrix(fViewMatrix);
-            SkScalar invScale = SkScalarInvert(fMaxScale);
-            modMatrix.preScale(invScale, invScale);
-            textRatio = fTextSize * fMaxScale / fScaledFallbackTextSize;
+            scaledPaint.setTextSize(fTransformedFallbackTextSize);
+            textRatio = fTextSize / fTransformedFallbackTextSize;
             cache = blob->setupCache(runIndex, props, scalerContextFlags, scaledPaint,
-                                     &modMatrix);
+                                     &SkMatrix::I());
         } else {
             cache = blob->setupCache(runIndex, props, scalerContextFlags, paint,
                                      &fViewMatrix);
@@ -925,14 +901,14 @@
         SkPoint* glyphPos = fFallbackPos.begin();
         while (text < stop) {
             const SkGlyph& glyph = glyphCacheProc(cache.get(), &text);
-            fViewMatrix.mapPoints(glyphPos, 1);
-            if (!fUseScaledFallback) {
+            if (!fUseTransformedFallback) {
+                fViewMatrix.mapPoints(glyphPos, 1);
                 glyphPos->fX = SkScalarFloorToScalar(glyphPos->fX);
                 glyphPos->fY = SkScalarFloorToScalar(glyphPos->fY);
             }
             GrAtlasTextContext::BmpAppendGlyph(blob, runIndex, glyphCache, &currStrike, glyph,
                                                glyphPos->fX, glyphPos->fY, textColor,
-                                               cache.get(), textRatio);
+                                               cache.get(), textRatio, fUseTransformedFallback);
             glyphPos++;
         }
     }
diff --git a/src/gpu/text/GrAtlasTextContext.h b/src/gpu/text/GrAtlasTextContext.h
index e2531b3..aa6557c 100644
--- a/src/gpu/text/GrAtlasTextContext.h
+++ b/src/gpu/text/GrAtlasTextContext.h
@@ -85,8 +85,8 @@
             , fTextSize(pathPaint.getTextSize())
             , fMaxTextSize(glyphCache->getGlyphSizeLimit())
             , fTextRatio(textRatio)
-            , fScaledFallbackTextSize(fMaxTextSize)
-            , fUseScaledFallback(false) {
+            , fTransformedFallbackTextSize(fMaxTextSize)
+            , fUseTransformedFallback(false) {
             fMaxScale = viewMatrix.getMaxScale();
         }
 
@@ -102,9 +102,9 @@
         SkScalar fTextSize;
         SkScalar fMaxTextSize;
         SkScalar fTextRatio;
-        SkScalar fScaledFallbackTextSize;
+        SkScalar fTransformedFallbackTextSize;
         SkScalar fMaxScale;
-        bool fUseScaledFallback;
+        bool fUseTransformedFallback;
     };
 
     // sets up the descriptor on the blob and returns a detached cache.  Client must attach
@@ -184,7 +184,7 @@
 
     static void BmpAppendGlyph(GrAtlasTextBlob*, int runIndex, GrGlyphCache*,
                                sk_sp<GrTextStrike>*, const SkGlyph&, SkScalar sx, SkScalar sy,
-                               GrColor color, SkGlyphCache*, SkScalar textRatio);
+                               GrColor color, SkGlyphCache*, SkScalar textRatio, bool needsXform);
 
     static void DfAppendGlyph(GrAtlasTextBlob*, int runIndex, GrGlyphCache*,
                               sk_sp<GrTextStrike>*, const SkGlyph&, SkScalar sx, SkScalar sy,