/** * Draw a single path element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op, bool antiAlias, uint8_t alpha) { SkPaint paint; if (stroke.isHairlineStyle()) { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(SK_Scalar1); } else { if (stroke.isFillStyle()) { paint.setStyle(SkPaint::kFill_Style); } else { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeJoin(stroke.getJoin()); paint.setStrokeCap(stroke.getCap()); paint.setStrokeWidth(stroke.getWidth()); } } SkXfermode* mode = SkXfermode::Create(op_to_mode(op)); paint.setXfermode(mode); paint.setAntiAlias(antiAlias); paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); fDraw.drawPath(path, paint); SkSafeUnref(mode); }
/** * Draw a single path element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op, bool antiAlias, uint8_t alpha) { SkPaint paint; if (stroke.isHairlineStyle()) { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(SK_Scalar1); } else { if (stroke.isFillStyle()) { paint.setStyle(SkPaint::kFill_Style); } else { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeJoin(stroke.getJoin()); paint.setStrokeCap(stroke.getCap()); paint.setStrokeWidth(stroke.getWidth()); } } paint.setAntiAlias(antiAlias); if (SkRegion::kReplace_Op == op && 0xFF == alpha) { SkASSERT(0xFF == paint.getAlpha()); fDraw.drawPathCoverage(path, paint); } else { paint.setXfermodeMode(op_to_mode(op)); paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); fDraw.drawPath(path, paint); } }
void GrDrawTarget::stencilPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType fill) { // TODO: extract portions of checkDraw that are relevant to path stenciling. GrAssert(NULL != path); GrAssert(fCaps.pathStencilingSupport()); GrAssert(!stroke.isHairlineStyle()); GrAssert(!SkPath::IsInverseFillType(fill)); this->onStencilPath(path, stroke, fill); }
bool GrStencilAndCoverPathRenderer::canDrawPath(const SkPath& path, const SkStrokeRec& stroke, const GrDrawTarget* target, bool antiAlias) const { return !stroke.isHairlineStyle() && !antiAlias && // doesn't do per-path AA, relies on the target having MSAA target->getDrawState().getRenderTarget()->getStencilBuffer() && target->getDrawState().getStencil().isDisabled(); }
static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) { #if STENCIL_OFF return true; #else if (!stroke.isHairlineStyle() && !path.isInverseFillType()) { return path.isConvex(); } return false; #endif }
bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path, const SkStrokeRec& stroke, const GrDrawTarget* target, bool antiAlias) const { if (!stroke.isHairlineStyle() || !antiAlias) { return false; } static const uint32_t gReqDerivMask = SkPath::kCubic_SegmentMask | SkPath::kQuad_SegmentMask; if (!target->getCaps().shaderDerivativeSupport() && (gReqDerivMask & path.getSegmentMasks())) { return false; } return true; }
/** * Draw a single path element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op, bool antiAlias, uint8_t alpha) { SkPaint paint; if (stroke.isHairlineStyle()) { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(SK_Scalar1); } else { if (stroke.isFillStyle()) { paint.setStyle(SkPaint::kFill_Style); } else { paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeJoin(stroke.getJoin()); paint.setStrokeCap(stroke.getCap()); paint.setStrokeWidth(stroke.getWidth()); } } paint.setAntiAlias(antiAlias); SkTBlitterAllocator allocator; SkBlitter* blitter = nullptr; if (kBlitter_CompressionMode == fCompressionMode) { SkASSERT(fCompressedBuffer.get()); blitter = SkTextureCompressor::CreateBlitterForFormat( fPixels.width(), fPixels.height(), fCompressedBuffer.get(), &allocator, fCompressedFormat); } if (SkRegion::kReplace_Op == op && 0xFF == alpha) { SkASSERT(0xFF == paint.getAlpha()); fDraw.drawPathCoverage(path, paint, blitter); } else { paint.setXfermodeMode(op_to_mode(op)); paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); fDraw.drawPath(path, paint, blitter); } }
bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path, const SkStrokeRec& stroke, GrDrawTarget* target, bool antiAlias) { SkASSERT(!antiAlias); SkASSERT(!stroke.isHairlineStyle()); GrDrawState* drawState = target->drawState(); SkASSERT(drawState->getStencil().isDisabled()); SkAutoTUnref<GrPath> p(fGpu->createPath(path)); SkPath::FillType nonInvertedFill = SkPath::ConvertToNonInverseFillType(path.getFillType()); target->stencilPath(p, stroke, nonInvertedFill); // TODO: Use built in cover operation rather than a rect draw. This will require making our // fragment shaders be able to eat varyings generated by a matrix. // fill the path, zero out the stencil SkRect bounds = p->getBounds(); SkScalar bloat = drawState->getViewMatrix().getMaxStretch() * SK_ScalarHalf; GrDrawState::AutoViewMatrixRestore avmr; if (nonInvertedFill == path.getFillType()) { GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kZero_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); *drawState->stencil() = kStencilPass; } else { GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass, kZero_StencilOp, kZero_StencilOp, // We know our rect will hit pixels outside the clip and the user bits will be 0 // outside the clip. So we can't just fill where the user bits are 0. We also need to // check that the clip bit is set. kEqualIfInClip_StencilFunc, 0xffff, 0x0000, 0xffff); SkMatrix vmi; bounds.setLTRB(0, 0, SkIntToScalar(drawState->getRenderTarget()->width()), SkIntToScalar(drawState->getRenderTarget()->height())); // mapRect through persp matrix may not be correct if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) { vmi.mapRect(&bounds); // theoretically could set bloat = 0, instead leave it because of matrix inversion // precision. } else { avmr.setIdentity(drawState); bloat = 0; } *drawState->stencil() = kInvertedStencilPass; } bounds.outset(bloat, bloat); target->drawSimpleRect(bounds, NULL); target->drawState()->stencil()->setDisabled(); return true; }
bool GrDefaultPathRenderer::createGeom(const SkPath& path, const SkStrokeRec& stroke, SkScalar srcSpaceTol, GrDrawTarget* target, GrPrimitiveType* primType, int* vertexCnt, int* indexCnt, GrDrawTarget::AutoReleaseGeometry* arg) { { SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol); int contourCnt; int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt, srcSpaceTol); if (maxPts <= 0) { return false; } if (maxPts > ((int)SK_MaxU16 + 1)) { GrPrintf("Path not rendered, too many verts (%d)\n", maxPts); return false; } bool indexed = contourCnt > 1; const bool isHairline = stroke.isHairlineStyle(); int maxIdxs = 0; if (isHairline) { if (indexed) { maxIdxs = 2 * maxPts; *primType = kLines_GrPrimitiveType; } else { *primType = kLineStrip_GrPrimitiveType; } } else { if (indexed) { maxIdxs = 3 * maxPts; *primType = kTriangles_GrPrimitiveType; } else { *primType = kTriangleFan_GrPrimitiveType; } } target->drawState()->setDefaultVertexAttribs(); if (!arg->set(target, maxPts, maxIdxs)) { return false; } uint16_t* idxBase = reinterpret_cast<uint16_t*>(arg->indices()); uint16_t* idx = idxBase; uint16_t subpathIdxStart = 0; SkPoint* base = reinterpret_cast<SkPoint*>(arg->vertices()); SkASSERT(NULL != base); SkPoint* vert = base; SkPoint pts[4]; bool first = true; int subpath = 0; SkPath::Iter iter(path, false); for (;;) { SkPath::Verb verb = iter.next(pts); switch (verb) { case SkPath::kConic_Verb: SkASSERT(0); break; case SkPath::kMove_Verb: if (!first) { uint16_t currIdx = (uint16_t) (vert - base); subpathIdxStart = currIdx; ++subpath; } *vert = pts[0]; vert++; break; case SkPath::kLine_Verb: if (indexed) { uint16_t prevIdx = (uint16_t)(vert - base) - 1; append_countour_edge_indices(isHairline, subpathIdxStart, prevIdx, &idx); } *(vert++) = pts[1]; break; case SkPath::kQuad_Verb: { // first pt of quad is the pt we ended on in previous step uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1; uint16_t numPts = (uint16_t) GrPathUtils::generateQuadraticPoints( pts[0], pts[1], pts[2], srcSpaceTolSqd, &vert, GrPathUtils::quadraticPointCount(pts, srcSpaceTol)); if (indexed) { for (uint16_t i = 0; i < numPts; ++i) { append_countour_edge_indices(isHairline, subpathIdxStart, firstQPtIdx + i, &idx); } } break; } case SkPath::kCubic_Verb: { // first pt of cubic is the pt we ended on in previous step uint16_t firstCPtIdx = (uint16_t)(vert - base) - 1; uint16_t numPts = (uint16_t) GrPathUtils::generateCubicPoints( pts[0], pts[1], pts[2], pts[3], srcSpaceTolSqd, &vert, GrPathUtils::cubicPointCount(pts, srcSpaceTol)); if (indexed) { for (uint16_t i = 0; i < numPts; ++i) { append_countour_edge_indices(isHairline, subpathIdxStart, firstCPtIdx + i, &idx); } } break; } case SkPath::kClose_Verb: break; case SkPath::kDone_Verb: // uint16_t currIdx = (uint16_t) (vert - base); goto FINISHED; } first = false; } FINISHED: SkASSERT((vert - base) <= maxPts); SkASSERT((idx - idxBase) <= maxIdxs); *vertexCnt = static_cast<int>(vert - base); *indexCnt = static_cast<int>(idx - idxBase); } return true; }
bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path, const SkStrokeRec& stroke, GrDrawTarget* target, bool antiAlias) { SkASSERT(!antiAlias); SkASSERT(!stroke.isHairlineStyle()); GrDrawState* drawState = target->drawState(); SkASSERT(drawState->getStencil().isDisabled()); SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke)); if (path.isInverseFillType()) { GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass, kZero_StencilOp, kZero_StencilOp, // We know our rect will hit pixels outside the clip and the user bits will be 0 // outside the clip. So we can't just fill where the user bits are 0. We also need to // check that the clip bit is set. kEqualIfInClip_StencilFunc, 0xffff, 0x0000, 0xffff); drawState->setStencil(kInvertedStencilPass); // fake inverse with a stencil and cover target->stencilPath(p, convert_skpath_filltype(path.getFillType())); GrDrawState::AutoViewMatrixRestore avmr; SkRect bounds = SkRect::MakeLTRB(0, 0, SkIntToScalar(drawState->getRenderTarget()->width()), SkIntToScalar(drawState->getRenderTarget()->height())); SkMatrix vmi; // mapRect through persp matrix may not be correct if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) { vmi.mapRect(&bounds); // theoretically could set bloat = 0, instead leave it because of matrix inversion // precision. SkScalar bloat = drawState->getViewMatrix().getMaxScale() * SK_ScalarHalf; bounds.outset(bloat, bloat); } else { avmr.setIdentity(drawState); } target->drawSimpleRect(bounds); } else { GR_STATIC_CONST_SAME_STENCIL(kStencilPass, kZero_StencilOp, kZero_StencilOp, kNotEqual_StencilFunc, 0xffff, 0x0000, 0xffff); drawState->setStencil(kStencilPass); target->drawPath(p, convert_skpath_filltype(path.getFillType())); } target->drawState()->stencil()->setDisabled(); return true; }
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); }