GrStencilAndCoverTextContext::TextRun::TextRun(const SkPaint& fontAndStroke) : fStroke(fontAndStroke), fFont(fontAndStroke), fTotalGlyphCount(0) { 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; } // When drawing from canonically sized paths, the actual local coords are fTextRatio * coords. fLocalMatrixTemplate.setScale(fTextRatio, fTextRatio); }
static void path_bold(SkCanvas* canvas, const SkPath& path, const SkPaint& paint) { SkPaint p(paint); canvas->drawPath(path, p); p.setStyle(SkPaint::kStrokeAndFill_Style); SkScalar fakeBoldScale = SkScalarInterpFunc(p.getTextSize(), kStdFakeBoldInterpKeys, kStdFakeBoldInterpValues, kStdFakeBoldInterpLength); SkScalar extra = SkScalarMul(p.getTextSize(), fakeBoldScale); p.setStrokeWidth(extra); canvas->save(); canvas->translate(0, 120); canvas->drawPath(path, p); canvas->restore(); }
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()); } } }
void GrStencilAndCoverTextContext::init(GrRenderTarget* rt, const GrClip& clip, const GrPaint& paint, const SkPaint& skPaint, size_t textByteLength, RenderMode renderMode, const SkMatrix& viewMatrix, const SkIRect& regionClipBounds) { GrTextContext::init(rt, clip, paint, skPaint, regionClipBounds); fContextInitialMatrix = viewMatrix; fViewMatrix = viewMatrix; fLocalMatrix = SkMatrix::I(); const bool otherBackendsWillDrawAsPaths = SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix); fUsingDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths && kMaxAccuracy_RenderMode == renderMode && SkToBool(fContextInitialMatrix.getType() & (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)); 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 = GrStrokeInfo(SkStrokeRec::kFill_InitStyle); fTextRatio = fTextInverseRatio = 1.0f; // Glyphs loaded by GPU path rendering have an inverted y-direction. SkMatrix m; m.setScale(1, -1); fViewMatrix = m; // Post-flip the initial matrix so we're left with just the flip after // the paint preConcats the inverse. m = fContextInitialMatrix; m.postScale(1, -1); if (!m.invert(&fLocalMatrix)) { SkDebugf("Not invertible!\n"); return; } fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, &fContextInitialMatrix, true /*ignoreGamma*/); fGlyphs = get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(), &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. fStroke = GrStrokeInfo(fSkPaint); fSkPaint.setStyle(SkPaint::kFill_Style); if (fStroke.isHairlineStyle()) { // Approximate hairline stroke. SkScalar strokeWidth = SK_Scalar1 / (SkVector::Make(fContextInitialMatrix.getScaleX(), fContextInitialMatrix.getSkewY()).length()); fStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/); } else if (fSkPaint.isFakeBoldText() && #ifdef SK_USE_FREETYPE_EMBOLDEN kMaxPerformance_RenderMode == renderMode && #endif 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(), kStdFakeBoldInterpKeys, kStdFakeBoldInterpValues, kStdFakeBoldInterpLength); SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale); fStroke.setStrokeStyle(fStroke.needToApply() ? fStroke.getWidth() + extra : extra, true /*strokeAndFill*/); fSkPaint.setFakeBoldText(false); } bool canUseRawPaths; if (!fStroke.isDashed() && (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode)) { // We can draw the glyphs from canonically sized paths. fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; fTextInverseRatio = SkPaint::kCanonicalTextSizeForPaths / fSkPaint.getTextSize(); // Compensate for the glyphs being scaled by fTextRatio. if (!fStroke.isFillStyle()) { fStroke.setStrokeStyle(fStroke.getWidth() / fTextRatio, SkStrokeRec::kStrokeAndFill_Style == fStroke.getStyle()); } fSkPaint.setLinearText(true); fSkPaint.setLCDRenderText(false); fSkPaint.setAutohinted(false); fSkPaint.setHinting(SkPaint::kNo_Hinting); fSkPaint.setSubpixelText(true); fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() && 0 == fSkPaint.getTextSkewX() && !fSkPaint.isFakeBoldText() && !fSkPaint.isVerticalText(); } else { fTextRatio = fTextInverseRatio = 1.0f; canUseRawPaths = false; } SkMatrix textMatrix; // Glyphs loaded by GPU path rendering have an inverted y-direction. textMatrix.setScale(fTextRatio, -fTextRatio); fViewMatrix.preConcat(textMatrix); fLocalMatrix = textMatrix; fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, true /*ignoreGamma*/); fGlyphs = canUseRawPaths ? get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, fStroke) : get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(), &fGlyphCache->getDescriptor(), fStroke); } fStateRestore.set(&fPipelineBuilder); fPipelineBuilder.setFromPaint(fPaint, fRenderTarget, fClip); GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kZero_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); *fPipelineBuilder.stencil() = kStencilPass; SkASSERT(0 == fQueuedGlyphCount); SkASSERT(kGlyphBufferSize == fFallbackGlyphsIdx); }
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); }
void GrStencilAndCoverTextContext::init(const GrPaint& paint, const SkPaint& skPaint, size_t textByteLength, RenderMode renderMode, SkScalar textTranslateY) { GrTextContext::init(paint, skPaint); fContextInitialMatrix = fContext->getMatrix(); const bool otherBackendsWillDrawAsPaths = SkDraw::ShouldDrawTextAsPaths(skPaint, fContextInitialMatrix); fNeedsDeviceSpaceGlyphs = !otherBackendsWillDrawAsPaths && kMaxAccuracy_RenderMode == renderMode && SkToBool(fContextInitialMatrix.getType() & (SkMatrix::kScale_Mask | SkMatrix::kAffine_Mask)); if (fNeedsDeviceSpaceGlyphs) { // SkDraw::ShouldDrawTextAsPaths takes care of perspective transforms. SkASSERT(!fContextInitialMatrix.hasPerspective()); SkASSERT(0 == textTranslateY); // TODO: Handle textTranslateY in device-space usecase. fTextRatio = fTextInverseRatio = 1.0f; // Glyphs loaded by GPU path rendering have an inverted y-direction. SkMatrix m; m.setScale(1, -1); fContext->setMatrix(m); // Post-flip the initial matrix so we're left with just the flip after // the paint preConcats the inverse. m = fContextInitialMatrix; 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)); } 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); fSkPaint.setStyle(SkPaint::kFill_Style); if (gpuStroke.isHairlineStyle()) { // Approximate hairline stroke. SkScalar strokeWidth = SK_Scalar1 / (SkVector::Make(fContextInitialMatrix.getScaleX(), fContextInitialMatrix.getSkewY()).length()); gpuStroke.setStrokeStyle(strokeWidth, false /*strokeAndFill*/); } else if (fSkPaint.isFakeBoldText() && #ifdef SK_USE_FREETYPE_EMBOLDEN kMaxPerformance_RenderMode == renderMode && #endif SkStrokeRec::kStroke_Style != gpuStroke.getStyle()) { // Instead of baking fake bold into the glyph outlines, do it with the GPU stroke. SkScalar fakeBoldScale = SkScalarInterpFunc(fSkPaint.getTextSize(), kStdFakeBoldInterpKeys, kStdFakeBoldInterpValues, kStdFakeBoldInterpLength); SkScalar extra = SkScalarMul(fSkPaint.getTextSize(), fakeBoldScale); gpuStroke.setStrokeStyle(gpuStroke.needToApply() ? gpuStroke.getWidth() + extra : extra, true /*strokeAndFill*/); fSkPaint.setFakeBoldText(false); } bool canUseRawPaths; if (otherBackendsWillDrawAsPaths || kMaxPerformance_RenderMode == renderMode) { // We can draw the glyphs from canonically sized paths. fTextRatio = fSkPaint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths; 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()); } fSkPaint.setLinearText(true); fSkPaint.setLCDRenderText(false); fSkPaint.setAutohinted(false); fSkPaint.setHinting(SkPaint::kNo_Hinting); fSkPaint.setSubpixelText(true); fSkPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths)); canUseRawPaths = SK_Scalar1 == fSkPaint.getTextScaleX() && 0 == fSkPaint.getTextSkewX() && !fSkPaint.isFakeBoldText() && !fSkPaint.isVerticalText(); } else { fTextRatio = fTextInverseRatio = 1.0f; canUseRawPaths = false; } SkMatrix textMatrix; textMatrix.setTranslate(0, textTranslateY); // Glyphs loaded by GPU path rendering have an inverted y-direction. textMatrix.preScale(fTextRatio, -fTextRatio); fPaint.localCoordChange(textMatrix); fContext->concatMatrix(textMatrix); fGlyphCache = fSkPaint.detachCache(&fDeviceProperties, NULL, false); fGlyphs = canUseRawPaths ? get_gr_glyphs(fContext, fSkPaint.getTypeface(), NULL, gpuStroke) : get_gr_glyphs(fContext, fGlyphCache->getScalerContext()->getTypeface(), &fGlyphCache->getDescriptor(), gpuStroke); } fStateRestore.set(fDrawTarget->drawState()); fDrawTarget->drawState()->setFromPaint(fPaint, fContext->getMatrix(), fContext->getRenderTarget()); GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kZero_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); *fDrawTarget->drawState()->stencil() = kStencilPass; SkASSERT(0 == fPendingGlyphCount); }