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); }
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); }