blob: 43b27431ba5766a93f8e3254ae3c78bc02ee5f73 [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"
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +000015#include "SkRasterClip.h"
robertphillips@google.coma72eef32012-05-01 17:22:59 +000016
17//#define GR_AA_CLIP 1
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +000018//#define GR_SW_CLIP 1
robertphillips@google.coma72eef32012-05-01 17:22:59 +000019
robertphillips@google.comf294b772012-04-27 14:29:26 +000020////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +000021void ScissoringSettings::setupScissoring(GrGpu* gpu) {
22 if (!fEnableScissoring) {
23 gpu->disableScissor();
24 return;
25 }
26
27 gpu->enableScissoring(fScissorRect);
28}
29
robertphillips@google.coma72eef32012-05-01 17:22:59 +000030namespace {
31// set up the draw state to enable the aa clipping mask. Besides setting up the
32// sampler matrix this also alters the vertex layout
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +000033void setup_drawstate_aaclip(GrGpu* gpu,
34 GrTexture* result,
35 const GrRect &bound) {
robertphillips@google.coma72eef32012-05-01 17:22:59 +000036 GrDrawState* drawState = gpu->drawState();
37 GrAssert(drawState);
38
39 static const int maskStage = GrPaint::kTotalStages+1;
40
41 GrMatrix mat;
42 mat.setIDiv(result->width(), result->height());
43 mat.preTranslate(-bound.fLeft, -bound.fTop);
44 mat.preConcat(drawState->getViewMatrix());
45
46 drawState->sampler(maskStage)->reset(GrSamplerState::kClamp_WrapMode,
47 GrSamplerState::kNearest_Filter,
48 mat);
49
50 drawState->setTexture(maskStage, result);
51
52 // The AA clipping determination happens long after the geometry has
53 // been set up to draw. Here we directly enable the AA clip mask stage
54 gpu->addToVertexLayout(
55 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(maskStage));
56}
57
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +000058bool create_mask_in_sw() {
59 return false;
60}
61
robertphillips@google.coma72eef32012-05-01 17:22:59 +000062}
63
robertphillips@google.comf294b772012-04-27 14:29:26 +000064////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +000065// sort out what kind of clip mask needs to be created: alpha, stencil,
66// scissor, or entirely software
robertphillips@google.com1e945b72012-04-16 18:03:03 +000067bool GrClipMaskManager::createClipMask(GrGpu* gpu,
68 const GrClip& clipIn,
69 ScissoringSettings* scissorSettings) {
70
71 GrAssert(scissorSettings);
72
73 scissorSettings->fEnableScissoring = false;
74 fClipMaskInStencil = false;
robertphillips@google.comf294b772012-04-27 14:29:26 +000075 fClipMaskInAlpha = false;
robertphillips@google.com1e945b72012-04-16 18:03:03 +000076
77 GrDrawState* drawState = gpu->drawState();
78 if (!drawState->isClipState()) {
79 return true;
80 }
81
82 GrRenderTarget* rt = drawState->getRenderTarget();
83
84 // GrDrawTarget should have filtered this for us
85 GrAssert(NULL != rt);
86
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +000087#if GR_SW_CLIP
88 if (create_mask_in_sw()) {
89 // The clip geometry is complex enough that it will be more
90 // efficient to create it entirely in software
91 GrTexture* result = NULL;
92 GrRect bound;
93 if (this->createSoftwareClipMask(gpu, clipIn, &result, &bound)) {
94 fClipMaskInAlpha = true;
95
96 setup_drawstate_aaclip(gpu, result, bound);
97 return true;
98 }
99 }
100#endif
101
robertphillips@google.comf294b772012-04-27 14:29:26 +0000102#if GR_AA_CLIP
103 // If MSAA is enabled use the (faster) stencil path for AA clipping
104 // otherwise the alpha clip mask is our only option
105 if (clipIn.requiresAA() && 0 == rt->numSamples()) {
106 // Since we are going to create a destination texture of the correct
107 // size for the mask (rather than being bound by the size of the
108 // render target) we aren't going to use scissoring like the stencil
109 // path does (see scissorSettings below)
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000110 GrTexture* result = NULL;
111 GrRect bound;
112 if (this->createAlphaClipMask(gpu, clipIn, &result, &bound)) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000113 fClipMaskInAlpha = true;
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000114
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000115 setup_drawstate_aaclip(gpu, result, bound);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000116 return true;
117 }
118
119 // if alpha clip mask creation fails fall through to the stencil
120 // buffer method
121 }
122#endif // GR_AA_CLIP
123
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000124 GrRect bounds;
125 GrRect rtRect;
126 rtRect.setLTRB(0, 0,
127 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
128 if (clipIn.hasConservativeBounds()) {
129 bounds = clipIn.getConservativeBounds();
130 if (!bounds.intersect(rtRect)) {
131 bounds.setEmpty();
132 }
133 } else {
134 bounds = rtRect;
135 }
136
137 bounds.roundOut(&scissorSettings->fScissorRect);
138 if (scissorSettings->fScissorRect.isEmpty()) {
139 scissorSettings->fScissorRect.setLTRB(0,0,0,0);
140 // TODO: I think we can do an early exit here - after refactoring try:
141 // set fEnableScissoring to true but leave fClipMaskInStencil false
142 // and return - everything is going to be scissored away anyway!
143 }
144 scissorSettings->fEnableScissoring = true;
145
146 // use the stencil clip if we can't represent the clip as a rectangle.
147 fClipMaskInStencil = !clipIn.isRect() && !clipIn.isEmpty() &&
148 !bounds.isEmpty();
149
150 if (fClipMaskInStencil) {
151 return this->createStencilClipMask(gpu, clipIn, bounds, scissorSettings);
152 }
153
154 return true;
155}
156
157#define VISUALIZE_COMPLEX_CLIP 0
158
159#if VISUALIZE_COMPLEX_CLIP
160 #include "GrRandom.h"
161 GrRandom gRandom;
162 #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU());
163#else
164 #define SET_RANDOM_COLOR
165#endif
166
167namespace {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000168////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000169// determines how many elements at the head of the clip can be skipped and
170// whether the initial clear should be to the inside- or outside-the-clip value,
171// and what op should be used to draw the first element that isn't skipped.
172int process_initial_clip_elements(const GrClip& clip,
173 const GrRect& bounds,
174 bool* clearToInside,
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000175 SkRegion::Op* startOp) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000176
177 // logically before the first element of the clip stack is
178 // processed the clip is entirely open. However, depending on the
179 // first set op we may prefer to clear to 0 for performance. We may
180 // also be able to skip the initial clip paths/rects. We loop until
181 // we cannot skip an element.
182 int curr;
183 bool done = false;
184 *clearToInside = true;
185 int count = clip.getElementCount();
186
187 for (curr = 0; curr < count && !done; ++curr) {
188 switch (clip.getOp(curr)) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000189 case SkRegion::kReplace_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000190 // replace ignores everything previous
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000191 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000192 *clearToInside = false;
193 done = true;
194 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000195 case SkRegion::kIntersect_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000196 // if this element contains the entire bounds then we
197 // can skip it.
198 if (kRect_ClipType == clip.getElementType(curr)
199 && clip.getRect(curr).contains(bounds)) {
200 break;
201 }
202 // if everything is initially clearToInside then intersect is
203 // same as clear to 0 and treat as a replace. Otherwise,
204 // set stays empty.
205 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000206 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000207 *clearToInside = false;
208 done = true;
209 }
210 break;
211 // we can skip a leading union.
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000212 case SkRegion::kUnion_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000213 // if everything is initially outside then union is
214 // same as replace. Otherwise, every pixel is still
215 // clearToInside
216 if (!*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000217 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000218 done = true;
219 }
220 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000221 case SkRegion::kXOR_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000222 // xor is same as difference or replace both of which
223 // can be 1-pass instead of 2 for xor.
224 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000225 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000226 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000227 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000228 }
229 done = true;
230 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000231 case SkRegion::kDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000232 // if all pixels are clearToInside then we have to process the
233 // difference, otherwise it has no effect and all pixels
234 // remain outside.
235 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000236 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000237 done = true;
238 }
239 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000240 case SkRegion::kReverseDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000241 // if all pixels are clearToInside then reverse difference
242 // produces empty set. Otherise it is same as replace
243 if (*clearToInside) {
244 *clearToInside = false;
245 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000246 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000247 done = true;
248 }
249 break;
250 default:
251 GrCrash("Unknown set op.");
252 }
253 }
254 return done ? curr-1 : count;
255}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000256
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000257}
258
robertphillips@google.comf294b772012-04-27 14:29:26 +0000259
260namespace {
261
262////////////////////////////////////////////////////////////////////////////////
263// set up the OpenGL blend function to perform the specified
264// boolean operation for alpha clip mask creation
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000265void setup_boolean_blendcoeffs(GrDrawState* drawState, SkRegion::Op op) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000266
267 switch (op) {
268 case SkRegion::kReplace_Op:
269 drawState->setBlendFunc(kOne_BlendCoeff, kZero_BlendCoeff);
270 break;
271 case SkRegion::kIntersect_Op:
272 drawState->setBlendFunc(kDC_BlendCoeff, kZero_BlendCoeff);
273 break;
274 case SkRegion::kUnion_Op:
275 drawState->setBlendFunc(kOne_BlendCoeff, kISC_BlendCoeff);
276 break;
277 case SkRegion::kXOR_Op:
278 drawState->setBlendFunc(kIDC_BlendCoeff, kISC_BlendCoeff);
279 break;
280 case SkRegion::kDifference_Op:
281 drawState->setBlendFunc(kZero_BlendCoeff, kISC_BlendCoeff);
282 break;
283 case SkRegion::kReverseDifference_Op:
284 drawState->setBlendFunc(kIDC_BlendCoeff, kZero_BlendCoeff);
285 break;
286 default:
287 GrAssert(false);
288 break;
289 }
290}
291
292}
293
294////////////////////////////////////////////////////////////////////////////////
295bool GrClipMaskManager::drawPath(GrGpu* gpu,
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000296 const SkPath& path,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000297 GrPathFill fill,
298 bool doAA) {
299
300 GrPathRenderer* pr = this->getClipPathRenderer(gpu, path, fill, doAA);
301 if (NULL == pr) {
302 return false;
303 }
304
305 pr->drawPath(path, fill, NULL, gpu, 0, doAA);
306 return true;
307}
308
309////////////////////////////////////////////////////////////////////////////////
310bool GrClipMaskManager::drawClipShape(GrGpu* gpu,
311 GrTexture* target,
312 const GrClip& clipIn,
313 int index) {
314 GrDrawState* drawState = gpu->drawState();
315 GrAssert(NULL != drawState);
316
317 drawState->setRenderTarget(target->asRenderTarget());
318
319 if (kRect_ClipType == clipIn.getElementType(index)) {
320 if (clipIn.getDoAA(index)) {
321 // convert the rect to a path for AA
322 SkPath temp;
323 temp.addRect(clipIn.getRect(index));
324
325 return this->drawPath(gpu, temp,
326 kEvenOdd_PathFill, clipIn.getDoAA(index));
327 } else {
328 gpu->drawSimpleRect(clipIn.getRect(index), NULL, 0);
329 }
330 } else {
331 return this->drawPath(gpu,
332 clipIn.getPath(index),
333 clipIn.getPathFill(index),
334 clipIn.getDoAA(index));
335 }
336 return true;
337}
338
339void GrClipMaskManager::drawTexture(GrGpu* gpu,
340 GrTexture* target,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000341 GrTexture* texture) {
342 GrDrawState* drawState = gpu->drawState();
343 GrAssert(NULL != drawState);
344
345 // no AA here since it is encoded in the texture
346 drawState->setRenderTarget(target->asRenderTarget());
347
348 GrMatrix sampleM;
349 sampleM.setIDiv(texture->width(), texture->height());
350 drawState->setTexture(0, texture);
351
352 drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
353 GrSamplerState::kNearest_Filter,
354 sampleM);
355
robertphillips@google.comf105b102012-05-14 12:18:26 +0000356 GrRect rect = GrRect::MakeWH(SkIntToScalar(target->width()),
357 SkIntToScalar(target->height()));
358
robertphillips@google.comf294b772012-04-27 14:29:26 +0000359 gpu->drawSimpleRect(rect, NULL, 1 << 0);
360
361 drawState->setTexture(0, NULL);
362}
363
364namespace {
365
366void clear(GrGpu* gpu,
367 GrTexture* target,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000368 GrColor color) {
369 GrDrawState* drawState = gpu->drawState();
370 GrAssert(NULL != drawState);
371
372 // zap entire target to specified color
373 drawState->setRenderTarget(target->asRenderTarget());
374 gpu->clear(NULL, color);
375}
376
robertphillips@google.comf105b102012-05-14 12:18:26 +0000377}
378
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000379// get a texture to act as a temporary buffer for AA clip boolean operations
380// TODO: given the expense of createTexture we may want to just cache this too
robertphillips@google.comf105b102012-05-14 12:18:26 +0000381void GrClipMaskManager::getTemp(const GrRect& bounds,
382 GrAutoScratchTexture* temp) {
383 if (NULL != temp->texture()) {
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000384 // we've already allocated the temp texture
385 return;
386 }
387
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000388 const GrTextureDesc desc = {
389 kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
390 SkScalarCeilToInt(bounds.width()),
391 SkScalarCeilToInt(bounds.height()),
392 kAlpha_8_GrPixelConfig,
393 0 // samples
394 };
395
robertphillips@google.comf105b102012-05-14 12:18:26 +0000396 temp->set(fAACache.getContext(), desc);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000397}
398
robertphillips@google.comf105b102012-05-14 12:18:26 +0000399
400void GrClipMaskManager::setupCache(const GrClip& clipIn,
401 const GrRect& bounds) {
402 // Since we are setting up the cache we know the last lookup was a miss
403 // Free up the currently cached mask so it can be reused
404 fAACache.reset();
405
406 const GrTextureDesc desc = {
407 kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
408 SkScalarCeilToInt(bounds.width()),
409 SkScalarCeilToInt(bounds.height()),
410 kAlpha_8_GrPixelConfig,
411 0 // samples
412 };
413
414 fAACache.acquireMask(clipIn, desc, bounds);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000415}
416
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000417////////////////////////////////////////////////////////////////////////////////
418// Shared preamble between gpu and SW-only AA clip mask creation paths.
419// Handles caching, determination of clip mask bound & allocation (if needed)
420// of the result texture
421// Returns true if there is no more work to be done (i.e., we got a cache hit)
422bool GrClipMaskManager::clipMaskPreamble(GrGpu* gpu,
423 const GrClip& clipIn,
424 GrTexture** result,
robertphillips@google.comf105b102012-05-14 12:18:26 +0000425 GrRect *resultBounds) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000426 GrDrawState* origDrawState = gpu->drawState();
427 GrAssert(origDrawState->isClipState());
428
429 GrRenderTarget* rt = origDrawState->getRenderTarget();
430 GrAssert(NULL != rt);
431
432 GrRect rtRect;
433 rtRect.setLTRB(0, 0,
434 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
435
436 // unlike the stencil path the alpha path is not bound to the size of the
437 // render target - determine the minimum size required for the mask
438 GrRect bounds;
439
440 if (clipIn.hasConservativeBounds()) {
441 bounds = clipIn.getConservativeBounds();
442 if (!bounds.intersect(rtRect)) {
443 // the mask will be empty in this case
444 GrAssert(false);
445 bounds.setEmpty();
446 }
447 } else {
448 // still locked to the size of the render target
449 bounds = rtRect;
450 }
451
452 bounds.roundOut();
453
454 // need to outset a pixel since the standard bounding box computation
455 // path doesn't leave any room for antialiasing (esp. w.r.t. rects)
456 bounds.outset(SkIntToScalar(1), SkIntToScalar(1));
457
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000458 // TODO: make sure we don't outset if bounds are still 0,0 @ min
459
robertphillips@google.comf294b772012-04-27 14:29:26 +0000460 GrAssert(SkScalarIsInt(bounds.width()));
461 GrAssert(SkScalarIsInt(bounds.height()));
462
robertphillips@google.com8fff3562012-05-11 12:53:50 +0000463 if (fAACache.canReuse(clipIn,
464 SkScalarCeilToInt(bounds.width()),
465 SkScalarCeilToInt(bounds.height()))) {
466 *result = fAACache.getLastMask();
467 fAACache.getLastBound(resultBounds);
468 return true;
469 }
470
robertphillips@google.comf105b102012-05-14 12:18:26 +0000471 this->setupCache(clipIn, bounds);
472
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000473 *resultBounds = bounds;
474 return false;
475}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000476
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000477////////////////////////////////////////////////////////////////////////////////
478// Create a 8-bit clip mask in alpha
479bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu,
480 const GrClip& clipIn,
481 GrTexture** result,
482 GrRect *resultBounds) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000483
robertphillips@google.comf105b102012-05-14 12:18:26 +0000484 if (this->clipMaskPreamble(gpu, clipIn, result, resultBounds)) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000485 return true;
486 }
487
robertphillips@google.comf105b102012-05-14 12:18:26 +0000488 GrTexture* accum = fAACache.getLastMask();
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000489 if (NULL == accum) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000490 fClipMaskInAlpha = false;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000491 fAACache.reset();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000492 return false;
493 }
494
495 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
496 GrDrawState* drawState = gpu->drawState();
497
498 GrDrawTarget::AutoGeometryPush agp(gpu);
499
500 int count = clipIn.getElementCount();
501
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000502 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000503 // if we were able to trim down the size of the mask we need to
504 // offset the paths & rects that will be used to compute it
505 GrMatrix m;
506
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000507 m.setTranslate(-resultBounds->fLeft, -resultBounds->fTop);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000508
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000509 drawState->setViewMatrix(m);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000510 }
511
512 bool clearToInside;
513 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
514 int start = process_initial_clip_elements(clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000515 *resultBounds,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000516 &clearToInside,
517 &startOp);
518
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000519 clear(gpu, accum, clearToInside ? 0xffffffff : 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000520
robertphillips@google.comf105b102012-05-14 12:18:26 +0000521 GrAutoScratchTexture temp;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000522
robertphillips@google.comf294b772012-04-27 14:29:26 +0000523 // walk through each clip element and perform its set op
524 for (int c = start; c < count; ++c) {
525
526 SkRegion::Op op = (c == start) ? startOp : clipIn.getOp(c);
527
528 if (SkRegion::kReplace_Op == op) {
529 // TODO: replace is actually a lot faster then intersection
530 // for this path - refactor the stencil path so it can handle
531 // replace ops and alter GrClip to allow them through
532
533 // clear the accumulator and draw the new object directly into it
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000534 clear(gpu, accum, 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000535
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000536 setup_boolean_blendcoeffs(drawState, op);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000537 this->drawClipShape(gpu, accum, clipIn, c);
538
539 } else if (SkRegion::kReverseDifference_Op == op ||
540 SkRegion::kIntersect_Op == op) {
541 // there is no point in intersecting a screen filling rectangle.
542 if (SkRegion::kIntersect_Op == op &&
543 kRect_ClipType == clipIn.getElementType(c) &&
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000544 clipIn.getRect(c).contains(*resultBounds)) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000545 continue;
546 }
547
robertphillips@google.comf105b102012-05-14 12:18:26 +0000548 getTemp(*resultBounds, &temp);
549 if (NULL == temp.texture()) {
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000550 fClipMaskInAlpha = false;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000551 fAACache.reset();
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000552 return false;
553 }
554
robertphillips@google.comf294b772012-04-27 14:29:26 +0000555 // clear the temp target & draw into it
robertphillips@google.comf105b102012-05-14 12:18:26 +0000556 clear(gpu, temp.texture(), 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000557
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000558 setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000559 this->drawClipShape(gpu, temp.texture(), clipIn, c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000560
561 // TODO: rather than adding these two translations here
562 // compute the bounding box needed to render the texture
563 // into temp
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000564 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000565 GrMatrix m;
566
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000567 m.setTranslate(resultBounds->fLeft, resultBounds->fTop);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000568
569 drawState->preConcatViewMatrix(m);
570 }
571
572 // Now draw into the accumulator using the real operation
573 // and the temp buffer as a texture
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000574 setup_boolean_blendcoeffs(drawState, op);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000575 this->drawTexture(gpu, accum, temp.texture());
robertphillips@google.comf294b772012-04-27 14:29:26 +0000576
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000577 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000578 GrMatrix m;
579
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000580 m.setTranslate(-resultBounds->fLeft, -resultBounds->fTop);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000581
582 drawState->preConcatViewMatrix(m);
583 }
584
585 } else {
586 // all the remaining ops can just be directly draw into
587 // the accumulation buffer
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000588 setup_boolean_blendcoeffs(drawState, op);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000589 this->drawClipShape(gpu, accum, clipIn, c);
590 }
591 }
592
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000593 *result = accum;
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000594
robertphillips@google.comf294b772012-04-27 14:29:26 +0000595 return true;
596}
597
598////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000599// Create a 1-bit clip mask in the stencil buffer
600bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
601 const GrClip& clipIn,
602 const GrRect& bounds,
603 ScissoringSettings* scissorSettings) {
604
605 GrAssert(fClipMaskInStencil);
606
607 GrDrawState* drawState = gpu->drawState();
608 GrAssert(drawState->isClipState());
609
610 GrRenderTarget* rt = drawState->getRenderTarget();
611 GrAssert(NULL != rt);
612
613 // TODO: dynamically attach a SB when needed.
614 GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
615 if (NULL == stencilBuffer) {
616 return false;
617 }
618
619 if (stencilBuffer->mustRenderClip(clipIn, rt->width(), rt->height())) {
620
621 stencilBuffer->setLastClip(clipIn, rt->width(), rt->height());
622
623 // we set the current clip to the bounds so that our recursive
624 // draws are scissored to them. We use the copy of the complex clip
625 // we just stashed on the SB to render from. We set it back after
626 // we finish drawing it into the stencil.
627 const GrClip& clipCopy = stencilBuffer->getLastClip();
628 gpu->setClip(GrClip(bounds));
629
630 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
631 drawState = gpu->drawState();
632 drawState->setRenderTarget(rt);
633 GrDrawTarget::AutoGeometryPush agp(gpu);
634
635 gpu->disableScissor();
636#if !VISUALIZE_COMPLEX_CLIP
637 drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
638#endif
639
640 int count = clipCopy.getElementCount();
641 int clipBit = stencilBuffer->bits();
642 SkASSERT((clipBit <= 16) &&
643 "Ganesh only handles 16b or smaller stencil buffers");
644 clipBit = (1 << (clipBit-1));
645
646 GrRect rtRect;
647 rtRect.setLTRB(0, 0,
648 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
649
650 bool clearToInside;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000651 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000652 int start = process_initial_clip_elements(clipCopy,
653 rtRect,
654 &clearToInside,
655 &startOp);
656
657 gpu->clearStencilClip(scissorSettings->fScissorRect, clearToInside);
658
659 // walk through each clip element and perform its set op
660 // with the existing clip.
661 for (int c = start; c < count; ++c) {
662 GrPathFill fill;
663 bool fillInverted;
664 // enabled at bottom of loop
665 drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
666
667 bool canRenderDirectToStencil; // can the clip element be drawn
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000668 // directly to the stencil buffer
669 // with a non-inverted fill rule
670 // without extra passes to
671 // resolve in/out status.
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000672
robertphillips@google.comf294b772012-04-27 14:29:26 +0000673 SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
674
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000675 GrPathRenderer* pr = NULL;
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000676 const SkPath* clipPath = NULL;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000677 if (kRect_ClipType == clipCopy.getElementType(c)) {
678 canRenderDirectToStencil = true;
679 fill = kEvenOdd_PathFill;
680 fillInverted = false;
681 // there is no point in intersecting a screen filling
682 // rectangle.
robertphillips@google.comf294b772012-04-27 14:29:26 +0000683 if (SkRegion::kIntersect_Op == op &&
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000684 clipCopy.getRect(c).contains(rtRect)) {
685 continue;
686 }
687 } else {
688 fill = clipCopy.getPathFill(c);
689 fillInverted = GrIsFillInverted(fill);
690 fill = GrNonInvertedFill(fill);
691 clipPath = &clipCopy.getPath(c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000692 pr = this->getClipPathRenderer(gpu, *clipPath, fill, false);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000693 if (NULL == pr) {
694 fClipMaskInStencil = false;
695 gpu->setClip(clipCopy); // restore to the original
696 return false;
697 }
698 canRenderDirectToStencil =
699 !pr->requiresStencilPass(*clipPath, fill, gpu);
700 }
701
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000702 int passes;
703 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
704
705 bool canDrawDirectToClip; // Given the renderer, the element,
706 // fill rule, and set operation can
707 // we render the element directly to
708 // stencil bit used for clipping.
709 canDrawDirectToClip =
710 GrStencilSettings::GetClipPasses(op,
711 canRenderDirectToStencil,
712 clipBit,
713 fillInverted,
714 &passes, stencilSettings);
715
716 // draw the element to the client stencil bits if necessary
717 if (!canDrawDirectToClip) {
718 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
719 kIncClamp_StencilOp,
720 kIncClamp_StencilOp,
721 kAlways_StencilFunc,
722 0xffff,
723 0x0000,
724 0xffff);
725 SET_RANDOM_COLOR
726 if (kRect_ClipType == clipCopy.getElementType(c)) {
727 *drawState->stencil() = gDrawToStencil;
728 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
729 } else {
730 if (canRenderDirectToStencil) {
731 *drawState->stencil() = gDrawToStencil;
732 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
733 } else {
734 pr->drawPathToStencil(*clipPath, fill, gpu);
735 }
736 }
737 }
738
739 // now we modify the clip bit by rendering either the clip
740 // element directly or a bounding rect of the entire clip.
741 drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
742 for (int p = 0; p < passes; ++p) {
743 *drawState->stencil() = stencilSettings[p];
744 if (canDrawDirectToClip) {
745 if (kRect_ClipType == clipCopy.getElementType(c)) {
746 SET_RANDOM_COLOR
747 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
748 } else {
749 SET_RANDOM_COLOR
750 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
751 }
752 } else {
753 SET_RANDOM_COLOR
754 gpu->drawSimpleRect(bounds, NULL, 0);
755 }
756 }
757 }
758 // restore clip
759 gpu->setClip(clipCopy);
760 // recusive draws would have disabled this since they drew with
761 // the clip bounds as clip.
762 fClipMaskInStencil = true;
763 }
764
765 return true;
766}
767
robertphillips@google.comf294b772012-04-27 14:29:26 +0000768////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000769bool GrClipMaskManager::createSoftwareClipMask(GrGpu* gpu,
770 const GrClip& clipIn,
771 GrTexture** result,
772 GrRect *resultBounds) {
773
robertphillips@google.comf105b102012-05-14 12:18:26 +0000774 if (this->clipMaskPreamble(gpu, clipIn, result, resultBounds)) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000775 return true;
776 }
777
robertphillips@google.comf105b102012-05-14 12:18:26 +0000778 GrTexture* accum = fAACache.getLastMask();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000779 if (NULL == accum) {
780 fClipMaskInAlpha = false;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000781 fAACache.reset();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000782 return false;
783 }
784
785#if 0
786 SkRasterClip rasterClip;
787
788 // TODO: refactor GrClip out of existance and use SkCanvas's ClipVisitor
789 // - may have to move it to SkClipStack
790 for (int i = 0; i < clipIn.getElementCount(); ++i) {
791 if (kRect_ClipType == clipIn.getElementType(i)) {
792 rasterClip.op(clipIn.getRect(i), clipIn.getOp(i), clipIn.getDoAA(i));
793 } else {
794 GrAssert(kPath_ClipType == clipIn.getElementType(i));
795
796 SkIPoint deviceSize = SkIPoint::Make(resultBounds->width(),
797 resultBounds->height());
798
799 SkRasterClip::clipPathHelper(&rasterClip,
800 clipIn.getPath(i),
801 clipIn.getOp(i),
802 clipIn.getDoAA(i),
803 deviceSize);
804 }
805 }
806
807 // TODO: need to get pixels out of SkRasterClip & into the texture!
808#endif
809
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000810 *result = accum;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000811
812 return true;
813}
814
815
816////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000817GrPathRenderer* GrClipMaskManager::getClipPathRenderer(GrGpu* gpu,
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000818 const SkPath& path,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000819 GrPathFill fill,
820 bool antiAlias) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000821 if (NULL == fPathRendererChain) {
822 fPathRendererChain =
823 new GrPathRendererChain(gpu->getContext(),
robertphillips@google.comf294b772012-04-27 14:29:26 +0000824 GrPathRendererChain::kNone_UsageFlag);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000825 }
robertphillips@google.comf294b772012-04-27 14:29:26 +0000826 return fPathRendererChain->getPathRenderer(path, fill, gpu, antiAlias);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000827}
828
robertphillips@google.comf294b772012-04-27 14:29:26 +0000829////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comf105b102012-05-14 12:18:26 +0000830void GrClipMaskManager::releaseResources() {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000831 // in case path renderer has any GrResources, start from scratch
832 GrSafeSetNull(fPathRendererChain);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000833 fAACache.releaseResources();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000834}