blob: 410223cd6247cbdd5747f03ba0ab7b22a9460bad [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
robertphillips@google.comb4f06d72012-05-15 12:10:05 +000097
98/*
99 * Convert a boolean operation into a transfer mode code
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000100 */
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000101SkXfermode::Mode op_to_mode(SkRegion::Op op) {
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000102
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000103 static const SkXfermode::Mode modeMap[] = {
104 SkXfermode::kSrcOut_Mode, // kDifference_Op
105 SkXfermode::kMultiply_Mode, // kIntersect_Op
106 SkXfermode::kSrcOver_Mode, // kUnion_Op
107 SkXfermode::kXor_Mode, // kXOR_Op
108 SkXfermode::kClear_Mode, // kReverseDifference_Op
109 SkXfermode::kSrc_Mode, // kReplace_Op
110 };
111
112 return modeMap[op];
113}
114
115}
116
117/**
118 * Draw a single rect element of the clip stack into the accumulation bitmap
119 */
120void GrSWMaskHelper::draw(const GrRect& clientRect, SkRegion::Op op,
121 bool antiAlias) {
122 SkPaint paint;
123
124 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
125
126 paint.setXfermode(mode);
127 paint.setAntiAlias(antiAlias);
128 paint.setColor(SK_ColorWHITE);
129
130 fDraw.drawRect(clientRect, paint);
131
132 SkSafeUnref(mode);
133}
134
135/**
136 * Draw a single path element of the clip stack into the accumulation bitmap
137 */
138void GrSWMaskHelper::draw(const SkPath& clientPath, SkRegion::Op op,
139 GrPathFill fill, bool antiAlias) {
140
141 SkPaint paint;
142 SkPath tmpPath;
143 const SkPath* pathToDraw = &clientPath;
144 if (kHairLine_PathFill == fill) {
145 paint.setStyle(SkPaint::kStroke_Style);
146 paint.setStrokeWidth(SK_Scalar1);
147 } else {
148 paint.setStyle(SkPaint::kFill_Style);
149 SkPath::FillType skfill = gr_fill_to_sk_fill(fill);
150 if (skfill != pathToDraw->getFillType()) {
151 tmpPath = *pathToDraw;
152 tmpPath.setFillType(skfill);
153 pathToDraw = &tmpPath;
154 }
155 }
156 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
157
158 paint.setXfermode(mode);
159 paint.setAntiAlias(antiAlias);
160 paint.setColor(SK_ColorWHITE);
161
162 fDraw.drawPath(*pathToDraw, paint);
163
164 SkSafeUnref(mode);
165}
166
167bool GrSWMaskHelper::init(const GrIRect& pathDevBounds, const GrPoint* translate) {
168 fMatrix = fContext->getMatrix();
169 if (NULL != translate) {
170 fMatrix.postTranslate(translate->fX, translate->fY);
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000171 }
172
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000173 fMatrix.postTranslate(-pathDevBounds.fLeft * SK_Scalar1,
174 -pathDevBounds.fTop * SK_Scalar1);
175 GrIRect bounds = GrIRect::MakeWH(pathDevBounds.width(),
176 pathDevBounds.height());
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000177
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000178 fBM.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
179 if (!fBM.allocPixels()) {
180 return false;
181 }
182 sk_bzero(fBM.getPixels(), fBM.getSafeSize());
183
184 sk_bzero(&fDraw, sizeof(fDraw));
185 fRasterClip.setRect(bounds);
186 fDraw.fRC = &fRasterClip;
187 fDraw.fClip = &fRasterClip.bwRgn();
188 fDraw.fMatrix = &fMatrix;
189 fDraw.fBitmap = &fBM;
190 return true;
191}
192
193/**
194 * Get a texture (from the texture cache) of the correct size & format
195 */
196bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* tex) {
197 const GrTextureDesc desc = {
198 kNone_GrTextureFlags,
199 fBM.width(),
200 fBM.height(),
201 kAlpha_8_GrPixelConfig,
202 0 // samples
203 };
204
205 tex->set(fContext, desc);
206 GrTexture* texture = tex->texture();
207
208 if (NULL == texture) {
209 return false;
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000210 }
211
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000212 return true;
213}
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000214
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000215/**
216 * Move the result of the software mask generation back to the gpu
217 */
218void GrSWMaskHelper::toTexture(GrTexture *texture) {
219 SkAutoLockPixels alp(fBM);
220 texture->writePixels(0, 0, fBM.width(), fBM.height(),
221 kAlpha_8_GrPixelConfig,
222 fBM.getPixels(), fBM.rowBytes());
223}
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000224
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000225namespace {
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000226////////////////////////////////////////////////////////////////////////////////
227/**
228 * sw rasterizes path to A8 mask using the context's matrix and uploads to a
229 * scratch texture.
230 */
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000231bool sw_draw_path_to_mask_texture(const SkPath& clientPath,
232 const GrIRect& pathDevBounds,
233 GrPathFill fill,
234 GrContext* context,
235 const GrPoint* translate,
236 GrAutoScratchTexture* tex,
237 bool antiAlias) {
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000238 GrSWMaskHelper helper(context);
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000239
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000240 if (!helper.init(pathDevBounds, translate)) {
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000241 return false;
242 }
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000243
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000244 helper.draw(clientPath, SkRegion::kReplace_Op, fill, antiAlias);
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000245
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000246 if (!helper.getTexture(tex)) {
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000247 return false;
248 }
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000249
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000250 helper.toTexture(tex->texture());
251
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000252 return true;
253}
254
255////////////////////////////////////////////////////////////////////////////////
256void draw_around_inv_path(GrDrawTarget* target,
257 GrDrawState::StageMask stageMask,
258 const GrIRect& clipBounds,
259 const GrIRect& pathBounds) {
260 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
261 GrRect rect;
262 if (clipBounds.fTop < pathBounds.fTop) {
263 rect.iset(clipBounds.fLeft, clipBounds.fTop,
264 clipBounds.fRight, pathBounds.fTop);
265 target->drawSimpleRect(rect, NULL, stageMask);
266 }
267 if (clipBounds.fLeft < pathBounds.fLeft) {
268 rect.iset(clipBounds.fLeft, pathBounds.fTop,
269 pathBounds.fLeft, pathBounds.fBottom);
270 target->drawSimpleRect(rect, NULL, stageMask);
271 }
272 if (clipBounds.fRight > pathBounds.fRight) {
273 rect.iset(pathBounds.fRight, pathBounds.fTop,
274 clipBounds.fRight, pathBounds.fBottom);
275 target->drawSimpleRect(rect, NULL, stageMask);
276 }
277 if (clipBounds.fBottom > pathBounds.fBottom) {
278 rect.iset(clipBounds.fLeft, pathBounds.fBottom,
279 clipBounds.fRight, clipBounds.fBottom);
280 target->drawSimpleRect(rect, NULL, stageMask);
281 }
282}
283
284}
285
286////////////////////////////////////////////////////////////////////////////////
287// return true on success; false on failure
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000288bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path,
289 GrPathFill fill,
290 const GrVec* translate,
291 GrDrawTarget* target,
292 GrDrawState::StageMask stageMask,
293 bool antiAlias) {
294
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000295 if (NULL == fContext) {
296 return false;
297 }
298
299 GrAutoScratchTexture ast;
300 GrIRect pathBounds, clipBounds;
301 if (!get_path_and_clip_bounds(target, path, translate,
302 &pathBounds, &clipBounds)) {
303 return true; // path is empty so there is nothing to do
304 }
305 if (sw_draw_path_to_mask_texture(path, pathBounds,
306 fill, fContext,
307 translate, &ast, antiAlias)) {
308 GrTexture* texture = ast.texture();
309 GrAssert(NULL != texture);
310 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
311 enum {
robertphillips@google.com7ff2e372012-05-02 19:27:44 +0000312 // the SW path renderer shares this stage with glyph
313 // rendering (kGlyphMaskStage in GrBatchedTextContext)
314 kPathMaskStage = GrPaint::kTotalStages,
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000315 };
robertphillips@google.combeeb97c2012-05-09 21:15:28 +0000316 GrAssert(NULL == target->drawState()->getTexture(kPathMaskStage));
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000317 target->drawState()->setTexture(kPathMaskStage, texture);
318 target->drawState()->sampler(kPathMaskStage)->reset();
319 GrScalar w = GrIntToScalar(pathBounds.width());
320 GrScalar h = GrIntToScalar(pathBounds.height());
321 GrRect maskRect = GrRect::MakeWH(w / texture->width(),
322 h / texture->height());
323 const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
324 srcRects[kPathMaskStage] = &maskRect;
325 stageMask |= 1 << kPathMaskStage;
326 GrRect dstRect = GrRect::MakeLTRB(
327 SK_Scalar1* pathBounds.fLeft,
328 SK_Scalar1* pathBounds.fTop,
329 SK_Scalar1* pathBounds.fRight,
330 SK_Scalar1* pathBounds.fBottom);
331 target->drawRect(dstRect, NULL, stageMask, srcRects, NULL);
332 target->drawState()->setTexture(kPathMaskStage, NULL);
333 if (GrIsFillInverted(fill)) {
334 draw_around_inv_path(target, stageMask,
335 clipBounds, pathBounds);
336 }
337 return true;
338 }
339
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000340 return false;
341}
342