/** * Draw a single path element of the clip stack into the accumulation bitmap */ void GrSWMaskHelper::drawShape(const GrShape& shape, SkRegion::Op op, GrAA aa, uint8_t alpha) { SkPaint paint; paint.setPathEffect(shape.style().refPathEffect()); shape.style().strokeRec().applyToPaint(&paint); paint.setAntiAlias(GrAA::kYes == aa); SkPath path; shape.asPath(&path); if (SkRegion::kReplace_Op == op && 0xFF == alpha) { SkASSERT(0xFF == paint.getAlpha()); fDraw.drawPathCoverage(path, paint); } else { paint.setBlendMode(op_to_mode(op)); paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha)); fDraw.drawPath(path, paint); } }
bool addPathToAtlas(GrVertexBatch::Target* target, FlushInfo* flushInfo, GrBatchAtlas* atlas, ShapeData* shapeData, const GrShape& shape, bool antiAlias, uint32_t dimension, SkScalar scale) const { const SkRect& bounds = shape.bounds(); // generate bounding rect for bitmap draw SkRect scaledBounds = bounds; // scale to mip level size scaledBounds.fLeft *= scale; scaledBounds.fTop *= scale; scaledBounds.fRight *= scale; scaledBounds.fBottom *= scale; // move the origin to an integer boundary (gives better results) SkScalar dx = SkScalarFraction(scaledBounds.fLeft); SkScalar dy = SkScalarFraction(scaledBounds.fTop); scaledBounds.offset(-dx, -dy); // get integer boundary SkIRect devPathBounds; scaledBounds.roundOut(&devPathBounds); // pad to allow room for antialiasing const int intPad = SkScalarCeilToInt(kAntiAliasPad); // pre-move origin (after outset, will be 0,0) int width = devPathBounds.width(); int height = devPathBounds.height(); devPathBounds.fLeft = intPad; devPathBounds.fTop = intPad; devPathBounds.fRight = intPad + width; devPathBounds.fBottom = intPad + height; devPathBounds.outset(intPad, intPad); // draw path to bitmap SkMatrix drawMatrix; drawMatrix.setTranslate(-bounds.left(), -bounds.top()); drawMatrix.postScale(scale, scale); drawMatrix.postTranslate(kAntiAliasPad, kAntiAliasPad); // setup bitmap backing SkASSERT(devPathBounds.fLeft == 0); SkASSERT(devPathBounds.fTop == 0); SkAutoPixmapStorage dst; if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(), devPathBounds.height()))) { return false; } sk_bzero(dst.writable_addr(), dst.getSafeSize()); // rasterize path SkPaint paint; paint.setStyle(SkPaint::kFill_Style); paint.setAntiAlias(antiAlias); SkDraw draw; sk_bzero(&draw, sizeof(draw)); SkRasterClip rasterClip; rasterClip.setRect(devPathBounds); draw.fRC = &rasterClip; draw.fMatrix = &drawMatrix; draw.fDst = dst; SkPath path; shape.asPath(&path); draw.drawPathCoverage(path, paint); // generate signed distance field devPathBounds.outset(SK_DistanceFieldPad, SK_DistanceFieldPad); width = devPathBounds.width(); height = devPathBounds.height(); // TODO We should really generate this directly into the plot somehow SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char)); // Generate signed distance field SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(), (const unsigned char*)dst.addr(), dst.width(), dst.height(), dst.rowBytes()); // add to atlas SkIPoint16 atlasLocation; GrBatchAtlas::AtlasID id; if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) { this->flush(target, flushInfo); if (!atlas->addToAtlas(&id, target, width, height, dfStorage.get(), &atlasLocation)) { return false; } } // add to cache shapeData->fKey.set(shape, dimension); shapeData->fScale = scale; shapeData->fID = id; // change the scaled rect to match the size of the inset distance field scaledBounds.fRight = scaledBounds.fLeft + SkIntToScalar(devPathBounds.width() - 2*SK_DistanceFieldInset); scaledBounds.fBottom = scaledBounds.fTop + SkIntToScalar(devPathBounds.height() - 2*SK_DistanceFieldInset); // shift the origin to the correct place relative to the distance field // need to also restore the fractional translation scaledBounds.offset(-SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dx, -SkIntToScalar(SK_DistanceFieldInset) - kAntiAliasPad + dy); shapeData->fBounds = scaledBounds; // origin we render from is inset from distance field edge atlasLocation.fX += SK_DistanceFieldInset; atlasLocation.fY += SK_DistanceFieldInset; shapeData->fAtlasLocation = atlasLocation; fShapeCache->add(shapeData); fShapeList->addToTail(shapeData); #ifdef DF_PATH_TRACKING ++g_NumCachedPaths; #endif return true; }
bool GrDefaultPathRenderer::internalDrawPath(GrDrawContext* drawContext, const GrPaint& paint, const GrUserStencilSettings& userStencilSettings, const GrClip& clip, const SkMatrix& viewMatrix, const GrShape& shape, bool stencilOnly) { SkPath path; shape.asPath(&path); SkScalar hairlineCoverage; uint8_t newCoverage = 0xff; bool isHairline = false; if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) { newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff); isHairline = true; } else { SkASSERT(shape.style().isSimpleFill()); } int passCount = 0; const GrUserStencilSettings* passes[3]; GrDrawFace drawFace[3]; bool reverse = false; bool lastPassIsBounds; if (isHairline) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = &userStencilSettings; } lastPassIsBounds = false; drawFace[0] = GrDrawFace::kBoth; } else { if (single_pass_shape(shape)) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = &userStencilSettings; } drawFace[0] = GrDrawFace::kBoth; lastPassIsBounds = false; } else { switch (path.getFillType()) { case SkPath::kInverseEvenOdd_FillType: reverse = true; // fallthrough case SkPath::kEvenOdd_FillType: passes[0] = &gEOStencilPass; if (stencilOnly) { passCount = 1; lastPassIsBounds = false; } else { passCount = 2; lastPassIsBounds = true; if (reverse) { passes[1] = &gInvEOColorPass; } else { passes[1] = &gEOColorPass; } } drawFace[0] = drawFace[1] = GrDrawFace::kBoth; break; case SkPath::kInverseWinding_FillType: reverse = true; // fallthrough case SkPath::kWinding_FillType: if (fSeparateStencil) { if (fStencilWrapOps) { passes[0] = &gWindStencilSeparateWithWrap; } else { passes[0] = &gWindStencilSeparateNoWrap; } passCount = 2; drawFace[0] = GrDrawFace::kBoth; } else { if (fStencilWrapOps) { passes[0] = &gWindSingleStencilWithWrapInc; passes[1] = &gWindSingleStencilWithWrapDec; } else { passes[0] = &gWindSingleStencilNoWrapInc; passes[1] = &gWindSingleStencilNoWrapDec; } // which is cw and which is ccw is arbitrary. drawFace[0] = GrDrawFace::kCW; drawFace[1] = GrDrawFace::kCCW; passCount = 3; } if (stencilOnly) { lastPassIsBounds = false; --passCount; } else { lastPassIsBounds = true; drawFace[passCount-1] = GrDrawFace::kBoth; if (reverse) { passes[passCount-1] = &gInvWindColorPass; } else { passes[passCount-1] = &gWindColorPass; } } break; default: SkDEBUGFAIL("Unknown path fFill!"); return false; } } } SkScalar tol = GrPathUtils::kDefaultTolerance; SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds()); SkRect devBounds; GetPathDevBounds(path, drawContext->width(), drawContext->height(), viewMatrix, &devBounds); for (int p = 0; p < passCount; ++p) { if (lastPassIsBounds && (p == passCount-1)) { SkRect bounds; SkMatrix localMatrix = SkMatrix::I(); if (reverse) { // draw over the dev bounds (which will be the whole dst surface for inv fill). bounds = devBounds; SkMatrix vmi; // mapRect through persp matrix may not be correct if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) { vmi.mapRect(&bounds); } else { if (!viewMatrix.invert(&localMatrix)) { return false; } } } else { bounds = path.getBounds(); } const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() : viewMatrix; SkAutoTUnref<GrDrawBatch> batch( GrRectBatchFactory::CreateNonAAFill(paint.getColor(), viewM, bounds, nullptr, &localMatrix)); SkASSERT(GrDrawFace::kBoth == drawFace[p]); GrPipelineBuilder pipelineBuilder(paint, drawContext->mustUseHWAA(paint)); pipelineBuilder.setDrawFace(drawFace[p]); pipelineBuilder.setUserStencil(passes[p]); drawContext->drawBatch(pipelineBuilder, clip, batch); } else { SkAutoTUnref<GrDrawBatch> batch(new DefaultPathBatch(paint.getColor(), path, srcSpaceTol, newCoverage, viewMatrix, isHairline, devBounds)); GrPipelineBuilder pipelineBuilder(paint, drawContext->mustUseHWAA(paint)); pipelineBuilder.setDrawFace(drawFace[p]); pipelineBuilder.setUserStencil(passes[p]); if (passCount > 1) { pipelineBuilder.setDisableColorXPFactory(); } drawContext->drawBatch(pipelineBuilder, clip, batch); } } return true; }
bool GrDefaultPathRenderer::internalDrawPath(GrRenderTargetContext* renderTargetContext, GrPaint&& paint, GrAAType aaType, const GrUserStencilSettings& userStencilSettings, const GrClip& clip, const SkMatrix& viewMatrix, const GrShape& shape, bool stencilOnly) { auto context = renderTargetContext->surfPriv().getContext(); SkASSERT(GrAAType::kCoverage != aaType); SkPath path; shape.asPath(&path); SkScalar hairlineCoverage; uint8_t newCoverage = 0xff; bool isHairline = false; if (IsStrokeHairlineOrEquivalent(shape.style(), viewMatrix, &hairlineCoverage)) { newCoverage = SkScalarRoundToInt(hairlineCoverage * 0xff); isHairline = true; } else { SkASSERT(shape.style().isSimpleFill()); } int passCount = 0; const GrUserStencilSettings* passes[2]; bool reverse = false; bool lastPassIsBounds; if (isHairline) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = &userStencilSettings; } lastPassIsBounds = false; } else { if (single_pass_shape(shape)) { passCount = 1; if (stencilOnly) { passes[0] = &gDirectToStencil; } else { passes[0] = &userStencilSettings; } lastPassIsBounds = false; } else { switch (path.getFillType()) { case SkPath::kInverseEvenOdd_FillType: reverse = true; // fallthrough case SkPath::kEvenOdd_FillType: passes[0] = &gEOStencilPass; if (stencilOnly) { passCount = 1; lastPassIsBounds = false; } else { passCount = 2; lastPassIsBounds = true; if (reverse) { passes[1] = &gInvEOColorPass; } else { passes[1] = &gEOColorPass; } } break; case SkPath::kInverseWinding_FillType: reverse = true; // fallthrough case SkPath::kWinding_FillType: passes[0] = &gWindStencilPass; passCount = 2; if (stencilOnly) { lastPassIsBounds = false; --passCount; } else { lastPassIsBounds = true; if (reverse) { passes[passCount-1] = &gInvWindColorPass; } else { passes[passCount-1] = &gWindColorPass; } } break; default: SkDEBUGFAIL("Unknown path fFill!"); return false; } } } SkScalar tol = GrPathUtils::kDefaultTolerance; SkScalar srcSpaceTol = GrPathUtils::scaleToleranceToSrc(tol, viewMatrix, path.getBounds()); SkRect devBounds; GetPathDevBounds(path, renderTargetContext->asRenderTargetProxy()->worstCaseWidth(), renderTargetContext->asRenderTargetProxy()->worstCaseHeight(), viewMatrix, &devBounds); for (int p = 0; p < passCount; ++p) { if (lastPassIsBounds && (p == passCount-1)) { SkRect bounds; SkMatrix localMatrix = SkMatrix::I(); if (reverse) { // draw over the dev bounds (which will be the whole dst surface for inv fill). bounds = devBounds; SkMatrix vmi; // mapRect through persp matrix may not be correct if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) { vmi.mapRect(&bounds); } else { if (!viewMatrix.invert(&localMatrix)) { return false; } } } else { bounds = path.getBounds(); } const SkMatrix& viewM = (reverse && viewMatrix.hasPerspective()) ? SkMatrix::I() : viewMatrix; // This is a non-coverage aa rect op since we assert aaType != kCoverage at the start assert_alive(paint); renderTargetContext->priv().stencilRect(clip, passes[p], std::move(paint), GrAA(aaType == GrAAType::kMSAA), viewM, bounds, &localMatrix); } else { bool stencilPass = stencilOnly || passCount > 1; std::unique_ptr<GrDrawOp> op; if (stencilPass) { GrPaint stencilPaint; stencilPaint.setXPFactory(GrDisableColorXPFactory::Get()); op = DefaultPathOp::Make(context, std::move(stencilPaint), path, srcSpaceTol, newCoverage, viewMatrix, isHairline, aaType, devBounds, passes[p]); } else { assert_alive(paint); op = DefaultPathOp::Make(context, std::move(paint), path, srcSpaceTol, newCoverage, viewMatrix, isHairline, aaType, devBounds, passes[p]); } renderTargetContext->addDrawOp(clip, std::move(op)); } } return true; }