Add color emoji fallback for nvpr text
authorcdalton <cdalton@nvidia.com>
Mon, 1 Dec 2014 16:38:55 +0000 (08:38 -0800)
committerCommit bot <commit-bot@chromium.org>
Mon, 1 Dec 2014 16:38:55 +0000 (08:38 -0800)
BUG=skia:

Review URL: https://codereview.chromium.org/759613005

include/core/SkStrokeRec.h
src/core/SkStrokeRec.cpp
src/gpu/GrStencilAndCoverTextContext.cpp
src/gpu/GrStencilAndCoverTextContext.h

index 0c5892f..68fa145 100644 (file)
@@ -85,6 +85,11 @@ public:
      */
     bool applyToPath(SkPath* dst, const SkPath& src) const;
 
+    /**
+     *  Apply these stroke parameters to a paint.
+     */
+    void applyToPaint(SkPaint* paint) const;
+
     bool operator==(const SkStrokeRec& other) const {
             return fWidth == other.fWidth &&
                    fMiterLimit == other.fMiterLimit &&
index 22f0562..95f56b7 100644 (file)
@@ -114,3 +114,16 @@ bool SkStrokeRec::applyToPath(SkPath* dst, const SkPath& src) const {
     stroker.strokePath(src, dst);
     return true;
 }
+
+void SkStrokeRec::applyToPaint(SkPaint* paint) const {
+    if (fWidth < 0) {  // fill
+        paint->setStyle(SkPaint::kFill_Style);
+        return;
+    }
+
+    paint->setStyle(fStrokeAndFill ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style);
+    paint->setStrokeWidth(fWidth);
+    paint->setStrokeMiter(fMiterLimit);
+    paint->setStrokeCap(fCap);
+    paint->setStrokeJoin(fJoin);
+}
index d6efe98..d33bc7a 100644 (file)
@@ -23,7 +23,9 @@
 GrStencilAndCoverTextContext::GrStencilAndCoverTextContext(
     GrContext* context, const SkDeviceProperties& properties)
     : GrTextContext(context, properties)
-    , fPendingGlyphCount(0) {
+    , fStroke(SkStrokeRec::kFill_InitStyle)
+    , fQueuedGlyphCount(0)
+    , fFallbackGlyphsIdx(kGlyphBufferSize) {
 }
 
 GrStencilAndCoverTextContext* GrStencilAndCoverTextContext::Create(GrContext* context,
@@ -63,10 +65,10 @@ bool GrStencilAndCoverTextContext::canDraw(const SkPaint& paint) {
 }
 
 void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint,
-                                            const SkPaint& skPaint,
-                                            const char text[],
-                                            size_t byteLength,
-                                            SkScalar x, SkScalar y) {
+                                              const SkPaint& skPaint,
+                                              const char text[],
+                                              size_t byteLength,
+                                              SkScalar x, SkScalar y) {
     SkASSERT(byteLength == 0 || text != NULL);
 
     if (text == NULL || byteLength == 0 /*|| fRC->isEmpty()*/) {
@@ -91,7 +93,7 @@ void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint,
     this->init(paint, skPaint, byteLength, kMaxAccuracy_RenderMode);
 
     // Transform our starting point.
-    if (fNeedsDeviceSpaceGlyphs) {
+    if (fUsingDeviceSpaceGlyphs) {
         SkPoint loc;
         fContextInitialMatrix.mapXY(x, y, &loc);
         x = loc.fX;
@@ -140,7 +142,7 @@ void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint,
         const SkGlyph& glyph = glyphCacheProc(fGlyphCache, &text, 0, 0);
         fx += SkFixedMul_portable(autokern.adjust(glyph), fixedSizeRatio);
         if (glyph.fWidth) {
-            this->appendGlyph(glyph.getGlyphID(), SkFixedToScalar(fx), SkFixedToScalar(fy));
+            this->appendGlyph(glyph, SkPoint::Make(SkFixedToScalar(fx), SkFixedToScalar(fy)));
         }
 
         fx += SkFixedMul_portable(glyph.fAdvanceX, fixedSizeRatio);
@@ -151,12 +153,12 @@ void GrStencilAndCoverTextContext::onDrawText(const GrPaint& paint,
 }
 
 void GrStencilAndCoverTextContext::onDrawPosText(const GrPaint& paint,
-                                               const SkPaint& skPaint,
-                                               const char text[],
-                                               size_t byteLength,
-                                               const SkScalar pos[],
-                                               int scalarsPerPosition,
-                                               const SkPoint& offset) {
+                                                 const SkPaint& skPaint,
+                                                 const char text[],
+                                                 size_t byteLength,
+                                                 const SkScalar pos[],
+                                                 int scalarsPerPosition,
+                                                 const SkPoint& offset) {
     SkASSERT(byteLength == 0 || text != NULL);
     SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition);
 
@@ -189,7 +191,7 @@ void GrStencilAndCoverTextContext::onDrawPosText(const GrPaint& paint,
             SkPoint loc;
             alignProc(tmsLoc, glyph, &loc);
 
-            this->appendGlyph(glyph.getGlyphID(), loc.x(), loc.y());
+            this->appendGlyph(glyph, loc);
         }
         pos += scalarsPerPosition;
     }
@@ -232,15 +234,19 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
     const bool otherBackendsWillDrawAsPaths =
         SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix);
 
-    fNeedsDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
+    fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths &&
                               kMaxAccuracy_RenderMode == renderMode &&
                               SkToBool(fContextInitialMatrix.getType() &
                                        (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask));
 
-    if (fNeedsDeviceSpaceGlyphs) {
+    if (fUsingDeviceSpaceGlyphs) {
         // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms.
         SkASSERT(!fContextInitialMatrix.hasPerspective());
 
+        // The whole shape (including stroke) will be baked into the glyph outlines. Make
+        // NVPR just fill the baked shapes.
+        fStroke = SkStrokeRec(SkStrokeRec::kFill_InitStyle);
+
         fTextRatio = fTextInverseRatio = 1.0f;
 
         // Glyphs loaded by GPU path rendering have an inverted y-direction.
@@ -254,30 +260,27 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
         m.postScale(1, -1);
         fPaint.localCoordChangeInverse(m);
 
-        // The whole shape (including stroke) will be baked into the glyph outlines. Make
-        // NVPR just fill the baked shapes.
         fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix, false);
         fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
-                                &fGlyphCache->getDescriptor(),
-                                SkStrokeRec(SkStrokeRec::kFill_InitStyle));
+                                &fGlyphCache->getDescriptor(), fStroke);
     } else {
         // Don't bake strokes into the glyph outlines. We will stroke the glyphs
         // using the GPU instead. This is the fast path.
-        SkStrokeRec gpuStroke = SkStrokeRec(fSkPaint);
+        fStroke = SkStrokeRec(fSkPaint);
         fSkPaint.setStyle(SkPaint::kFill_Style);
 
-        if (gpuStroke.isHairlineStyle()) {
+        if (fStroke.isHairlineStyle()) {
             // Approximate hairline stroke.
             SkScalar strokeWidth = SK_Scalar1 /
                 (SkVector::Make(fContextInitialMatrix.getScaleX(),
                                 fContextInitialMatrix.getSkewY()).length());
-            gpuStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
+            fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/);
 
         } else if (fSkPaint.isFakeBoldText() &&
 #ifdef SK_USE_FREETYPE_EMBOLDEN
                    kMaxPerformance_RenderMode == renderMode &&
 #endif
-                   SkStrokeRec::kStroke_Style != gpuStroke.getStyle()) {
+                   SkStrokeRec::kStroke_Style != fStroke.getStyle()) {
 
             // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke.
             SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(),
@@ -285,8 +288,8 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
                                                         kStdFakeBoldInterpValues,
                                                         kStdFakeBoldInterpLength);
             SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale);
-            gpuStroke.setStrokeStyle(gpuStroke.needToApply() ? gpuStroke.getWidth() + extra : extra,
-                                     true /*strokeAndFill*/);
+            fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra,
+                                   true /*strokeAndFill*/);
 
             fSkPaint.setFakeBoldText(false);
         }
@@ -299,9 +302,9 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
             fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize();
 
             // Compensate for the glyphs being scaled by fTextRatio.
-            if (!gpuStroke.isFillStyle()) {
-                gpuStroke.setStrokeStyle(gpuStroke.getWidth() / fTextRatio,
-                                         SkStrokeRec::kStrokeAndFill_Style == gpuStroke.getStyle());
+            if (!fStroke.isFillStyle()) {
+                fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio,
+                                       SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle());
             }
 
             fSkPaint.setLinearText(true);
@@ -328,9 +331,9 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
 
         fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false);
         fGlyphs = canUseRawPaths ?
-                      get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, gpuStroke) :
+                      get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, fStroke) :
                       get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(),
-                                    &fGlyphCache->getDescriptor(), gpuStroke);
+                                    &fGlyphCache->getDescriptor(), fStroke);
     }
 
     fStateRestore.set(&fDrawState);
@@ -347,31 +350,90 @@ void GrStencilAndCoverTextContext::init(const GrPaint& paint,
 
     *fDrawState.stencil() = kStencilPass;
 
-    SkASSERT(0 == fPendingGlyphCount);
+    SkASSERT(0 == fQueuedGlyphCount);
+    SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx);
+}
+
+bool GrStencilAndCoverTextContext::mapToFallbackContext(GrContext::AutoMatrix& autoMatrix,
+                                                        SkMatrix* inverse) {
+    // The current view matrix is flipped because GPU path rendering glyphs have an
+    // inverted y-direction. Unflip the view matrix for the fallback context. If using
+    // device-space glyphs, we'll also need to restore the original view matrix since
+    // we moved that transfomation into our local glyph cache for this scenario. Also
+    // track the inverse operation so the caller can unmap the paint and glyph positions.
+    if (fUsingDeviceSpaceGlyphs) {
+        autoMatrix.set(fContext, fContextInitialMatrix);
+        if (!fContextInitialMatrix.invert(inverse)) {
+            return false;
+        }
+        inverse->preScale(1, -1);
+    } else {
+        inverse->setScale(1, -1);
+        const SkMatrix& unflip = *inverse; // unflip is equal to its own inverse.
+        autoMatrix.setPreConcat(fContext, unflip);
+    }
+    return true;
 }
 
-inline void GrStencilAndCoverTextContext::appendGlyph(uint16_t glyphID, float x, float y) {
-    if (fPendingGlyphCount >= kGlyphBufferSize) {
+inline void GrStencilAndCoverTextContext::appendGlyph(const SkGlyph& glyph, const SkPoint& pos) {
+    if (fQueuedGlyphCount >= fFallbackGlyphsIdx) {
+        SkASSERT(fQueuedGlyphCount == fFallbackGlyphsIdx);
         this->flush();
     }
 
-    fIndexBuffer[fPendingGlyphCount] = glyphID;
-    fTransformBuffer[2 * fPendingGlyphCount] = fTextInverseRatio * x;
-    fTransformBuffer[2 * fPendingGlyphCount + 1] = -fTextInverseRatio * y;
+    // Stick the glyphs we can't draw at the end of the buffer, growing backwards.
+    int index = (SkMask::kARGB32_Format == glyph.fMaskFormat) ?
+                --fFallbackGlyphsIdx : fQueuedGlyphCount++;
 
-    ++fPendingGlyphCount;
+    fGlyphIndices[index] = glyph.getGlyphID();
+    fGlyphPositions[index].set(fTextInverseRatio * pos.x(), -fTextInverseRatio * pos.y());
+}
+
+static const SkScalar* get_xy_scalar_array(const SkPoint* pointArray) {
+    GR_STATIC_ASSERT(2 * sizeof(SkScalar) == sizeof(SkPoint));
+    GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
+
+    return &pointArray[0].fX;
 }
 
 void GrStencilAndCoverTextContext::flush() {
-    if (0 == fPendingGlyphCount) {
-        return;
+    if (fQueuedGlyphCount > 0) {
+        fDrawTarget->drawPaths(&fDrawState, fGlyphs,
+                               fGlyphIndices, GrPathRange::kU16_PathIndexType,
+                               get_xy_scalar_array(fGlyphPositions),
+                               GrPathRendering::kTranslate_PathTransformType,
+                               fQueuedGlyphCount, GrPathRendering::kWinding_FillType);
+
+        fQueuedGlyphCount = 0;
     }
 
-    fDrawTarget->drawPaths(&fDrawState, fGlyphs, fIndexBuffer, GrPathRange::kU16_PathIndexType,
-                           fTransformBuffer, GrPathRendering::kTranslate_PathTransformType,
-                           fPendingGlyphCount, GrPathRendering::kWinding_FillType);
+    if (fFallbackGlyphsIdx < kGlyphBufferSize) {
+        int fallbackGlyphCount = kGlyphBufferSize - fFallbackGlyphsIdx;
+
+        GrPaint paintFallback(fPaint);
 
-    fPendingGlyphCount = 0;
+        SkPaint skPaintFallback(fSkPaint);
+        if (!fUsingDeviceSpaceGlyphs) {
+            fStroke.applyToPaint(&skPaintFallback);
+        }
+        skPaintFallback.setTextAlign(SkPaint::kLeft_Align); // Align has already been accounted for.
+        skPaintFallback.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+        GrContext::AutoMatrix autoMatrix;
+        SkMatrix inverse;
+        if (this->mapToFallbackContext(autoMatrix, &inverse)) {
+            paintFallback.localCoordChangeInverse(inverse);
+            inverse.mapPoints(&fGlyphPositions[fFallbackGlyphsIdx], fallbackGlyphCount);
+        }
+
+        fFallbackTextContext->drawPosText(paintFallback, skPaintFallback,
+                                          (char*)&fGlyphIndices[fFallbackGlyphsIdx],
+                                          2 * fallbackGlyphCount,
+                                          get_xy_scalar_array(&fGlyphPositions[fFallbackGlyphsIdx]),
+                                          2, SkPoint::Make(0, 0));
+
+        fFallbackGlyphsIdx = kGlyphBufferSize;
+    }
 }
 
 void GrStencilAndCoverTextContext::finish() {
index 88b4eec..1f60886 100644 (file)
@@ -57,11 +57,13 @@ private:
     float                           fTextInverseRatio;
     SkGlyphCache*                   fGlyphCache;
     GrPathRange*                    fGlyphs;
-    uint16_t                        fIndexBuffer[kGlyphBufferSize];
-    float                           fTransformBuffer[2 * kGlyphBufferSize];
-    int                             fPendingGlyphCount;
+    SkStrokeRec                     fStroke;
+    uint16_t                        fGlyphIndices[kGlyphBufferSize];
+    SkPoint                         fGlyphPositions[kGlyphBufferSize];
+    int                             fQueuedGlyphCount;
+    int                             fFallbackGlyphsIdx;
     SkMatrix                        fContextInitialMatrix;
-    bool                            fNeedsDeviceSpaceGlyphs;
+    bool                            fUsingDeviceSpaceGlyphs;
 
     GrStencilAndCoverTextContext(GrContext*, const SkDeviceProperties&);
 
@@ -76,7 +78,8 @@ private:
                                const SkPoint& offset) SK_OVERRIDE;
 
     void init(const GrPaint&, const SkPaint&, size_t textByteLength, RenderMode);
-    void appendGlyph(uint16_t glyphID, float x, float y);
+    bool mapToFallbackContext(GrContext::AutoMatrix&, SkMatrix* inverse);
+    void appendGlyph(const SkGlyph&, const SkPoint&);
     void flush();
     void finish();