blob: 2566a53db694ac59d3be37e692d2fcb5933d1690 [file] [log] [blame]
robertphillips@google.comf4c2c522012-04-27 12:08:47 +00001
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"
robertphillips@google.comed4155d2012-05-01 14:30:24 +000010#include "GrContext.h"
robertphillips@google.com58b20212012-06-27 20:44:52 +000011#include "GrSWMaskHelper.h"
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000012
robertphillips@google.comed4155d2012-05-01 14:30:24 +000013////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000014bool GrSoftwarePathRenderer::canDrawPath(const SkPath& path,
15 GrPathFill fill,
16 const GrDrawTarget* target,
17 bool antiAlias) const {
robertphillips@google.comed4155d2012-05-01 14:30:24 +000018 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
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000025 return false;
26 }
27
robertphillips@google.comed4155d2012-05-01 14:30:24 +000028 return true;
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000029}
30
robertphillips@google.comed4155d2012-05-01 14:30:24 +000031namespace {
32
33////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comed4155d2012-05-01 14:30:24 +000034// gets device coord bounds of path (not considering the fill) and clip. The
35// path bounds will be a subset of the clip bounds. returns false if
36// path bounds would be empty.
37bool get_path_and_clip_bounds(const GrDrawTarget* target,
38 const SkPath& path,
39 const GrVec* translate,
40 GrIRect* pathBounds,
41 GrIRect* clipBounds) {
42 // compute bounds as intersection of rt size, clip, and path
43 const GrRenderTarget* rt = target->getDrawState().getRenderTarget();
44 if (NULL == rt) {
45 return false;
46 }
47 *pathBounds = GrIRect::MakeWH(rt->width(), rt->height());
48 const GrClip& clip = target->getClip();
49 if (clip.hasConservativeBounds()) {
50 clip.getConservativeBounds().roundOut(clipBounds);
51 if (!pathBounds->intersect(*clipBounds)) {
52 return false;
53 }
54 } else {
55 // pathBounds is currently the rt extent, set clip bounds to that rect.
56 *clipBounds = *pathBounds;
57 }
58 GrRect pathSBounds = path.getBounds();
59 if (!pathSBounds.isEmpty()) {
60 if (NULL != translate) {
61 pathSBounds.offset(*translate);
62 }
63 target->getDrawState().getViewMatrix().mapRect(&pathSBounds,
64 pathSBounds);
65 GrIRect pathIBounds;
66 pathSBounds.roundOut(&pathIBounds);
67 if (!pathBounds->intersect(pathIBounds)) {
bsalomon@google.com276c1fa2012-06-19 13:22:45 +000068 // set the correct path bounds, as this would be used later.
69 *pathBounds = pathIBounds;
robertphillips@google.comed4155d2012-05-01 14:30:24 +000070 return false;
71 }
72 } else {
bsalomon@google.com276c1fa2012-06-19 13:22:45 +000073 *pathBounds = GrIRect::EmptyIRect();
robertphillips@google.comed4155d2012-05-01 14:30:24 +000074 return false;
75 }
76 return true;
77}
78
79////////////////////////////////////////////////////////////////////////////////
80/**
81 * sw rasterizes path to A8 mask using the context's matrix and uploads to a
82 * scratch texture.
83 */
robertphillips@google.comed4155d2012-05-01 14:30:24 +000084bool sw_draw_path_to_mask_texture(const SkPath& clientPath,
85 const GrIRect& pathDevBounds,
86 GrPathFill fill,
87 GrContext* context,
88 const GrPoint* translate,
89 GrAutoScratchTexture* tex,
90 bool antiAlias) {
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +000091 GrSWMaskHelper helper(context);
robertphillips@google.comed4155d2012-05-01 14:30:24 +000092
robertphillips@google.comfa662942012-05-17 12:20:22 +000093 if (!helper.init(pathDevBounds, translate, true)) {
robertphillips@google.comed4155d2012-05-01 14:30:24 +000094 return false;
95 }
robertphillips@google.comed4155d2012-05-01 14:30:24 +000096
robertphillips@google.comfa662942012-05-17 12:20:22 +000097 helper.draw(clientPath, SkRegion::kReplace_Op,
98 fill, antiAlias, SK_ColorWHITE);
robertphillips@google.comed4155d2012-05-01 14:30:24 +000099
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000100 if (!helper.getTexture(tex)) {
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000101 return false;
102 }
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000103
robertphillips@google.comc82a8b72012-06-21 20:15:48 +0000104 helper.toTexture(tex->texture(), false);
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000105
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000106 return true;
107}
108
109////////////////////////////////////////////////////////////////////////////////
110void draw_around_inv_path(GrDrawTarget* target,
111 GrDrawState::StageMask stageMask,
112 const GrIRect& clipBounds,
113 const GrIRect& pathBounds) {
114 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
115 GrRect rect;
116 if (clipBounds.fTop < pathBounds.fTop) {
117 rect.iset(clipBounds.fLeft, clipBounds.fTop,
118 clipBounds.fRight, pathBounds.fTop);
119 target->drawSimpleRect(rect, NULL, stageMask);
120 }
121 if (clipBounds.fLeft < pathBounds.fLeft) {
122 rect.iset(clipBounds.fLeft, pathBounds.fTop,
123 pathBounds.fLeft, pathBounds.fBottom);
124 target->drawSimpleRect(rect, NULL, stageMask);
125 }
126 if (clipBounds.fRight > pathBounds.fRight) {
127 rect.iset(pathBounds.fRight, pathBounds.fTop,
128 clipBounds.fRight, pathBounds.fBottom);
129 target->drawSimpleRect(rect, NULL, stageMask);
130 }
131 if (clipBounds.fBottom > pathBounds.fBottom) {
132 rect.iset(clipBounds.fLeft, pathBounds.fBottom,
133 clipBounds.fRight, clipBounds.fBottom);
134 target->drawSimpleRect(rect, NULL, stageMask);
135 }
136}
137
138}
139
140////////////////////////////////////////////////////////////////////////////////
141// return true on success; false on failure
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000142bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path,
143 GrPathFill fill,
144 const GrVec* translate,
145 GrDrawTarget* target,
146 GrDrawState::StageMask stageMask,
147 bool antiAlias) {
148
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000149 if (NULL == fContext) {
150 return false;
151 }
152
153 GrAutoScratchTexture ast;
154 GrIRect pathBounds, clipBounds;
155 if (!get_path_and_clip_bounds(target, path, translate,
156 &pathBounds, &clipBounds)) {
bsalomon@google.com276c1fa2012-06-19 13:22:45 +0000157 if (GrIsFillInverted(fill)) {
158 draw_around_inv_path(target, stageMask,
159 clipBounds, pathBounds);
160 }
161 return true;
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000162 }
163 if (sw_draw_path_to_mask_texture(path, pathBounds,
164 fill, fContext,
165 translate, &ast, antiAlias)) {
robertphillips@google.com15c0fea2012-06-22 12:41:43 +0000166 SkAutoTUnref<GrTexture> texture(ast.detach());
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000167 GrAssert(NULL != texture);
168 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
169 enum {
robertphillips@google.com7ff2e372012-05-02 19:27:44 +0000170 // the SW path renderer shares this stage with glyph
171 // rendering (kGlyphMaskStage in GrBatchedTextContext)
172 kPathMaskStage = GrPaint::kTotalStages,
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000173 };
robertphillips@google.combeeb97c2012-05-09 21:15:28 +0000174 GrAssert(NULL == target->drawState()->getTexture(kPathMaskStage));
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000175 target->drawState()->setTexture(kPathMaskStage, texture);
176 target->drawState()->sampler(kPathMaskStage)->reset();
177 GrScalar w = GrIntToScalar(pathBounds.width());
178 GrScalar h = GrIntToScalar(pathBounds.height());
179 GrRect maskRect = GrRect::MakeWH(w / texture->width(),
180 h / texture->height());
robertphillips@google.com15c0fea2012-06-22 12:41:43 +0000181
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000182 const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
183 srcRects[kPathMaskStage] = &maskRect;
184 stageMask |= 1 << kPathMaskStage;
185 GrRect dstRect = GrRect::MakeLTRB(
186 SK_Scalar1* pathBounds.fLeft,
187 SK_Scalar1* pathBounds.fTop,
188 SK_Scalar1* pathBounds.fRight,
189 SK_Scalar1* pathBounds.fBottom);
190 target->drawRect(dstRect, NULL, stageMask, srcRects, NULL);
191 target->drawState()->setTexture(kPathMaskStage, NULL);
192 if (GrIsFillInverted(fill)) {
193 draw_around_inv_path(target, stageMask,
194 clipBounds, pathBounds);
195 }
196 return true;
197 }
198
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000199 return false;
200}