blob: f8e81a3b80eb900f426b7864b548d8be3c439bd5 [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,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +000035 const GrIRect &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());
robertphillips@google.com6623fcd2012-05-15 16:47:23 +000043 mat.preTranslate(SkIntToScalar(-bound.fLeft), SkIntToScalar(-bound.fTop));
robertphillips@google.coma72eef32012-05-01 17:22:59 +000044 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;
robertphillips@google.com6623fcd2012-05-15 16:47:23 +000092 GrIRect bound;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +000093 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;
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000111 GrIRect bound;
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000112 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.com5acc0e32012-05-17 12:01:02 +0000124 // Either a hard (stencil buffer) clip was explicitly requested or
125 // an antialiased clip couldn't be created. In either case, free up
126 // the texture in the antialiased mask cache.
127 // TODO: this may require more investigation. Ganesh performs a lot of
128 // utility draws (e.g., clears, InOderDrawBuffer playbacks) that hit
129 // the stencil buffer path. These may be incorrectly messing up the
130 // AA cache.
131 fAACache.reset();
132
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000133 GrRect bounds;
134 GrRect rtRect;
135 rtRect.setLTRB(0, 0,
136 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
137 if (clipIn.hasConservativeBounds()) {
138 bounds = clipIn.getConservativeBounds();
139 if (!bounds.intersect(rtRect)) {
140 bounds.setEmpty();
141 }
142 } else {
143 bounds = rtRect;
144 }
145
146 bounds.roundOut(&scissorSettings->fScissorRect);
147 if (scissorSettings->fScissorRect.isEmpty()) {
148 scissorSettings->fScissorRect.setLTRB(0,0,0,0);
149 // TODO: I think we can do an early exit here - after refactoring try:
150 // set fEnableScissoring to true but leave fClipMaskInStencil false
151 // and return - everything is going to be scissored away anyway!
152 }
153 scissorSettings->fEnableScissoring = true;
154
155 // use the stencil clip if we can't represent the clip as a rectangle.
156 fClipMaskInStencil = !clipIn.isRect() && !clipIn.isEmpty() &&
157 !bounds.isEmpty();
158
159 if (fClipMaskInStencil) {
160 return this->createStencilClipMask(gpu, clipIn, bounds, scissorSettings);
161 }
162
163 return true;
164}
165
166#define VISUALIZE_COMPLEX_CLIP 0
167
168#if VISUALIZE_COMPLEX_CLIP
169 #include "GrRandom.h"
170 GrRandom gRandom;
171 #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU());
172#else
173 #define SET_RANDOM_COLOR
174#endif
175
176namespace {
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000177/**
178 * Does "container" contain "containee"? If either is empty then
179 * no containment is possible.
180 */
181bool contains(const SkRect& container, const SkIRect& containee) {
182 return !containee.isEmpty() && !container.isEmpty() &&
183 container.fLeft <= SkIntToScalar(containee.fLeft) &&
184 container.fTop <= SkIntToScalar(containee.fTop) &&
185 container.fRight >= SkIntToScalar(containee.fRight) &&
186 container.fBottom >= SkIntToScalar(containee.fBottom);
187}
188
189
robertphillips@google.comf294b772012-04-27 14:29:26 +0000190////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000191// determines how many elements at the head of the clip can be skipped and
192// whether the initial clear should be to the inside- or outside-the-clip value,
193// and what op should be used to draw the first element that isn't skipped.
194int process_initial_clip_elements(const GrClip& clip,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000195 const GrIRect& bounds,
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000196 bool* clearToInside,
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000197 SkRegion::Op* startOp) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000198
199 // logically before the first element of the clip stack is
200 // processed the clip is entirely open. However, depending on the
201 // first set op we may prefer to clear to 0 for performance. We may
202 // also be able to skip the initial clip paths/rects. We loop until
203 // we cannot skip an element.
204 int curr;
205 bool done = false;
206 *clearToInside = true;
207 int count = clip.getElementCount();
208
209 for (curr = 0; curr < count && !done; ++curr) {
210 switch (clip.getOp(curr)) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000211 case SkRegion::kReplace_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000212 // replace ignores everything previous
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000213 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000214 *clearToInside = false;
215 done = true;
216 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000217 case SkRegion::kIntersect_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000218 // if this element contains the entire bounds then we
219 // can skip it.
220 if (kRect_ClipType == clip.getElementType(curr)
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000221 && contains(clip.getRect(curr), bounds)) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000222 break;
223 }
224 // if everything is initially clearToInside then intersect is
225 // same as clear to 0 and treat as a replace. Otherwise,
226 // set stays empty.
227 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000228 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000229 *clearToInside = false;
230 done = true;
231 }
232 break;
233 // we can skip a leading union.
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000234 case SkRegion::kUnion_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000235 // if everything is initially outside then union is
236 // same as replace. Otherwise, every pixel is still
237 // clearToInside
238 if (!*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000239 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000240 done = true;
241 }
242 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000243 case SkRegion::kXOR_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000244 // xor is same as difference or replace both of which
245 // can be 1-pass instead of 2 for xor.
246 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000247 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000248 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000249 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000250 }
251 done = true;
252 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000253 case SkRegion::kDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000254 // if all pixels are clearToInside then we have to process the
255 // difference, otherwise it has no effect and all pixels
256 // remain outside.
257 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000258 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000259 done = true;
260 }
261 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000262 case SkRegion::kReverseDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000263 // if all pixels are clearToInside then reverse difference
264 // produces empty set. Otherise it is same as replace
265 if (*clearToInside) {
266 *clearToInside = false;
267 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000268 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000269 done = true;
270 }
271 break;
272 default:
273 GrCrash("Unknown set op.");
274 }
275 }
276 return done ? curr-1 : count;
277}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000278
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000279}
280
robertphillips@google.comf294b772012-04-27 14:29:26 +0000281
282namespace {
283
284////////////////////////////////////////////////////////////////////////////////
285// set up the OpenGL blend function to perform the specified
286// boolean operation for alpha clip mask creation
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000287void setup_boolean_blendcoeffs(GrDrawState* drawState, SkRegion::Op op) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000288
289 switch (op) {
290 case SkRegion::kReplace_Op:
291 drawState->setBlendFunc(kOne_BlendCoeff, kZero_BlendCoeff);
292 break;
293 case SkRegion::kIntersect_Op:
294 drawState->setBlendFunc(kDC_BlendCoeff, kZero_BlendCoeff);
295 break;
296 case SkRegion::kUnion_Op:
297 drawState->setBlendFunc(kOne_BlendCoeff, kISC_BlendCoeff);
298 break;
299 case SkRegion::kXOR_Op:
300 drawState->setBlendFunc(kIDC_BlendCoeff, kISC_BlendCoeff);
301 break;
302 case SkRegion::kDifference_Op:
303 drawState->setBlendFunc(kZero_BlendCoeff, kISC_BlendCoeff);
304 break;
305 case SkRegion::kReverseDifference_Op:
306 drawState->setBlendFunc(kIDC_BlendCoeff, kZero_BlendCoeff);
307 break;
308 default:
309 GrAssert(false);
310 break;
311 }
312}
313
314}
315
316////////////////////////////////////////////////////////////////////////////////
317bool GrClipMaskManager::drawPath(GrGpu* gpu,
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000318 const SkPath& path,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000319 GrPathFill fill,
320 bool doAA) {
321
322 GrPathRenderer* pr = this->getClipPathRenderer(gpu, path, fill, doAA);
323 if (NULL == pr) {
324 return false;
325 }
326
327 pr->drawPath(path, fill, NULL, gpu, 0, doAA);
328 return true;
329}
330
331////////////////////////////////////////////////////////////////////////////////
332bool GrClipMaskManager::drawClipShape(GrGpu* gpu,
333 GrTexture* target,
334 const GrClip& clipIn,
335 int index) {
336 GrDrawState* drawState = gpu->drawState();
337 GrAssert(NULL != drawState);
338
339 drawState->setRenderTarget(target->asRenderTarget());
340
341 if (kRect_ClipType == clipIn.getElementType(index)) {
342 if (clipIn.getDoAA(index)) {
343 // convert the rect to a path for AA
344 SkPath temp;
345 temp.addRect(clipIn.getRect(index));
346
347 return this->drawPath(gpu, temp,
348 kEvenOdd_PathFill, clipIn.getDoAA(index));
349 } else {
350 gpu->drawSimpleRect(clipIn.getRect(index), NULL, 0);
351 }
352 } else {
353 return this->drawPath(gpu,
354 clipIn.getPath(index),
355 clipIn.getPathFill(index),
356 clipIn.getDoAA(index));
357 }
358 return true;
359}
360
361void GrClipMaskManager::drawTexture(GrGpu* gpu,
362 GrTexture* target,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000363 GrTexture* texture) {
364 GrDrawState* drawState = gpu->drawState();
365 GrAssert(NULL != drawState);
366
367 // no AA here since it is encoded in the texture
368 drawState->setRenderTarget(target->asRenderTarget());
369
370 GrMatrix sampleM;
371 sampleM.setIDiv(texture->width(), texture->height());
372 drawState->setTexture(0, texture);
373
374 drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
375 GrSamplerState::kNearest_Filter,
376 sampleM);
377
robertphillips@google.comf105b102012-05-14 12:18:26 +0000378 GrRect rect = GrRect::MakeWH(SkIntToScalar(target->width()),
379 SkIntToScalar(target->height()));
380
robertphillips@google.comf294b772012-04-27 14:29:26 +0000381 gpu->drawSimpleRect(rect, NULL, 1 << 0);
382
383 drawState->setTexture(0, NULL);
384}
385
386namespace {
387
388void clear(GrGpu* gpu,
389 GrTexture* target,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000390 GrColor color) {
391 GrDrawState* drawState = gpu->drawState();
392 GrAssert(NULL != drawState);
393
394 // zap entire target to specified color
395 drawState->setRenderTarget(target->asRenderTarget());
396 gpu->clear(NULL, color);
397}
398
robertphillips@google.comf105b102012-05-14 12:18:26 +0000399}
400
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000401// get a texture to act as a temporary buffer for AA clip boolean operations
402// TODO: given the expense of createTexture we may want to just cache this too
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000403void GrClipMaskManager::getTemp(const GrIRect& bounds,
robertphillips@google.comf105b102012-05-14 12:18:26 +0000404 GrAutoScratchTexture* temp) {
405 if (NULL != temp->texture()) {
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000406 // we've already allocated the temp texture
407 return;
408 }
409
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000410 const GrTextureDesc desc = {
411 kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000412 bounds.width(),
413 bounds.height(),
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000414 kAlpha_8_GrPixelConfig,
415 0 // samples
416 };
417
robertphillips@google.comf105b102012-05-14 12:18:26 +0000418 temp->set(fAACache.getContext(), desc);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000419}
420
robertphillips@google.comf105b102012-05-14 12:18:26 +0000421
422void GrClipMaskManager::setupCache(const GrClip& clipIn,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000423 const GrIRect& bounds) {
robertphillips@google.comf105b102012-05-14 12:18:26 +0000424 // Since we are setting up the cache we know the last lookup was a miss
425 // Free up the currently cached mask so it can be reused
426 fAACache.reset();
427
428 const GrTextureDesc desc = {
429 kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000430 bounds.width(),
431 bounds.height(),
robertphillips@google.comf105b102012-05-14 12:18:26 +0000432 kAlpha_8_GrPixelConfig,
433 0 // samples
434 };
435
436 fAACache.acquireMask(clipIn, desc, bounds);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000437}
438
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000439////////////////////////////////////////////////////////////////////////////////
440// Shared preamble between gpu and SW-only AA clip mask creation paths.
441// Handles caching, determination of clip mask bound & allocation (if needed)
442// of the result texture
443// Returns true if there is no more work to be done (i.e., we got a cache hit)
444bool GrClipMaskManager::clipMaskPreamble(GrGpu* gpu,
445 const GrClip& clipIn,
446 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000447 GrIRect *resultBounds) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000448 GrDrawState* origDrawState = gpu->drawState();
449 GrAssert(origDrawState->isClipState());
450
451 GrRenderTarget* rt = origDrawState->getRenderTarget();
452 GrAssert(NULL != rt);
453
454 GrRect rtRect;
455 rtRect.setLTRB(0, 0,
456 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
457
458 // unlike the stencil path the alpha path is not bound to the size of the
459 // render target - determine the minimum size required for the mask
460 GrRect bounds;
461
462 if (clipIn.hasConservativeBounds()) {
463 bounds = clipIn.getConservativeBounds();
464 if (!bounds.intersect(rtRect)) {
465 // the mask will be empty in this case
466 GrAssert(false);
467 bounds.setEmpty();
468 }
469 } else {
470 // still locked to the size of the render target
471 bounds = rtRect;
472 }
473
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000474 GrIRect intBounds;
475 bounds.roundOut(&intBounds);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000476
477 // need to outset a pixel since the standard bounding box computation
478 // path doesn't leave any room for antialiasing (esp. w.r.t. rects)
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000479 intBounds.outset(1, 1);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000480
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000481 // TODO: make sure we don't outset if bounds are still 0,0 @ min
482
robertphillips@google.com8fff3562012-05-11 12:53:50 +0000483 if (fAACache.canReuse(clipIn,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000484 intBounds.width(),
485 intBounds.height())) {
robertphillips@google.com8fff3562012-05-11 12:53:50 +0000486 *result = fAACache.getLastMask();
487 fAACache.getLastBound(resultBounds);
488 return true;
489 }
490
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000491 this->setupCache(clipIn, intBounds);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000492
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000493 *resultBounds = intBounds;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000494 return false;
495}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000496
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000497////////////////////////////////////////////////////////////////////////////////
498// Create a 8-bit clip mask in alpha
499bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu,
500 const GrClip& clipIn,
501 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000502 GrIRect *resultBounds) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000503
robertphillips@google.comf105b102012-05-14 12:18:26 +0000504 if (this->clipMaskPreamble(gpu, clipIn, result, resultBounds)) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000505 return true;
506 }
507
robertphillips@google.comf105b102012-05-14 12:18:26 +0000508 GrTexture* accum = fAACache.getLastMask();
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000509 if (NULL == accum) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000510 fClipMaskInAlpha = false;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000511 fAACache.reset();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000512 return false;
513 }
514
515 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
516 GrDrawState* drawState = gpu->drawState();
517
518 GrDrawTarget::AutoGeometryPush agp(gpu);
519
520 int count = clipIn.getElementCount();
521
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000522 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000523 // if we were able to trim down the size of the mask we need to
524 // offset the paths & rects that will be used to compute it
525 GrMatrix m;
526
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000527 m.setTranslate(SkIntToScalar(-resultBounds->fLeft),
528 SkIntToScalar(-resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000529
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000530 drawState->setViewMatrix(m);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000531 }
532
533 bool clearToInside;
534 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
535 int start = process_initial_clip_elements(clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000536 *resultBounds,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000537 &clearToInside,
538 &startOp);
539
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000540 clear(gpu, accum, clearToInside ? 0xffffffff : 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000541
robertphillips@google.comf105b102012-05-14 12:18:26 +0000542 GrAutoScratchTexture temp;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000543
robertphillips@google.comf294b772012-04-27 14:29:26 +0000544 // walk through each clip element and perform its set op
545 for (int c = start; c < count; ++c) {
546
547 SkRegion::Op op = (c == start) ? startOp : clipIn.getOp(c);
548
549 if (SkRegion::kReplace_Op == op) {
550 // TODO: replace is actually a lot faster then intersection
551 // for this path - refactor the stencil path so it can handle
552 // replace ops and alter GrClip to allow them through
553
554 // clear the accumulator and draw the new object directly into it
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000555 clear(gpu, accum, 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000556
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000557 setup_boolean_blendcoeffs(drawState, op);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000558 this->drawClipShape(gpu, accum, clipIn, c);
559
560 } else if (SkRegion::kReverseDifference_Op == op ||
561 SkRegion::kIntersect_Op == op) {
562 // there is no point in intersecting a screen filling rectangle.
563 if (SkRegion::kIntersect_Op == op &&
564 kRect_ClipType == clipIn.getElementType(c) &&
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000565 contains(clipIn.getRect(c), *resultBounds)) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000566 continue;
567 }
568
robertphillips@google.comf105b102012-05-14 12:18:26 +0000569 getTemp(*resultBounds, &temp);
570 if (NULL == temp.texture()) {
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000571 fClipMaskInAlpha = false;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000572 fAACache.reset();
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000573 return false;
574 }
575
robertphillips@google.comf294b772012-04-27 14:29:26 +0000576 // clear the temp target & draw into it
robertphillips@google.comf105b102012-05-14 12:18:26 +0000577 clear(gpu, temp.texture(), 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000578
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000579 setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000580 this->drawClipShape(gpu, temp.texture(), clipIn, c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000581
582 // TODO: rather than adding these two translations here
583 // compute the bounding box needed to render the texture
584 // into temp
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000585 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000586 GrMatrix m;
587
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000588 m.setTranslate(SkIntToScalar(resultBounds->fLeft),
589 SkIntToScalar(resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000590
591 drawState->preConcatViewMatrix(m);
592 }
593
594 // Now draw into the accumulator using the real operation
595 // and the temp buffer as a texture
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000596 setup_boolean_blendcoeffs(drawState, op);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000597 this->drawTexture(gpu, accum, temp.texture());
robertphillips@google.comf294b772012-04-27 14:29:26 +0000598
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000599 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000600 GrMatrix m;
601
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000602 m.setTranslate(SkIntToScalar(-resultBounds->fLeft),
603 SkIntToScalar(-resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000604
605 drawState->preConcatViewMatrix(m);
606 }
607
608 } else {
609 // all the remaining ops can just be directly draw into
610 // the accumulation buffer
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000611 setup_boolean_blendcoeffs(drawState, op);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000612 this->drawClipShape(gpu, accum, clipIn, c);
613 }
614 }
615
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000616 *result = accum;
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000617
robertphillips@google.comf294b772012-04-27 14:29:26 +0000618 return true;
619}
620
621////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000622// Create a 1-bit clip mask in the stencil buffer
623bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
624 const GrClip& clipIn,
625 const GrRect& bounds,
626 ScissoringSettings* scissorSettings) {
627
628 GrAssert(fClipMaskInStencil);
629
630 GrDrawState* drawState = gpu->drawState();
631 GrAssert(drawState->isClipState());
632
633 GrRenderTarget* rt = drawState->getRenderTarget();
634 GrAssert(NULL != rt);
635
636 // TODO: dynamically attach a SB when needed.
637 GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
638 if (NULL == stencilBuffer) {
639 return false;
640 }
641
642 if (stencilBuffer->mustRenderClip(clipIn, rt->width(), rt->height())) {
643
644 stencilBuffer->setLastClip(clipIn, rt->width(), rt->height());
645
646 // we set the current clip to the bounds so that our recursive
647 // draws are scissored to them. We use the copy of the complex clip
648 // we just stashed on the SB to render from. We set it back after
649 // we finish drawing it into the stencil.
650 const GrClip& clipCopy = stencilBuffer->getLastClip();
651 gpu->setClip(GrClip(bounds));
652
653 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
654 drawState = gpu->drawState();
655 drawState->setRenderTarget(rt);
656 GrDrawTarget::AutoGeometryPush agp(gpu);
657
658 gpu->disableScissor();
659#if !VISUALIZE_COMPLEX_CLIP
660 drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
661#endif
662
663 int count = clipCopy.getElementCount();
664 int clipBit = stencilBuffer->bits();
665 SkASSERT((clipBit <= 16) &&
666 "Ganesh only handles 16b or smaller stencil buffers");
667 clipBit = (1 << (clipBit-1));
668
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000669 GrIRect rtRect = GrIRect::MakeWH(rt->width(), rt->height());
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000670
671 bool clearToInside;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000672 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000673 int start = process_initial_clip_elements(clipCopy,
674 rtRect,
675 &clearToInside,
676 &startOp);
677
678 gpu->clearStencilClip(scissorSettings->fScissorRect, clearToInside);
679
680 // walk through each clip element and perform its set op
681 // with the existing clip.
682 for (int c = start; c < count; ++c) {
683 GrPathFill fill;
684 bool fillInverted;
685 // enabled at bottom of loop
686 drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
687
688 bool canRenderDirectToStencil; // can the clip element be drawn
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000689 // directly to the stencil buffer
690 // with a non-inverted fill rule
691 // without extra passes to
692 // resolve in/out status.
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000693
robertphillips@google.comf294b772012-04-27 14:29:26 +0000694 SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
695
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000696 GrPathRenderer* pr = NULL;
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000697 const SkPath* clipPath = NULL;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000698 if (kRect_ClipType == clipCopy.getElementType(c)) {
699 canRenderDirectToStencil = true;
700 fill = kEvenOdd_PathFill;
701 fillInverted = false;
702 // there is no point in intersecting a screen filling
703 // rectangle.
robertphillips@google.comf294b772012-04-27 14:29:26 +0000704 if (SkRegion::kIntersect_Op == op &&
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000705 contains(clipCopy.getRect(c), rtRect)) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000706 continue;
707 }
708 } else {
709 fill = clipCopy.getPathFill(c);
710 fillInverted = GrIsFillInverted(fill);
711 fill = GrNonInvertedFill(fill);
712 clipPath = &clipCopy.getPath(c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000713 pr = this->getClipPathRenderer(gpu, *clipPath, fill, false);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000714 if (NULL == pr) {
715 fClipMaskInStencil = false;
716 gpu->setClip(clipCopy); // restore to the original
717 return false;
718 }
719 canRenderDirectToStencil =
720 !pr->requiresStencilPass(*clipPath, fill, gpu);
721 }
722
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000723 int passes;
724 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
725
726 bool canDrawDirectToClip; // Given the renderer, the element,
727 // fill rule, and set operation can
728 // we render the element directly to
729 // stencil bit used for clipping.
730 canDrawDirectToClip =
731 GrStencilSettings::GetClipPasses(op,
732 canRenderDirectToStencil,
733 clipBit,
734 fillInverted,
735 &passes, stencilSettings);
736
737 // draw the element to the client stencil bits if necessary
738 if (!canDrawDirectToClip) {
739 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
740 kIncClamp_StencilOp,
741 kIncClamp_StencilOp,
742 kAlways_StencilFunc,
743 0xffff,
744 0x0000,
745 0xffff);
746 SET_RANDOM_COLOR
747 if (kRect_ClipType == clipCopy.getElementType(c)) {
748 *drawState->stencil() = gDrawToStencil;
749 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
750 } else {
751 if (canRenderDirectToStencil) {
752 *drawState->stencil() = gDrawToStencil;
753 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
754 } else {
755 pr->drawPathToStencil(*clipPath, fill, gpu);
756 }
757 }
758 }
759
760 // now we modify the clip bit by rendering either the clip
761 // element directly or a bounding rect of the entire clip.
762 drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
763 for (int p = 0; p < passes; ++p) {
764 *drawState->stencil() = stencilSettings[p];
765 if (canDrawDirectToClip) {
766 if (kRect_ClipType == clipCopy.getElementType(c)) {
767 SET_RANDOM_COLOR
768 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
769 } else {
770 SET_RANDOM_COLOR
771 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
772 }
773 } else {
774 SET_RANDOM_COLOR
775 gpu->drawSimpleRect(bounds, NULL, 0);
776 }
777 }
778 }
779 // restore clip
780 gpu->setClip(clipCopy);
781 // recusive draws would have disabled this since they drew with
782 // the clip bounds as clip.
783 fClipMaskInStencil = true;
784 }
785
786 return true;
787}
788
robertphillips@google.comf294b772012-04-27 14:29:26 +0000789////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000790bool GrClipMaskManager::createSoftwareClipMask(GrGpu* gpu,
791 const GrClip& clipIn,
792 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000793 GrIRect *resultBounds) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000794
robertphillips@google.comf105b102012-05-14 12:18:26 +0000795 if (this->clipMaskPreamble(gpu, clipIn, result, resultBounds)) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000796 return true;
797 }
798
robertphillips@google.comf105b102012-05-14 12:18:26 +0000799 GrTexture* accum = fAACache.getLastMask();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000800 if (NULL == accum) {
801 fClipMaskInAlpha = false;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000802 fAACache.reset();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000803 return false;
804 }
805
806#if 0
807 SkRasterClip rasterClip;
808
809 // TODO: refactor GrClip out of existance and use SkCanvas's ClipVisitor
810 // - may have to move it to SkClipStack
811 for (int i = 0; i < clipIn.getElementCount(); ++i) {
812 if (kRect_ClipType == clipIn.getElementType(i)) {
813 rasterClip.op(clipIn.getRect(i), clipIn.getOp(i), clipIn.getDoAA(i));
814 } else {
815 GrAssert(kPath_ClipType == clipIn.getElementType(i));
816
817 SkIPoint deviceSize = SkIPoint::Make(resultBounds->width(),
818 resultBounds->height());
819
820 SkRasterClip::clipPathHelper(&rasterClip,
821 clipIn.getPath(i),
822 clipIn.getOp(i),
823 clipIn.getDoAA(i),
824 deviceSize);
825 }
826 }
827
828 // TODO: need to get pixels out of SkRasterClip & into the texture!
829#endif
830
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000831 *result = accum;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000832
833 return true;
834}
835
836
837////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000838GrPathRenderer* GrClipMaskManager::getClipPathRenderer(GrGpu* gpu,
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000839 const SkPath& path,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000840 GrPathFill fill,
841 bool antiAlias) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000842 if (NULL == fPathRendererChain) {
843 fPathRendererChain =
844 new GrPathRendererChain(gpu->getContext(),
robertphillips@google.comf294b772012-04-27 14:29:26 +0000845 GrPathRendererChain::kNone_UsageFlag);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000846 }
robertphillips@google.comf294b772012-04-27 14:29:26 +0000847 return fPathRendererChain->getPathRenderer(path, fill, gpu, antiAlias);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000848}
849
robertphillips@google.comf294b772012-04-27 14:29:26 +0000850////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comf105b102012-05-14 12:18:26 +0000851void GrClipMaskManager::releaseResources() {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000852 // in case path renderer has any GrResources, start from scratch
853 GrSafeSetNull(fPathRendererChain);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000854 fAACache.releaseResources();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000855}