blob: 1a892d80dc956dcb947377f091b7140465e255cb [file] [log] [blame]
robertphillips@google.com1e945b72012-04-16 18:03:03 +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 "GrClipMaskManager.h"
10#include "GrGpu.h"
11#include "GrRenderTarget.h"
12#include "GrStencilBuffer.h"
13#include "GrPathRenderer.h"
robertphillips@google.coma72eef32012-05-01 17:22:59 +000014#include "GrPaint.h"
15
16//#define GR_AA_CLIP 1
17
robertphillips@google.comf294b772012-04-27 14:29:26 +000018////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +000019void ScissoringSettings::setupScissoring(GrGpu* gpu) {
20 if (!fEnableScissoring) {
21 gpu->disableScissor();
22 return;
23 }
24
25 gpu->enableScissoring(fScissorRect);
26}
27
robertphillips@google.coma72eef32012-05-01 17:22:59 +000028namespace {
29// set up the draw state to enable the aa clipping mask. Besides setting up the
30// sampler matrix this also alters the vertex layout
31void setupDrawStateAAClip(GrGpu* gpu, GrTexture* result, const GrRect &bound) {
32 GrDrawState* drawState = gpu->drawState();
33 GrAssert(drawState);
34
35 static const int maskStage = GrPaint::kTotalStages+1;
36
37 GrMatrix mat;
38 mat.setIDiv(result->width(), result->height());
39 mat.preTranslate(-bound.fLeft, -bound.fTop);
40 mat.preConcat(drawState->getViewMatrix());
41
42 drawState->sampler(maskStage)->reset(GrSamplerState::kClamp_WrapMode,
43 GrSamplerState::kNearest_Filter,
44 mat);
45
46 drawState->setTexture(maskStage, result);
47
48 // The AA clipping determination happens long after the geometry has
49 // been set up to draw. Here we directly enable the AA clip mask stage
50 gpu->addToVertexLayout(
51 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(maskStage));
52}
53
54}
55
robertphillips@google.comf294b772012-04-27 14:29:26 +000056////////////////////////////////////////////////////////////////////////////////
57// sort out what kind of clip mask needs to be created: alpha, stencil
58// or scissor
robertphillips@google.com1e945b72012-04-16 18:03:03 +000059bool GrClipMaskManager::createClipMask(GrGpu* gpu,
60 const GrClip& clipIn,
61 ScissoringSettings* scissorSettings) {
62
63 GrAssert(scissorSettings);
64
65 scissorSettings->fEnableScissoring = false;
66 fClipMaskInStencil = false;
robertphillips@google.comf294b772012-04-27 14:29:26 +000067 fClipMaskInAlpha = false;
robertphillips@google.com1e945b72012-04-16 18:03:03 +000068
69 GrDrawState* drawState = gpu->drawState();
70 if (!drawState->isClipState()) {
71 return true;
72 }
73
74 GrRenderTarget* rt = drawState->getRenderTarget();
75
76 // GrDrawTarget should have filtered this for us
77 GrAssert(NULL != rt);
78
robertphillips@google.comf294b772012-04-27 14:29:26 +000079#if GR_AA_CLIP
80 // If MSAA is enabled use the (faster) stencil path for AA clipping
81 // otherwise the alpha clip mask is our only option
82 if (clipIn.requiresAA() && 0 == rt->numSamples()) {
83 // Since we are going to create a destination texture of the correct
84 // size for the mask (rather than being bound by the size of the
85 // render target) we aren't going to use scissoring like the stencil
86 // path does (see scissorSettings below)
robertphillips@google.coma72eef32012-05-01 17:22:59 +000087 GrTexture* result = NULL;
88 GrRect bound;
89 if (this->createAlphaClipMask(gpu, clipIn, &result, &bound)) {
robertphillips@google.comf294b772012-04-27 14:29:26 +000090 fClipMaskInAlpha = true;
robertphillips@google.coma72eef32012-05-01 17:22:59 +000091
92 setupDrawStateAAClip(gpu, result, bound);
robertphillips@google.comf294b772012-04-27 14:29:26 +000093 return true;
94 }
95
96 // if alpha clip mask creation fails fall through to the stencil
97 // buffer method
98 }
99#endif // GR_AA_CLIP
100
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000101 GrRect bounds;
102 GrRect rtRect;
103 rtRect.setLTRB(0, 0,
104 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
105 if (clipIn.hasConservativeBounds()) {
106 bounds = clipIn.getConservativeBounds();
107 if (!bounds.intersect(rtRect)) {
108 bounds.setEmpty();
109 }
110 } else {
111 bounds = rtRect;
112 }
113
114 bounds.roundOut(&scissorSettings->fScissorRect);
115 if (scissorSettings->fScissorRect.isEmpty()) {
116 scissorSettings->fScissorRect.setLTRB(0,0,0,0);
117 // TODO: I think we can do an early exit here - after refactoring try:
118 // set fEnableScissoring to true but leave fClipMaskInStencil false
119 // and return - everything is going to be scissored away anyway!
120 }
121 scissorSettings->fEnableScissoring = true;
122
123 // use the stencil clip if we can't represent the clip as a rectangle.
124 fClipMaskInStencil = !clipIn.isRect() && !clipIn.isEmpty() &&
125 !bounds.isEmpty();
126
127 if (fClipMaskInStencil) {
128 return this->createStencilClipMask(gpu, clipIn, bounds, scissorSettings);
129 }
130
131 return true;
132}
133
134#define VISUALIZE_COMPLEX_CLIP 0
135
136#if VISUALIZE_COMPLEX_CLIP
137 #include "GrRandom.h"
138 GrRandom gRandom;
139 #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU());
140#else
141 #define SET_RANDOM_COLOR
142#endif
143
144namespace {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000145////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000146// determines how many elements at the head of the clip can be skipped and
147// whether the initial clear should be to the inside- or outside-the-clip value,
148// and what op should be used to draw the first element that isn't skipped.
149int process_initial_clip_elements(const GrClip& clip,
150 const GrRect& bounds,
151 bool* clearToInside,
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000152 SkRegion::Op* startOp) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000153
154 // logically before the first element of the clip stack is
155 // processed the clip is entirely open. However, depending on the
156 // first set op we may prefer to clear to 0 for performance. We may
157 // also be able to skip the initial clip paths/rects. We loop until
158 // we cannot skip an element.
159 int curr;
160 bool done = false;
161 *clearToInside = true;
162 int count = clip.getElementCount();
163
164 for (curr = 0; curr < count && !done; ++curr) {
165 switch (clip.getOp(curr)) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000166 case SkRegion::kReplace_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000167 // replace ignores everything previous
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000168 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000169 *clearToInside = false;
170 done = true;
171 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000172 case SkRegion::kIntersect_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000173 // if this element contains the entire bounds then we
174 // can skip it.
175 if (kRect_ClipType == clip.getElementType(curr)
176 && clip.getRect(curr).contains(bounds)) {
177 break;
178 }
179 // if everything is initially clearToInside then intersect is
180 // same as clear to 0 and treat as a replace. Otherwise,
181 // set stays empty.
182 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000183 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000184 *clearToInside = false;
185 done = true;
186 }
187 break;
188 // we can skip a leading union.
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000189 case SkRegion::kUnion_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000190 // if everything is initially outside then union is
191 // same as replace. Otherwise, every pixel is still
192 // clearToInside
193 if (!*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000194 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000195 done = true;
196 }
197 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000198 case SkRegion::kXOR_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000199 // xor is same as difference or replace both of which
200 // can be 1-pass instead of 2 for xor.
201 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000202 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000203 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000204 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000205 }
206 done = true;
207 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000208 case SkRegion::kDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000209 // if all pixels are clearToInside then we have to process the
210 // difference, otherwise it has no effect and all pixels
211 // remain outside.
212 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000213 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000214 done = true;
215 }
216 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000217 case SkRegion::kReverseDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000218 // if all pixels are clearToInside then reverse difference
219 // produces empty set. Otherise it is same as replace
220 if (*clearToInside) {
221 *clearToInside = false;
222 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000223 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000224 done = true;
225 }
226 break;
227 default:
228 GrCrash("Unknown set op.");
229 }
230 }
231 return done ? curr-1 : count;
232}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000233
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000234}
235
robertphillips@google.comf294b772012-04-27 14:29:26 +0000236
237namespace {
238
239////////////////////////////////////////////////////////////////////////////////
240// set up the OpenGL blend function to perform the specified
241// boolean operation for alpha clip mask creation
242void setUpBooleanBlendCoeffs(GrDrawState* drawState, SkRegion::Op op) {
243
244 switch (op) {
245 case SkRegion::kReplace_Op:
246 drawState->setBlendFunc(kOne_BlendCoeff, kZero_BlendCoeff);
247 break;
248 case SkRegion::kIntersect_Op:
249 drawState->setBlendFunc(kDC_BlendCoeff, kZero_BlendCoeff);
250 break;
251 case SkRegion::kUnion_Op:
252 drawState->setBlendFunc(kOne_BlendCoeff, kISC_BlendCoeff);
253 break;
254 case SkRegion::kXOR_Op:
255 drawState->setBlendFunc(kIDC_BlendCoeff, kISC_BlendCoeff);
256 break;
257 case SkRegion::kDifference_Op:
258 drawState->setBlendFunc(kZero_BlendCoeff, kISC_BlendCoeff);
259 break;
260 case SkRegion::kReverseDifference_Op:
261 drawState->setBlendFunc(kIDC_BlendCoeff, kZero_BlendCoeff);
262 break;
263 default:
264 GrAssert(false);
265 break;
266 }
267}
268
269}
270
271////////////////////////////////////////////////////////////////////////////////
272bool GrClipMaskManager::drawPath(GrGpu* gpu,
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000273 const SkPath& path,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000274 GrPathFill fill,
275 bool doAA) {
276
277 GrPathRenderer* pr = this->getClipPathRenderer(gpu, path, fill, doAA);
278 if (NULL == pr) {
279 return false;
280 }
281
282 pr->drawPath(path, fill, NULL, gpu, 0, doAA);
283 return true;
284}
285
286////////////////////////////////////////////////////////////////////////////////
287bool GrClipMaskManager::drawClipShape(GrGpu* gpu,
288 GrTexture* target,
289 const GrClip& clipIn,
290 int index) {
291 GrDrawState* drawState = gpu->drawState();
292 GrAssert(NULL != drawState);
293
294 drawState->setRenderTarget(target->asRenderTarget());
295
296 if (kRect_ClipType == clipIn.getElementType(index)) {
297 if (clipIn.getDoAA(index)) {
298 // convert the rect to a path for AA
299 SkPath temp;
300 temp.addRect(clipIn.getRect(index));
301
302 return this->drawPath(gpu, temp,
303 kEvenOdd_PathFill, clipIn.getDoAA(index));
304 } else {
305 gpu->drawSimpleRect(clipIn.getRect(index), NULL, 0);
306 }
307 } else {
308 return this->drawPath(gpu,
309 clipIn.getPath(index),
310 clipIn.getPathFill(index),
311 clipIn.getDoAA(index));
312 }
313 return true;
314}
315
316void GrClipMaskManager::drawTexture(GrGpu* gpu,
317 GrTexture* target,
318 const GrRect& rect,
319 GrTexture* texture) {
320 GrDrawState* drawState = gpu->drawState();
321 GrAssert(NULL != drawState);
322
323 // no AA here since it is encoded in the texture
324 drawState->setRenderTarget(target->asRenderTarget());
325
326 GrMatrix sampleM;
327 sampleM.setIDiv(texture->width(), texture->height());
328 drawState->setTexture(0, texture);
329
330 drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
331 GrSamplerState::kNearest_Filter,
332 sampleM);
333
334 gpu->drawSimpleRect(rect, NULL, 1 << 0);
335
336 drawState->setTexture(0, NULL);
337}
338
339namespace {
340
341void clear(GrGpu* gpu,
342 GrTexture* target,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000343 GrColor color) {
344 GrDrawState* drawState = gpu->drawState();
345 GrAssert(NULL != drawState);
346
347 // zap entire target to specified color
348 drawState->setRenderTarget(target->asRenderTarget());
349 gpu->clear(NULL, color);
350}
351
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000352// get a texture to act as a temporary buffer for AA clip boolean operations
353// TODO: given the expense of createTexture we may want to just cache this too
354void needTemp(GrGpu *gpu, const GrTextureDesc& desc, GrTexture** temp) {
355 if (NULL != *temp) {
356 // we've already allocated the temp texture
357 return;
358 }
359
360 *temp = gpu->createTexture(desc, NULL, 0);
361}
362
363}
364
365void GrClipMaskManager::getAccum(GrGpu* gpu,
366 const GrTextureDesc& desc,
367 GrTexture** accum) {
368 GrAssert(NULL == *accum);
369
370 // since we are getting an accumulator we know our cache is shot. See
371 // if we can reuse the texture stored in the cache
372 if (fAACache.getLastMaskWidth() >= desc.fWidth &&
373 fAACache.getLastMaskHeight() >= desc.fHeight) {
374 // we can just reuse the existing texture
375 *accum = fAACache.detachLastMask();
376 fAACache.reset();
377 } else {
378 *accum = gpu->createTexture(desc, NULL, 0);
379 }
380
381 GrAssert(1 == (*accum)->getRefCnt());
robertphillips@google.comf294b772012-04-27 14:29:26 +0000382}
383
384////////////////////////////////////////////////////////////////////////////////
385// Create a 8-bit clip mask in alpha
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000386bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu,
387 const GrClip& clipIn,
388 GrTexture** result,
389 GrRect *resultBounds) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000390
391 GrDrawState* origDrawState = gpu->drawState();
392 GrAssert(origDrawState->isClipState());
393
394 GrRenderTarget* rt = origDrawState->getRenderTarget();
395 GrAssert(NULL != rt);
396
robertphillips@google.comfd6daf52012-05-02 19:32:32 +0000397 if (fAACache.canReuse(clipIn, rt->width(), rt->height())) {
398 *result = fAACache.getLastMask();
399 *resultBounds = fAACache.getLastBound();
400 return true;
401 }
402
robertphillips@google.comf294b772012-04-27 14:29:26 +0000403 GrRect rtRect;
404 rtRect.setLTRB(0, 0,
405 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
406
robertphillips@google.comfd6daf52012-05-02 19:32:32 +0000407
robertphillips@google.comf294b772012-04-27 14:29:26 +0000408 // unlike the stencil path the alpha path is not bound to the size of the
409 // render target - determine the minimum size required for the mask
410 GrRect bounds;
411
412 if (clipIn.hasConservativeBounds()) {
413 bounds = clipIn.getConservativeBounds();
414 if (!bounds.intersect(rtRect)) {
415 // the mask will be empty in this case
416 GrAssert(false);
417 bounds.setEmpty();
418 }
419 } else {
420 // still locked to the size of the render target
421 bounds = rtRect;
422 }
423
424 bounds.roundOut();
425
426 // need to outset a pixel since the standard bounding box computation
427 // path doesn't leave any room for antialiasing (esp. w.r.t. rects)
428 bounds.outset(SkIntToScalar(1), SkIntToScalar(1));
429
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000430 // TODO: make sure we don't outset if bounds are still 0,0 @ min
431
robertphillips@google.comf294b772012-04-27 14:29:26 +0000432 GrAssert(SkScalarIsInt(bounds.width()));
433 GrAssert(SkScalarIsInt(bounds.height()));
434
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000435 const GrTextureDesc desc = {
robertphillips@google.com180bc882012-05-03 18:03:05 +0000436 kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000437 SkScalarCeilToInt(bounds.width()),
438 SkScalarCeilToInt(bounds.height()),
439 kAlpha_8_GrPixelConfig,
440 0 // samples
441 };
442
443 GrRect newRTBounds;
444 newRTBounds.setLTRB(0, 0, bounds.width(), bounds.height());
445
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000446 GrTexture* accum = NULL, *temp = NULL;
447
448 getAccum(gpu, desc, &accum);
449 if (NULL == accum) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000450 fClipMaskInAlpha = false;
robertphillips@google.comfd6daf52012-05-02 19:32:32 +0000451 SkSafeUnref(accum);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000452 return false;
453 }
454
455 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
456 GrDrawState* drawState = gpu->drawState();
457
458 GrDrawTarget::AutoGeometryPush agp(gpu);
459
460 int count = clipIn.getElementCount();
461
462 if (0 != bounds.fTop || 0 != bounds.fLeft) {
463 // if we were able to trim down the size of the mask we need to
464 // offset the paths & rects that will be used to compute it
465 GrMatrix m;
466
467 m.setTranslate(-bounds.fLeft, -bounds.fTop);
468
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000469 drawState->setViewMatrix(m);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000470 }
471
472 bool clearToInside;
473 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
474 int start = process_initial_clip_elements(clipIn,
475 bounds,
476 &clearToInside,
477 &startOp);
478
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000479 clear(gpu, accum, clearToInside ? 0xffffffff : 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000480
481 // walk through each clip element and perform its set op
482 for (int c = start; c < count; ++c) {
483
484 SkRegion::Op op = (c == start) ? startOp : clipIn.getOp(c);
485
486 if (SkRegion::kReplace_Op == op) {
487 // TODO: replace is actually a lot faster then intersection
488 // for this path - refactor the stencil path so it can handle
489 // replace ops and alter GrClip to allow them through
490
491 // clear the accumulator and draw the new object directly into it
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000492 clear(gpu, accum, 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000493
494 setUpBooleanBlendCoeffs(drawState, op);
495 this->drawClipShape(gpu, accum, clipIn, c);
496
497 } else if (SkRegion::kReverseDifference_Op == op ||
498 SkRegion::kIntersect_Op == op) {
499 // there is no point in intersecting a screen filling rectangle.
500 if (SkRegion::kIntersect_Op == op &&
501 kRect_ClipType == clipIn.getElementType(c) &&
502 clipIn.getRect(c).contains(bounds)) {
503 continue;
504 }
505
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000506 needTemp(gpu, desc, &temp);
507 if (NULL == temp) {
508 fClipMaskInAlpha = false;
509 SkSafeUnref(accum);
510 return false;
511 }
512
robertphillips@google.comf294b772012-04-27 14:29:26 +0000513 // clear the temp target & draw into it
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000514 clear(gpu, temp, 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000515
516 setUpBooleanBlendCoeffs(drawState, SkRegion::kReplace_Op);
517 this->drawClipShape(gpu, temp, clipIn, c);
518
519 // TODO: rather than adding these two translations here
520 // compute the bounding box needed to render the texture
521 // into temp
522 if (0 != bounds.fTop || 0 != bounds.fLeft) {
523 GrMatrix m;
524
525 m.setTranslate(bounds.fLeft, bounds.fTop);
526
527 drawState->preConcatViewMatrix(m);
528 }
529
530 // Now draw into the accumulator using the real operation
531 // and the temp buffer as a texture
532 setUpBooleanBlendCoeffs(drawState, op);
533 this->drawTexture(gpu, accum, newRTBounds, temp);
534
535 if (0 != bounds.fTop || 0 != bounds.fLeft) {
536 GrMatrix m;
537
538 m.setTranslate(-bounds.fLeft, -bounds.fTop);
539
540 drawState->preConcatViewMatrix(m);
541 }
542
543 } else {
544 // all the remaining ops can just be directly draw into
545 // the accumulation buffer
546 setUpBooleanBlendCoeffs(drawState, op);
547 this->drawClipShape(gpu, accum, clipIn, c);
548 }
549 }
550
robertphillips@google.comfd6daf52012-05-02 19:32:32 +0000551 fAACache.set(clipIn, rt->width(), rt->height(), accum, bounds);
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000552 *result = accum;
553 *resultBounds = bounds;
robertphillips@google.comfd6daf52012-05-02 19:32:32 +0000554 SkSafeUnref(accum); // fAACache still has a ref to accum
555 SkSafeUnref(temp);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000556
robertphillips@google.comf294b772012-04-27 14:29:26 +0000557 return true;
558}
559
560////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000561// Create a 1-bit clip mask in the stencil buffer
562bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
563 const GrClip& clipIn,
564 const GrRect& bounds,
565 ScissoringSettings* scissorSettings) {
566
567 GrAssert(fClipMaskInStencil);
568
569 GrDrawState* drawState = gpu->drawState();
570 GrAssert(drawState->isClipState());
571
572 GrRenderTarget* rt = drawState->getRenderTarget();
573 GrAssert(NULL != rt);
574
575 // TODO: dynamically attach a SB when needed.
576 GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
577 if (NULL == stencilBuffer) {
578 return false;
579 }
580
581 if (stencilBuffer->mustRenderClip(clipIn, rt->width(), rt->height())) {
582
583 stencilBuffer->setLastClip(clipIn, rt->width(), rt->height());
584
585 // we set the current clip to the bounds so that our recursive
586 // draws are scissored to them. We use the copy of the complex clip
587 // we just stashed on the SB to render from. We set it back after
588 // we finish drawing it into the stencil.
589 const GrClip& clipCopy = stencilBuffer->getLastClip();
590 gpu->setClip(GrClip(bounds));
591
592 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
593 drawState = gpu->drawState();
594 drawState->setRenderTarget(rt);
595 GrDrawTarget::AutoGeometryPush agp(gpu);
596
597 gpu->disableScissor();
598#if !VISUALIZE_COMPLEX_CLIP
599 drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
600#endif
601
602 int count = clipCopy.getElementCount();
603 int clipBit = stencilBuffer->bits();
604 SkASSERT((clipBit <= 16) &&
605 "Ganesh only handles 16b or smaller stencil buffers");
606 clipBit = (1 << (clipBit-1));
607
608 GrRect rtRect;
609 rtRect.setLTRB(0, 0,
610 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
611
612 bool clearToInside;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000613 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000614 int start = process_initial_clip_elements(clipCopy,
615 rtRect,
616 &clearToInside,
617 &startOp);
618
619 gpu->clearStencilClip(scissorSettings->fScissorRect, clearToInside);
620
621 // walk through each clip element and perform its set op
622 // with the existing clip.
623 for (int c = start; c < count; ++c) {
624 GrPathFill fill;
625 bool fillInverted;
626 // enabled at bottom of loop
627 drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
628
629 bool canRenderDirectToStencil; // can the clip element be drawn
630 // directly to the stencil buffer
631 // with a non-inverted fill rule
632 // without extra passes to
633 // resolve in/out status.
634
robertphillips@google.comf294b772012-04-27 14:29:26 +0000635 SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
636
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000637 GrPathRenderer* pr = NULL;
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000638 const SkPath* clipPath = NULL;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000639 if (kRect_ClipType == clipCopy.getElementType(c)) {
640 canRenderDirectToStencil = true;
641 fill = kEvenOdd_PathFill;
642 fillInverted = false;
643 // there is no point in intersecting a screen filling
644 // rectangle.
robertphillips@google.comf294b772012-04-27 14:29:26 +0000645 if (SkRegion::kIntersect_Op == op &&
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000646 clipCopy.getRect(c).contains(rtRect)) {
647 continue;
648 }
649 } else {
650 fill = clipCopy.getPathFill(c);
651 fillInverted = GrIsFillInverted(fill);
652 fill = GrNonInvertedFill(fill);
653 clipPath = &clipCopy.getPath(c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000654 pr = this->getClipPathRenderer(gpu, *clipPath, fill, false);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000655 if (NULL == pr) {
656 fClipMaskInStencil = false;
657 gpu->setClip(clipCopy); // restore to the original
658 return false;
659 }
660 canRenderDirectToStencil =
661 !pr->requiresStencilPass(*clipPath, fill, gpu);
662 }
663
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000664 int passes;
665 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
666
667 bool canDrawDirectToClip; // Given the renderer, the element,
668 // fill rule, and set operation can
669 // we render the element directly to
670 // stencil bit used for clipping.
671 canDrawDirectToClip =
672 GrStencilSettings::GetClipPasses(op,
673 canRenderDirectToStencil,
674 clipBit,
675 fillInverted,
676 &passes, stencilSettings);
677
678 // draw the element to the client stencil bits if necessary
679 if (!canDrawDirectToClip) {
680 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
681 kIncClamp_StencilOp,
682 kIncClamp_StencilOp,
683 kAlways_StencilFunc,
684 0xffff,
685 0x0000,
686 0xffff);
687 SET_RANDOM_COLOR
688 if (kRect_ClipType == clipCopy.getElementType(c)) {
689 *drawState->stencil() = gDrawToStencil;
690 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
691 } else {
692 if (canRenderDirectToStencil) {
693 *drawState->stencil() = gDrawToStencil;
694 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
695 } else {
696 pr->drawPathToStencil(*clipPath, fill, gpu);
697 }
698 }
699 }
700
701 // now we modify the clip bit by rendering either the clip
702 // element directly or a bounding rect of the entire clip.
703 drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
704 for (int p = 0; p < passes; ++p) {
705 *drawState->stencil() = stencilSettings[p];
706 if (canDrawDirectToClip) {
707 if (kRect_ClipType == clipCopy.getElementType(c)) {
708 SET_RANDOM_COLOR
709 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
710 } else {
711 SET_RANDOM_COLOR
712 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
713 }
714 } else {
715 SET_RANDOM_COLOR
716 gpu->drawSimpleRect(bounds, NULL, 0);
717 }
718 }
719 }
720 // restore clip
721 gpu->setClip(clipCopy);
722 // recusive draws would have disabled this since they drew with
723 // the clip bounds as clip.
724 fClipMaskInStencil = true;
725 }
726
727 return true;
728}
729
robertphillips@google.comf294b772012-04-27 14:29:26 +0000730////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000731GrPathRenderer* GrClipMaskManager::getClipPathRenderer(GrGpu* gpu,
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000732 const SkPath& path,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000733 GrPathFill fill,
734 bool antiAlias) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000735 if (NULL == fPathRendererChain) {
736 fPathRendererChain =
737 new GrPathRendererChain(gpu->getContext(),
robertphillips@google.comf294b772012-04-27 14:29:26 +0000738 GrPathRendererChain::kNone_UsageFlag);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000739 }
robertphillips@google.comf294b772012-04-27 14:29:26 +0000740 return fPathRendererChain->getPathRenderer(path, fill, gpu, antiAlias);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000741}
742
robertphillips@google.comf294b772012-04-27 14:29:26 +0000743////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000744void GrClipMaskManager::freeResources() {
745 // in case path renderer has any GrResources, start from scratch
746 GrSafeSetNull(fPathRendererChain);
747}