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#include "GrSoftwarePathRenderer.h"
10#include "GrContext.h"
11#include "GrSWMaskHelper.h"
12
13////////////////////////////////////////////////////////////////////////////////
14bool GrSoftwarePathRenderer::canDrawPath(const SkPath&,
15                                         const SkStrokeRec&,
16                                         const GrDrawTarget*,
17                                         bool antiAlias) const {
18    if (!antiAlias || NULL == fContext) {
19        // TODO: We could allow the SW path to also handle non-AA paths but
20        // this would mean that GrDefaultPathRenderer would never be called
21        // (since it appears after the SW renderer in the path renderer
22        // chain). Some testing would need to be done r.e. performance
23        // and consistency of the resulting images before removing
24        // the "!antiAlias" clause from the above test
25        return false;
26    }
27
28    return true;
29}
30
31GrPathRenderer::StencilSupport GrSoftwarePathRenderer::onGetStencilSupport(
32    const SkPath&,
33    const SkStrokeRec&,
34    const GrDrawTarget*) const {
35    return GrPathRenderer::kNoSupport_StencilSupport;
36}
37
38namespace {
39
40////////////////////////////////////////////////////////////////////////////////
41// gets device coord bounds of path (not considering the fill) and clip. The
42// path bounds will be a subset of the clip bounds. returns false if
43// path bounds would be empty.
44bool get_path_and_clip_bounds(const GrDrawTarget* target,
45                              const SkPath& path,
46                              const SkMatrix& matrix,
47                              SkIRect* devPathBounds,
48                              SkIRect* devClipBounds) {
49    // compute bounds as intersection of rt size, clip, and path
50    const GrRenderTarget* rt = target->getDrawState().getRenderTarget();
51    if (NULL == rt) {
52        return false;
53    }
54    *devPathBounds = SkIRect::MakeWH(rt->width(), rt->height());
55
56    target->getClip()->getConservativeBounds(rt, devClipBounds);
57
58    // TODO: getConservativeBounds already intersects with the
59    // render target's bounding box. Remove this next line
60    if (!devPathBounds->intersect(*devClipBounds)) {
61        return false;
62    }
63
64    if (!path.getBounds().isEmpty()) {
65        SkRect pathSBounds;
66        matrix.mapRect(&pathSBounds, path.getBounds());
67        SkIRect pathIBounds;
68        pathSBounds.roundOut(&pathIBounds);
69        if (!devPathBounds->intersect(pathIBounds)) {
70            // set the correct path bounds, as this would be used later.
71            *devPathBounds = pathIBounds;
72            return false;
73        }
74    } else {
75        *devPathBounds = SkIRect::EmptyIRect();
76        return false;
77    }
78    return true;
79}
80
81////////////////////////////////////////////////////////////////////////////////
82void draw_around_inv_path(GrDrawTarget* target,
83                          const SkIRect& devClipBounds,
84                          const SkIRect& devPathBounds) {
85    GrDrawState::AutoViewMatrixRestore avmr;
86    if (!avmr.setIdentity(target->drawState())) {
87        return;
88    }
89    SkRect rect;
90    if (devClipBounds.fTop < devPathBounds.fTop) {
91        rect.iset(devClipBounds.fLeft, devClipBounds.fTop,
92                  devClipBounds.fRight, devPathBounds.fTop);
93        target->drawSimpleRect(rect, NULL);
94    }
95    if (devClipBounds.fLeft < devPathBounds.fLeft) {
96        rect.iset(devClipBounds.fLeft, devPathBounds.fTop,
97                  devPathBounds.fLeft, devPathBounds.fBottom);
98        target->drawSimpleRect(rect, NULL);
99    }
100    if (devClipBounds.fRight > devPathBounds.fRight) {
101        rect.iset(devPathBounds.fRight, devPathBounds.fTop,
102                  devClipBounds.fRight, devPathBounds.fBottom);
103        target->drawSimpleRect(rect, NULL);
104    }
105    if (devClipBounds.fBottom > devPathBounds.fBottom) {
106        rect.iset(devClipBounds.fLeft, devPathBounds.fBottom,
107                  devClipBounds.fRight, devClipBounds.fBottom);
108        target->drawSimpleRect(rect, NULL);
109    }
110}
111
112}
113
114////////////////////////////////////////////////////////////////////////////////
115// return true on success; false on failure
116bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path,
117                                        const SkStrokeRec& stroke,
118                                        GrDrawTarget* target,
119                                        bool antiAlias) {
120
121    if (NULL == fContext) {
122        return false;
123    }
124
125    GrDrawState* drawState = target->drawState();
126
127    SkMatrix vm = drawState->getViewMatrix();
128
129    SkIRect devPathBounds, devClipBounds;
130    if (!get_path_and_clip_bounds(target, path, vm,
131                                  &devPathBounds, &devClipBounds)) {
132        if (path.isInverseFillType()) {
133            draw_around_inv_path(target, devClipBounds, devPathBounds);
134        }
135        return true;
136    }
137
138    SkAutoTUnref<GrTexture> texture(
139            GrSWMaskHelper::DrawPathMaskToTexture(fContext, path, stroke,
140                                                  devPathBounds,
141                                                  antiAlias, &vm));
142    if (NULL == texture) {
143        return false;
144    }
145
146    GrSWMaskHelper::DrawToTargetWithPathMask(texture, target, devPathBounds);
147
148    if (path.isInverseFillType()) {
149        draw_around_inv_path(target, devClipBounds, devPathBounds);
150    }
151
152    return true;
153}
154