blob: 6780e0f6af78a99c16079dd7ec930faad7a89885 [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 "GrPaint.h"
11#include "SkPaint.h"
12#include "GrRenderTarget.h"
13#include "GrContext.h"
14#include "SkDraw.h"
15#include "SkRasterClip.h"
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000016
robertphillips@google.comed4155d2012-05-01 14:30:24 +000017////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000018bool GrSoftwarePathRenderer::canDrawPath(const SkPath& path,
19 GrPathFill fill,
20 const GrDrawTarget* target,
21 bool antiAlias) const {
robertphillips@google.comed4155d2012-05-01 14:30:24 +000022 if (!antiAlias || NULL == fContext) {
23 // TODO: We could allow the SW path to also handle non-AA paths but
24 // this would mean that GrDefaultPathRenderer would never be called
25 // (since it appears after the SW renderer in the path renderer
26 // chain). Some testing would need to be done r.e. performance
27 // and consistency of the resulting images before removing
28 // the "!antiAlias" clause from the above test
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000029 return false;
30 }
31
robertphillips@google.comed4155d2012-05-01 14:30:24 +000032 return true;
robertphillips@google.comf4c2c522012-04-27 12:08:47 +000033}
34
robertphillips@google.comed4155d2012-05-01 14:30:24 +000035namespace {
36
37////////////////////////////////////////////////////////////////////////////////
38SkPath::FillType gr_fill_to_sk_fill(GrPathFill fill) {
39 switch (fill) {
40 case kWinding_PathFill:
41 return SkPath::kWinding_FillType;
42 case kEvenOdd_PathFill:
43 return SkPath::kEvenOdd_FillType;
44 case kInverseWinding_PathFill:
45 return SkPath::kInverseWinding_FillType;
46 case kInverseEvenOdd_PathFill:
47 return SkPath::kInverseEvenOdd_FillType;
48 default:
49 GrCrash("Unexpected fill.");
50 return SkPath::kWinding_FillType;
51 }
52}
53
54////////////////////////////////////////////////////////////////////////////////
55// gets device coord bounds of path (not considering the fill) and clip. The
56// path bounds will be a subset of the clip bounds. returns false if
57// path bounds would be empty.
58bool get_path_and_clip_bounds(const GrDrawTarget* target,
59 const SkPath& path,
60 const GrVec* translate,
61 GrIRect* pathBounds,
62 GrIRect* clipBounds) {
63 // compute bounds as intersection of rt size, clip, and path
64 const GrRenderTarget* rt = target->getDrawState().getRenderTarget();
65 if (NULL == rt) {
66 return false;
67 }
68 *pathBounds = GrIRect::MakeWH(rt->width(), rt->height());
69 const GrClip& clip = target->getClip();
70 if (clip.hasConservativeBounds()) {
71 clip.getConservativeBounds().roundOut(clipBounds);
72 if (!pathBounds->intersect(*clipBounds)) {
73 return false;
74 }
75 } else {
76 // pathBounds is currently the rt extent, set clip bounds to that rect.
77 *clipBounds = *pathBounds;
78 }
79 GrRect pathSBounds = path.getBounds();
80 if (!pathSBounds.isEmpty()) {
81 if (NULL != translate) {
82 pathSBounds.offset(*translate);
83 }
84 target->getDrawState().getViewMatrix().mapRect(&pathSBounds,
85 pathSBounds);
86 GrIRect pathIBounds;
87 pathSBounds.roundOut(&pathIBounds);
88 if (!pathBounds->intersect(pathIBounds)) {
89 return false;
90 }
91 } else {
92 return false;
93 }
94 return true;
95}
96
97////////////////////////////////////////////////////////////////////////////////
98/**
99 * sw rasterizes path to A8 mask using the context's matrix and uploads to a
100 * scratch texture.
101 */
102
103bool sw_draw_path_to_mask_texture(const SkPath& clientPath,
104 const GrIRect& pathDevBounds,
105 GrPathFill fill,
106 GrContext* context,
107 const GrPoint* translate,
108 GrAutoScratchTexture* tex,
109 bool antiAlias) {
110 SkPaint paint;
111 SkPath tmpPath;
112 const SkPath* pathToDraw = &clientPath;
113 if (kHairLine_PathFill == fill) {
114 paint.setStyle(SkPaint::kStroke_Style);
115 paint.setStrokeWidth(SK_Scalar1);
116 } else {
117 paint.setStyle(SkPaint::kFill_Style);
118 SkPath::FillType skfill = gr_fill_to_sk_fill(fill);
119 if (skfill != pathToDraw->getFillType()) {
120 tmpPath = *pathToDraw;
121 tmpPath.setFillType(skfill);
122 pathToDraw = &tmpPath;
123 }
124 }
125 paint.setAntiAlias(antiAlias);
126 paint.setColor(SK_ColorWHITE);
127
128 GrMatrix matrix = context->getMatrix();
129 if (NULL != translate) {
130 matrix.postTranslate(translate->fX, translate->fY);
131 }
132
133 matrix.postTranslate(-pathDevBounds.fLeft * SK_Scalar1,
134 -pathDevBounds.fTop * SK_Scalar1);
135 GrIRect bounds = GrIRect::MakeWH(pathDevBounds.width(),
136 pathDevBounds.height());
137
138 SkBitmap bm;
139 bm.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
140 if (!bm.allocPixels()) {
141 return false;
142 }
143 sk_bzero(bm.getPixels(), bm.getSafeSize());
144
145 SkDraw draw;
146 sk_bzero(&draw, sizeof(draw));
147 SkRasterClip rc(bounds);
148 draw.fRC = &rc;
149 draw.fClip = &rc.bwRgn();
150 draw.fMatrix = &matrix;
151 draw.fBitmap = &bm;
152 draw.drawPath(*pathToDraw, paint);
153
154 const GrTextureDesc desc = {
155 kNone_GrTextureFlags,
156 bounds.fRight,
157 bounds.fBottom,
158 kAlpha_8_GrPixelConfig,
159 0 // samples
160 };
161
162 tex->set(context, desc);
163 GrTexture* texture = tex->texture();
164
165 if (NULL == texture) {
166 return false;
167 }
168 SkAutoLockPixels alp(bm);
169 texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
170 bm.getPixels(), bm.rowBytes());
171 return true;
172}
173
174////////////////////////////////////////////////////////////////////////////////
175void draw_around_inv_path(GrDrawTarget* target,
176 GrDrawState::StageMask stageMask,
177 const GrIRect& clipBounds,
178 const GrIRect& pathBounds) {
179 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
180 GrRect rect;
181 if (clipBounds.fTop < pathBounds.fTop) {
182 rect.iset(clipBounds.fLeft, clipBounds.fTop,
183 clipBounds.fRight, pathBounds.fTop);
184 target->drawSimpleRect(rect, NULL, stageMask);
185 }
186 if (clipBounds.fLeft < pathBounds.fLeft) {
187 rect.iset(clipBounds.fLeft, pathBounds.fTop,
188 pathBounds.fLeft, pathBounds.fBottom);
189 target->drawSimpleRect(rect, NULL, stageMask);
190 }
191 if (clipBounds.fRight > pathBounds.fRight) {
192 rect.iset(pathBounds.fRight, pathBounds.fTop,
193 clipBounds.fRight, pathBounds.fBottom);
194 target->drawSimpleRect(rect, NULL, stageMask);
195 }
196 if (clipBounds.fBottom > pathBounds.fBottom) {
197 rect.iset(clipBounds.fLeft, pathBounds.fBottom,
198 clipBounds.fRight, clipBounds.fBottom);
199 target->drawSimpleRect(rect, NULL, stageMask);
200 }
201}
202
203}
204
205////////////////////////////////////////////////////////////////////////////////
206// return true on success; false on failure
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000207bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path,
208 GrPathFill fill,
209 const GrVec* translate,
210 GrDrawTarget* target,
211 GrDrawState::StageMask stageMask,
212 bool antiAlias) {
213
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000214 if (NULL == fContext) {
215 return false;
216 }
217
218 GrAutoScratchTexture ast;
219 GrIRect pathBounds, clipBounds;
220 if (!get_path_and_clip_bounds(target, path, translate,
221 &pathBounds, &clipBounds)) {
222 return true; // path is empty so there is nothing to do
223 }
224 if (sw_draw_path_to_mask_texture(path, pathBounds,
225 fill, fContext,
226 translate, &ast, antiAlias)) {
227 GrTexture* texture = ast.texture();
228 GrAssert(NULL != texture);
229 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
230 enum {
231 kPathMaskStage = GrPaint::kTotalStages,
232 };
233 target->drawState()->setTexture(kPathMaskStage, texture);
234 target->drawState()->sampler(kPathMaskStage)->reset();
235 GrScalar w = GrIntToScalar(pathBounds.width());
236 GrScalar h = GrIntToScalar(pathBounds.height());
237 GrRect maskRect = GrRect::MakeWH(w / texture->width(),
238 h / texture->height());
239 const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
240 srcRects[kPathMaskStage] = &maskRect;
241 stageMask |= 1 << kPathMaskStage;
242 GrRect dstRect = GrRect::MakeLTRB(
243 SK_Scalar1* pathBounds.fLeft,
244 SK_Scalar1* pathBounds.fTop,
245 SK_Scalar1* pathBounds.fRight,
246 SK_Scalar1* pathBounds.fBottom);
247 target->drawRect(dstRect, NULL, stageMask, srcRects, NULL);
248 target->drawState()->setTexture(kPathMaskStage, NULL);
249 if (GrIsFillInverted(fill)) {
250 draw_around_inv_path(target, stageMask,
251 clipBounds, pathBounds);
252 }
253 return true;
254 }
255
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000256 return false;
257}
258