blob: c16b4106a123454d4a5554620f6326721579d138 [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
430namespace {
431
432void clear(GrGpu* gpu,
433 GrTexture* target,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000434 GrColor color) {
435 GrDrawState* drawState = gpu->drawState();
436 GrAssert(NULL != drawState);
437
438 // zap entire target to specified color
439 drawState->setRenderTarget(target->asRenderTarget());
440 gpu->clear(NULL, color);
441}
442
robertphillips@google.comf105b102012-05-14 12:18:26 +0000443}
444
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000445// get a texture to act as a temporary buffer for AA clip boolean operations
446// TODO: given the expense of createTexture we may want to just cache this too
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000447void GrClipMaskManager::getTemp(const GrIRect& bounds,
robertphillips@google.comf105b102012-05-14 12:18:26 +0000448 GrAutoScratchTexture* temp) {
449 if (NULL != temp->texture()) {
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000450 // we've already allocated the temp texture
451 return;
452 }
453
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000454 GrTextureDesc desc;
455 desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
456 desc.fWidth = bounds.width();
457 desc.fHeight = bounds.height();
458 desc.fConfig = kAlpha_8_GrPixelConfig;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000459
robertphillips@google.com2c756812012-05-22 20:28:23 +0000460 temp->set(this->getContext(), desc);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000461}
462
robertphillips@google.comf105b102012-05-14 12:18:26 +0000463
464void GrClipMaskManager::setupCache(const GrClip& clipIn,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000465 const GrIRect& bounds) {
robertphillips@google.comf105b102012-05-14 12:18:26 +0000466 // Since we are setting up the cache we know the last lookup was a miss
467 // Free up the currently cached mask so it can be reused
468 fAACache.reset();
469
robertphillips@google.com75b3c962012-06-07 12:08:45 +0000470 GrTextureDesc desc;
471 desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
472 desc.fWidth = bounds.width();
473 desc.fHeight = bounds.height();
474 desc.fConfig = kAlpha_8_GrPixelConfig;
robertphillips@google.comf105b102012-05-14 12:18:26 +0000475
476 fAACache.acquireMask(clipIn, desc, bounds);
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000477}
478
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000479////////////////////////////////////////////////////////////////////////////////
480// Shared preamble between gpu and SW-only AA clip mask creation paths.
481// Handles caching, determination of clip mask bound & allocation (if needed)
482// of the result texture
483// 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 +0000484bool GrClipMaskManager::clipMaskPreamble(const GrClip& clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000485 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000486 GrIRect *resultBounds) {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000487 GrDrawState* origDrawState = fGpu->drawState();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000488 GrAssert(origDrawState->isClipState());
489
490 GrRenderTarget* rt = origDrawState->getRenderTarget();
491 GrAssert(NULL != rt);
492
493 GrRect rtRect;
494 rtRect.setLTRB(0, 0,
495 GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
496
497 // unlike the stencil path the alpha path is not bound to the size of the
498 // render target - determine the minimum size required for the mask
499 GrRect bounds;
500
501 if (clipIn.hasConservativeBounds()) {
502 bounds = clipIn.getConservativeBounds();
503 if (!bounds.intersect(rtRect)) {
504 // the mask will be empty in this case
505 GrAssert(false);
506 bounds.setEmpty();
507 }
508 } else {
509 // still locked to the size of the render target
510 bounds = rtRect;
511 }
512
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000513 GrIRect intBounds;
514 bounds.roundOut(&intBounds);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000515
516 // need to outset a pixel since the standard bounding box computation
517 // path doesn't leave any room for antialiasing (esp. w.r.t. rects)
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000518 intBounds.outset(1, 1);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000519
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000520 // TODO: make sure we don't outset if bounds are still 0,0 @ min
521
robertphillips@google.com8fff3562012-05-11 12:53:50 +0000522 if (fAACache.canReuse(clipIn,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000523 intBounds.width(),
524 intBounds.height())) {
robertphillips@google.com8fff3562012-05-11 12:53:50 +0000525 *result = fAACache.getLastMask();
526 fAACache.getLastBound(resultBounds);
527 return true;
528 }
529
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000530 this->setupCache(clipIn, intBounds);
robertphillips@google.comf105b102012-05-14 12:18:26 +0000531
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000532 *resultBounds = intBounds;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000533 return false;
534}
robertphillips@google.comf294b772012-04-27 14:29:26 +0000535
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000536////////////////////////////////////////////////////////////////////////////////
537// Create a 8-bit clip mask in alpha
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000538bool GrClipMaskManager::createAlphaClipMask(const GrClip& clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000539 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000540 GrIRect *resultBounds) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000541
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000542 GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
543
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000544 if (this->clipMaskPreamble(clipIn, result, resultBounds)) {
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000545 fCurrClipMaskType = kAlpha_ClipMaskType;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000546 return true;
547 }
548
robertphillips@google.comf105b102012-05-14 12:18:26 +0000549 GrTexture* accum = fAACache.getLastMask();
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000550 if (NULL == accum) {
robertphillips@google.comf105b102012-05-14 12:18:26 +0000551 fAACache.reset();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000552 return false;
553 }
554
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000555 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
556 GrDrawState* drawState = fGpu->drawState();
robertphillips@google.comf294b772012-04-27 14:29:26 +0000557
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000558 GrDrawTarget::AutoGeometryPush agp(fGpu);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000559
560 int count = clipIn.getElementCount();
561
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000562 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000563 // if we were able to trim down the size of the mask we need to
564 // offset the paths & rects that will be used to compute it
565 GrMatrix m;
566
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000567 m.setTranslate(SkIntToScalar(-resultBounds->fLeft),
568 SkIntToScalar(-resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000569
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000570 drawState->setViewMatrix(m);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000571 }
572
573 bool clearToInside;
574 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
575 int start = process_initial_clip_elements(clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000576 *resultBounds,
robertphillips@google.comf294b772012-04-27 14:29:26 +0000577 &clearToInside,
578 &startOp);
579
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000580 clear(fGpu, accum, clearToInside ? 0xffffffff : 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000581
robertphillips@google.comf105b102012-05-14 12:18:26 +0000582 GrAutoScratchTexture temp;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000583
robertphillips@google.comf294b772012-04-27 14:29:26 +0000584 // walk through each clip element and perform its set op
585 for (int c = start; c < count; ++c) {
586
587 SkRegion::Op op = (c == start) ? startOp : clipIn.getOp(c);
588
589 if (SkRegion::kReplace_Op == op) {
590 // TODO: replace is actually a lot faster then intersection
591 // for this path - refactor the stencil path so it can handle
592 // replace ops and alter GrClip to allow them through
593
594 // clear the accumulator and draw the new object directly into it
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000595 clear(fGpu, accum, 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000596
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000597 setup_boolean_blendcoeffs(drawState, op);
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000598 this->drawClipShape(accum, clipIn, c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000599
600 } else if (SkRegion::kReverseDifference_Op == op ||
601 SkRegion::kIntersect_Op == op) {
602 // there is no point in intersecting a screen filling rectangle.
603 if (SkRegion::kIntersect_Op == op &&
604 kRect_ClipType == clipIn.getElementType(c) &&
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000605 contains(clipIn.getRect(c), *resultBounds)) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000606 continue;
607 }
608
robertphillips@google.comf105b102012-05-14 12:18:26 +0000609 getTemp(*resultBounds, &temp);
610 if (NULL == temp.texture()) {
robertphillips@google.comf105b102012-05-14 12:18:26 +0000611 fAACache.reset();
robertphillips@google.com6d62df42012-05-07 18:07:36 +0000612 return false;
613 }
614
robertphillips@google.comf294b772012-04-27 14:29:26 +0000615 // clear the temp target & draw into it
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000616 clear(fGpu, temp.texture(), 0x00000000);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000617
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000618 setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000619 this->drawClipShape(temp.texture(), clipIn, c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000620
621 // TODO: rather than adding these two translations here
622 // compute the bounding box needed to render the texture
623 // into temp
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000624 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000625 GrMatrix m;
626
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000627 m.setTranslate(SkIntToScalar(resultBounds->fLeft),
628 SkIntToScalar(resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000629
630 drawState->preConcatViewMatrix(m);
631 }
632
633 // Now draw into the accumulator using the real operation
634 // and the temp buffer as a texture
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000635 setup_boolean_blendcoeffs(drawState, op);
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000636 this->drawTexture(accum, temp.texture());
robertphillips@google.comf294b772012-04-27 14:29:26 +0000637
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000638 if (0 != resultBounds->fTop || 0 != resultBounds->fLeft) {
robertphillips@google.comf294b772012-04-27 14:29:26 +0000639 GrMatrix m;
640
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000641 m.setTranslate(SkIntToScalar(-resultBounds->fLeft),
642 SkIntToScalar(-resultBounds->fTop));
robertphillips@google.comf294b772012-04-27 14:29:26 +0000643
644 drawState->preConcatViewMatrix(m);
645 }
646
647 } else {
648 // all the remaining ops can just be directly draw into
649 // the accumulation buffer
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000650 setup_boolean_blendcoeffs(drawState, op);
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000651 this->drawClipShape(accum, clipIn, c);
robertphillips@google.comf294b772012-04-27 14:29:26 +0000652 }
653 }
654
robertphillips@google.coma72eef32012-05-01 17:22:59 +0000655 *result = accum;
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000656 fCurrClipMaskType = kAlpha_ClipMaskType;
robertphillips@google.comf294b772012-04-27 14:29:26 +0000657 return true;
658}
659
660////////////////////////////////////////////////////////////////////////////////
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000661// Create a 1-bit clip mask in the stencil buffer
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000662bool GrClipMaskManager::createStencilClipMask(const GrClip& clipIn,
bsalomon@google.coma3201942012-06-21 19:58:20 +0000663 const GrIRect& bounds) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000664
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000665 GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000666
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000667 GrDrawState* drawState = fGpu->drawState();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000668 GrAssert(drawState->isClipState());
669
670 GrRenderTarget* rt = drawState->getRenderTarget();
671 GrAssert(NULL != rt);
672
673 // TODO: dynamically attach a SB when needed.
674 GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
675 if (NULL == stencilBuffer) {
676 return false;
677 }
678
679 if (stencilBuffer->mustRenderClip(clipIn, rt->width(), rt->height())) {
680
681 stencilBuffer->setLastClip(clipIn, rt->width(), rt->height());
682
683 // we set the current clip to the bounds so that our recursive
684 // draws are scissored to them. We use the copy of the complex clip
685 // we just stashed on the SB to render from. We set it back after
686 // we finish drawing it into the stencil.
687 const GrClip& clipCopy = stencilBuffer->getLastClip();
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000688 fGpu->setClip(GrClip(bounds));
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000689
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000690 GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
691 drawState = fGpu->drawState();
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000692 drawState->setRenderTarget(rt);
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000693 GrDrawTarget::AutoGeometryPush agp(fGpu);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000694
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000695#if !VISUALIZE_COMPLEX_CLIP
696 drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
697#endif
698
699 int count = clipCopy.getElementCount();
700 int clipBit = stencilBuffer->bits();
701 SkASSERT((clipBit <= 16) &&
702 "Ganesh only handles 16b or smaller stencil buffers");
703 clipBit = (1 << (clipBit-1));
704
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000705 GrIRect rtRect = GrIRect::MakeWH(rt->width(), rt->height());
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000706
707 bool clearToInside;
robertphillips@google.com0f191f32012-04-25 15:23:36 +0000708 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000709 int start = process_initial_clip_elements(clipCopy,
710 rtRect,
711 &clearToInside,
712 &startOp);
713
bsalomon@google.coma3201942012-06-21 19:58:20 +0000714 fGpu->clearStencilClip(bounds, clearToInside);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000715
716 // walk through each clip element and perform its set op
717 // with the existing clip.
718 for (int c = start; c < count; ++c) {
719 GrPathFill fill;
720 bool fillInverted;
721 // enabled at bottom of loop
722 drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
723
724 bool canRenderDirectToStencil; // can the clip element be drawn
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +0000725 // directly to the stencil buffer
726 // with a non-inverted fill rule
727 // without extra passes to
728 // resolve in/out status.
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000729
robertphillips@google.comf294b772012-04-27 14:29:26 +0000730 SkRegion::Op op = (c == start) ? startOp : clipCopy.getOp(c);
731
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000732 GrPathRenderer* pr = NULL;
bsalomon@google.com8d033a12012-04-27 15:52:53 +0000733 const SkPath* clipPath = NULL;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000734 if (kRect_ClipType == clipCopy.getElementType(c)) {
735 canRenderDirectToStencil = true;
bsalomon@google.com47059542012-06-06 20:51:20 +0000736 fill = kEvenOdd_GrPathFill;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000737 fillInverted = false;
738 // there is no point in intersecting a screen filling
739 // rectangle.
robertphillips@google.comf294b772012-04-27 14:29:26 +0000740 if (SkRegion::kIntersect_Op == op &&
robertphillips@google.com6623fcd2012-05-15 16:47:23 +0000741 contains(clipCopy.getRect(c), rtRect)) {
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000742 continue;
743 }
744 } else {
745 fill = clipCopy.getPathFill(c);
746 fillInverted = GrIsFillInverted(fill);
747 fill = GrNonInvertedFill(fill);
748 clipPath = &clipCopy.getPath(c);
robertphillips@google.com2c756812012-05-22 20:28:23 +0000749 pr = this->getContext()->getPathRenderer(*clipPath,
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000750 fill, fGpu, false,
robertphillips@google.com72176b22012-05-23 13:19:12 +0000751 true);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000752 if (NULL == pr) {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000753 fGpu->setClip(clipCopy); // restore to the original
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000754 return false;
755 }
756 canRenderDirectToStencil =
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000757 !pr->requiresStencilPass(*clipPath, fill, fGpu);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000758 }
759
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000760 int passes;
761 GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
762
763 bool canDrawDirectToClip; // Given the renderer, the element,
764 // fill rule, and set operation can
765 // we render the element directly to
766 // stencil bit used for clipping.
767 canDrawDirectToClip =
768 GrStencilSettings::GetClipPasses(op,
769 canRenderDirectToStencil,
770 clipBit,
771 fillInverted,
772 &passes, stencilSettings);
773
774 // draw the element to the client stencil bits if necessary
775 if (!canDrawDirectToClip) {
776 GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
777 kIncClamp_StencilOp,
778 kIncClamp_StencilOp,
779 kAlways_StencilFunc,
780 0xffff,
781 0x0000,
782 0xffff);
783 SET_RANDOM_COLOR
784 if (kRect_ClipType == clipCopy.getElementType(c)) {
785 *drawState->stencil() = gDrawToStencil;
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000786 fGpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000787 } else {
788 if (canRenderDirectToStencil) {
789 *drawState->stencil() = gDrawToStencil;
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000790 pr->drawPath(*clipPath, fill, NULL, fGpu, 0, false);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000791 } else {
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000792 pr->drawPathToStencil(*clipPath, fill, fGpu);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000793 }
794 }
795 }
796
797 // now we modify the clip bit by rendering either the clip
798 // element directly or a bounding rect of the entire clip.
799 drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
800 for (int p = 0; p < passes; ++p) {
801 *drawState->stencil() = stencilSettings[p];
802 if (canDrawDirectToClip) {
803 if (kRect_ClipType == clipCopy.getElementType(c)) {
804 SET_RANDOM_COLOR
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000805 fGpu->drawSimpleRect(clipCopy.getRect(c), NULL, 0);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000806 } else {
807 SET_RANDOM_COLOR
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000808 pr->drawPath(*clipPath, fill, NULL, fGpu, 0, false);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000809 }
810 } else {
811 SET_RANDOM_COLOR
bsalomon@google.coma3201942012-06-21 19:58:20 +0000812 GrRect rect = GrRect::MakeLTRB(
813 SkIntToScalar(bounds.fLeft),
814 SkIntToScalar(bounds.fTop),
815 SkIntToScalar(bounds.fRight),
816 SkIntToScalar(bounds.fBottom));
817 fGpu->drawSimpleRect(rect, NULL, 0);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000818 }
819 }
820 }
821 // restore clip
bsalomon@google.com13b85aa2012-06-15 21:09:40 +0000822 fGpu->setClip(clipCopy);
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000823 }
bsalomon@google.comc8f7f472012-06-18 13:44:51 +0000824 // set this last because recursive draws may overwrite it back to kNone.
825 GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
826 fCurrClipMaskType = kStencil_ClipMaskType;
robertphillips@google.com1e945b72012-04-16 18:03:03 +0000827 return true;
828}
829
bsalomon@google.com411dad02012-06-05 20:24:20 +0000830// mapping of clip-respecting stencil funcs to normal stencil funcs
831// mapping depends on whether stencil-clipping is in effect.
832static const GrStencilFunc
833 gSpecialToBasicStencilFunc[2][kClipStencilFuncCount] = {
834 {// Stencil-Clipping is DISABLED, we are effectively always inside the clip
835 // In the Clip Funcs
836 kAlways_StencilFunc, // kAlwaysIfInClip_StencilFunc
837 kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
838 kLess_StencilFunc, // kLessIfInClip_StencilFunc
839 kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
840 // Special in the clip func that forces user's ref to be 0.
841 kNotEqual_StencilFunc, // kNonZeroIfInClip_StencilFunc
842 // make ref 0 and do normal nequal.
843 },
844 {// Stencil-Clipping is ENABLED
845 // In the Clip Funcs
846 kEqual_StencilFunc, // kAlwaysIfInClip_StencilFunc
847 // eq stencil clip bit, mask
848 // out user bits.
849
850 kEqual_StencilFunc, // kEqualIfInClip_StencilFunc
851 // add stencil bit to mask and ref
852
853 kLess_StencilFunc, // kLessIfInClip_StencilFunc
854 kLEqual_StencilFunc, // kLEqualIfInClip_StencilFunc
855 // for both of these we can add
856 // the clip bit to the mask and
857 // ref and compare as normal
858 // Special in the clip func that forces user's ref to be 0.
859 kLess_StencilFunc, // kNonZeroIfInClip_StencilFunc
860 // make ref have only the clip bit set
861 // and make comparison be less
862 // 10..0 < 1..user_bits..
863 }
864};
865
bsalomon@google.coma3201942012-06-21 19:58:20 +0000866namespace {
867// Sets the settings to clip against the stencil buffer clip while ignoring the
868// client bits.
869const GrStencilSettings& basic_apply_stencil_clip_settings() {
870 // stencil settings to use when clip is in stencil
871 GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
872 kKeep_StencilOp,
873 kKeep_StencilOp,
874 kAlwaysIfInClip_StencilFunc,
875 0x0000,
876 0x0000,
877 0x0000);
878 return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
879}
880}
881
882void GrClipMaskManager::setGpuStencil() {
883 // We make two copies of the StencilSettings here (except in the early
884 // exit scenario. One copy from draw state to the stack var. Then another
885 // from the stack var to the gpu. We could make this class hold a ptr to
886 // GrGpu's fStencilSettings and eliminate the stack copy here.
887
888 const GrDrawState& drawState = fGpu->getDrawState();
889
890 // use stencil for clipping if clipping is enabled and the clip
891 // has been written into the stencil.
892 GrClipMaskManager::StencilClipMode clipMode;
893 if (this->isClipInStencil() && drawState.isClipState()) {
894 clipMode = GrClipMaskManager::kRespectClip_StencilClipMode;
895 // We can't be modifying the clip and respecting it at the same time.
896 GrAssert(!drawState.isStateFlagEnabled(
897 GrGpu::kModifyStencilClip_StateBit));
898 } else if (drawState.isStateFlagEnabled(
899 GrGpu::kModifyStencilClip_StateBit)) {
900 clipMode = GrClipMaskManager::kModifyClip_StencilClipMode;
901 } else {
902 clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode;
903 }
904
905 GrStencilSettings settings;
906 // The GrGpu client may not be using the stencil buffer but we may need to
907 // enable it in order to respect a stencil clip.
908 if (drawState.getStencil().isDisabled()) {
909 if (GrClipMaskManager::kRespectClip_StencilClipMode == clipMode) {
910 settings = basic_apply_stencil_clip_settings();
911 } else {
912 fGpu->disableStencil();
913 return;
914 }
915 } else {
916 settings = drawState.getStencil();
917 }
918
919 // TODO: dynamically attach a stencil buffer
920 int stencilBits = 0;
921 GrStencilBuffer* stencilBuffer =
922 drawState.getRenderTarget()->getStencilBuffer();
923 if (NULL != stencilBuffer) {
924 stencilBits = stencilBuffer->bits();
925 }
926
927#if GR_DEBUG
928 if (!fGpu->getCaps().fStencilWrapOpsSupport) {
929 GrAssert(settings.frontPassOp() != kIncWrap_StencilOp);
930 GrAssert(settings.frontPassOp() != kDecWrap_StencilOp);
931 GrAssert(settings.frontFailOp() != kIncWrap_StencilOp);
932 GrAssert(settings.backFailOp() != kDecWrap_StencilOp);
933 GrAssert(settings.backPassOp() != kIncWrap_StencilOp);
934 GrAssert(settings.backPassOp() != kDecWrap_StencilOp);
935 GrAssert(settings.backFailOp() != kIncWrap_StencilOp);
936 GrAssert(settings.frontFailOp() != kDecWrap_StencilOp);
937 }
938#endif
939 this->adjustStencilParams(&settings, clipMode, stencilBits);
940 fGpu->setStencilSettings(settings);
941}
942
943void GrClipMaskManager::adjustStencilParams(GrStencilSettings* settings,
944 StencilClipMode mode,
945 int stencilBitCnt) {
bsalomon@google.com411dad02012-06-05 20:24:20 +0000946 GrAssert(stencilBitCnt > 0);
bsalomon@google.com411dad02012-06-05 20:24:20 +0000947
948 if (kModifyClip_StencilClipMode == mode) {
bsalomon@google.coma3201942012-06-21 19:58:20 +0000949 // We assume that this clip manager itself is drawing to the GrGpu and
950 // has already setup the correct values.
951 return;
bsalomon@google.com411dad02012-06-05 20:24:20 +0000952 }
bsalomon@google.coma3201942012-06-21 19:58:20 +0000953
bsalomon@google.com411dad02012-06-05 20:24:20 +0000954 unsigned int clipBit = (1 << (stencilBitCnt - 1));
955 unsigned int userBits = clipBit - 1;
956
bsalomon@google.coma3201942012-06-21 19:58:20 +0000957 GrStencilSettings::Face face = GrStencilSettings::kFront_Face;
958 bool twoSided = fGpu->getCaps().fTwoSidedStencilSupport;
bsalomon@google.com411dad02012-06-05 20:24:20 +0000959
bsalomon@google.coma3201942012-06-21 19:58:20 +0000960 bool finished = false;
961 while (!finished) {
962 GrStencilFunc func = settings->func(face);
963 uint16_t writeMask = settings->writeMask(face);
964 uint16_t funcMask = settings->funcMask(face);
965 uint16_t funcRef = settings->funcRef(face);
966
967 GrAssert((unsigned) func < kStencilFuncCount);
968
969 writeMask &= userBits;
970
971 if (func >= kBasicStencilFuncCount) {
972 int respectClip = kRespectClip_StencilClipMode == mode;
973 if (respectClip) {
974 // The GrGpu class should have checked this
975 GrAssert(this->isClipInStencil());
976 switch (func) {
977 case kAlwaysIfInClip_StencilFunc:
978 funcMask = clipBit;
979 funcRef = clipBit;
980 break;
981 case kEqualIfInClip_StencilFunc:
982 case kLessIfInClip_StencilFunc:
983 case kLEqualIfInClip_StencilFunc:
984 funcMask = (funcMask & userBits) | clipBit;
985 funcRef = (funcRef & userBits) | clipBit;
986 break;
987 case kNonZeroIfInClip_StencilFunc:
988 funcMask = (funcMask & userBits) | clipBit;
989 funcRef = clipBit;
990 break;
991 default:
992 GrCrash("Unknown stencil func");
993 }
994 } else {
995 funcMask &= userBits;
996 funcRef &= userBits;
bsalomon@google.com411dad02012-06-05 20:24:20 +0000997 }
bsalomon@google.coma3201942012-06-21 19:58:20 +0000998 const GrStencilFunc* table =
999 gSpecialToBasicStencilFunc[respectClip];
1000 func = table[func - kBasicStencilFuncCount];
1001 GrAssert(func >= 0 && func < kBasicStencilFuncCount);
bsalomon@google.com411dad02012-06-05 20:24:20 +00001002 } else {
bsalomon@google.coma3201942012-06-21 19:58:20 +00001003 funcMask &= userBits;
1004 funcRef &= userBits;
bsalomon@google.com411dad02012-06-05 20:24:20 +00001005 }
bsalomon@google.coma3201942012-06-21 19:58:20 +00001006
1007 settings->setFunc(face, func);
1008 settings->setWriteMask(face, writeMask);
1009 settings->setFuncMask(face, funcMask);
1010 settings->setFuncRef(face, funcRef);
1011
1012 if (GrStencilSettings::kFront_Face == face) {
1013 face = GrStencilSettings::kBack_Face;
1014 finished = !twoSided;
1015 } else {
1016 finished = true;
1017 }
bsalomon@google.com411dad02012-06-05 20:24:20 +00001018 }
bsalomon@google.coma3201942012-06-21 19:58:20 +00001019 if (!twoSided) {
1020 settings->copyFrontSettingsToBack();
1021 }
bsalomon@google.com411dad02012-06-05 20:24:20 +00001022}
1023
1024////////////////////////////////////////////////////////////////////////////////
1025
robertphillips@google.comfa662942012-05-17 12:20:22 +00001026namespace {
1027
1028GrPathFill invert_fill(GrPathFill fill) {
1029 static const GrPathFill gInvertedFillTable[] = {
bsalomon@google.com47059542012-06-06 20:51:20 +00001030 kInverseWinding_GrPathFill, // kWinding_GrPathFill
1031 kInverseEvenOdd_GrPathFill, // kEvenOdd_GrPathFill
1032 kWinding_GrPathFill, // kInverseWinding_GrPathFill
1033 kEvenOdd_GrPathFill, // kInverseEvenOdd_GrPathFill
1034 kHairLine_GrPathFill, // kHairLine_GrPathFill
robertphillips@google.comfa662942012-05-17 12:20:22 +00001035 };
bsalomon@google.com47059542012-06-06 20:51:20 +00001036 GR_STATIC_ASSERT(0 == kWinding_GrPathFill);
1037 GR_STATIC_ASSERT(1 == kEvenOdd_GrPathFill);
1038 GR_STATIC_ASSERT(2 == kInverseWinding_GrPathFill);
1039 GR_STATIC_ASSERT(3 == kInverseEvenOdd_GrPathFill);
1040 GR_STATIC_ASSERT(4 == kHairLine_GrPathFill);
1041 GR_STATIC_ASSERT(5 == kGrPathFillCount);
robertphillips@google.comfa662942012-05-17 12:20:22 +00001042 return gInvertedFillTable[fill];
1043}
1044
1045}
1046
bsalomon@google.com13b85aa2012-06-15 21:09:40 +00001047bool GrClipMaskManager::createSoftwareClipMask(const GrClip& clipIn,
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001048 GrTexture** result,
robertphillips@google.com6623fcd2012-05-15 16:47:23 +00001049 GrIRect *resultBounds) {
bsalomon@google.comc8f7f472012-06-18 13:44:51 +00001050 GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001051
bsalomon@google.com13b85aa2012-06-15 21:09:40 +00001052 if (this->clipMaskPreamble(clipIn, result, resultBounds)) {
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001053 return true;
1054 }
1055
robertphillips@google.comf105b102012-05-14 12:18:26 +00001056 GrTexture* accum = fAACache.getLastMask();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001057 if (NULL == accum) {
robertphillips@google.comf105b102012-05-14 12:18:26 +00001058 fAACache.reset();
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001059 return false;
1060 }
1061
robertphillips@google.com2c756812012-05-22 20:28:23 +00001062 GrSWMaskHelper helper(this->getContext());
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001063
robertphillips@google.comfa662942012-05-17 12:20:22 +00001064 helper.init(*resultBounds, NULL, false);
1065
1066 int count = clipIn.getElementCount();
1067
1068 bool clearToInside;
1069 SkRegion::Op startOp = SkRegion::kReplace_Op; // suppress warning
1070 int start = process_initial_clip_elements(clipIn,
1071 *resultBounds,
1072 &clearToInside,
1073 &startOp);
1074
1075 helper.clear(clearToInside ? SK_ColorWHITE : 0x00000000);
1076
1077 for (int i = start; i < count; ++i) {
1078
1079 SkRegion::Op op = (i == start) ? startOp : clipIn.getOp(i);
1080
1081 if (SkRegion::kIntersect_Op == op ||
1082 SkRegion::kReverseDifference_Op == op) {
1083 // Intersect and reverse difference require modifying pixels
1084 // outside of the geometry that is being "drawn". In both cases
1085 // we erase all the pixels outside of the geometry but
1086 // leave the pixels inside the geometry alone. For reverse
1087 // difference we invert all the pixels before clearing the ones
1088 // outside the geometry.
1089 if (SkRegion::kReverseDifference_Op == op) {
1090 SkRect temp = SkRect::MakeLTRB(
1091 SkIntToScalar(resultBounds->left()),
1092 SkIntToScalar(resultBounds->top()),
1093 SkIntToScalar(resultBounds->right()),
1094 SkIntToScalar(resultBounds->bottom()));
1095
1096 // invert the entire scene
1097 helper.draw(temp, SkRegion::kXOR_Op, false, SK_ColorWHITE);
1098 }
1099
1100 if (kRect_ClipType == clipIn.getElementType(i)) {
1101
1102 // convert the rect to a path so we can invert the fill
1103 SkPath temp;
1104 temp.addRect(clipIn.getRect(i));
1105
1106 helper.draw(temp, SkRegion::kReplace_Op,
bsalomon@google.com47059542012-06-06 20:51:20 +00001107 kInverseEvenOdd_GrPathFill, clipIn.getDoAA(i),
robertphillips@google.comfa662942012-05-17 12:20:22 +00001108 0x00000000);
1109 } else {
1110 GrAssert(kPath_ClipType == clipIn.getElementType(i));
1111
1112 helper.draw(clipIn.getPath(i),
1113 SkRegion::kReplace_Op,
1114 invert_fill(clipIn.getPathFill(i)),
1115 clipIn.getDoAA(i),
1116 0x00000000);
1117 }
1118
1119 continue;
1120 }
1121
1122 // The other ops (union, xor, diff) only affect pixels inside
1123 // the geometry so they can just be drawn normally
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001124 if (kRect_ClipType == clipIn.getElementType(i)) {
robertphillips@google.comfa662942012-05-17 12:20:22 +00001125
1126 helper.draw(clipIn.getRect(i),
1127 op,
1128 clipIn.getDoAA(i), SK_ColorWHITE);
1129
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001130 } else {
1131 GrAssert(kPath_ClipType == clipIn.getElementType(i));
1132
robertphillips@google.comfa662942012-05-17 12:20:22 +00001133 helper.draw(clipIn.getPath(i),
1134 op,
1135 clipIn.getPathFill(i),
1136 clipIn.getDoAA(i), SK_ColorWHITE);
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001137 }
1138 }
1139
robertphillips@google.comfa662942012-05-17 12:20:22 +00001140 // Because we are using the scratch texture cache, "accum" may be
1141 // larger than expected and have some cruft in the areas we aren't using.
1142 // Clear it out.
1143
1144 // TODO: need a simpler way to clear the texture - can we combine
1145 // the clear and the writePixels (inside toTexture)
bsalomon@google.com13b85aa2012-06-15 21:09:40 +00001146 GrDrawState* drawState = fGpu->drawState();
robertphillips@google.comfa662942012-05-17 12:20:22 +00001147 GrAssert(NULL != drawState);
1148 GrRenderTarget* temp = drawState->getRenderTarget();
bsalomon@google.com13b85aa2012-06-15 21:09:40 +00001149 clear(fGpu, accum, 0x00000000);
robertphillips@google.comfa662942012-05-17 12:20:22 +00001150 // can't leave the accum bound as a rendertarget
1151 drawState->setRenderTarget(temp);
1152
1153 helper.toTexture(accum);
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001154
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001155 *result = accum;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001156
bsalomon@google.comc8f7f472012-06-18 13:44:51 +00001157 fCurrClipMaskType = kAlpha_ClipMaskType;
robertphillips@google.com6b70a7b2012-05-11 15:32:48 +00001158 return true;
1159}
1160
robertphillips@google.comf294b772012-04-27 14:29:26 +00001161////////////////////////////////////////////////////////////////////////////////
robertphillips@google.comf105b102012-05-14 12:18:26 +00001162void GrClipMaskManager::releaseResources() {
robertphillips@google.comf105b102012-05-14 12:18:26 +00001163 fAACache.releaseResources();
robertphillips@google.com1e945b72012-04-16 18:03:03 +00001164}