diff options
Diffstat (limited to 'src/gpu/ops/GrStencilAndCoverPathRenderer.cpp')
-rw-r--r-- | src/gpu/ops/GrStencilAndCoverPathRenderer.cpp | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp new file mode 100644 index 0000000000..16765e0bac --- /dev/null +++ b/src/gpu/ops/GrStencilAndCoverPathRenderer.cpp @@ -0,0 +1,165 @@ +/* + * Copyright 2012 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrStencilAndCoverPathRenderer.h" +#include "GrCaps.h" +#include "GrContext.h" +#include "GrDrawPathOp.h" +#include "GrFixedClip.h" +#include "GrGpu.h" +#include "GrPath.h" +#include "GrPipelineBuilder.h" +#include "GrRenderTarget.h" +#include "GrRenderTargetContextPriv.h" +#include "GrResourceProvider.h" +#include "GrStencilPathOp.h" +#include "GrStyle.h" +#include "ops/GrRectOpFactory.h" + +GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrResourceProvider* resourceProvider, + const GrCaps& caps) { + if (caps.shaderCaps()->pathRenderingSupport()) { + return new GrStencilAndCoverPathRenderer(resourceProvider); + } else { + return nullptr; + } +} + +GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrResourceProvider* resourceProvider) + : fResourceProvider(resourceProvider) { +} + +bool GrStencilAndCoverPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { + // GrPath doesn't support hairline paths. An arbitrary path effect could produce a hairline + // path. + if (args.fShape->style().strokeRec().isHairlineStyle() || + args.fShape->style().hasNonDashPathEffect()) { + return false; + } + if (args.fHasUserStencilSettings) { + return false; + } + // doesn't do per-path AA, relies on the target having MSAA. + return (GrAAType::kCoverage != args.fAAType); +} + +static GrPath* get_gr_path(GrResourceProvider* resourceProvider, const GrShape& shape) { + GrUniqueKey key; + bool isVolatile; + GrPath::ComputeKey(shape, &key, &isVolatile); + sk_sp<GrPath> path; + if (!isVolatile) { + path.reset( + static_cast<GrPath*>(resourceProvider->findAndRefResourceByUniqueKey(key))); + } + if (!path) { + SkPath skPath; + shape.asPath(&skPath); + path.reset(resourceProvider->createPath(skPath, shape.style())); + if (!isVolatile) { + resourceProvider->assignUniqueKeyToResource(key, path.get()); + } + } else { +#ifdef SK_DEBUG + SkPath skPath; + shape.asPath(&skPath); + SkASSERT(path->isEqualTo(skPath, shape.style())); +#endif + } + return path.release(); +} + +void GrStencilAndCoverPathRenderer::onStencilPath(const StencilPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), + "GrStencilAndCoverPathRenderer::onStencilPath"); + sk_sp<GrPath> p(get_gr_path(fResourceProvider, *args.fShape)); + args.fRenderTargetContext->priv().stencilPath(*args.fClip, args.fAAType, + *args.fViewMatrix, p.get()); +} + +bool GrStencilAndCoverPathRenderer::onDrawPath(const DrawPathArgs& args) { + GR_AUDIT_TRAIL_AUTO_FRAME(args.fRenderTargetContext->auditTrail(), + "GrStencilAndCoverPathRenderer::onDrawPath"); + SkASSERT(!args.fShape->style().strokeRec().isHairlineStyle()); + + const SkMatrix& viewMatrix = *args.fViewMatrix; + + + sk_sp<GrPath> path(get_gr_path(fResourceProvider, *args.fShape)); + + if (args.fShape->inverseFilled()) { + SkMatrix invert = SkMatrix::I(); + SkRect bounds = + SkRect::MakeLTRB(0, 0, + SkIntToScalar(args.fRenderTargetContext->width()), + SkIntToScalar(args.fRenderTargetContext->height())); + SkMatrix vmi; + // mapRect through persp matrix may not be correct + if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) { + vmi.mapRect(&bounds); + // theoretically could set bloat = 0, instead leave it because of matrix inversion + // precision. + SkScalar bloat = viewMatrix.getMaxScale() * SK_ScalarHalf; + bounds.outset(bloat, bloat); + } else { + if (!viewMatrix.invert(&invert)) { + return false; + } + } + const SkMatrix& viewM = viewMatrix.hasPerspective() ? SkMatrix::I() : viewMatrix; + + sk_sp<GrDrawOp> coverOp(GrRectOpFactory::MakeNonAAFill(args.fPaint->getColor(), viewM, + bounds, nullptr, &invert)); + + // fake inverse with a stencil and cover + args.fRenderTargetContext->priv().stencilPath(*args.fClip, args.fAAType, viewMatrix, + path.get()); + + { + static constexpr GrUserStencilSettings kInvertedCoverPass( + GrUserStencilSettings::StaticInit< + 0x0000, + // 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. + GrUserStencilTest::kEqualIfInClip, + 0xffff, + GrUserStencilOp::kKeep, + GrUserStencilOp::kZero, + 0xffff>() + ); + // We have to suppress enabling MSAA for mixed samples or we will get seams due to + // coverage modulation along the edge where two triangles making up the rect meet. + GrAAType coverAAType = args.fAAType; + if (GrAAType::kMixedSamples == coverAAType) { + coverAAType = GrAAType::kNone; + } + GrPipelineBuilder pipelineBuilder(*args.fPaint, coverAAType); + pipelineBuilder.setUserStencil(&kInvertedCoverPass); + + args.fRenderTargetContext->addDrawOp(pipelineBuilder, *args.fClip, std::move(coverOp)); + } + } else { + static constexpr GrUserStencilSettings kCoverPass( + GrUserStencilSettings::StaticInit< + 0x0000, + GrUserStencilTest::kNotEqual, + 0xffff, + GrUserStencilOp::kZero, + GrUserStencilOp::kKeep, + 0xffff>() + ); + + sk_sp<GrDrawOp> op = GrDrawPathOp::Make(viewMatrix, args.fPaint->getColor(), path.get()); + + GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fAAType); + pipelineBuilder.setUserStencil(&kCoverPass); + args.fRenderTargetContext->addDrawOp(pipelineBuilder, *args.fClip, std::move(op)); + } + + return true; +} |