1
2/*
3 * Copyright 2012 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9
10#include "GrStencilAndCoverPathRenderer.h"
11#include "GrContext.h"
12#include "GrDrawTargetCaps.h"
13#include "GrGpu.h"
14#include "GrPath.h"
15#include "GrRenderTarget.h"
16#include "GrRenderTargetPriv.h"
17#include "GrResourceProvider.h"
18#include "GrStrokeInfo.h"
19
20/*
21 * For now paths only natively support winding and even odd fill types
22 */
23static GrPathRendering::FillType convert_skpath_filltype(SkPath::FillType fill) {
24    switch (fill) {
25        default:
26            SkFAIL("Incomplete Switch\n");
27        case SkPath::kWinding_FillType:
28        case SkPath::kInverseWinding_FillType:
29            return GrPathRendering::kWinding_FillType;
30        case SkPath::kEvenOdd_FillType:
31        case SkPath::kInverseEvenOdd_FillType:
32            return GrPathRendering::kEvenOdd_FillType;
33    }
34}
35
36GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrContext* context) {
37    SkASSERT(context);
38    SkASSERT(context->getGpu());
39    if (context->getGpu()->caps()->shaderCaps()->pathRenderingSupport()) {
40        return SkNEW_ARGS(GrStencilAndCoverPathRenderer, (context->getGpu()));
41    } else {
42        return NULL;
43    }
44}
45
46GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrGpu* gpu) {
47    SkASSERT(gpu->caps()->shaderCaps()->pathRenderingSupport());
48    fGpu = gpu;
49    gpu->ref();
50}
51
52GrStencilAndCoverPathRenderer::~GrStencilAndCoverPathRenderer() {
53    fGpu->unref();
54}
55
56bool GrStencilAndCoverPathRenderer::canDrawPath(const GrDrawTarget* target,
57                                                const GrPipelineBuilder* pipelineBuilder,
58                                                const SkMatrix& viewMatrix,
59                                                const SkPath& path,
60                                                const GrStrokeInfo& stroke,
61                                                bool antiAlias) const {
62    return !stroke.getStrokeRec().isHairlineStyle() &&
63        !stroke.isDashed() &&
64        !antiAlias && // doesn't do per-path AA, relies on the target having MSAA
65        pipelineBuilder->getStencil().isDisabled();
66}
67
68GrPathRenderer::StencilSupport
69GrStencilAndCoverPathRenderer::onGetStencilSupport(const GrDrawTarget*,
70                                                   const GrPipelineBuilder*,
71                                                   const SkPath&,
72                                                   const GrStrokeInfo&) const {
73    return GrPathRenderer::kStencilOnly_StencilSupport;
74}
75
76static GrPath* get_gr_path(GrGpu* gpu, const SkPath& skPath, const SkStrokeRec& stroke) {
77    GrContext* ctx = gpu->getContext();
78    GrUniqueKey key;
79    GrPath::ComputeKey(skPath, stroke, &key);
80    SkAutoTUnref<GrPath> path(
81        static_cast<GrPath*>(ctx->resourceProvider()->findAndRefResourceByUniqueKey(key)));
82    if (NULL == path || !path->isEqualTo(skPath, stroke)) {
83        path.reset(gpu->pathRendering()->createPath(skPath, stroke));
84        ctx->resourceProvider()->assignUniqueKeyToResource(key, path);
85    }
86    return path.detach();
87}
88
89void GrStencilAndCoverPathRenderer::onStencilPath(GrDrawTarget* target,
90                                                  GrPipelineBuilder* pipelineBuilder,
91                                                  const SkMatrix& viewMatrix,
92                                                  const SkPath& path,
93                                                  const GrStrokeInfo& stroke) {
94    SkASSERT(!path.isInverseFillType());
95    SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(GrColor_WHITE, viewMatrix));
96    SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke.getStrokeRec()));
97    target->stencilPath(pipelineBuilder, pp, p, convert_skpath_filltype(path.getFillType()));
98}
99
100bool GrStencilAndCoverPathRenderer::onDrawPath(GrDrawTarget* target,
101                                               GrPipelineBuilder* pipelineBuilder,
102                                               GrColor color,
103                                               const SkMatrix& viewMatrix,
104                                               const SkPath& path,
105                                               const GrStrokeInfo& stroke,
106                                               bool antiAlias) {
107    SkASSERT(!antiAlias);
108    SkASSERT(!stroke.getStrokeRec().isHairlineStyle());
109    SkASSERT(!stroke.isDashed());
110    SkASSERT(pipelineBuilder->getStencil().isDisabled());
111
112    SkAutoTUnref<GrPath> p(get_gr_path(fGpu, path, stroke.getStrokeRec()));
113
114    if (path.isInverseFillType()) {
115        GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass,
116            kZero_StencilOp,
117            kZero_StencilOp,
118            // We know our rect will hit pixels outside the clip and the user bits will be 0
119            // outside the clip. So we can't just fill where the user bits are 0. We also need to
120            // check that the clip bit is set.
121            kEqualIfInClip_StencilFunc,
122            0xffff,
123            0x0000,
124            0xffff);
125
126        pipelineBuilder->setStencil(kInvertedStencilPass);
127
128        // fake inverse with a stencil and cover
129        SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(GrColor_WHITE, viewMatrix));
130        target->stencilPath(pipelineBuilder, pp, p, convert_skpath_filltype(path.getFillType()));
131
132        SkMatrix invert = SkMatrix::I();
133        SkRect bounds =
134            SkRect::MakeLTRB(0, 0, SkIntToScalar(pipelineBuilder->getRenderTarget()->width()),
135                             SkIntToScalar(pipelineBuilder->getRenderTarget()->height()));
136        SkMatrix vmi;
137        // mapRect through persp matrix may not be correct
138        if (!viewMatrix.hasPerspective() && viewMatrix.invert(&vmi)) {
139            vmi.mapRect(&bounds);
140            // theoretically could set bloat = 0, instead leave it because of matrix inversion
141            // precision.
142            SkScalar bloat = viewMatrix.getMaxScale() * SK_ScalarHalf;
143            bounds.outset(bloat, bloat);
144        } else {
145            if (!viewMatrix.invert(&invert)) {
146                return false;
147            }
148        }
149        const SkMatrix& viewM = viewMatrix.hasPerspective() ? SkMatrix::I() : viewMatrix;
150        target->drawRect(pipelineBuilder, color, viewM, bounds, NULL, &invert);
151    } else {
152        GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
153            kZero_StencilOp,
154            kZero_StencilOp,
155            kNotEqual_StencilFunc,
156            0xffff,
157            0x0000,
158            0xffff);
159
160        pipelineBuilder->setStencil(kStencilPass);
161        SkAutoTUnref<GrPathProcessor> pp(GrPathProcessor::Create(color, viewMatrix));
162        target->drawPath(pipelineBuilder, pp, p, convert_skpath_filltype(path.getFillType()));
163    }
164
165    pipelineBuilder->stencil()->setDisabled();
166    return true;
167}
168