GrPathRange* GrStencilAndCoverTextContext::TextRun::createGlyphs(GrContext* ctx) const { GrPathRange* glyphs = static_cast<GrPathRange*>( ctx->resourceProvider()->findAndRefResourceByUniqueKey(fGlyphPathsKey)); if (nullptr == glyphs) { if (fUsingRawGlyphPaths) { glyphs = ctx->resourceProvider()->createGlyphs(fFont.getTypeface(), nullptr, fStroke); } else { SkGlyphCache* cache = this->getGlyphCache(); glyphs = ctx->resourceProvider()->createGlyphs(cache->getScalerContext()->getTypeface(), &cache->getDescriptor(), fStroke); } ctx->resourceProvider()->assignUniqueKeyToResource(fGlyphPathsKey, glyphs); } return glyphs; }
GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) : fStyle(fontAndStroke) , fFont(fontAndStroke) , fTotalGlyphCount(0) , fFallbackGlyphCount(0) , fDetachedGlyphCache(nullptr) , fLastDrawnGlyphsID(SK_InvalidUniqueID) { SkASSERT(fFont.getTextSize() > 0); SkASSERT(!fStyle.hasNonDashPathEffect()); // Arbitrary path effects not supported. SkASSERT(!fStyle.isSimpleHairline()); // Hairlines are not supported. // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path // rendering API for stroking). fFont.setStyle(SkPaint::kFill_Style); if (fFont.isFakeBoldText() && fStyle.isSimpleFill()) { const SkStrokeRec& stroke = fStyle.strokeRec(); // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke. SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(), kStdFakeBoldInterpKeys, kStdFakeBoldInterpValues, kStdFakeBoldInterpLength); SkScalar extra = fFont.getTextSize() * fakeBoldScale; SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle); strokeRec.setStrokeStyle(stroke.needToApply() ? stroke.getWidth() + extra : extra, true /*strokeAndFill*/); fStyle = GrStyle(strokeRec, fStyle.refPathEffect()); fFont.setFakeBoldText(false); } if (!fFont.getPathEffect() && !fStyle.isDashed()) { const SkStrokeRec& stroke = fStyle.strokeRec(); // We can draw the glyphs from canonically sized paths. fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize(); // Compensate for the glyphs being scaled by fTextRatio. if (!fStyle.isSimpleFill()) { SkStrokeRec strokeRec(SkStrokeRec::kFill_InitStyle); strokeRec.setStrokeStyle(stroke.getWidth() / fTextRatio, SkStrokeRec::kStrokeAndFill_Style == stroke.getStyle()); fStyle = GrStyle(strokeRec, fStyle.refPathEffect()); } fFont.setLinearText(true); fFont.setLCDRenderText(false); fFont.setAutohinted(false); fFont.setHinting(SkPaint::kNo_Hinting); fFont.setSubpixelText(true); fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() && 0 == fFont.getTextSkewX() && !fFont.isFakeBoldText() && !fFont.isVerticalText(); } else { fTextRatio = fTextInverseRatio = 1.0f; fUsingRawGlyphPaths = false; } // Generate the key that will be used to cache the GPU glyph path objects. if (fUsingRawGlyphPaths && fStyle.isSimpleFill()) { static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain(); const SkTypeface* typeface = fFont.getTypeface(); GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1); reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0; } else { static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain(); int styleDataCount = GrStyle::KeySize(fStyle, GrStyle::Apply::kPathEffectAndStrokeRec); // Key should be valid since we opted out of drawing arbitrary path effects. SkASSERT(styleDataCount >= 0); if (fUsingRawGlyphPaths) { const SkTypeface* typeface = fFont.getTypeface(); GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + styleDataCount); reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0; reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount; if (styleDataCount) { write_style_key(&builder[2], fStyle); } } else { SkGlyphCache* glyphCache = this->getGlyphCache(); const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface(); const SkDescriptor* desc = &glyphCache->getDescriptor(); int descDataCount = (desc->getLength() + 3) / 4; GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + styleDataCount + descDataCount); reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0; reinterpret_cast<uint32_t&>(builder[1]) = styleDataCount | (descDataCount << 16); if (styleDataCount) { write_style_key(&builder[2], fStyle); } memcpy(&builder[2 + styleDataCount], desc, desc->getLength()); } } }
GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) : fStroke(fontAndStroke), fFont(fontAndStroke), fTotalGlyphCount(0), fDetachedGlyphCache(nullptr), fLastDrawnGlyphsID(SK_InvalidUniqueID) { SkASSERT(!fStroke.isHairlineStyle()); // Hairlines are not supported. // Setting to "fill" ensures that no strokes get baked into font outlines. (We use the GPU path // rendering API for stroking). fFont.setStyle(SkPaint::kFill_Style); if (fFont.isFakeBoldText() && SkStrokeRec::kStroke_Style != fStroke.getStyle()) { // Instead of letting fake bold get baked into the glyph outlines, do it with GPU stroke. SkScalar fakeBoldScale = SkScalarInterpFunc(fFont.getTextSize(), kStdFakeBoldInterpKeys, kStdFakeBoldInterpValues, kStdFakeBoldInterpLength); SkScalar extra = SkScalarMul(fFont.getTextSize(), fakeBoldScale); fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra, true /*strokeAndFill*/); fFont.setFakeBoldText(false); } if (!fFont.getPathEffect() && !fStroke.isDashed()) { // We can draw the glyphs from canonically sized paths. fTextRatio = fFont.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fFont.getTextSize(); // Compensate for the glyphs being scaled by fTextRatio. if (!fStroke.isFillStyle()) { fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio, SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle()); } fFont.setLinearText(true); fFont.setLCDRenderText(false); fFont.setAutohinted(false); fFont.setHinting(SkPaint::kNo_Hinting); fFont.setSubpixelText(true); fFont.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); fUsingRawGlyphPaths = SK_Scalar1 == fFont.getTextScaleX() && 0 == fFont.getTextSkewX() && !fFont.isFakeBoldText() && !fFont.isVerticalText(); } else { fTextRatio = fTextInverseRatio = 1.0f; fUsingRawGlyphPaths = false; } // Generate the key that will be used to cache the GPU glyph path objects. if (fUsingRawGlyphPaths && fStroke.isFillStyle()) { static const GrUniqueKey::Domain kRawFillPathGlyphDomain = GrUniqueKey::GenerateDomain(); const SkTypeface* typeface = fFont.getTypeface(); GrUniqueKey::Builder builder(&fGlyphPathsKey, kRawFillPathGlyphDomain, 1); reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0; } else { static const GrUniqueKey::Domain kPathGlyphDomain = GrUniqueKey::GenerateDomain(); int strokeDataCount = fStroke.computeUniqueKeyFragmentData32Cnt(); if (fUsingRawGlyphPaths) { const SkTypeface* typeface = fFont.getTypeface(); GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + strokeDataCount); reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0; reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount; fStroke.asUniqueKeyFragment(&builder[2]); } else { SkGlyphCache* glyphCache = this->getGlyphCache(); const SkTypeface* typeface = glyphCache->getScalerContext()->getTypeface(); const SkDescriptor* desc = &glyphCache->getDescriptor(); int descDataCount = (desc->getLength() + 3) / 4; GrUniqueKey::Builder builder(&fGlyphPathsKey, kPathGlyphDomain, 2 + strokeDataCount + descDataCount); reinterpret_cast<uint32_t&>(builder[0]) = typeface ? typeface->uniqueID() : 0; reinterpret_cast<uint32_t&>(builder[1]) = strokeDataCount | (descDataCount << 16); fStroke.asUniqueKeyFragment(&builder[2]); memcpy(&builder[2 + strokeDataCount], desc, desc->getLength()); } } // When drawing from canonically sized paths, the actual local coords are fTextRatio * coords. fLocalMatrixTemplate.setScale(fTextRatio, fTextRatio); }