blob: 15aae1e2dc173e35785505a48991822fbba3d33d [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.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.com6623fcd2012-05-15 16:47:23 +0000168/**
169 * Does "container" contain "containee"? If either is empty then
170 * no containment is possible.
171 */
172bool contains(const SkRect& container, const SkIRect& containee) {
173 return !containee.isEmpty() && !container.isEmpty() &&
174 container.fLeft <= SkIntToScalar(containee.fLeft) &&
175 container.fTop <= SkIntToScalar(containee.fTop) &&
176 container.fRight >= SkIntToScalar(containee.fRight) &&
177 container.fBottom >= SkIntToScalar(containee.fBottom);
178}
179
180
robertphillips@google.comf294b772012-04-27 14:29:26 +0000181////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000182// determines how many elements at the head of the clip can be skipped and
183// whether the initial clear should be to the inside- or outside-the-clip value,
184// and what op should be used to draw the first element that isn't skipped.
185int process_initial_clip_elements(const GrClip& clip,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000186 const GrIRect& bounds,
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000187 bool* clearToInside,
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000188 SkRegion::Op* startOp) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000189
190 // logically before the first element of the clip stack is
191 // processed the clip is entirely open. However, depending on the
192 // first set op we may prefer to clear to 0 for performance. We may
193 // also be able to skip the initial clip paths/rects. We loop until
194 // we cannot skip an element.
195 int curr;
196 bool done = false;
197 *clearToInside = true;
198 int count = clip.getElementCount();
199
200 for (curr = 0; curr < count && !done; ++curr) {
201 switch (clip.getOp(curr)) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000202 case SkRegion::kReplace_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000203 // replace ignores everything previous
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000204 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000205 *clearToInside = false;
206 done = true;
207 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000208 case SkRegion::kIntersect_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000209 // if this element contains the entire bounds then we
210 // can skip it.
211 if (kRect_ClipType == clip.getElementType(curr)
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000212 && contains(clip.getRect(curr), bounds)) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000213 break;
214 }
215 // if everything is initially clearToInside then intersect is
216 // same as clear to 0 and treat as a replace. Otherwise,
217 // set stays empty.
218 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000219 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000220 *clearToInside = false;
221 done = true;
222 }
223 break;
224 // we can skip a leading union.
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000225 case SkRegion::kUnion_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000226 // if everything is initially outside then union is
227 // same as replace. Otherwise, every pixel is still
228 // clearToInside
229 if (!*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000230 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000231 done = true;
232 }
233 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000234 case SkRegion::kXOR_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000235 // xor is same as difference or replace both of which
236 // can be 1-pass instead of 2 for xor.
237 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000238 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000239 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000240 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000241 }
242 done = true;
243 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000244 case SkRegion::kDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000245 // if all pixels are clearToInside then we have to process the
246 // difference, otherwise it has no effect and all pixels
247 // remain outside.
248 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000249 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000250 done = true;
251 }
252 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000253 case SkRegion::kReverseDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000254 // if all pixels are clearToInside then reverse difference
255 // produces empty set. Otherise it is same as replace
256 if (*clearToInside) {
257 *clearToInside = false;
258 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000259 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000260 done = true;
261 }
262 break;
263 default:
264 GrCrash("Unknown set op.");
265 }
266 }
267 return done ? curr-1 : count;
268}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000269
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000270}
271
robertphillips@google.comf294b772012-04-27 14:29:26 +0000272
273namespace {
274
275////////////////////////////////////////////////////////////////////////////////
276// set up the OpenGL blend function to perform the specified
277// boolean operation for alpha clip mask creation
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000278void setup_boolean_blendcoeffs(GrDrawState* drawState, SkRegion::Op op) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000279
280 switch (op) {
281 case SkRegion::kReplace_Op:
282 drawState->setBlendFunc(kOne_BlendCoeff, kZero_BlendCoeff);
283 break;
284 case SkRegion::kIntersect_Op:
285 drawState->setBlendFunc(kDC_BlendCoeff, kZero_BlendCoeff);
286 break;
287 case SkRegion::kUnion_Op:
288 drawState->setBlendFunc(kOne_BlendCoeff, kISC_BlendCoeff);
289 break;
290 case SkRegion::kXOR_Op:
291 drawState->setBlendFunc(kIDC_BlendCoeff, kISC_BlendCoeff);
292 break;
293 case SkRegion::kDifference_Op:
294 drawState->setBlendFunc(kZero_BlendCoeff, kISC_BlendCoeff);
295 break;
296 case SkRegion::kReverseDifference_Op:
297 drawState->setBlendFunc(kIDC_BlendCoeff, kZero_BlendCoeff);
298 break;
299 default:
300 GrAssert(false);
301 break;
302 }
303}
304
305}
306
307////////////////////////////////////////////////////////////////////////////////
308bool GrClipMaskManager::drawPath(GrGpu* gpu,
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000309 const SkPath& path,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000310 GrPathFill fill,
311 bool doAA) {
312
313 GrPathRenderer* pr = this->getClipPathRenderer(gpu, path, fill, doAA);
314 if (NULL == pr) {
315 return false;
316 }
317
318 pr->drawPath(path, fill, NULL, gpu, 0, doAA);
319 return true;
320}
321
322////////////////////////////////////////////////////////////////////////////////
323bool GrClipMaskManager::drawClipShape(GrGpu* gpu,
324 GrTexture* target,
325 const GrClip& clipIn,
326 int index) {
327 GrDrawState* drawState = gpu->drawState();
328 GrAssert(NULL != drawState);
329
330 drawState->setRenderTarget(target->asRenderTarget());
331
332 if (kRect_ClipType == clipIn.getElementType(index)) {
333 if (clipIn.getDoAA(index)) {
334 // convert the rect to a path for AA
335 SkPath temp;
336 temp.addRect(clipIn.getRect(index));
337
338 return this->drawPath(gpu, temp,
339 kEvenOdd_PathFill, clipIn.getDoAA(index));
340 } else {
341 gpu->drawSimpleRect(clipIn.getRect(index), NULL, 0);
342 }
343 } else {
344 return this->drawPath(gpu,
345 clipIn.getPath(index),
346 clipIn.getPathFill(index),
347 clipIn.getDoAA(index));
348 }
349 return true;
350}
351
352void GrClipMaskManager::drawTexture(GrGpu* gpu,
353 GrTexture* target,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000354 GrTexture* texture) {
355 GrDrawState* drawState = gpu->drawState();
356 GrAssert(NULL != drawState);
357
358 // no AA here since it is encoded in the texture
359 drawState->setRenderTarget(target->asRenderTarget());
360
361 GrMatrix sampleM;
362 sampleM.setIDiv(texture->width(), texture->height());
363 drawState->setTexture(0, texture);
364
365 drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
366 GrSamplerState::kNearest_Filter,
367 sampleM);
368
robertphillips@google.comf105b102012-05-14 12:18:26 +0000369 GrRect rect = GrRect::MakeWH(SkIntToScalar(target->width()),
370 SkIntToScalar(target->height()));
371
robertphillips@google.comf294b772012-04-27 14:29:26 +0000372 gpu->drawSimpleRect(rect, NULL, 1 << 0);
373
374 drawState->setTexture(0, NULL);
375}
376
377namespace {
378
379void clear(GrGpu* gpu,
380 GrTexture* target,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000381 GrColor color) {
382 GrDrawState* drawState = gpu->drawState();
383 GrAssert(NULL != drawState);
384
385 // zap entire target to specified color
386 drawState->setRenderTarget(target->asRenderTarget());
387 gpu->clear(NULL, color);
388}
389
robertphillips@google.comf105b102012-05-14 12:18:26 +0000390}
391
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000392// get a texture to act as a temporary buffer for AA clip boolean operations
393// TODO: given the expense of createTexture we may want to just cache this too
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000394void GrClipMaskManager::getTemp(const GrIRect& bounds,
robertphillips@google.comf105b102012-05-14 12:18:26 +0000395 GrAutoScratchTexture* temp) {
396 if (NULL != temp->texture()) {
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000397 // we've already allocated the temp texture
398 return;
399 }
400
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000401 const GrTextureDesc desc = {
402 kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000403 bounds.width(),
404 bounds.height(),
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000405 kAlpha_8_GrPixelConfig,
406 0 // samples
407 };
408
robertphillips@google.comf105b102012-05-14 12:18:26 +0000409 temp->set(fAACache.getContext(), desc);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000410}
411
robertphillips@google.comf105b102012-05-14 12:18:26 +0000412
413void GrClipMaskManager::setupCache(const GrClip& clipIn,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000414 const GrIRect& bounds) {
robertphillips@google.comf105b102012-05-14 12:18:26 +0000415 // Since we are setting up the cache we know the last lookup was a miss
416 // Free up the currently cached mask so it can be reused
417 fAACache.reset();
418
419 const GrTextureDesc desc = {
420 kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000421 bounds.width(),
422 bounds.height(),
robertphillips@google.comf105b102012-05-14 12:18:26 +0000423 kAlpha_8_GrPixelConfig,
424 0 // samples
425 };
426
427 fAACache.acquireMask(clipIn, desc, bounds);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000428}
429
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000430////////////////////////////////////////////////////////////////////////////////
431// Shared preamble between gpu and SW-only AA clip mask creation paths.
432// Handles caching, determination of clip mask bound & allocation (if needed)
433// of the result texture
434// Returns true if there is no more work to be done (i.e., we got a cache hit)
435bool GrClipMaskManager::clipMaskPreamble(GrGpu* gpu,
436 const GrClip& clipIn,
437 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000438 GrIRect *resultBounds) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000439 GrDrawState* origDrawState = gpu->drawState();
440 GrAssert(origDrawState->isClipState());
441
442 GrRenderTarget* rt = origDrawState->getRenderTarget();
443 GrAssert(NULL != rt);
444
445 GrRect rtRect;
446 rtRect.setLTRB(0, 0,
447 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
448
449 // unlike the stencil path the alpha path is not bound to the size of the
450 // render target - determine the minimum size required for the mask
451 GrRect bounds;
452
453 if (clipIn.hasConservativeBounds()) {
454 bounds = clipIn.getConservativeBounds();
455 if (!bounds.intersect(rtRect)) {
456 // the mask will be empty in this case
457 GrAssert(false);
458 bounds.setEmpty();
459 }
460 } else {
461 // still locked to the size of the render target
462 bounds = rtRect;
463 }
464
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000465 GrIRect intBounds;
466 bounds.roundOut(&intBounds);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000467
468 // need to outset a pixel since the standard bounding box computation
469 // path doesn't leave any room for antialiasing (esp. w.r.t. rects)
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000470 intBounds.outset(1, 1);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000471
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000472 // TODO: make sure we don't outset if bounds are still 0,0 @ min
473
robertphillips@google.com8fff3562012-05-11 12:53:50 +0000474 if (fAACache.canReuse(clipIn,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000475 intBounds.width(),
476 intBounds.height())) {
robertphillips@google.com8fff3562012-05-11 12:53:50 +0000477 *result = fAACache.getLastMask();
478 fAACache.getLastBound(resultBounds);
479 return true;
480 }
481
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000482 this->setupCache(clipIn, intBounds);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000483
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000484 *resultBounds = intBounds;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000485 return false;
486}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000487
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000488////////////////////////////////////////////////////////////////////////////////
489// Create a 8-bit clip mask in alpha
490bool GrClipMaskManager::createAlphaClipMask(GrGpu* gpu,
491 const GrClip& clipIn,
492 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000493 GrIRect *resultBounds) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000494
robertphillips@google.comf105b102012-05-14 12:18:26 +0000495 if (this->clipMaskPreamble(gpu, clipIn, result, resultBounds)) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000496 return true;
497 }
498
robertphillips@google.comf105b102012-05-14 12:18:26 +0000499 GrTexture* accum = fAACache.getLastMask();
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000500 if (NULL == accum) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000501 fClipMaskInAlpha = false;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000502 fAACache.reset();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000503 return false;
504 }
505
506 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
507 GrDrawState* drawState = gpu->drawState();
508
509 GrDrawTarget::AutoGeometryPush agp(gpu);
510
511 int count = clipIn.getElementCount();
512
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000513 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000514 // if we were able to trim down the size of the mask we need to
515 // offset the paths & rects that will be used to compute it
516 GrMatrix m;
517
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000518 m.setTranslate(SkIntToScalar(-resultBounds->fLeft),
519 SkIntToScalar(-resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000520
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000521 drawState->setViewMatrix(m);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000522 }
523
524 bool clearToInside;
525 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
526 int start = process_initial_clip_elements(clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000527 *resultBounds,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000528 &clearToInside,
529 &startOp);
530
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000531 clear(gpu, accum, clearToInside ? 0xffffffff : 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000532
robertphillips@google.comf105b102012-05-14 12:18:26 +0000533 GrAutoScratchTexture temp;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000534
robertphillips@google.comf294b772012-04-27 14:29:26 +0000535 // walk through each clip element and perform its set op
536 for (int c = start; c < count; ++c) {
537
538 SkRegion::Op op = (c == start) ? startOp : clipIn.getOp(c);
539
540 if (SkRegion::kReplace_Op == op) {
541 // TODO: replace is actually a lot faster then intersection
542 // for this path - refactor the stencil path so it can handle
543 // replace ops and alter GrClip to allow them through
544
545 // clear the accumulator and draw the new object directly into it
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000546 clear(gpu, accum, 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000547
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000548 setup_boolean_blendcoeffs(drawState, op);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000549 this->drawClipShape(gpu, accum, clipIn, c);
550
551 } else if (SkRegion::kReverseDifference_Op == op ||
552 SkRegion::kIntersect_Op == op) {
553 // there is no point in intersecting a screen filling rectangle.
554 if (SkRegion::kIntersect_Op == op &&
555 kRect_ClipType == clipIn.getElementType(c) &&
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000556 contains(clipIn.getRect(c), *resultBounds)) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000557 continue;
558 }
559
robertphillips@google.comf105b102012-05-14 12:18:26 +0000560 getTemp(*resultBounds, &temp);
561 if (NULL == temp.texture()) {
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000562 fClipMaskInAlpha = false;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000563 fAACache.reset();
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000564 return false;
565 }
566
robertphillips@google.comf294b772012-04-27 14:29:26 +0000567 // clear the temp target & draw into it
robertphillips@google.comf105b102012-05-14 12:18:26 +0000568 clear(gpu, temp.texture(), 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000569
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000570 setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000571 this->drawClipShape(gpu, temp.texture(), clipIn, c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000572
573 // TODO: rather than adding these two translations here
574 // compute the bounding box needed to render the texture
575 // into temp
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000576 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000577 GrMatrix m;
578
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000579 m.setTranslate(SkIntToScalar(resultBounds->fLeft),
580 SkIntToScalar(resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000581
582 drawState->preConcatViewMatrix(m);
583 }
584
585 // Now draw into the accumulator using the real operation
586 // and the temp buffer as a texture
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000587 setup_boolean_blendcoeffs(drawState, op);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000588 this->drawTexture(gpu, accum, temp.texture());
robertphillips@google.comf294b772012-04-27 14:29:26 +0000589
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000590 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000591 GrMatrix m;
592
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000593 m.setTranslate(SkIntToScalar(-resultBounds->fLeft),
594 SkIntToScalar(-resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000595
596 drawState->preConcatViewMatrix(m);
597 }
598
599 } else {
600 // all the remaining ops can just be directly draw into
601 // the accumulation buffer
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000602 setup_boolean_blendcoeffs(drawState, op);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000603 this->drawClipShape(gpu, accum, clipIn, c);
604 }
605 }
606
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000607 *result = accum;
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000608
robertphillips@google.comf294b772012-04-27 14:29:26 +0000609 return true;
610}
611
612////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000613// Create a 1-bit clip mask in the stencil buffer
614bool GrClipMaskManager::createStencilClipMask(GrGpu* gpu,
615 const GrClip& clipIn,
616 const GrRect& bounds,
617 ScissoringSettings* scissorSettings) {
618
619 GrAssert(fClipMaskInStencil);
620
621 GrDrawState* drawState = gpu->drawState();
622 GrAssert(drawState->isClipState());
623
624 GrRenderTarget* rt = drawState->getRenderTarget();
625 GrAssert(NULL != rt);
626
627 // TODO: dynamically attach a SB when needed.
628 GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
629 if (NULL == stencilBuffer) {
630 return false;
631 }
632
633 if (stencilBuffer->mustRenderClip(clipIn, rt->width(), rt->height())) {
634
635 stencilBuffer->setLastClip(clipIn, rt->width(), rt->height());
636
637 // we set the current clip to the bounds so that our recursive
638 // draws are scissored to them. We use the copy of the complex clip
639 // we just stashed on the SB to render from. We set it back after
640 // we finish drawing it into the stencil.
641 const GrClip& clipCopy = stencilBuffer->getLastClip();
642 gpu->setClip(GrClip(bounds));
643
644 GrDrawTarget::AutoStateRestore asr(gpu, GrDrawTarget::kReset_ASRInit);
645 drawState = gpu->drawState();
646 drawState->setRenderTarget(rt);
647 GrDrawTarget::AutoGeometryPush agp(gpu);
648
649 gpu->disableScissor();
650#if !VISUALIZE_COMPLEX_CLIP
651 drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
652#endif
653
654 int count = clipCopy.getElementCount();
655 int clipBit = stencilBuffer->bits();
656 SkASSERT((clipBit <= 16) &&
657 "Ganesh only handles 16b or smaller stencil buffers");
658 clipBit = (1 << (clipBit-1));
659
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000660 GrIRect rtRect = GrIRect::MakeWH(rt->width(), rt->height());
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000661
662 bool clearToInside;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000663 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000664 int start = process_initial_clip_elements(clipCopy,
665 rtRect,
666 &clearToInside,
667 &startOp);
668
669 gpu->clearStencilClip(scissorSettings->fScissorRect, clearToInside);
670
671 // walk through each clip element and perform its set op
672 // with the existing clip.
673 for (int c = start; c < count; ++c) {
674 GrPathFill fill;
675 bool fillInverted;
676 // enabled at bottom of loop
677 drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
678
679 bool canRenderDirectToStencil; // can the clip element be drawn
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000680 // directly to the stencil buffer
681 // with a non-inverted fill rule
682 // without extra passes to
683 // resolve in/out status.
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000684
robertphillips@google.comf294b772012-04-27 14:29:26 +0000685 SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
686
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000687 GrPathRenderer* pr = NULL;
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000688 const SkPath* clipPath = NULL;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000689 if (kRect_ClipType == clipCopy.getElementType(c)) {
690 canRenderDirectToStencil = true;
691 fill = kEvenOdd_PathFill;
692 fillInverted = false;
693 // there is no point in intersecting a screen filling
694 // rectangle.
robertphillips@google.comf294b772012-04-27 14:29:26 +0000695 if (SkRegion::kIntersect_Op == op &&
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000696 contains(clipCopy.getRect(c), rtRect)) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000697 continue;
698 }
699 } else {
700 fill = clipCopy.getPathFill(c);
701 fillInverted = GrIsFillInverted(fill);
702 fill = GrNonInvertedFill(fill);
703 clipPath = &clipCopy.getPath(c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000704 pr = this->getClipPathRenderer(gpu, *clipPath, fill, false);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000705 if (NULL == pr) {
706 fClipMaskInStencil = false;
707 gpu->setClip(clipCopy); // restore to the original
708 return false;
709 }
710 canRenderDirectToStencil =
711 !pr->requiresStencilPass(*clipPath, fill, gpu);
712 }
713
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000714 int passes;
715 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
716
717 bool canDrawDirectToClip; // Given the renderer, the element,
718 // fill rule, and set operation can
719 // we render the element directly to
720 // stencil bit used for clipping.
721 canDrawDirectToClip =
722 GrStencilSettings::GetClipPasses(op,
723 canRenderDirectToStencil,
724 clipBit,
725 fillInverted,
726 &passes, stencilSettings);
727
728 // draw the element to the client stencil bits if necessary
729 if (!canDrawDirectToClip) {
730 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
731 kIncClamp_StencilOp,
732 kIncClamp_StencilOp,
733 kAlways_StencilFunc,
734 0xffff,
735 0x0000,
736 0xffff);
737 SET_RANDOM_COLOR
738 if (kRect_ClipType == clipCopy.getElementType(c)) {
739 *drawState->stencil() = gDrawToStencil;
740 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
741 } else {
742 if (canRenderDirectToStencil) {
743 *drawState->stencil() = gDrawToStencil;
744 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
745 } else {
746 pr->drawPathToStencil(*clipPath, fill, gpu);
747 }
748 }
749 }
750
751 // now we modify the clip bit by rendering either the clip
752 // element directly or a bounding rect of the entire clip.
753 drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
754 for (int p = 0; p < passes; ++p) {
755 *drawState->stencil() = stencilSettings[p];
756 if (canDrawDirectToClip) {
757 if (kRect_ClipType == clipCopy.getElementType(c)) {
758 SET_RANDOM_COLOR
759 gpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
760 } else {
761 SET_RANDOM_COLOR
762 pr->drawPath(*clipPath, fill, NULL, gpu, 0, false);
763 }
764 } else {
765 SET_RANDOM_COLOR
766 gpu->drawSimpleRect(bounds, NULL, 0);
767 }
768 }
769 }
770 // restore clip
771 gpu->setClip(clipCopy);
772 // recusive draws would have disabled this since they drew with
773 // the clip bounds as clip.
774 fClipMaskInStencil = true;
775 }
776
777 return true;
778}
779
robertphillips@google.comf294b772012-04-27 14:29:26 +0000780////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000781bool GrClipMaskManager::createSoftwareClipMask(GrGpu* gpu,
782 const GrClip& clipIn,
783 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000784 GrIRect *resultBounds) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000785
robertphillips@google.comf105b102012-05-14 12:18:26 +0000786 if (this->clipMaskPreamble(gpu, clipIn, result, resultBounds)) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000787 return true;
788 }
789
robertphillips@google.comf105b102012-05-14 12:18:26 +0000790 GrTexture* accum = fAACache.getLastMask();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000791 if (NULL == accum) {
792 fClipMaskInAlpha = false;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000793 fAACache.reset();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000794 return false;
795 }
796
797#if 0
798 SkRasterClip rasterClip;
799
800 // TODO: refactor GrClip out of existance and use SkCanvas's ClipVisitor
801 // - may have to move it to SkClipStack
802 for (int i = 0; i < clipIn.getElementCount(); ++i) {
803 if (kRect_ClipType == clipIn.getElementType(i)) {
804 rasterClip.op(clipIn.getRect(i), clipIn.getOp(i), clipIn.getDoAA(i));
805 } else {
806 GrAssert(kPath_ClipType == clipIn.getElementType(i));
807
808 SkIPoint deviceSize = SkIPoint::Make(resultBounds->width(),
809 resultBounds->height());
810
811 SkRasterClip::clipPathHelper(&rasterClip,
812 clipIn.getPath(i),
813 clipIn.getOp(i),
814 clipIn.getDoAA(i),
815 deviceSize);
816 }
817 }
818
819 // TODO: need to get pixels out of SkRasterClip & into the texture!
820#endif
821
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000822 *result = accum;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000823
824 return true;
825}
826
827
828////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000829GrPathRenderer* GrClipMaskManager::getClipPathRenderer(GrGpu* gpu,
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000830 const SkPath& path,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000831 GrPathFill fill,
832 bool antiAlias) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000833 if (NULL == fPathRendererChain) {
834 fPathRendererChain =
835 new GrPathRendererChain(gpu->getContext(),
robertphillips@google.comf294b772012-04-27 14:29:26 +0000836 GrPathRendererChain::kNone_UsageFlag);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000837 }
robertphillips@google.comf294b772012-04-27 14:29:26 +0000838 return fPathRendererChain->getPathRenderer(path, fill, gpu, antiAlias);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000839}
840
robertphillips@google.comf294b772012-04-27 14:29:26 +0000841////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comf105b102012-05-14 12:18:26 +0000842void GrClipMaskManager::releaseResources() {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000843 // in case path renderer has any GrResources, start from scratch
844 GrSafeSetNull(fPathRendererChain);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000845 fAACache.releaseResources();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000846}