blob: 31221629867997349e16d242d76b8a1a3fc10734 [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) {
bsalomon@google.com47059542012-06-06 20:51:20 +000040 case kWinding_GrPathFill:
robertphillips@google.comed4155d2012-05-01 14:30:24 +000041 return SkPath::kWinding_FillType;
bsalomon@google.com47059542012-06-06 20:51:20 +000042 case kEvenOdd_GrPathFill:
robertphillips@google.comed4155d2012-05-01 14:30:24 +000043 return SkPath::kEvenOdd_FillType;
bsalomon@google.com47059542012-06-06 20:51:20 +000044 case kInverseWinding_GrPathFill:
robertphillips@google.comed4155d2012-05-01 14:30:24 +000045 return SkPath::kInverseWinding_FillType;
bsalomon@google.com47059542012-06-06 20:51:20 +000046 case kInverseEvenOdd_GrPathFill:
robertphillips@google.comed4155d2012-05-01 14:30:24 +000047 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)) {
bsalomon@google.com276c1fa2012-06-19 13:22:45 +000089 // set the correct path bounds, as this would be used later.
90 *pathBounds = pathIBounds;
robertphillips@google.comed4155d2012-05-01 14:30:24 +000091 return false;
92 }
93 } else {
bsalomon@google.com276c1fa2012-06-19 13:22:45 +000094 *pathBounds = GrIRect::EmptyIRect();
robertphillips@google.comed4155d2012-05-01 14:30:24 +000095 return false;
96 }
97 return true;
98}
99
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000100
101/*
102 * Convert a boolean operation into a transfer mode code
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000103 */
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000104SkXfermode::Mode op_to_mode(SkRegion::Op op) {
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000105
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000106 static const SkXfermode::Mode modeMap[] = {
robertphillips@google.comfa662942012-05-17 12:20:22 +0000107 SkXfermode::kDstOut_Mode, // kDifference_Op
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000108 SkXfermode::kMultiply_Mode, // kIntersect_Op
109 SkXfermode::kSrcOver_Mode, // kUnion_Op
110 SkXfermode::kXor_Mode, // kXOR_Op
111 SkXfermode::kClear_Mode, // kReverseDifference_Op
112 SkXfermode::kSrc_Mode, // kReplace_Op
113 };
114
115 return modeMap[op];
116}
117
118}
119
120/**
121 * Draw a single rect element of the clip stack into the accumulation bitmap
122 */
123void GrSWMaskHelper::draw(const GrRect& clientRect, SkRegion::Op op,
robertphillips@google.comfa662942012-05-17 12:20:22 +0000124 bool antiAlias, GrColor color) {
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000125 SkPaint paint;
126
127 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
128
129 paint.setXfermode(mode);
130 paint.setAntiAlias(antiAlias);
robertphillips@google.comfa662942012-05-17 12:20:22 +0000131 paint.setColor(color);
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000132
133 fDraw.drawRect(clientRect, paint);
134
135 SkSafeUnref(mode);
136}
137
138/**
139 * Draw a single path element of the clip stack into the accumulation bitmap
140 */
141void GrSWMaskHelper::draw(const SkPath& clientPath, SkRegion::Op op,
robertphillips@google.comfa662942012-05-17 12:20:22 +0000142 GrPathFill fill, bool antiAlias, GrColor color) {
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000143
144 SkPaint paint;
145 SkPath tmpPath;
146 const SkPath* pathToDraw = &clientPath;
bsalomon@google.com47059542012-06-06 20:51:20 +0000147 if (kHairLine_GrPathFill == fill) {
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000148 paint.setStyle(SkPaint::kStroke_Style);
149 paint.setStrokeWidth(SK_Scalar1);
150 } else {
151 paint.setStyle(SkPaint::kFill_Style);
152 SkPath::FillType skfill = gr_fill_to_sk_fill(fill);
153 if (skfill != pathToDraw->getFillType()) {
154 tmpPath = *pathToDraw;
155 tmpPath.setFillType(skfill);
156 pathToDraw = &tmpPath;
157 }
158 }
159 SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
160
161 paint.setXfermode(mode);
162 paint.setAntiAlias(antiAlias);
robertphillips@google.comfa662942012-05-17 12:20:22 +0000163 paint.setColor(color);
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000164
165 fDraw.drawPath(*pathToDraw, paint);
166
167 SkSafeUnref(mode);
168}
169
robertphillips@google.comfa662942012-05-17 12:20:22 +0000170bool GrSWMaskHelper::init(const GrIRect& pathDevBounds,
171 const GrPoint* translate,
172 bool useMatrix) {
173 if (useMatrix) {
174 fMatrix = fContext->getMatrix();
175 } else {
176 fMatrix.setIdentity();
177 }
178
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000179 if (NULL != translate) {
180 fMatrix.postTranslate(translate->fX, translate->fY);
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000181 }
182
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000183 fMatrix.postTranslate(-pathDevBounds.fLeft * SK_Scalar1,
robertphillips@google.comfa662942012-05-17 12:20:22 +0000184 -pathDevBounds.fTop * SK_Scalar1);
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000185 GrIRect bounds = GrIRect::MakeWH(pathDevBounds.width(),
robertphillips@google.comfa662942012-05-17 12:20:22 +0000186 pathDevBounds.height());
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000187
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000188 fBM.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
189 if (!fBM.allocPixels()) {
190 return false;
191 }
192 sk_bzero(fBM.getPixels(), fBM.getSafeSize());
193
194 sk_bzero(&fDraw, sizeof(fDraw));
195 fRasterClip.setRect(bounds);
196 fDraw.fRC = &fRasterClip;
197 fDraw.fClip = &fRasterClip.bwRgn();
198 fDraw.fMatrix = &fMatrix;
199 fDraw.fBitmap = &fBM;
200 return true;
201}
202
203/**
204 * Get a texture (from the texture cache) of the correct size & format
205 */
206bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* tex) {
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000207 GrTextureDesc desc;
208 desc.fWidth = fBM.width();
209 desc.fHeight = fBM.height();
210 desc.fConfig = kAlpha_8_GrPixelConfig;
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000211
212 tex->set(fContext, desc);
213 GrTexture* texture = tex->texture();
214
215 if (NULL == texture) {
216 return false;
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000217 }
218
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000219 return true;
220}
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000221
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000222/**
223 * Move the result of the software mask generation back to the gpu
224 */
225void GrSWMaskHelper::toTexture(GrTexture *texture) {
226 SkAutoLockPixels alp(fBM);
227 texture->writePixels(0, 0, fBM.width(), fBM.height(),
228 kAlpha_8_GrPixelConfig,
229 fBM.getPixels(), fBM.rowBytes());
230}
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000231
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000232namespace {
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000233////////////////////////////////////////////////////////////////////////////////
234/**
235 * sw rasterizes path to A8 mask using the context's matrix and uploads to a
236 * scratch texture.
237 */
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000238bool sw_draw_path_to_mask_texture(const SkPath& clientPath,
239 const GrIRect& pathDevBounds,
240 GrPathFill fill,
241 GrContext* context,
242 const GrPoint* translate,
243 GrAutoScratchTexture* tex,
244 bool antiAlias) {
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000245 GrSWMaskHelper helper(context);
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000246
robertphillips@google.comfa662942012-05-17 12:20:22 +0000247 if (!helper.init(pathDevBounds, translate, true)) {
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000248 return false;
249 }
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000250
robertphillips@google.comfa662942012-05-17 12:20:22 +0000251 helper.draw(clientPath, SkRegion::kReplace_Op,
252 fill, antiAlias, SK_ColorWHITE);
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000253
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000254 if (!helper.getTexture(tex)) {
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000255 return false;
256 }
robertphillips@google.com6f31a3b2012-05-14 17:37:05 +0000257
robertphillips@google.comb4f06d72012-05-15 12:10:05 +0000258 helper.toTexture(tex->texture());
259
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000260 return true;
261}
262
263////////////////////////////////////////////////////////////////////////////////
264void draw_around_inv_path(GrDrawTarget* target,
265 GrDrawState::StageMask stageMask,
266 const GrIRect& clipBounds,
267 const GrIRect& pathBounds) {
268 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
269 GrRect rect;
270 if (clipBounds.fTop < pathBounds.fTop) {
271 rect.iset(clipBounds.fLeft, clipBounds.fTop,
272 clipBounds.fRight, pathBounds.fTop);
273 target->drawSimpleRect(rect, NULL, stageMask);
274 }
275 if (clipBounds.fLeft < pathBounds.fLeft) {
276 rect.iset(clipBounds.fLeft, pathBounds.fTop,
277 pathBounds.fLeft, pathBounds.fBottom);
278 target->drawSimpleRect(rect, NULL, stageMask);
279 }
280 if (clipBounds.fRight > pathBounds.fRight) {
281 rect.iset(pathBounds.fRight, pathBounds.fTop,
282 clipBounds.fRight, pathBounds.fBottom);
283 target->drawSimpleRect(rect, NULL, stageMask);
284 }
285 if (clipBounds.fBottom > pathBounds.fBottom) {
286 rect.iset(clipBounds.fLeft, pathBounds.fBottom,
287 clipBounds.fRight, clipBounds.fBottom);
288 target->drawSimpleRect(rect, NULL, stageMask);
289 }
290}
291
292}
293
294////////////////////////////////////////////////////////////////////////////////
295// return true on success; false on failure
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000296bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path,
297 GrPathFill fill,
298 const GrVec* translate,
299 GrDrawTarget* target,
300 GrDrawState::StageMask stageMask,
301 bool antiAlias) {
302
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000303 if (NULL == fContext) {
304 return false;
305 }
306
307 GrAutoScratchTexture ast;
308 GrIRect pathBounds, clipBounds;
309 if (!get_path_and_clip_bounds(target, path, translate,
310 &pathBounds, &clipBounds)) {
bsalomon@google.com276c1fa2012-06-19 13:22:45 +0000311 if (GrIsFillInverted(fill)) {
312 draw_around_inv_path(target, stageMask,
313 clipBounds, pathBounds);
314 }
315 return true;
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000316 }
317 if (sw_draw_path_to_mask_texture(path, pathBounds,
318 fill, fContext,
319 translate, &ast, antiAlias)) {
320 GrTexture* texture = ast.texture();
321 GrAssert(NULL != texture);
322 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
323 enum {
robertphillips@google.com7ff2e372012-05-02 19:27:44 +0000324 // the SW path renderer shares this stage with glyph
325 // rendering (kGlyphMaskStage in GrBatchedTextContext)
326 kPathMaskStage = GrPaint::kTotalStages,
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000327 };
robertphillips@google.combeeb97c2012-05-09 21:15:28 +0000328 GrAssert(NULL == target->drawState()->getTexture(kPathMaskStage));
robertphillips@google.comed4155d2012-05-01 14:30:24 +0000329 target->drawState()->setTexture(kPathMaskStage, texture);
330 target->drawState()->sampler(kPathMaskStage)->reset();
331 GrScalar w = GrIntToScalar(pathBounds.width());
332 GrScalar h = GrIntToScalar(pathBounds.height());
333 GrRect maskRect = GrRect::MakeWH(w / texture->width(),
334 h / texture->height());
335 const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
336 srcRects[kPathMaskStage] = &maskRect;
337 stageMask |= 1 << kPathMaskStage;
338 GrRect dstRect = GrRect::MakeLTRB(
339 SK_Scalar1* pathBounds.fLeft,
340 SK_Scalar1* pathBounds.fTop,
341 SK_Scalar1* pathBounds.fRight,
342 SK_Scalar1* pathBounds.fBottom);
343 target->drawRect(dstRect, NULL, stageMask, srcRects, NULL);
344 target->drawState()->setTexture(kPathMaskStage, NULL);
345 if (GrIsFillInverted(fill)) {
346 draw_around_inv_path(target, stageMask,
347 clipBounds, pathBounds);
348 }
349 return true;
350 }
351
robertphillips@google.comf4c2c522012-04-27 12:08:47 +0000352 return false;
353}