blob: a1ac0ab9b104fff71dffc755c96b41c279d83ef0 [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.com1e945b72012-04-16 18:03:03 +000018
robertphillips@google.comf294b772012-04-27 14:29:26 +000019////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +000020void ScissoringSettings::setupScissoring(GrGpu* gpu) {
21 if (!fEnableScissoring) {
22 gpu->disableScissor();
23 return;
24 }
25
26 gpu->enableScissoring(fScissorRect);
27}
28
robertphillips@google.coma72eef32012-05-01 17:22:59 +000029namespace {
30// set up the draw state to enable the aa clipping mask. Besides setting up the
31// sampler matrix this also alters the vertex layout
32void setupDrawStateAAClip(GrGpu* gpu, GrTexture* result, const GrRect &bound) {
33 GrDrawState* drawState = gpu->drawState();
34 GrAssert(drawState);
35
36 static const int maskStage = GrPaint::kTotalStages+1;
37
38 GrMatrix mat;
39 mat.setIDiv(result->width(), result->height());
40 mat.preTranslate(-bound.fLeft, -bound.fTop);
41 mat.preConcat(drawState->getViewMatrix());
42
43 drawState->sampler(maskStage)->reset(GrSamplerState::kClamp_WrapMode,
44 GrSamplerState::kNearest_Filter,
45 mat);
46
47 drawState->setTexture(maskStage, result);
48
49 // The AA clipping determination happens long after the geometry has
50 // been set up to draw. Here we directly enable the AA clip mask stage
51 gpu->addToVertexLayout(
52 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(maskStage));
53}
54
55}
56
robertphillips@google.comf294b772012-04-27 14:29:26 +000057////////////////////////////////////////////////////////////////////////////////
58// sort out what kind of clip mask needs to be created: alpha, stencil
59// or scissor
robertphillips@google.com1e945b72012-04-16 18:03:03 +000060bool GrClipMaskManager::createClipMask(GrGpu* gpu,
61 const GrClip& clipIn,
62 ScissoringSettings* scissorSettings) {
63
64 GrAssert(scissorSettings);
65
66 scissorSettings->fEnableScissoring = false;
67 fClipMaskInStencil = false;
robertphillips@google.comf294b772012-04-27 14:29:26 +000068 fClipMaskInAlpha = false;
robertphillips@google.com1e945b72012-04-16 18:03:03 +000069
70 GrDrawState* drawState = gpu->drawState();
71 if (!drawState->isClipState()) {
72 return true;
73 }
74
75 GrRenderTarget* rt = drawState->getRenderTarget();
76
77 // GrDrawTarget should have filtered this for us
78 GrAssert(NULL != rt);
79
robertphillips@google.comf294b772012-04-27 14:29:26 +000080#if GR_AA_CLIP
81 // If MSAA is enabled use the (faster) stencil path for AA clipping
82 // otherwise the alpha clip mask is our only option
83 if (clipIn.requiresAA() && 0 == rt->numSamples()) {
84 // Since we are going to create a destination texture of the correct
85 // size for the mask (rather than being bound by the size of the
86 // render target) we aren't going to use scissoring like the stencil
87 // path does (see scissorSettings below)
robertphillips@google.coma72eef32012-05-01 17:22:59 +000088 GrTexture* result = NULL;
89 GrRect bound;
90 if (this->createAlphaClipMask(gpu, clipIn, &result, &bound)) {
robertphillips@google.comf294b772012-04-27 14:29:26 +000091 fClipMaskInAlpha = true;
robertphillips@google.coma72eef32012-05-01 17:22:59 +000092
93 setupDrawStateAAClip(gpu, result, bound);
robertphillips@google.comf294b772012-04-27 14:29:26 +000094 return true;
95 }
96
97 // if alpha clip mask creation fails fall through to the stencil
98 // buffer method
99 }
100#endif // GR_AA_CLIP
101
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000102 GrRect bounds;
103 GrRect rtRect;
104 rtRect.setLTRB(0, 0,
105 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
106 if (clipIn.hasConservativeBounds()) {
107 bounds = clipIn.getConservativeBounds();
108 if (!bounds.intersect(rtRect)) {
109 bounds.setEmpty();
110 }
111 } else {
112 bounds = rtRect;
113 }
114
115 bounds.roundOut(&scissorSettings->fScissorRect);
116 if (scissorSettings->fScissorRect.isEmpty()) {
117 scissorSettings->fScissorRect.setLTRB(0,0,0,0);
118 // TODO: I think we can do an early exit here - after refactoring try:
119 // set fEnableScissoring to true but leave fClipMaskInStencil false
120 // and return - everything is going to be scissored away anyway!
121 }
122 scissorSettings->fEnableScissoring = true;
123
124 // use the stencil clip if we can't represent the clip as a rectangle.
125 fClipMaskInStencil = !clipIn.isRect() && !clipIn.isEmpty() &&
126 !bounds.isEmpty();
127
128 if (fClipMaskInStencil) {
129 return this->createStencilClipMask(gpu, clipIn, bounds, scissorSettings);
130 }
131
132 return true;
133}
134
135#define VISUALIZE_COMPLEX_CLIP 0
136
137#if VISUALIZE_COMPLEX_CLIP
138 #include "GrRandom.h"
139 GrRandom gRandom;
140 #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU());
141#else
142 #define SET_RANDOM_COLOR
143#endif
144
145namespace {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000146////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000147// determines how many elements at the head of the clip can be skipped and
148// whether the initial clear should be to the inside- or outside-the-clip value,
149// and what op should be used to draw the first element that isn't skipped.
150int process_initial_clip_elements(const GrClip& clip,
151 const GrRect& bounds,
152 bool* clearToInside,
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000153 SkRegion::Op* startOp) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000154
155 // logically before the first element of the clip stack is
156 // processed the clip is entirely open. However, depending on the
157 // first set op we may prefer to clear to 0 for performance. We may
158 // also be able to skip the initial clip paths/rects. We loop until
159 // we cannot skip an element.
160 int curr;
161 bool done = false;
162 *clearToInside = true;
163 int count = clip.getElementCount();
164
165 for (curr = 0; curr < count && !done; ++curr) {
166 switch (clip.getOp(curr)) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000167 case SkRegion::kReplace_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000168 // replace ignores everything previous
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000169 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000170 *clearToInside = false;
171 done = true;
172 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000173 case SkRegion::kIntersect_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000174 // if this element contains the entire bounds then we
175 // can skip it.
176 if (kRect_ClipType == clip.getElementType(curr)
177 && clip.getRect(curr).contains(bounds)) {
178 break;
179 }
180 // if everything is initially clearToInside then intersect is
181 // same as clear to 0 and treat as a replace. Otherwise,
182 // set stays empty.
183 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000184 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000185 *clearToInside = false;
186 done = true;
187 }
188 break;
189 // we can skip a leading union.
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000190 case SkRegion::kUnion_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000191 // if everything is initially outside then union is
192 // same as replace. Otherwise, every pixel is still
193 // clearToInside
194 if (!*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000195 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000196 done = true;
197 }
198 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000199 case SkRegion::kXOR_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000200 // xor is same as difference or replace both of which
201 // can be 1-pass instead of 2 for xor.
202 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000203 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000204 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000205 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000206 }
207 done = true;
208 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000209 case SkRegion::kDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000210 // if all pixels are clearToInside then we have to process the
211 // difference, otherwise it has no effect and all pixels
212 // remain outside.
213 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000214 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000215 done = true;
216 }
217 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000218 case SkRegion::kReverseDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000219 // if all pixels are clearToInside then reverse difference
220 // produces empty set. Otherise it is same as replace
221 if (*clearToInside) {
222 *clearToInside = false;
223 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000224 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000225 done = true;
226 }
227 break;
228 default:
229 GrCrash("Unknown set op.");
230 }
231 }
232 return done ? curr-1 : count;
233}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000234
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000235}
236
robertphillips@google.comf294b772012-04-27 14:29:26 +0000237
238namespace {
239
240////////////////////////////////////////////////////////////////////////////////
241// set up the OpenGL blend function to perform the specified
242// boolean operation for alpha clip mask creation
243void setUpBooleanBlendCoeffs(GrDrawState* drawState, SkRegion::Op op) {
244
245 switch (op) {
246 case SkRegion::kReplace_Op:
247 drawState->setBlendFunc(kOne_BlendCoeff, kZero_BlendCoeff);
248 break;
249 case SkRegion::kIntersect_Op:
250 drawState->setBlendFunc(kDC_BlendCoeff, kZero_BlendCoeff);
251 break;
252 case SkRegion::kUnion_Op:
253 drawState->setBlendFunc(kOne_BlendCoeff, kISC_BlendCoeff);
254 break;
255 case SkRegion::kXOR_Op:
256 drawState->setBlendFunc(kIDC_BlendCoeff, kISC_BlendCoeff);
257 break;
258 case SkRegion::kDifference_Op:
259 drawState->setBlendFunc(kZero_BlendCoeff, kISC_BlendCoeff);
260 break;
261 case SkRegion::kReverseDifference_Op:
262 drawState->setBlendFunc(kIDC_BlendCoeff, kZero_BlendCoeff);
263 break;
264 default:
265 GrAssert(false);
266 break;
267 }
268}
269
270}
271
272////////////////////////////////////////////////////////////////////////////////
273bool GrClipMaskManager::drawPath(GrGpu* gpu,
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000274 const SkPath& path,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000275 GrPathFill fill,
276 bool doAA) {
277
278 GrPathRenderer* pr = this->getClipPathRenderer(gpu, path, fill, doAA);
279 if (NULL == pr) {
280 return false;
281 }
282
283 pr->drawPath(path, fill, NULL, gpu, 0, doAA);
284 return true;
285}
286
287////////////////////////////////////////////////////////////////////////////////
288bool GrClipMaskManager::drawClipShape(GrGpu* gpu,
289 GrTexture* target,
290 const GrClip& clipIn,
291 int index) {
292 GrDrawState* drawState = gpu->drawState();
293 GrAssert(NULL != drawState);
294
295 drawState->setRenderTarget(target->asRenderTarget());
296
297 if (kRect_ClipType == clipIn.getElementType(index)) {
298 if (clipIn.getDoAA(index)) {
299 // convert the rect to a path for AA
300 SkPath temp;
301 temp.addRect(clipIn.getRect(index));
302
303 return this->drawPath(gpu, temp,
304 kEvenOdd_PathFill, clipIn.getDoAA(index));
305 } else {
306 gpu->drawSimpleRect(clipIn.getRect(index), NULL, 0);
307 }
308 } else {
309 return this->drawPath(gpu,
310 clipIn.getPath(index),
311 clipIn.getPathFill(index),
312 clipIn.getDoAA(index));
313 }
314 return true;
315}
316
317void GrClipMaskManager::drawTexture(GrGpu* gpu,
318 GrTexture* target,
319 const GrRect& rect,
320 GrTexture* texture) {
321 GrDrawState* drawState = gpu->drawState();
322 GrAssert(NULL != drawState);
323
324 // no AA here since it is encoded in the texture
325 drawState->setRenderTarget(target->asRenderTarget());
326
327 GrMatrix sampleM;
328 sampleM.setIDiv(texture->width(), texture->height());
329 drawState->setTexture(0, texture);
330
331 drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
332 GrSamplerState::kNearest_Filter,
333 sampleM);
334
335 gpu->drawSimpleRect(rect, NULL, 1 << 0);
336
337 drawState->setTexture(0, NULL);
338}
339
340namespace {
341
342void clear(GrGpu* gpu,
343 GrTexture* target,
344 const GrRect& bounds,
345 GrColor color) {
346 GrDrawState* drawState = gpu->drawState();
347 GrAssert(NULL != drawState);
348
349 // zap entire target to specified color
350 drawState->setRenderTarget(target->asRenderTarget());
351 gpu->clear(NULL, color);
352}
353
354}
355
356////////////////////////////////////////////////////////////////////////////////
357// Create a 8-bit clip mask in alpha
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000358bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu,
359 const GrClip& clipIn,
360 GrTexture** result,
361 GrRect *resultBounds) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000362
363 GrDrawState* origDrawState = gpu->drawState();
364 GrAssert(origDrawState->isClipState());
365
366 GrRenderTarget* rt = origDrawState->getRenderTarget();
367 GrAssert(NULL != rt);
368
369 GrRect rtRect;
370 rtRect.setLTRB(0, 0,
371 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
372
373 // unlike the stencil path the alpha path is not bound to the size of the
374 // render target - determine the minimum size required for the mask
375 GrRect bounds;
376
377 if (clipIn.hasConservativeBounds()) {
378 bounds = clipIn.getConservativeBounds();
379 if (!bounds.intersect(rtRect)) {
380 // the mask will be empty in this case
381 GrAssert(false);
382 bounds.setEmpty();
383 }
384 } else {
385 // still locked to the size of the render target
386 bounds = rtRect;
387 }
388
389 bounds.roundOut();
390
391 // need to outset a pixel since the standard bounding box computation
392 // path doesn't leave any room for antialiasing (esp. w.r.t. rects)
393 bounds.outset(SkIntToScalar(1), SkIntToScalar(1));
394
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000395 // TODO: make sure we don't outset if bounds are still 0,0 @ min
396
robertphillips@google.comf294b772012-04-27 14:29:26 +0000397 GrAssert(SkScalarIsInt(bounds.width()));
398 GrAssert(SkScalarIsInt(bounds.height()));
399
400 GrTextureDesc desc = {
401 kRenderTarget_GrTextureFlagBit,
402 SkScalarCeilToInt(bounds.width()),
403 SkScalarCeilToInt(bounds.height()),
404 kAlpha_8_GrPixelConfig,
405 0 // samples
406 };
407
408 GrRect newRTBounds;
409 newRTBounds.setLTRB(0, 0, bounds.width(), bounds.height());
410
411 GrTexture* accum = gpu->createTexture(desc, NULL, 0);
412 GrTexture* temp = gpu->createTexture(desc, NULL, 0);
413 if (NULL == accum || NULL == temp) {
414 // TODO: free up accum & temp here!
415 fClipMaskInAlpha = false;
416 return false;
417 }
418
419 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
420 GrDrawState* drawState = gpu->drawState();
421
422 GrDrawTarget::AutoGeometryPush agp(gpu);
423
424 int count = clipIn.getElementCount();
425
426 if (0 != bounds.fTop || 0 != bounds.fLeft) {
427 // if we were able to trim down the size of the mask we need to
428 // offset the paths & rects that will be used to compute it
429 GrMatrix m;
430
431 m.setTranslate(-bounds.fLeft, -bounds.fTop);
432
433 drawState->preConcatViewMatrix(m);
434 }
435
436 bool clearToInside;
437 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
438 int start = process_initial_clip_elements(clipIn,
439 bounds,
440 &clearToInside,
441 &startOp);
442
443 clear(gpu, accum, newRTBounds, clearToInside ? 0xffffffff : 0x00000000);
444
445 // walk through each clip element and perform its set op
446 for (int c = start; c < count; ++c) {
447
448 SkRegion::Op op = (c == start) ? startOp : clipIn.getOp(c);
449
450 if (SkRegion::kReplace_Op == op) {
451 // TODO: replace is actually a lot faster then intersection
452 // for this path - refactor the stencil path so it can handle
453 // replace ops and alter GrClip to allow them through
454
455 // clear the accumulator and draw the new object directly into it
456 clear(gpu, accum, newRTBounds, 0x00000000);
457
458 setUpBooleanBlendCoeffs(drawState, op);
459 this->drawClipShape(gpu, accum, clipIn, c);
460
461 } else if (SkRegion::kReverseDifference_Op == op ||
462 SkRegion::kIntersect_Op == op) {
463 // there is no point in intersecting a screen filling rectangle.
464 if (SkRegion::kIntersect_Op == op &&
465 kRect_ClipType == clipIn.getElementType(c) &&
466 clipIn.getRect(c).contains(bounds)) {
467 continue;
468 }
469
470 // clear the temp target & draw into it
471 clear(gpu, temp, newRTBounds, 0x00000000);
472
473 setUpBooleanBlendCoeffs(drawState, SkRegion::kReplace_Op);
474 this->drawClipShape(gpu, temp, clipIn, c);
475
476 // TODO: rather than adding these two translations here
477 // compute the bounding box needed to render the texture
478 // into temp
479 if (0 != bounds.fTop || 0 != bounds.fLeft) {
480 GrMatrix m;
481
482 m.setTranslate(bounds.fLeft, bounds.fTop);
483
484 drawState->preConcatViewMatrix(m);
485 }
486
487 // Now draw into the accumulator using the real operation
488 // and the temp buffer as a texture
489 setUpBooleanBlendCoeffs(drawState, op);
490 this->drawTexture(gpu, accum, newRTBounds, temp);
491
492 if (0 != bounds.fTop || 0 != bounds.fLeft) {
493 GrMatrix m;
494
495 m.setTranslate(-bounds.fLeft, -bounds.fTop);
496
497 drawState->preConcatViewMatrix(m);
498 }
499
500 } else {
501 // all the remaining ops can just be directly draw into
502 // the accumulation buffer
503 setUpBooleanBlendCoeffs(drawState, op);
504 this->drawClipShape(gpu, accum, clipIn, c);
505 }
506 }
507
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000508 *result = accum;
509 *resultBounds = bounds;
robertphillips@google.comf294b772012-04-27 14:29:26 +0000510 return true;
511}
512
513////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000514// Create a 1-bit clip mask in the stencil buffer
515bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
516 const GrClip& clipIn,
517 const GrRect& bounds,
518 ScissoringSettings* scissorSettings) {
519
520 GrAssert(fClipMaskInStencil);
521
522 GrDrawState* drawState = gpu->drawState();
523 GrAssert(drawState->isClipState());
524
525 GrRenderTarget* rt = drawState->getRenderTarget();
526 GrAssert(NULL != rt);
527
528 // TODO: dynamically attach a SB when needed.
529 GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
530 if (NULL == stencilBuffer) {
531 return false;
532 }
533
534 if (stencilBuffer->mustRenderClip(clipIn, rt->width(), rt->height())) {
535
536 stencilBuffer->setLastClip(clipIn, rt->width(), rt->height());
537
538 // we set the current clip to the bounds so that our recursive
539 // draws are scissored to them. We use the copy of the complex clip
540 // we just stashed on the SB to render from. We set it back after
541 // we finish drawing it into the stencil.
542 const GrClip& clipCopy = stencilBuffer->getLastClip();
543 gpu->setClip(GrClip(bounds));
544
545 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
546 drawState = gpu->drawState();
547 drawState->setRenderTarget(rt);
548 GrDrawTarget::AutoGeometryPush agp(gpu);
549
550 gpu->disableScissor();
551#if !VISUALIZE_COMPLEX_CLIP
552 drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
553#endif
554
555 int count = clipCopy.getElementCount();
556 int clipBit = stencilBuffer->bits();
557 SkASSERT((clipBit <= 16) &&
558 "Ganesh only handles 16b or smaller stencil buffers");
559 clipBit = (1 << (clipBit-1));
560
561 GrRect rtRect;
562 rtRect.setLTRB(0, 0,
563 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
564
565 bool clearToInside;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000566 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000567 int start = process_initial_clip_elements(clipCopy,
568 rtRect,
569 &clearToInside,
570 &startOp);
571
572 gpu->clearStencilClip(scissorSettings->fScissorRect, clearToInside);
573
574 // walk through each clip element and perform its set op
575 // with the existing clip.
576 for (int c = start; c < count; ++c) {
577 GrPathFill fill;
578 bool fillInverted;
579 // enabled at bottom of loop
580 drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
581
582 bool canRenderDirectToStencil; // can the clip element be drawn
583 // directly to the stencil buffer
584 // with a non-inverted fill rule
585 // without extra passes to
586 // resolve in/out status.
587
robertphillips@google.comf294b772012-04-27 14:29:26 +0000588 SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
589
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000590 GrPathRenderer* pr = NULL;
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000591 const SkPath* clipPath = NULL;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000592 if (kRect_ClipType == clipCopy.getElementType(c)) {
593 canRenderDirectToStencil = true;
594 fill = kEvenOdd_PathFill;
595 fillInverted = false;
596 // there is no point in intersecting a screen filling
597 // rectangle.
robertphillips@google.comf294b772012-04-27 14:29:26 +0000598 if (SkRegion::kIntersect_Op == op &&
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000599 clipCopy.getRect(c).contains(rtRect)) {
600 continue;
601 }
602 } else {
603 fill = clipCopy.getPathFill(c);
604 fillInverted = GrIsFillInverted(fill);
605 fill = GrNonInvertedFill(fill);
606 clipPath = &clipCopy.getPath(c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000607 pr = this->getClipPathRenderer(gpu, *clipPath, fill, false);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000608 if (NULL == pr) {
609 fClipMaskInStencil = false;
610 gpu->setClip(clipCopy); // restore to the original
611 return false;
612 }
613 canRenderDirectToStencil =
614 !pr->requiresStencilPass(*clipPath, fill, gpu);
615 }
616
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000617 int passes;
618 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
619
620 bool canDrawDirectToClip; // Given the renderer, the element,
621 // fill rule, and set operation can
622 // we render the element directly to
623 // stencil bit used for clipping.
624 canDrawDirectToClip =
625 GrStencilSettings::GetClipPasses(op,
626 canRenderDirectToStencil,
627 clipBit,
628 fillInverted,
629 &passes, stencilSettings);
630
631 // draw the element to the client stencil bits if necessary
632 if (!canDrawDirectToClip) {
633 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
634 kIncClamp_StencilOp,
635 kIncClamp_StencilOp,
636 kAlways_StencilFunc,
637 0xffff,
638 0x0000,
639 0xffff);
640 SET_RANDOM_COLOR
641 if (kRect_ClipType == clipCopy.getElementType(c)) {
642 *drawState->stencil() = gDrawToStencil;
643 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
644 } else {
645 if (canRenderDirectToStencil) {
646 *drawState->stencil() = gDrawToStencil;
647 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
648 } else {
649 pr->drawPathToStencil(*clipPath, fill, gpu);
650 }
651 }
652 }
653
654 // now we modify the clip bit by rendering either the clip
655 // element directly or a bounding rect of the entire clip.
656 drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
657 for (int p = 0; p < passes; ++p) {
658 *drawState->stencil() = stencilSettings[p];
659 if (canDrawDirectToClip) {
660 if (kRect_ClipType == clipCopy.getElementType(c)) {
661 SET_RANDOM_COLOR
662 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
663 } else {
664 SET_RANDOM_COLOR
665 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
666 }
667 } else {
668 SET_RANDOM_COLOR
669 gpu->drawSimpleRect(bounds, NULL, 0);
670 }
671 }
672 }
673 // restore clip
674 gpu->setClip(clipCopy);
675 // recusive draws would have disabled this since they drew with
676 // the clip bounds as clip.
677 fClipMaskInStencil = true;
678 }
679
680 return true;
681}
682
robertphillips@google.comf294b772012-04-27 14:29:26 +0000683////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000684GrPathRenderer* GrClipMaskManager::getClipPathRenderer(GrGpu* gpu,
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000685 const SkPath& path,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000686 GrPathFill fill,
687 bool antiAlias) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000688 if (NULL == fPathRendererChain) {
689 fPathRendererChain =
690 new GrPathRendererChain(gpu->getContext(),
robertphillips@google.comf294b772012-04-27 14:29:26 +0000691 GrPathRendererChain::kNone_UsageFlag);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000692 }
robertphillips@google.comf294b772012-04-27 14:29:26 +0000693 return fPathRendererChain->getPathRenderer(path, fill, gpu, antiAlias);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000694}
695
robertphillips@google.comf294b772012-04-27 14:29:26 +0000696////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000697void GrClipMaskManager::freeResources() {
698 // in case path renderer has any GrResources, start from scratch
699 GrSafeSetNull(fPathRendererChain);
700}