bool GrStyle::applyToPath(SkPath* dst, SkStrokeRec::InitStyle* style, const SkPath& src, SkScalar resScale) const { SkASSERT(style); SkASSERT(dst); SkStrokeRec strokeRec = fStrokeRec; strokeRec.setResScale(resScale); const SkPath* pathForStrokeRec = &src; if (apply_path_effect(dst, &strokeRec, fPathEffect, src)) { pathForStrokeRec = dst; } else if (fPathEffect) { return false; } if (strokeRec.needToApply()) { if (!strokeRec.applyToPath(dst, *pathForStrokeRec)) { return false; } *style = SkStrokeRec::kFill_InitStyle; } else if (!fPathEffect) { // Nothing to do for path effect or stroke, fail. return false; } else { SkASSERT(SkStrokeRec::kFill_Style == strokeRec.getStyle() || SkStrokeRec::kHairline_Style == strokeRec.getStyle()); *style = strokeRec.getStyle() == SkStrokeRec::kFill_Style ? SkStrokeRec::kFill_InitStyle : SkStrokeRec::kHairline_InitStyle; } return true; }
uint64_t GrPath::ComputeStrokeKey(const SkStrokeRec& stroke) { enum { kStyleBits = 2, kJoinBits = 2, kCapBits = 2, kWidthBits = 29, kMiterBits = 29, kJoinShift = kStyleBits, kCapShift = kJoinShift + kJoinBits, kWidthShift = kCapShift + kCapBits, kMiterShift = kWidthShift + kWidthBits, kBitCount = kMiterShift + kMiterBits }; SK_COMPILE_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits), style_shift_will_be_wrong); SK_COMPILE_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits), cap_shift_will_be_wrong); SK_COMPILE_ASSERT(SkPaint::kCapCount <= (1 << kCapBits), miter_shift_will_be_wrong); SK_COMPILE_ASSERT(kBitCount == 64, wrong_stroke_key_size); if (!stroke.needToApply()) { return SkStrokeRec::kFill_Style; } uint64_t key = stroke.getStyle(); key |= stroke.getJoin() << kJoinShift; key |= stroke.getCap() << kCapShift; key |= get_top_n_float_bits<kWidthBits>(stroke.getWidth()) << kWidthShift; key |= get_top_n_float_bits<kMiterBits>(stroke.getMiter()) << kMiterShift; return key; }
Geometry(const SkStrokeRec& stroke) : fStroke(stroke) { if (!stroke.needToApply()) { // purify unused values to ensure binary equality fStroke.setStrokeParams(SkPaint::kDefault_Cap, SkPaint::kDefault_Join, SkIntToScalar(4)); if (fStroke.getWidth() < 0) { fStroke.setStrokeStyle(-1.0f); } } }
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); }