blob: 6b8074329d5bcc64bfe5f72486029f599a0fb438 [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.comfa662942012-05-17 12:20:22 +000016#include "GrAAConvexPathRenderer.h"
17#include "GrAAHairLinePathRenderer.h"
18
19// TODO: move GrSWMaskHelper out of GrSoftwarePathRender.h & remove this include
20#include "GrSoftwarePathRenderer.h"
robertphillips@google.coma72eef32012-05-01 17:22:59 +000021
22//#define GR_AA_CLIP 1
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +000023//#define GR_SW_CLIP 1
robertphillips@google.coma72eef32012-05-01 17:22:59 +000024
robertphillips@google.comf294b772012-04-27 14:29:26 +000025////////////////////////////////////////////////////////////////////////////////
robertphillips@google.coma72eef32012-05-01 17:22:59 +000026namespace {
27// set up the draw state to enable the aa clipping mask. Besides setting up the
28// sampler matrix this also alters the vertex layout
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +000029void setup_drawstate_aaclip(GrGpu* gpu,
30 GrTexture* result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +000031 const GrIRect &bound) {
robertphillips@google.coma72eef32012-05-01 17:22:59 +000032 GrDrawState* drawState = gpu->drawState();
33 GrAssert(drawState);
34
35 static const int maskStage = GrPaint::kTotalStages+1;
36
37 GrMatrix mat;
38 mat.setIDiv(result->width(), result->height());
robertphillips@google.com6623fcd2012-05-15 16:47:23 +000039 mat.preTranslate(SkIntToScalar(-bound.fLeft), SkIntToScalar(-bound.fTop));
robertphillips@google.coma72eef32012-05-01 17:22:59 +000040 mat.preConcat(drawState->getViewMatrix());
41
42 drawState->sampler(maskStage)->reset(GrSamplerState::kClamp_WrapMode,
43 GrSamplerState::kNearest_Filter,
44 mat);
45
46 drawState->setTexture(maskStage, result);
47
48 // The AA clipping determination happens long after the geometry has
49 // been set up to draw. Here we directly enable the AA clip mask stage
50 gpu->addToVertexLayout(
51 GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(maskStage));
52}
53
robertphillips@google.com8a4fc402012-05-24 12:42:24 +000054bool path_needs_SW_renderer(GrContext* context,
bsalomon@google.com13b85aa2012-06-15 21:09:40 +000055 GrGpu* gpu,
56 const SkPath& path,
57 GrPathFill fill,
58 bool doAA) {
robertphillips@google.com8a4fc402012-05-24 12:42:24 +000059 // last (false) parameter disallows use of the SW path renderer
60 return NULL == context->getPathRenderer(path, fill, gpu, doAA, false);
61}
62
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +000063}
64
robertphillips@google.comfa662942012-05-17 12:20:22 +000065/*
66 * This method traverses the clip stack to see if the GrSoftwarePathRenderer
67 * will be used on any element. If so, it returns true to indicate that the
68 * entire clip should be rendered in SW and then uploaded en masse to the gpu.
69 */
bsalomon@google.com13b85aa2012-06-15 21:09:40 +000070bool GrClipMaskManager::useSWOnlyPath(const GrClip& clipIn) {
robertphillips@google.coma3e5c632012-05-22 18:09:26 +000071
72 if (!clipIn.requiresAA()) {
73 // The stencil buffer can handle this case
74 return false;
75 }
robertphillips@google.comfa662942012-05-17 12:20:22 +000076
robertphillips@google.com8a4fc402012-05-24 12:42:24 +000077 // TODO: generalize this function so that when
robertphillips@google.comfa662942012-05-17 12:20:22 +000078 // a clip gets complex enough it can just be done in SW regardless
79 // of whether it would invoke the GrSoftwarePathRenderer.
80 bool useSW = false;
81
82 for (int i = 0; i < clipIn.getElementCount(); ++i) {
83
84 if (SkRegion::kReplace_Op == clipIn.getOp(i)) {
85 // Everything before a replace op can be ignored so start
86 // afresh w.r.t. determining if any element uses the SW path
87 useSW = false;
88 }
89
robertphillips@google.comf69a11b2012-06-15 13:58:07 +000090 // rects can always be drawn directly w/o using the software path
91 // so only paths need to be checked
92 if (kPath_ClipType == clipIn.getElementType(i) &&
bsalomon@google.com13b85aa2012-06-15 21:09:40 +000093 path_needs_SW_renderer(this->getContext(), fGpu,
robertphillips@google.comf69a11b2012-06-15 13:58:07 +000094 clipIn.getPath(i),
95 clipIn.getPathFill(i),
96 clipIn.getDoAA(i))) {
97 useSW = true;
robertphillips@google.comfa662942012-05-17 12:20:22 +000098 }
robertphillips@google.comfa662942012-05-17 12:20:22 +000099 }
100
101 return useSW;
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000102}
103
robertphillips@google.comf294b772012-04-27 14:29:26 +0000104////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000105// sort out what kind of clip mask needs to be created: alpha, stencil,
106// scissor, or entirely software
bsalomon@google.coma3201942012-06-21 19:58:20 +0000107bool GrClipMaskManager::setupClipping(const GrClip& clipIn) {
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000108 fCurrClipMaskType = kNone_ClipMaskType;
bsalomon@google.coma3201942012-06-21 19:58:20 +0000109
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000110 GrDrawState* drawState = fGpu->drawState();
bsalomon@google.coma3201942012-06-21 19:58:20 +0000111 if (!drawState->isClipState() || clipIn.isEmpty()) {
112 fGpu->disableScissor();
113 this->setGpuStencil();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000114 return true;
115 }
116
117 GrRenderTarget* rt = drawState->getRenderTarget();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000118 // GrDrawTarget should have filtered this for us
119 GrAssert(NULL != rt);
120
bsalomon@google.coma3201942012-06-21 19:58:20 +0000121 GrIRect bounds;
122 GrIRect rtRect;
123 rtRect.setLTRB(0, 0, rt->width(), rt->height());
124 if (clipIn.hasConservativeBounds()) {
125 GrRect softBounds = clipIn.getConservativeBounds();
126 softBounds.roundOut(&bounds);
127 if (!bounds.intersect(rtRect)) {
128 bounds.setEmpty();
129 }
130 if (bounds.isEmpty()) {
131 return false;
132 }
133 } else {
134 bounds = rtRect;
135 }
136
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000137#if GR_SW_CLIP
robertphillips@google.coma3e5c632012-05-22 18:09:26 +0000138 // If MSAA is enabled we can do everything in the stencil buffer.
139 // Otherwise check if we should just create the entire clip mask
140 // in software (this will only happen if the clip mask is anti-aliased
141 // and too complex for the gpu to handle in its entirety)
bsalomon@google.coma3201942012-06-21 19:58:20 +0000142 if (0 == rt->numSamples() && this->useSWOnlyPath(clipIn)) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000143 // The clip geometry is complex enough that it will be more
144 // efficient to create it entirely in software
145 GrTexture* result = NULL;
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000146 GrIRect bound;
bsalomon@google.coma3201942012-06-21 19:58:20 +0000147 if (this->createSoftwareClipMask(clipIn, &result, &bound)) {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000148 setup_drawstate_aaclip(fGpu, result, bound);
bsalomon@google.coma3201942012-06-21 19:58:20 +0000149 fGpu->disableScissor();
150 this->setGpuStencil();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000151 return true;
152 }
robertphillips@google.coma3e5c632012-05-22 18:09:26 +0000153
154 // if SW clip mask creation fails fall through to the other
155 // two possible methods (bottoming out at stencil clipping)
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000156 }
robertphillips@google.coma3e5c632012-05-22 18:09:26 +0000157#endif // GR_SW_CLIP
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000158
robertphillips@google.comf294b772012-04-27 14:29:26 +0000159#if GR_AA_CLIP
160 // If MSAA is enabled use the (faster) stencil path for AA clipping
161 // otherwise the alpha clip mask is our only option
robertphillips@google.coma3e5c632012-05-22 18:09:26 +0000162 if (0 == rt->numSamples() && clipIn.requiresAA()) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000163 // Since we are going to create a destination texture of the correct
164 // size for the mask (rather than being bound by the size of the
165 // render target) we aren't going to use scissoring like the stencil
166 // path does (see scissorSettings below)
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000167 GrTexture* result = NULL;
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000168 GrIRect bound;
bsalomon@google.coma3201942012-06-21 19:58:20 +0000169 if (this->createAlphaClipMask(clipIn, &result, &bound)) {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000170 setup_drawstate_aaclip(fGpu, result, bound);
bsalomon@google.coma3201942012-06-21 19:58:20 +0000171 fGpu->disableScissor();
172 this->setGpuStencil();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000173 return true;
174 }
175
176 // if alpha clip mask creation fails fall through to the stencil
177 // buffer method
178 }
179#endif // GR_AA_CLIP
180
robertphillips@google.com5acc0e32012-05-17 12:01:02 +0000181 // Either a hard (stencil buffer) clip was explicitly requested or
182 // an antialiased clip couldn't be created. In either case, free up
183 // the texture in the antialiased mask cache.
184 // TODO: this may require more investigation. Ganesh performs a lot of
robertphillips@google.coma3e5c632012-05-22 18:09:26 +0000185 // utility draws (e.g., clears, InOrderDrawBuffer playbacks) that hit
186 // the stencil buffer path. These may be "incorrectly" clearing the
robertphillips@google.com5acc0e32012-05-17 12:01:02 +0000187 // AA cache.
188 fAACache.reset();
189
bsalomon@google.coma3201942012-06-21 19:58:20 +0000190 // If the clip is a rectangle then just set the scissor. Otherwise, create
191 // a stencil mask.
192 if (clipIn.isRect()) {
193 fGpu->enableScissor(bounds);
194 this->setGpuStencil();
195 return true;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000196 }
197
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000198 // use the stencil clip if we can't represent the clip as a rectangle.
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000199 bool useStencil = !clipIn.isRect() && !clipIn.isEmpty() &&
200 !bounds.isEmpty();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000201
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000202 if (useStencil) {
bsalomon@google.coma3201942012-06-21 19:58:20 +0000203 this->createStencilClipMask(clipIn, bounds);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000204 }
bsalomon@google.coma3201942012-06-21 19:58:20 +0000205 // This must occur after createStencilClipMask. That function may change
206 // the scissor. Also, it only guarantees that the stencil mask is correct
207 // within the bounds it was passed, so we must use both stencil and scissor
208 // test to the bounds for the final draw.
209 fGpu->enableScissor(bounds);
210 this->setGpuStencil();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000211 return true;
212}
213
214#define VISUALIZE_COMPLEX_CLIP 0
215
216#if VISUALIZE_COMPLEX_CLIP
217 #include "GrRandom.h"
218 GrRandom gRandom;
219 #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU());
220#else
221 #define SET_RANDOM_COLOR
222#endif
223
224namespace {
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000225/**
226 * Does "container" contain "containee"? If either is empty then
227 * no containment is possible.
228 */
229bool contains(const SkRect& container, const SkIRect& containee) {
230 return !containee.isEmpty() && !container.isEmpty() &&
231 container.fLeft <= SkIntToScalar(containee.fLeft) &&
232 container.fTop <= SkIntToScalar(containee.fTop) &&
233 container.fRight >= SkIntToScalar(containee.fRight) &&
234 container.fBottom >= SkIntToScalar(containee.fBottom);
235}
236
237
robertphillips@google.comf294b772012-04-27 14:29:26 +0000238////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000239// determines how many elements at the head of the clip can be skipped and
240// whether the initial clear should be to the inside- or outside-the-clip value,
241// and what op should be used to draw the first element that isn't skipped.
242int process_initial_clip_elements(const GrClip& clip,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000243 const GrIRect& bounds,
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000244 bool* clearToInside,
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000245 SkRegion::Op* startOp) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000246
247 // logically before the first element of the clip stack is
248 // processed the clip is entirely open. However, depending on the
249 // first set op we may prefer to clear to 0 for performance. We may
250 // also be able to skip the initial clip paths/rects. We loop until
251 // we cannot skip an element.
252 int curr;
253 bool done = false;
254 *clearToInside = true;
255 int count = clip.getElementCount();
256
257 for (curr = 0; curr < count && !done; ++curr) {
258 switch (clip.getOp(curr)) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000259 case SkRegion::kReplace_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000260 // replace ignores everything previous
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000261 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000262 *clearToInside = false;
263 done = true;
264 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000265 case SkRegion::kIntersect_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000266 // if this element contains the entire bounds then we
267 // can skip it.
268 if (kRect_ClipType == clip.getElementType(curr)
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000269 && contains(clip.getRect(curr), bounds)) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000270 break;
271 }
272 // if everything is initially clearToInside then intersect is
273 // same as clear to 0 and treat as a replace. Otherwise,
274 // set stays empty.
275 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000276 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000277 *clearToInside = false;
278 done = true;
279 }
280 break;
281 // we can skip a leading union.
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000282 case SkRegion::kUnion_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000283 // if everything is initially outside then union is
284 // same as replace. Otherwise, every pixel is still
285 // clearToInside
286 if (!*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000287 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000288 done = true;
289 }
290 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000291 case SkRegion::kXOR_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000292 // xor is same as difference or replace both of which
293 // can be 1-pass instead of 2 for xor.
294 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000295 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000296 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000297 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000298 }
299 done = true;
300 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000301 case SkRegion::kDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000302 // if all pixels are clearToInside then we have to process the
303 // difference, otherwise it has no effect and all pixels
304 // remain outside.
305 if (*clearToInside) {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000306 *startOp = SkRegion::kDifference_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000307 done = true;
308 }
309 break;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000310 case SkRegion::kReverseDifference_Op:
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000311 // if all pixels are clearToInside then reverse difference
312 // produces empty set. Otherise it is same as replace
313 if (*clearToInside) {
314 *clearToInside = false;
315 } else {
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000316 *startOp = SkRegion::kReplace_Op;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000317 done = true;
318 }
319 break;
320 default:
321 GrCrash("Unknown set op.");
322 }
323 }
324 return done ? curr-1 : count;
325}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000326
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000327}
328
robertphillips@google.comf294b772012-04-27 14:29:26 +0000329
330namespace {
331
332////////////////////////////////////////////////////////////////////////////////
333// set up the OpenGL blend function to perform the specified
334// boolean operation for alpha clip mask creation
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000335void setup_boolean_blendcoeffs(GrDrawState* drawState, SkRegion::Op op) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000336
337 switch (op) {
338 case SkRegion::kReplace_Op:
bsalomon@google.com47059542012-06-06 20:51:20 +0000339 drawState->setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000340 break;
341 case SkRegion::kIntersect_Op:
bsalomon@google.com47059542012-06-06 20:51:20 +0000342 drawState->setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000343 break;
344 case SkRegion::kUnion_Op:
bsalomon@google.com47059542012-06-06 20:51:20 +0000345 drawState->setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000346 break;
347 case SkRegion::kXOR_Op:
bsalomon@google.com47059542012-06-06 20:51:20 +0000348 drawState->setBlendFunc(kIDC_GrBlendCoeff, kISC_GrBlendCoeff);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000349 break;
350 case SkRegion::kDifference_Op:
bsalomon@google.com47059542012-06-06 20:51:20 +0000351 drawState->setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000352 break;
353 case SkRegion::kReverseDifference_Op:
bsalomon@google.com47059542012-06-06 20:51:20 +0000354 drawState->setBlendFunc(kIDC_GrBlendCoeff, kZero_GrBlendCoeff);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000355 break;
356 default:
357 GrAssert(false);
358 break;
359 }
360}
361
robertphillips@google.comf294b772012-04-27 14:29:26 +0000362////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com2c756812012-05-22 20:28:23 +0000363bool draw_path(GrContext* context,
364 GrGpu* gpu,
365 const SkPath& path,
366 GrPathFill fill,
367 bool doAA) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000368
robertphillips@google.com72176b22012-05-23 13:19:12 +0000369 GrPathRenderer* pr = context->getPathRenderer(path, fill, gpu, doAA, true);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000370 if (NULL == pr) {
371 return false;
372 }
373
374 pr->drawPath(path, fill, NULL, gpu, 0, doAA);
375 return true;
376}
robertphillips@google.com72176b22012-05-23 13:19:12 +0000377
378}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000379
380////////////////////////////////////////////////////////////////////////////////
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000381bool GrClipMaskManager::drawClipShape(GrTexture* target,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000382 const GrClip& clipIn,
383 int index) {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000384 GrDrawState* drawState = fGpu->drawState();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000385 GrAssert(NULL != drawState);
386
387 drawState->setRenderTarget(target->asRenderTarget());
388
389 if (kRect_ClipType == clipIn.getElementType(index)) {
390 if (clipIn.getDoAA(index)) {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000391 getContext()->getAARectRenderer()->fillAARect(fGpu, fGpu,
robertphillips@google.comf69a11b2012-06-15 13:58:07 +0000392 clipIn.getRect(index),
393 true);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000394 } else {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000395 fGpu->drawSimpleRect(clipIn.getRect(index), NULL, 0);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000396 }
397 } else {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000398 return draw_path(this->getContext(), fGpu,
robertphillips@google.com2c756812012-05-22 20:28:23 +0000399 clipIn.getPath(index),
400 clipIn.getPathFill(index),
401 clipIn.getDoAA(index));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000402 }
403 return true;
404}
405
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000406void GrClipMaskManager::drawTexture(GrTexture* target,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000407 GrTexture* texture) {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000408 GrDrawState* drawState = fGpu->drawState();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000409 GrAssert(NULL != drawState);
410
411 // no AA here since it is encoded in the texture
412 drawState->setRenderTarget(target->asRenderTarget());
413
414 GrMatrix sampleM;
415 sampleM.setIDiv(texture->width(), texture->height());
416 drawState->setTexture(0, texture);
417
418 drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
419 GrSamplerState::kNearest_Filter,
420 sampleM);
421
robertphillips@google.comf105b102012-05-14 12:18:26 +0000422 GrRect rect = GrRect::MakeWH(SkIntToScalar(target->width()),
423 SkIntToScalar(target->height()));
424
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000425 fGpu->drawSimpleRect(rect, NULL, 1 << 0);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000426
427 drawState->setTexture(0, NULL);
428}
429
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000430// get a texture to act as a temporary buffer for AA clip boolean operations
431// TODO: given the expense of createTexture we may want to just cache this too
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000432void GrClipMaskManager::getTemp(const GrIRect& bounds,
robertphillips@google.comf105b102012-05-14 12:18:26 +0000433 GrAutoScratchTexture* temp) {
434 if (NULL != temp->texture()) {
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000435 // we've already allocated the temp texture
436 return;
437 }
438
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000439 GrTextureDesc desc;
440 desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
441 desc.fWidth = bounds.width();
442 desc.fHeight = bounds.height();
443 desc.fConfig = kAlpha_8_GrPixelConfig;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000444
robertphillips@google.com2c756812012-05-22 20:28:23 +0000445 temp->set(this->getContext(), desc);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000446}
447
robertphillips@google.comf105b102012-05-14 12:18:26 +0000448
449void GrClipMaskManager::setupCache(const GrClip& clipIn,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000450 const GrIRect& bounds) {
robertphillips@google.comf105b102012-05-14 12:18:26 +0000451 // Since we are setting up the cache we know the last lookup was a miss
452 // Free up the currently cached mask so it can be reused
453 fAACache.reset();
454
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000455 GrTextureDesc desc;
456 desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
457 desc.fWidth = bounds.width();
458 desc.fHeight = bounds.height();
459 desc.fConfig = kAlpha_8_GrPixelConfig;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000460
461 fAACache.acquireMask(clipIn, desc, bounds);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000462}
463
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000464////////////////////////////////////////////////////////////////////////////////
465// Shared preamble between gpu and SW-only AA clip mask creation paths.
466// Handles caching, determination of clip mask bound & allocation (if needed)
467// of the result texture
468// Returns true if there is no more work to be done (i.e., we got a cache hit)
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000469bool GrClipMaskManager::clipMaskPreamble(const GrClip& clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000470 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000471 GrIRect *resultBounds) {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000472 GrDrawState* origDrawState = fGpu->drawState();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000473 GrAssert(origDrawState->isClipState());
474
475 GrRenderTarget* rt = origDrawState->getRenderTarget();
476 GrAssert(NULL != rt);
477
478 GrRect rtRect;
479 rtRect.setLTRB(0, 0,
480 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
481
482 // unlike the stencil path the alpha path is not bound to the size of the
483 // render target - determine the minimum size required for the mask
484 GrRect bounds;
485
486 if (clipIn.hasConservativeBounds()) {
487 bounds = clipIn.getConservativeBounds();
488 if (!bounds.intersect(rtRect)) {
489 // the mask will be empty in this case
490 GrAssert(false);
491 bounds.setEmpty();
492 }
493 } else {
494 // still locked to the size of the render target
495 bounds = rtRect;
496 }
497
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000498 GrIRect intBounds;
499 bounds.roundOut(&intBounds);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000500
501 // need to outset a pixel since the standard bounding box computation
502 // path doesn't leave any room for antialiasing (esp. w.r.t. rects)
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000503 intBounds.outset(1, 1);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000504
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000505 // TODO: make sure we don't outset if bounds are still 0,0 @ min
506
robertphillips@google.com8fff3562012-05-11 12:53:50 +0000507 if (fAACache.canReuse(clipIn,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000508 intBounds.width(),
509 intBounds.height())) {
robertphillips@google.com8fff3562012-05-11 12:53:50 +0000510 *result = fAACache.getLastMask();
511 fAACache.getLastBound(resultBounds);
512 return true;
513 }
514
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000515 this->setupCache(clipIn, intBounds);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000516
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000517 *resultBounds = intBounds;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000518 return false;
519}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000520
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000521////////////////////////////////////////////////////////////////////////////////
522// Create a 8-bit clip mask in alpha
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000523bool GrClipMaskManager::createAlphaClipMask(const GrClip& clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000524 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000525 GrIRect *resultBounds) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000526
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000527 GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
528
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000529 if (this->clipMaskPreamble(clipIn, result, resultBounds)) {
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000530 fCurrClipMaskType = kAlpha_ClipMaskType;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000531 return true;
532 }
533
robertphillips@google.comf105b102012-05-14 12:18:26 +0000534 GrTexture* accum = fAACache.getLastMask();
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000535 if (NULL == accum) {
robertphillips@google.comf105b102012-05-14 12:18:26 +0000536 fAACache.reset();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000537 return false;
538 }
539
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000540 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
541 GrDrawState* drawState = fGpu->drawState();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000542
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000543 GrDrawTarget::AutoGeometryPush agp(fGpu);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000544
545 int count = clipIn.getElementCount();
546
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000547 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000548 // if we were able to trim down the size of the mask we need to
549 // offset the paths & rects that will be used to compute it
550 GrMatrix m;
551
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000552 m.setTranslate(SkIntToScalar(-resultBounds->fLeft),
553 SkIntToScalar(-resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000554
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000555 drawState->setViewMatrix(m);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000556 }
557
558 bool clearToInside;
559 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
560 int start = process_initial_clip_elements(clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000561 *resultBounds,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000562 &clearToInside,
563 &startOp);
564
robertphillips@google.comc82a8b72012-06-21 20:15:48 +0000565 fGpu->clear(NULL,
566 clearToInside ? 0xffffffff : 0x00000000,
567 accum->asRenderTarget());
robertphillips@google.comf294b772012-04-27 14:29:26 +0000568
robertphillips@google.comf105b102012-05-14 12:18:26 +0000569 GrAutoScratchTexture temp;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000570
robertphillips@google.comf294b772012-04-27 14:29:26 +0000571 // walk through each clip element and perform its set op
572 for (int c = start; c < count; ++c) {
573
574 SkRegion::Op op = (c == start) ? startOp : clipIn.getOp(c);
575
576 if (SkRegion::kReplace_Op == op) {
577 // TODO: replace is actually a lot faster then intersection
578 // for this path - refactor the stencil path so it can handle
579 // replace ops and alter GrClip to allow them through
580
581 // clear the accumulator and draw the new object directly into it
robertphillips@google.comc82a8b72012-06-21 20:15:48 +0000582 fGpu->clear(NULL, 0x00000000, accum->asRenderTarget());
robertphillips@google.comf294b772012-04-27 14:29:26 +0000583
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000584 setup_boolean_blendcoeffs(drawState, op);
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000585 this->drawClipShape(accum, clipIn, c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000586
587 } else if (SkRegion::kReverseDifference_Op == op ||
588 SkRegion::kIntersect_Op == op) {
589 // there is no point in intersecting a screen filling rectangle.
590 if (SkRegion::kIntersect_Op == op &&
591 kRect_ClipType == clipIn.getElementType(c) &&
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000592 contains(clipIn.getRect(c), *resultBounds)) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000593 continue;
594 }
595
robertphillips@google.comf105b102012-05-14 12:18:26 +0000596 getTemp(*resultBounds, &temp);
597 if (NULL == temp.texture()) {
robertphillips@google.comf105b102012-05-14 12:18:26 +0000598 fAACache.reset();
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000599 return false;
600 }
601
robertphillips@google.comf294b772012-04-27 14:29:26 +0000602 // clear the temp target & draw into it
robertphillips@google.comc82a8b72012-06-21 20:15:48 +0000603 fGpu->clear(NULL, 0x00000000, temp.texture()->asRenderTarget());
robertphillips@google.comf294b772012-04-27 14:29:26 +0000604
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000605 setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000606 this->drawClipShape(temp.texture(), clipIn, c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000607
608 // TODO: rather than adding these two translations here
609 // compute the bounding box needed to render the texture
610 // into temp
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000611 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000612 GrMatrix m;
613
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000614 m.setTranslate(SkIntToScalar(resultBounds->fLeft),
615 SkIntToScalar(resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000616
617 drawState->preConcatViewMatrix(m);
618 }
619
620 // Now draw into the accumulator using the real operation
621 // and the temp buffer as a texture
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000622 setup_boolean_blendcoeffs(drawState, op);
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000623 this->drawTexture(accum, temp.texture());
robertphillips@google.comf294b772012-04-27 14:29:26 +0000624
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000625 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000626 GrMatrix m;
627
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000628 m.setTranslate(SkIntToScalar(-resultBounds->fLeft),
629 SkIntToScalar(-resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000630
631 drawState->preConcatViewMatrix(m);
632 }
633
634 } else {
635 // all the remaining ops can just be directly draw into
636 // the accumulation buffer
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000637 setup_boolean_blendcoeffs(drawState, op);
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000638 this->drawClipShape(accum, clipIn, c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000639 }
640 }
641
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000642 *result = accum;
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000643 fCurrClipMaskType = kAlpha_ClipMaskType;
robertphillips@google.comf294b772012-04-27 14:29:26 +0000644 return true;
645}
646
647////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000648// Create a 1-bit clip mask in the stencil buffer
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000649bool GrClipMaskManager::createStencilClipMask(const GrClip& clipIn,
bsalomon@google.coma3201942012-06-21 19:58:20 +0000650 const GrIRect& bounds) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000651
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000652 GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000653
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000654 GrDrawState* drawState = fGpu->drawState();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000655 GrAssert(drawState->isClipState());
656
657 GrRenderTarget* rt = drawState->getRenderTarget();
658 GrAssert(NULL != rt);
659
660 // TODO: dynamically attach a SB when needed.
661 GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
662 if (NULL == stencilBuffer) {
663 return false;
664 }
665
666 if (stencilBuffer->mustRenderClip(clipIn, rt->width(), rt->height())) {
667
668 stencilBuffer->setLastClip(clipIn, rt->width(), rt->height());
669
670 // we set the current clip to the bounds so that our recursive
671 // draws are scissored to them. We use the copy of the complex clip
672 // we just stashed on the SB to render from. We set it back after
673 // we finish drawing it into the stencil.
674 const GrClip& clipCopy = stencilBuffer->getLastClip();
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000675 fGpu->setClip(GrClip(bounds));
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000676
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000677 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
678 drawState = fGpu->drawState();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000679 drawState->setRenderTarget(rt);
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000680 GrDrawTarget::AutoGeometryPush agp(fGpu);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000681
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000682#if !VISUALIZE_COMPLEX_CLIP
683 drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
684#endif
685
686 int count = clipCopy.getElementCount();
687 int clipBit = stencilBuffer->bits();
688 SkASSERT((clipBit <= 16) &&
689 "Ganesh only handles 16b or smaller stencil buffers");
690 clipBit = (1 << (clipBit-1));
691
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000692 GrIRect rtRect = GrIRect::MakeWH(rt->width(), rt->height());
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000693
694 bool clearToInside;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000695 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000696 int start = process_initial_clip_elements(clipCopy,
697 rtRect,
698 &clearToInside,
699 &startOp);
700
bsalomon@google.coma3201942012-06-21 19:58:20 +0000701 fGpu->clearStencilClip(bounds, clearToInside);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000702
703 // walk through each clip element and perform its set op
704 // with the existing clip.
705 for (int c = start; c < count; ++c) {
706 GrPathFill fill;
707 bool fillInverted;
708 // enabled at bottom of loop
709 drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
710
711 bool canRenderDirectToStencil; // can the clip element be drawn
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000712 // directly to the stencil buffer
713 // with a non-inverted fill rule
714 // without extra passes to
715 // resolve in/out status.
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000716
robertphillips@google.comf294b772012-04-27 14:29:26 +0000717 SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
718
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000719 GrPathRenderer* pr = NULL;
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000720 const SkPath* clipPath = NULL;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000721 if (kRect_ClipType == clipCopy.getElementType(c)) {
722 canRenderDirectToStencil = true;
bsalomon@google.com47059542012-06-06 20:51:20 +0000723 fill = kEvenOdd_GrPathFill;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000724 fillInverted = false;
725 // there is no point in intersecting a screen filling
726 // rectangle.
robertphillips@google.comf294b772012-04-27 14:29:26 +0000727 if (SkRegion::kIntersect_Op == op &&
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000728 contains(clipCopy.getRect(c), rtRect)) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000729 continue;
730 }
731 } else {
732 fill = clipCopy.getPathFill(c);
733 fillInverted = GrIsFillInverted(fill);
734 fill = GrNonInvertedFill(fill);
735 clipPath = &clipCopy.getPath(c);
robertphillips@google.com2c756812012-05-22 20:28:23 +0000736 pr = this->getContext()->getPathRenderer(*clipPath,
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000737 fill, fGpu, false,
robertphillips@google.com72176b22012-05-23 13:19:12 +0000738 true);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000739 if (NULL == pr) {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000740 fGpu->setClip(clipCopy); // restore to the original
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000741 return false;
742 }
743 canRenderDirectToStencil =
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000744 !pr->requiresStencilPass(*clipPath, fill, fGpu);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000745 }
746
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000747 int passes;
748 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
749
750 bool canDrawDirectToClip; // Given the renderer, the element,
751 // fill rule, and set operation can
752 // we render the element directly to
753 // stencil bit used for clipping.
754 canDrawDirectToClip =
755 GrStencilSettings::GetClipPasses(op,
756 canRenderDirectToStencil,
757 clipBit,
758 fillInverted,
759 &passes, stencilSettings);
760
761 // draw the element to the client stencil bits if necessary
762 if (!canDrawDirectToClip) {
763 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
764 kIncClamp_StencilOp,
765 kIncClamp_StencilOp,
766 kAlways_StencilFunc,
767 0xffff,
768 0x0000,
769 0xffff);
770 SET_RANDOM_COLOR
771 if (kRect_ClipType == clipCopy.getElementType(c)) {
772 *drawState->stencil() = gDrawToStencil;
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000773 fGpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000774 } else {
775 if (canRenderDirectToStencil) {
776 *drawState->stencil() = gDrawToStencil;
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000777 pr->drawPath(*clipPath, fill, NULL, fGpu, 0, false);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000778 } else {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000779 pr->drawPathToStencil(*clipPath, fill, fGpu);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000780 }
781 }
782 }
783
784 // now we modify the clip bit by rendering either the clip
785 // element directly or a bounding rect of the entire clip.
786 drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
787 for (int p = 0; p < passes; ++p) {
788 *drawState->stencil() = stencilSettings[p];
789 if (canDrawDirectToClip) {
790 if (kRect_ClipType == clipCopy.getElementType(c)) {
791 SET_RANDOM_COLOR
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000792 fGpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000793 } else {
794 SET_RANDOM_COLOR
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000795 pr->drawPath(*clipPath, fill, NULL, fGpu, 0, false);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000796 }
797 } else {
798 SET_RANDOM_COLOR
bsalomon@google.coma3201942012-06-21 19:58:20 +0000799 GrRect rect = GrRect::MakeLTRB(
800 SkIntToScalar(bounds.fLeft),
801 SkIntToScalar(bounds.fTop),
802 SkIntToScalar(bounds.fRight),
803 SkIntToScalar(bounds.fBottom));
804 fGpu->drawSimpleRect(rect, NULL, 0);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000805 }
806 }
807 }
808 // restore clip
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000809 fGpu->setClip(clipCopy);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000810 }
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000811 // set this last because recursive draws may overwrite it back to kNone.
812 GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
813 fCurrClipMaskType = kStencil_ClipMaskType;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000814 return true;
815}
816
bsalomon@google.com411dad02012-06-05 20:24:20 +0000817// mapping of clip-respecting stencil funcs to normal stencil funcs
818// mapping depends on whether stencil-clipping is in effect.
819static const GrStencilFunc
820 gSpecialToBasicStencilFunc[2][kClipStencilFuncCount] = {
821 {// Stencil-Clipping is DISABLED, we are effectively always inside the clip
822 // In the Clip Funcs
823 kAlways_StencilFunc, // kAlwaysIfInClip_StencilFunc
824 kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
825 kLess_StencilFunc, // kLessIfInClip_StencilFunc
826 kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
827 // Special in the clip func that forces user's ref to be 0.
828 kNotEqual_StencilFunc, // kNonZeroIfInClip_StencilFunc
829 // make ref 0 and do normal nequal.
830 },
831 {// Stencil-Clipping is ENABLED
832 // In the Clip Funcs
833 kEqual_StencilFunc, // kAlwaysIfInClip_StencilFunc
834 // eq stencil clip bit, mask
835 // out user bits.
836
837 kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
838 // add stencil bit to mask and ref
839
840 kLess_StencilFunc, // kLessIfInClip_StencilFunc
841 kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
842 // for both of these we can add
843 // the clip bit to the mask and
844 // ref and compare as normal
845 // Special in the clip func that forces user's ref to be 0.
846 kLess_StencilFunc, // kNonZeroIfInClip_StencilFunc
847 // make ref have only the clip bit set
848 // and make comparison be less
849 // 10..0 < 1..user_bits..
850 }
851};
852
bsalomon@google.coma3201942012-06-21 19:58:20 +0000853namespace {
854// Sets the settings to clip against the stencil buffer clip while ignoring the
855// client bits.
856const GrStencilSettings& basic_apply_stencil_clip_settings() {
857 // stencil settings to use when clip is in stencil
858 GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
859 kKeep_StencilOp,
860 kKeep_StencilOp,
861 kAlwaysIfInClip_StencilFunc,
862 0x0000,
863 0x0000,
864 0x0000);
865 return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
866}
867}
868
869void GrClipMaskManager::setGpuStencil() {
870 // We make two copies of the StencilSettings here (except in the early
871 // exit scenario. One copy from draw state to the stack var. Then another
872 // from the stack var to the gpu. We could make this class hold a ptr to
873 // GrGpu's fStencilSettings and eliminate the stack copy here.
874
875 const GrDrawState& drawState = fGpu->getDrawState();
876
877 // use stencil for clipping if clipping is enabled and the clip
878 // has been written into the stencil.
879 GrClipMaskManager::StencilClipMode clipMode;
880 if (this->isClipInStencil() && drawState.isClipState()) {
881 clipMode = GrClipMaskManager::kRespectClip_StencilClipMode;
882 // We can't be modifying the clip and respecting it at the same time.
883 GrAssert(!drawState.isStateFlagEnabled(
884 GrGpu::kModifyStencilClip_StateBit));
885 } else if (drawState.isStateFlagEnabled(
886 GrGpu::kModifyStencilClip_StateBit)) {
887 clipMode = GrClipMaskManager::kModifyClip_StencilClipMode;
888 } else {
889 clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode;
890 }
891
892 GrStencilSettings settings;
893 // The GrGpu client may not be using the stencil buffer but we may need to
894 // enable it in order to respect a stencil clip.
895 if (drawState.getStencil().isDisabled()) {
896 if (GrClipMaskManager::kRespectClip_StencilClipMode == clipMode) {
897 settings = basic_apply_stencil_clip_settings();
898 } else {
899 fGpu->disableStencil();
900 return;
901 }
902 } else {
903 settings = drawState.getStencil();
904 }
905
906 // TODO: dynamically attach a stencil buffer
907 int stencilBits = 0;
908 GrStencilBuffer* stencilBuffer =
909 drawState.getRenderTarget()->getStencilBuffer();
910 if (NULL != stencilBuffer) {
911 stencilBits = stencilBuffer->bits();
912 }
913
bsalomon@google.com9e553c62012-06-22 12:23:29 +0000914 GrAssert(fGpu->getCaps().fStencilWrapOpsSupport ||
915 !settings.usesWrapOp());
916 GrAssert(fGpu->getCaps().fTwoSidedStencilSupport || !settings.isTwoSided());
bsalomon@google.coma3201942012-06-21 19:58:20 +0000917 this->adjustStencilParams(&settings, clipMode, stencilBits);
918 fGpu->setStencilSettings(settings);
919}
920
921void GrClipMaskManager::adjustStencilParams(GrStencilSettings* settings,
922 StencilClipMode mode,
923 int stencilBitCnt) {
bsalomon@google.com411dad02012-06-05 20:24:20 +0000924 GrAssert(stencilBitCnt > 0);
bsalomon@google.com411dad02012-06-05 20:24:20 +0000925
926 if (kModifyClip_StencilClipMode == mode) {
bsalomon@google.coma3201942012-06-21 19:58:20 +0000927 // We assume that this clip manager itself is drawing to the GrGpu and
928 // has already setup the correct values.
929 return;
bsalomon@google.com411dad02012-06-05 20:24:20 +0000930 }
bsalomon@google.coma3201942012-06-21 19:58:20 +0000931
bsalomon@google.com411dad02012-06-05 20:24:20 +0000932 unsigned int clipBit = (1 << (stencilBitCnt - 1));
933 unsigned int userBits = clipBit - 1;
934
bsalomon@google.coma3201942012-06-21 19:58:20 +0000935 GrStencilSettings::Face face = GrStencilSettings::kFront_Face;
936 bool twoSided = fGpu->getCaps().fTwoSidedStencilSupport;
bsalomon@google.com411dad02012-06-05 20:24:20 +0000937
bsalomon@google.coma3201942012-06-21 19:58:20 +0000938 bool finished = false;
939 while (!finished) {
940 GrStencilFunc func = settings->func(face);
941 uint16_t writeMask = settings->writeMask(face);
942 uint16_t funcMask = settings->funcMask(face);
943 uint16_t funcRef = settings->funcRef(face);
944
945 GrAssert((unsigned) func < kStencilFuncCount);
946
947 writeMask &= userBits;
948
949 if (func >= kBasicStencilFuncCount) {
950 int respectClip = kRespectClip_StencilClipMode == mode;
951 if (respectClip) {
952 // The GrGpu class should have checked this
953 GrAssert(this->isClipInStencil());
954 switch (func) {
955 case kAlwaysIfInClip_StencilFunc:
956 funcMask = clipBit;
957 funcRef = clipBit;
958 break;
959 case kEqualIfInClip_StencilFunc:
960 case kLessIfInClip_StencilFunc:
961 case kLEqualIfInClip_StencilFunc:
962 funcMask = (funcMask & userBits) | clipBit;
963 funcRef = (funcRef & userBits) | clipBit;
964 break;
965 case kNonZeroIfInClip_StencilFunc:
966 funcMask = (funcMask & userBits) | clipBit;
967 funcRef = clipBit;
968 break;
969 default:
970 GrCrash("Unknown stencil func");
971 }
972 } else {
973 funcMask &= userBits;
974 funcRef &= userBits;
bsalomon@google.com411dad02012-06-05 20:24:20 +0000975 }
bsalomon@google.coma3201942012-06-21 19:58:20 +0000976 const GrStencilFunc* table =
977 gSpecialToBasicStencilFunc[respectClip];
978 func = table[func - kBasicStencilFuncCount];
979 GrAssert(func >= 0 && func < kBasicStencilFuncCount);
bsalomon@google.com411dad02012-06-05 20:24:20 +0000980 } else {
bsalomon@google.coma3201942012-06-21 19:58:20 +0000981 funcMask &= userBits;
982 funcRef &= userBits;
bsalomon@google.com411dad02012-06-05 20:24:20 +0000983 }
bsalomon@google.coma3201942012-06-21 19:58:20 +0000984
985 settings->setFunc(face, func);
986 settings->setWriteMask(face, writeMask);
987 settings->setFuncMask(face, funcMask);
988 settings->setFuncRef(face, funcRef);
989
990 if (GrStencilSettings::kFront_Face == face) {
991 face = GrStencilSettings::kBack_Face;
992 finished = !twoSided;
993 } else {
994 finished = true;
995 }
bsalomon@google.com411dad02012-06-05 20:24:20 +0000996 }
bsalomon@google.coma3201942012-06-21 19:58:20 +0000997 if (!twoSided) {
998 settings->copyFrontSettingsToBack();
999 }
bsalomon@google.com411dad02012-06-05 20:24:20 +00001000}
1001
1002////////////////////////////////////////////////////////////////////////////////
1003
robertphillips@google.comfa662942012-05-17 12:20:22 +00001004namespace {
1005
1006GrPathFill invert_fill(GrPathFill fill) {
1007 static const GrPathFill gInvertedFillTable[] = {
bsalomon@google.com47059542012-06-06 20:51:20 +00001008 kInverseWinding_GrPathFill, // kWinding_GrPathFill
1009 kInverseEvenOdd_GrPathFill, // kEvenOdd_GrPathFill
1010 kWinding_GrPathFill, // kInverseWinding_GrPathFill
1011 kEvenOdd_GrPathFill, // kInverseEvenOdd_GrPathFill
1012 kHairLine_GrPathFill, // kHairLine_GrPathFill
robertphillips@google.comfa662942012-05-17 12:20:22 +00001013 };
bsalomon@google.com47059542012-06-06 20:51:20 +00001014 GR_STATIC_ASSERT(0 == kWinding_GrPathFill);
1015 GR_STATIC_ASSERT(1 == kEvenOdd_GrPathFill);
1016 GR_STATIC_ASSERT(2 == kInverseWinding_GrPathFill);
1017 GR_STATIC_ASSERT(3 == kInverseEvenOdd_GrPathFill);
1018 GR_STATIC_ASSERT(4 == kHairLine_GrPathFill);
1019 GR_STATIC_ASSERT(5 == kGrPathFillCount);
robertphillips@google.comfa662942012-05-17 12:20:22 +00001020 return gInvertedFillTable[fill];
1021}
1022
1023}
1024
bsalomon@google.com13b85aa2012-06-15 21:09:40 +00001025bool GrClipMaskManager::createSoftwareClipMask(const GrClip& clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001026 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +00001027 GrIRect *resultBounds) {
bsalomon@google.comc8f7f472012-06-18 13:44:51 +00001028 GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001029
bsalomon@google.com13b85aa2012-06-15 21:09:40 +00001030 if (this->clipMaskPreamble(clipIn, result, resultBounds)) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001031 return true;
1032 }
1033
robertphillips@google.comf105b102012-05-14 12:18:26 +00001034 GrTexture* accum = fAACache.getLastMask();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001035 if (NULL == accum) {
robertphillips@google.comf105b102012-05-14 12:18:26 +00001036 fAACache.reset();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001037 return false;
1038 }
1039
robertphillips@google.com2c756812012-05-22 20:28:23 +00001040 GrSWMaskHelper helper(this->getContext());
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001041
robertphillips@google.comfa662942012-05-17 12:20:22 +00001042 helper.init(*resultBounds, NULL, false);
1043
1044 int count = clipIn.getElementCount();
1045
1046 bool clearToInside;
1047 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
1048 int start = process_initial_clip_elements(clipIn,
1049 *resultBounds,
1050 &clearToInside,
1051 &startOp);
1052
1053 helper.clear(clearToInside ? SK_ColorWHITE : 0x00000000);
1054
1055 for (int i = start; i < count; ++i) {
1056
1057 SkRegion::Op op = (i == start) ? startOp : clipIn.getOp(i);
1058
1059 if (SkRegion::kIntersect_Op == op ||
1060 SkRegion::kReverseDifference_Op == op) {
1061 // Intersect and reverse difference require modifying pixels
1062 // outside of the geometry that is being "drawn". In both cases
1063 // we erase all the pixels outside of the geometry but
1064 // leave the pixels inside the geometry alone. For reverse
1065 // difference we invert all the pixels before clearing the ones
1066 // outside the geometry.
1067 if (SkRegion::kReverseDifference_Op == op) {
1068 SkRect temp = SkRect::MakeLTRB(
1069 SkIntToScalar(resultBounds->left()),
1070 SkIntToScalar(resultBounds->top()),
1071 SkIntToScalar(resultBounds->right()),
1072 SkIntToScalar(resultBounds->bottom()));
1073
1074 // invert the entire scene
1075 helper.draw(temp, SkRegion::kXOR_Op, false, SK_ColorWHITE);
1076 }
1077
1078 if (kRect_ClipType == clipIn.getElementType(i)) {
1079
1080 // convert the rect to a path so we can invert the fill
1081 SkPath temp;
1082 temp.addRect(clipIn.getRect(i));
1083
1084 helper.draw(temp, SkRegion::kReplace_Op,
bsalomon@google.com47059542012-06-06 20:51:20 +00001085 kInverseEvenOdd_GrPathFill, clipIn.getDoAA(i),
robertphillips@google.comfa662942012-05-17 12:20:22 +00001086 0x00000000);
1087 } else {
1088 GrAssert(kPath_ClipType == clipIn.getElementType(i));
1089
1090 helper.draw(clipIn.getPath(i),
1091 SkRegion::kReplace_Op,
1092 invert_fill(clipIn.getPathFill(i)),
1093 clipIn.getDoAA(i),
1094 0x00000000);
1095 }
1096
1097 continue;
1098 }
1099
1100 // The other ops (union, xor, diff) only affect pixels inside
1101 // the geometry so they can just be drawn normally
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001102 if (kRect_ClipType == clipIn.getElementType(i)) {
robertphillips@google.comfa662942012-05-17 12:20:22 +00001103
1104 helper.draw(clipIn.getRect(i),
1105 op,
1106 clipIn.getDoAA(i), SK_ColorWHITE);
1107
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001108 } else {
1109 GrAssert(kPath_ClipType == clipIn.getElementType(i));
1110
robertphillips@google.comfa662942012-05-17 12:20:22 +00001111 helper.draw(clipIn.getPath(i),
1112 op,
1113 clipIn.getPathFill(i),
1114 clipIn.getDoAA(i), SK_ColorWHITE);
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001115 }
1116 }
1117
robertphillips@google.comfa662942012-05-17 12:20:22 +00001118 // Because we are using the scratch texture cache, "accum" may be
1119 // larger than expected and have some cruft in the areas we aren't using.
1120 // Clear it out.
1121
1122 // TODO: need a simpler way to clear the texture - can we combine
1123 // the clear and the writePixels (inside toTexture)
bsalomon@google.com13b85aa2012-06-15 21:09:40 +00001124 GrDrawState* drawState = fGpu->drawState();
robertphillips@google.comfa662942012-05-17 12:20:22 +00001125 GrAssert(NULL != drawState);
1126 GrRenderTarget* temp = drawState->getRenderTarget();
robertphillips@google.comc82a8b72012-06-21 20:15:48 +00001127 fGpu->clear(NULL, 0x00000000, accum->asRenderTarget());
robertphillips@google.comfa662942012-05-17 12:20:22 +00001128 // can't leave the accum bound as a rendertarget
1129 drawState->setRenderTarget(temp);
1130
robertphillips@google.comc82a8b72012-06-21 20:15:48 +00001131 helper.toTexture(accum, clearToInside);
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001132
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001133 *result = accum;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001134
bsalomon@google.comc8f7f472012-06-18 13:44:51 +00001135 fCurrClipMaskType = kAlpha_ClipMaskType;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001136 return true;
1137}
1138
robertphillips@google.comf294b772012-04-27 14:29:26 +00001139////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comf105b102012-05-14 12:18:26 +00001140void GrClipMaskManager::releaseResources() {
robertphillips@google.comf105b102012-05-14 12:18:26 +00001141 fAACache.releaseResources();
robertphillips@google.com1e945b72012-04-16 18:03:03 +00001142}