diff options
Diffstat (limited to 'src/gpu/text/GrAtlasTextContext.cpp')
-rw-r--r-- | src/gpu/text/GrAtlasTextContext.cpp | 485 |
1 files changed, 451 insertions, 34 deletions
diff --git a/src/gpu/text/GrAtlasTextContext.cpp b/src/gpu/text/GrAtlasTextContext.cpp index e5a8745fe1..40294a289f 100644 --- a/src/gpu/text/GrAtlasTextContext.cpp +++ b/src/gpu/text/GrAtlasTextContext.cpp @@ -7,8 +7,10 @@ #include "GrAtlasTextContext.h" #include "GrContext.h" #include "GrTextBlobCache.h" +#include "SkDistanceFieldGen.h" #include "SkDraw.h" #include "SkDrawFilter.h" +#include "SkFindAndPlaceGlyph.h" #include "SkGr.h" #include "ops/GrMeshDrawOp.h" @@ -24,7 +26,7 @@ bool GrAtlasTextContext::canDraw(const SkPaint& skPaint, const SkMatrix& viewMatrix, const SkSurfaceProps& props, const GrShaderCaps& shaderCaps) { - return GrTextUtils::CanDrawAsDistanceFields(skPaint, viewMatrix, props, shaderCaps) || + return CanDrawAsDistanceFields(skPaint, viewMatrix, props, shaderCaps) || !SkDraw::ShouldDrawTextAsPaths(skPaint, viewMatrix); } @@ -178,27 +180,26 @@ void GrAtlasTextContext::RegenerateTextBlob(GrAtlasTextBlob* cacheBlob, if (!runPaint.modifyForRun(it)) { continue; } - if (GrTextUtils::CanDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) { + if (CanDrawAsDistanceFields(runPaint, viewMatrix, props, shaderCaps)) { switch (it.positioning()) { case SkTextBlob::kDefault_Positioning: { - GrTextUtils::DrawDFText(cacheBlob, run, fontCache, props, runPaint, - scalerContextFlags, viewMatrix, - (const char*)it.glyphs(), textLen, x + offset.x(), - y + offset.y()); + DrawDFText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, + viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(), + y + offset.y()); break; } case SkTextBlob::kHorizontal_Positioning: { SkPoint dfOffset = SkPoint::Make(x, y + offset.y()); - GrTextUtils::DrawDFPosText( - cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, - viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1, dfOffset); + DrawDFPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, + viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1, + dfOffset); break; } case SkTextBlob::kFull_Positioning: { SkPoint dfOffset = SkPoint::Make(x, y); - GrTextUtils::DrawDFPosText( - cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, - viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, dfOffset); + DrawDFPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, + viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, + dfOffset); break; } } @@ -207,22 +208,19 @@ void GrAtlasTextContext::RegenerateTextBlob(GrAtlasTextBlob* cacheBlob, } else { switch (it.positioning()) { case SkTextBlob::kDefault_Positioning: - GrTextUtils::DrawBmpText(cacheBlob, run, fontCache, props, runPaint, - scalerContextFlags, viewMatrix, - (const char*)it.glyphs(), textLen, x + offset.x(), - y + offset.y()); + DrawBmpText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, + viewMatrix, (const char*)it.glyphs(), textLen, x + offset.x(), + y + offset.y()); break; case SkTextBlob::kHorizontal_Positioning: - GrTextUtils::DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, - scalerContextFlags, viewMatrix, - (const char*)it.glyphs(), textLen, it.pos(), 1, - SkPoint::Make(x, y + offset.y())); + DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, + viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 1, + SkPoint::Make(x, y + offset.y())); break; case SkTextBlob::kFull_Positioning: - GrTextUtils::DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, - scalerContextFlags, viewMatrix, - (const char*)it.glyphs(), textLen, it.pos(), 2, - SkPoint::Make(x, y)); + DrawBmpPosText(cacheBlob, run, fontCache, props, runPaint, scalerContextFlags, + viewMatrix, (const char*)it.glyphs(), textLen, it.pos(), 2, + SkPoint::Make(x, y)); break; } } @@ -246,12 +244,12 @@ GrAtlasTextContext::MakeDrawTextBlob(GrTextBlobCache* blobCache, sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1); blob->initThrowawayBlob(viewMatrix, x, y); - if (GrTextUtils::CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) { - GrTextUtils::DrawDFText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, - viewMatrix, text, byteLength, x, y); + if (CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) { + DrawDFText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text, + byteLength, x, y); } else { - GrTextUtils::DrawBmpText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, - viewMatrix, text, byteLength, x, y); + DrawBmpText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text, + byteLength, x, y); } return blob; } @@ -275,12 +273,12 @@ GrAtlasTextContext::MakeDrawPosTextBlob(GrTextBlobCache* blobCache, sk_sp<GrAtlasTextBlob> blob = blobCache->makeBlob(glyphCount, 1); blob->initThrowawayBlob(viewMatrix, offset.x(), offset.y()); - if (GrTextUtils::CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) { - GrTextUtils::DrawDFPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, - viewMatrix, text, byteLength, pos, scalarsPerPosition, offset); + if (CanDrawAsDistanceFields(paint, viewMatrix, props, shaderCaps)) { + DrawDFPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text, + byteLength, pos, scalarsPerPosition, offset); } else { - GrTextUtils::DrawBmpPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, - viewMatrix, text, byteLength, pos, scalarsPerPosition, offset); + DrawBmpPosText(blob.get(), 0, fontCache, props, paint, scalerContextFlags, viewMatrix, text, + byteLength, pos, scalarsPerPosition, offset); } return blob; } @@ -339,6 +337,425 @@ void GrAtlasTextContext::drawPosText(GrContext* context, GrTextUtils::Target* ta byteLength, pos, scalarsPerPosition, offset, regionClipBounds); } +void GrAtlasTextContext::DrawBmpText(GrAtlasTextBlob* blob, int runIndex, + GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props, + const GrTextUtils::Paint& paint, uint32_t scalerContextFlags, + const SkMatrix& viewMatrix, const char text[], + size_t byteLength, SkScalar x, SkScalar y) { + SkASSERT(byteLength == 0 || text != nullptr); + + // nothing to draw + if (text == nullptr || byteLength == 0) { + return; + } + + // Ensure the blob is set for bitmaptext + blob->setHasBitmap(); + + GrAtlasTextStrike* currStrike = nullptr; + + SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix); + SkFindAndPlaceGlyph::ProcessText(paint.skPaint().getTextEncoding(), text, byteLength, {x, y}, + viewMatrix, paint.skPaint().getTextAlign(), cache, + [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { + position += rounding; + BmpAppendGlyph(blob, runIndex, fontCache, &currStrike, + glyph, SkScalarFloorToInt(position.fX), + SkScalarFloorToInt(position.fY), + paint.filteredPremulColor(), cache); + }); + + SkGlyphCache::AttachCache(cache); +} + +void GrAtlasTextContext::DrawBmpPosText(GrAtlasTextBlob* blob, int runIndex, + GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props, + const GrTextUtils::Paint& paint, + uint32_t scalerContextFlags, const SkMatrix& viewMatrix, + const char text[], size_t byteLength, const SkScalar pos[], + int scalarsPerPosition, const SkPoint& offset) { + SkASSERT(byteLength == 0 || text != nullptr); + SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); + + // nothing to draw + if (text == nullptr || byteLength == 0) { + return; + } + + // Ensure the blob is set for bitmaptext + blob->setHasBitmap(); + + GrAtlasTextStrike* currStrike = nullptr; + + SkGlyphCache* cache = blob->setupCache(runIndex, props, scalerContextFlags, paint, &viewMatrix); + + SkFindAndPlaceGlyph::ProcessPosText( + paint.skPaint().getTextEncoding(), text, byteLength, offset, viewMatrix, pos, + scalarsPerPosition, paint.skPaint().getTextAlign(), cache, + [&](const SkGlyph& glyph, SkPoint position, SkPoint rounding) { + position += rounding; + BmpAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, + SkScalarFloorToInt(position.fX), SkScalarFloorToInt(position.fY), + paint.filteredPremulColor(), cache); + }); + + SkGlyphCache::AttachCache(cache); +} + +void GrAtlasTextContext::BmpAppendGlyph(GrAtlasTextBlob* blob, int runIndex, + GrAtlasGlyphCache* fontCache, GrAtlasTextStrike** strike, + const SkGlyph& skGlyph, int vx, int vy, GrColor color, + SkGlyphCache* cache) { + if (!*strike) { + *strike = fontCache->getStrike(cache); + } + + GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), + skGlyph.getSubXFixed(), + skGlyph.getSubYFixed(), + GrGlyph::kCoverage_MaskStyle); + GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, cache); + if (!glyph) { + return; + } + + int x = vx + glyph->fBounds.fLeft; + int y = vy + glyph->fBounds.fTop; + + int width = glyph->fBounds.width(); + int height = glyph->fBounds.height(); + + SkRect r; + r.fLeft = SkIntToScalar(x); + r.fTop = SkIntToScalar(y); + r.fRight = r.fLeft + SkIntToScalar(width); + r.fBottom = r.fTop + SkIntToScalar(height); + + blob->appendGlyph(runIndex, r, color, *strike, glyph, cache, skGlyph, SkIntToScalar(vx), + SkIntToScalar(vy), 1.0f, true); +} + +static const int kMinDFFontSize = 18; +static const int kSmallDFFontSize = 32; +static const int kSmallDFFontLimit = 32; +static const int kMediumDFFontSize = 72; +static const int kMediumDFFontLimit = 72; +static const int kLargeDFFontSize = 162; +#ifdef SK_BUILD_FOR_ANDROID +static const int kLargeDFFontLimit = 384; +#else +static const int kLargeDFFontLimit = 2 * kLargeDFFontSize; +#endif + +bool GrAtlasTextContext::CanDrawAsDistanceFields(const SkPaint& skPaint, const SkMatrix& viewMatrix, + const SkSurfaceProps& props, + const GrShaderCaps& caps) { + if (!viewMatrix.hasPerspective()) { + SkScalar maxScale = viewMatrix.getMaxScale(); + SkScalar scaledTextSize = maxScale * skPaint.getTextSize(); + // Hinted text looks far better at small resolutions + // Scaling up beyond 2x yields undesireable artifacts + if (scaledTextSize < kMinDFFontSize || scaledTextSize > kLargeDFFontLimit) { + return false; + } + + bool useDFT = props.isUseDeviceIndependentFonts(); +#if SK_FORCE_DISTANCE_FIELD_TEXT + useDFT = true; +#endif + + if (!useDFT && scaledTextSize < kLargeDFFontSize) { + return false; + } + } + + // rasterizers and mask filters modify alpha, which doesn't + // translate well to distance + if (skPaint.getRasterizer() || skPaint.getMaskFilter() || !caps.shaderDerivativeSupport()) { + return false; + } + + // TODO: add some stroking support + if (skPaint.getStyle() != SkPaint::kFill_Style) { + return false; + } + + return true; +} + +void GrAtlasTextContext::InitDistanceFieldPaint(GrAtlasTextBlob* blob, + SkPaint* skPaint, + SkScalar* textRatio, + const SkMatrix& viewMatrix) { + SkScalar textSize = skPaint->getTextSize(); + SkScalar scaledTextSize = textSize; + + if (viewMatrix.hasPerspective()) { + // for perspective, we simply force to the medium size + // TODO: compute a size based on approximate screen area + scaledTextSize = kMediumDFFontLimit; + } else { + SkScalar maxScale = viewMatrix.getMaxScale(); + // if we have non-unity scale, we need to choose our base text size + // based on the SkPaint's text size multiplied by the max scale factor + // TODO: do we need to do this if we're scaling down (i.e. maxScale < 1)? + if (maxScale > 0 && !SkScalarNearlyEqual(maxScale, SK_Scalar1)) { + scaledTextSize *= maxScale; + } + } + + // We have three sizes of distance field text, and within each size 'bucket' there is a floor + // and ceiling. A scale outside of this range would require regenerating the distance fields + SkScalar dfMaskScaleFloor; + SkScalar dfMaskScaleCeil; + if (scaledTextSize <= kSmallDFFontLimit) { + dfMaskScaleFloor = kMinDFFontSize; + dfMaskScaleCeil = kSmallDFFontLimit; + *textRatio = textSize / kSmallDFFontSize; + skPaint->setTextSize(SkIntToScalar(kSmallDFFontSize)); + } else if (scaledTextSize <= kMediumDFFontLimit) { + dfMaskScaleFloor = kSmallDFFontLimit; + dfMaskScaleCeil = kMediumDFFontLimit; + *textRatio = textSize / kMediumDFFontSize; + skPaint->setTextSize(SkIntToScalar(kMediumDFFontSize)); + } else { + dfMaskScaleFloor = kMediumDFFontLimit; + dfMaskScaleCeil = kLargeDFFontLimit; + *textRatio = textSize / kLargeDFFontSize; + skPaint->setTextSize(SkIntToScalar(kLargeDFFontSize)); + } + + // Because there can be multiple runs in the blob, we want the overall maxMinScale, and + // minMaxScale to make regeneration decisions. Specifically, we want the maximum minimum scale + // we can tolerate before we'd drop to a lower mip size, and the minimum maximum scale we can + // tolerate before we'd have to move to a large mip size. When we actually test these values + // we look at the delta in scale between the new viewmatrix and the old viewmatrix, and test + // against these values to decide if we can reuse or not(ie, will a given scale change our mip + // level) + SkASSERT(dfMaskScaleFloor <= scaledTextSize && scaledTextSize <= dfMaskScaleCeil); + blob->setMinAndMaxScale(dfMaskScaleFloor / scaledTextSize, dfMaskScaleCeil / scaledTextSize); + + skPaint->setAntiAlias(true); + skPaint->setLCDRenderText(false); + skPaint->setAutohinted(false); + skPaint->setHinting(SkPaint::kNormal_Hinting); + skPaint->setSubpixelText(true); +} + +void GrAtlasTextContext::DrawDFText(GrAtlasTextBlob* blob, int runIndex, + GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props, + const GrTextUtils::Paint& paint, uint32_t scalerContextFlags, + const SkMatrix& viewMatrix, const char text[], + size_t byteLength, SkScalar x, SkScalar y) { + SkASSERT(byteLength == 0 || text != nullptr); + + // nothing to draw + if (text == nullptr || byteLength == 0) { + return; + } + + const SkPaint& skPaint = paint.skPaint(); + SkPaint::GlyphCacheProc glyphCacheProc = + SkPaint::GetGlyphCacheProc(skPaint.getTextEncoding(), skPaint.isDevKernText(), true); + SkAutoDescriptor desc; + SkScalerContextEffects effects; + // We apply the fake-gamma by altering the distance in the shader, so we ignore the + // passed-in scaler context flags. (It's only used when we fall-back to bitmap text). + skPaint.getScalerContextDescriptor(&effects, &desc, props, SkPaint::kNone_ScalerContextFlags, + nullptr); + SkGlyphCache* origPaintCache = + SkGlyphCache::DetachCache(skPaint.getTypeface(), effects, desc.getDesc()); + + SkTArray<SkScalar> positions; + + const char* textPtr = text; + SkScalar stopX = 0; + SkScalar stopY = 0; + SkScalar origin = 0; + switch (skPaint.getTextAlign()) { + case SkPaint::kRight_Align: origin = SK_Scalar1; break; + case SkPaint::kCenter_Align: origin = SK_ScalarHalf; break; + case SkPaint::kLeft_Align: origin = 0; break; + } + + SkAutoKern autokern; + const char* stop = text + byteLength; + while (textPtr < stop) { + // don't need x, y here, since all subpixel variants will have the + // same advance + const SkGlyph& glyph = glyphCacheProc(origPaintCache, &textPtr); + + SkScalar width = SkFloatToScalar(glyph.fAdvanceX) + autokern.adjust(glyph); + positions.push_back(stopX + origin * width); + + SkScalar height = SkFloatToScalar(glyph.fAdvanceY); + positions.push_back(stopY + origin * height); + + stopX += width; + stopY += height; + } + SkASSERT(textPtr == stop); + + SkGlyphCache::AttachCache(origPaintCache); + + // now adjust starting point depending on alignment + SkScalar alignX = stopX; + SkScalar alignY = stopY; + if (skPaint.getTextAlign() == SkPaint::kCenter_Align) { + alignX = SkScalarHalf(alignX); + alignY = SkScalarHalf(alignY); + } else if (skPaint.getTextAlign() == SkPaint::kLeft_Align) { + alignX = 0; + alignY = 0; + } + x -= alignX; + y -= alignY; + SkPoint offset = SkPoint::Make(x, y); + + DrawDFPosText(blob, runIndex, fontCache, props, paint, scalerContextFlags, viewMatrix, text, + byteLength, positions.begin(), 2, offset); +} + +void GrAtlasTextContext::DrawDFPosText(GrAtlasTextBlob* blob, int runIndex, + GrAtlasGlyphCache* fontCache, const SkSurfaceProps& props, + const GrTextUtils::Paint& paint, uint32_t scalerContextFlags, + const SkMatrix& viewMatrix, const char text[], + size_t byteLength, const SkScalar pos[], + int scalarsPerPosition, const SkPoint& offset) { + SkASSERT(byteLength == 0 || text != nullptr); + SkASSERT(1 == scalarsPerPosition || 2 == scalarsPerPosition); + + // nothing to draw + if (text == nullptr || byteLength == 0) { + return; + } + + SkTDArray<char> fallbackTxt; + SkTDArray<SkScalar> fallbackPos; + + // Setup distance field paint and text ratio + SkScalar textRatio; + SkPaint dfPaint(paint); + GrAtlasTextContext::InitDistanceFieldPaint(blob, &dfPaint, &textRatio, viewMatrix); + blob->setHasDistanceField(); + blob->setSubRunHasDistanceFields(runIndex, paint.skPaint().isLCDRenderText(), + paint.skPaint().isAntiAlias()); + + GrAtlasTextStrike* currStrike = nullptr; + + // We apply the fake-gamma by altering the distance in the shader, so we ignore the + // passed-in scaler context flags. (It's only used when we fall-back to bitmap text). + SkGlyphCache* cache = + blob->setupCache(runIndex, props, SkPaint::kNone_ScalerContextFlags, dfPaint, nullptr); + SkPaint::GlyphCacheProc glyphCacheProc = + SkPaint::GetGlyphCacheProc(dfPaint.getTextEncoding(), dfPaint.isDevKernText(), true); + + const char* stop = text + byteLength; + + if (SkPaint::kLeft_Align == dfPaint.getTextAlign()) { + while (text < stop) { + const char* lastText = text; + // the last 2 parameters are ignored + const SkGlyph& glyph = glyphCacheProc(cache, &text); + + if (glyph.fWidth) { + SkScalar x = offset.x() + pos[0]; + SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); + + if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x, y, + paint.filteredPremulColor(), cache, textRatio, viewMatrix)) { + // couldn't append, send to fallback + fallbackTxt.append(SkToInt(text - lastText), lastText); + *fallbackPos.append() = pos[0]; + if (2 == scalarsPerPosition) { + *fallbackPos.append() = pos[1]; + } + } + } + pos += scalarsPerPosition; + } + } else { + SkScalar alignMul = + SkPaint::kCenter_Align == dfPaint.getTextAlign() ? SK_ScalarHalf : SK_Scalar1; + while (text < stop) { + const char* lastText = text; + // the last 2 parameters are ignored + const SkGlyph& glyph = glyphCacheProc(cache, &text); + + if (glyph.fWidth) { + SkScalar x = offset.x() + pos[0]; + SkScalar y = offset.y() + (2 == scalarsPerPosition ? pos[1] : 0); + + SkScalar advanceX = SkFloatToScalar(glyph.fAdvanceX) * alignMul * textRatio; + SkScalar advanceY = SkFloatToScalar(glyph.fAdvanceY) * alignMul * textRatio; + + if (!DfAppendGlyph(blob, runIndex, fontCache, &currStrike, glyph, x - advanceX, + y - advanceY, paint.filteredPremulColor(), cache, textRatio, + viewMatrix)) { + // couldn't append, send to fallback + fallbackTxt.append(SkToInt(text - lastText), lastText); + *fallbackPos.append() = pos[0]; + if (2 == scalarsPerPosition) { + *fallbackPos.append() = pos[1]; + } + } + } + pos += scalarsPerPosition; + } + } + + SkGlyphCache::AttachCache(cache); + if (fallbackTxt.count()) { + blob->initOverride(runIndex); + GrAtlasTextContext::DrawBmpPosText(blob, runIndex, fontCache, props, paint, + scalerContextFlags, viewMatrix, fallbackTxt.begin(), + fallbackTxt.count(), fallbackPos.begin(), + scalarsPerPosition, offset); + } +} + +bool GrAtlasTextContext::DfAppendGlyph(GrAtlasTextBlob* blob, int runIndex, + GrAtlasGlyphCache* cache, GrAtlasTextStrike** strike, + const SkGlyph& skGlyph, SkScalar sx, SkScalar sy, + GrColor color, SkGlyphCache* glyphCache, SkScalar textRatio, + const SkMatrix& viewMatrix) { + if (!*strike) { + *strike = cache->getStrike(glyphCache); + } + + GrGlyph::PackedID id = GrGlyph::Pack(skGlyph.getGlyphID(), + skGlyph.getSubXFixed(), + skGlyph.getSubYFixed(), + GrGlyph::kDistance_MaskStyle); + GrGlyph* glyph = (*strike)->getGlyph(skGlyph, id, glyphCache); + if (!glyph) { + return true; + } + + // fallback to color glyph support + if (kA8_GrMaskFormat != glyph->fMaskFormat) { + return false; + } + + SkScalar dx = SkIntToScalar(glyph->fBounds.fLeft + SK_DistanceFieldInset); + SkScalar dy = SkIntToScalar(glyph->fBounds.fTop + SK_DistanceFieldInset); + SkScalar width = SkIntToScalar(glyph->fBounds.width() - 2 * SK_DistanceFieldInset); + SkScalar height = SkIntToScalar(glyph->fBounds.height() - 2 * SK_DistanceFieldInset); + + SkScalar scale = textRatio; + dx *= scale; + dy *= scale; + width *= scale; + height *= scale; + sx += dx; + sy += dy; + SkRect glyphRect = SkRect::MakeXYWH(sx, sy, width, height); + + blob->appendGlyph(runIndex, glyphRect, color, *strike, glyph, glyphCache, skGlyph, sx - dx, + sy - dy, scale, false); + return true; +} + /////////////////////////////////////////////////////////////////////////////////////////////////// #if GR_TEST_UTILS |