blob: d5462876d1b77574d553f7fc4a8af275b3aa2492 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
fmalitac3b589a2014-06-05 12:40:07 -07007
8#include "SkCanvas.h"
reed@google.com5c3d1472011-02-22 19:12:23 +00009#include "SkClipStack.h"
10#include "SkPath.h"
robertphillips@google.com46f93502012-08-07 15:38:08 +000011#include "SkThread.h"
12
reed@google.com5c3d1472011-02-22 19:12:23 +000013#include <new>
14
robertphillips@google.com607fe072012-07-24 13:54:00 +000015
robertphillips@google.com46f93502012-08-07 15:38:08 +000016// 0-2 are reserved for invalid, empty & wide-open
bsalomon@google.comedb26fd2012-11-28 14:42:41 +000017static const int32_t kFirstUnreservedGenID = 3;
18int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
robertphillips@google.com46f93502012-08-07 15:38:08 +000019
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000020SkClipStack::Element::Element(const Element& that) {
21 switch (that.getType()) {
22 case kEmpty_Type:
23 fPath.reset();
24 break;
25 case kRect_Type: // Rect uses rrect
26 case kRRect_Type:
27 fPath.reset();
28 fRRect = that.fRRect;
29 break;
30 case kPath_Type:
31 fPath.set(that.getPath());
32 break;
33 }
34
35 fSaveCount = that.fSaveCount;
36 fOp = that.fOp;
37 fType = that.fType;
38 fDoAA = that.fDoAA;
39 fFiniteBoundType = that.fFiniteBoundType;
40 fFiniteBound = that.fFiniteBound;
41 fIsIntersectionOfRects = that.fIsIntersectionOfRects;
42 fGenID = that.fGenID;
43}
44
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000045bool SkClipStack::Element::operator== (const Element& element) const {
46 if (this == &element) {
47 return true;
48 }
49 if (fOp != element.fOp ||
50 fType != element.fType ||
51 fDoAA != element.fDoAA ||
52 fSaveCount != element.fSaveCount) {
53 return false;
54 }
55 switch (fType) {
56 case kPath_Type:
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000057 return this->getPath() == element.getPath();
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000058 case kRRect_Type:
59 return fRRect == element.fRRect;
60 case kRect_Type:
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +000061 return this->getRect() == element.getRect();
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000062 case kEmpty_Type:
63 return true;
64 default:
65 SkDEBUGFAIL("Unexpected type.");
66 return false;
67 }
68}
69
fmalitac3b589a2014-06-05 12:40:07 -070070void SkClipStack::Element::replay(SkCanvasClipVisitor* visitor) const {
71 static const SkRect kEmptyRect = { 0, 0, 0, 0 };
72
73 switch (fType) {
74 case kPath_Type:
75 visitor->clipPath(this->getPath(), this->getOp(), this->isAA());
76 break;
77 case kRRect_Type:
78 visitor->clipRRect(this->getRRect(), this->getOp(), this->isAA());
79 break;
80 case kRect_Type:
81 visitor->clipRect(this->getRect(), this->getOp(), this->isAA());
82 break;
83 case kEmpty_Type:
84 visitor->clipRect(kEmptyRect, SkRegion::kIntersect_Op, false);
85 break;
86 }
87}
88
bsalomon@google.comc6b3e482012-12-07 20:43:52 +000089void SkClipStack::Element::invertShapeFillType() {
90 switch (fType) {
91 case kRect_Type:
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000092 fPath.init();
93 fPath.get()->addRect(this->getRect());
94 fPath.get()->setFillType(SkPath::kInverseEvenOdd_FillType);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +000095 fType = kPath_Type;
96 break;
97 case kRRect_Type:
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +000098 fPath.init();
99 fPath.get()->addRRect(fRRect);
100 fPath.get()->setFillType(SkPath::kInverseEvenOdd_FillType);
bsalomon@google.comc6b3e482012-12-07 20:43:52 +0000101 fType = kPath_Type;
102 break;
103 case kPath_Type:
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000104 fPath.get()->toggleInverseFillType();
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000105 break;
bsalomon@google.comc6b3e482012-12-07 20:43:52 +0000106 case kEmpty_Type:
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000107 // Should this set to an empty, inverse filled path?
108 break;
109 }
110}
111
112void SkClipStack::Element::initPath(int saveCount, const SkPath& path, SkRegion::Op op,
113 bool doAA) {
114 if (!path.isInverseFillType()) {
115 if (SkPath::kNone_PathAsRect != path.asRect()) {
116 this->initRect(saveCount, path.getBounds(), op, doAA);
117 return;
118 }
119 SkRect ovalRect;
120 if (path.isOval(&ovalRect)) {
121 SkRRect rrect;
122 rrect.setOval(ovalRect);
123 this->initRRect(saveCount, rrect, op, doAA);
124 return;
125 }
126 }
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000127 fPath.set(path);
jvanverth0deb2d92014-10-24 12:41:32 -0700128 fPath.get()->setIsVolatile(true);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000129 fType = kPath_Type;
130 this->initCommon(saveCount, op, doAA);
131}
132
133void SkClipStack::Element::asPath(SkPath* path) const {
134 switch (fType) {
135 case kEmpty_Type:
136 path->reset();
137 break;
138 case kRect_Type:
139 path->reset();
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000140 path->addRect(this->getRect());
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000141 break;
142 case kRRect_Type:
143 path->reset();
144 path->addRRect(fRRect);
145 break;
146 case kPath_Type:
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000147 *path = *fPath.get();
bsalomon@google.comc6b3e482012-12-07 20:43:52 +0000148 break;
149 }
jvanverth0deb2d92014-10-24 12:41:32 -0700150 path->setIsVolatile(true);
bsalomon@google.comc6b3e482012-12-07 20:43:52 +0000151}
152
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +0000153void SkClipStack::Element::setEmpty() {
154 fType = kEmpty_Type;
155 fFiniteBound.setEmpty();
156 fFiniteBoundType = kNormal_BoundsType;
157 fIsIntersectionOfRects = false;
commit-bot@chromium.org9cb671a2014-02-16 14:45:45 +0000158 fRRect.setEmpty();
159 fPath.reset();
160 fGenID = kEmptyGenID;
161 SkDEBUGCODE(this->checkEmpty();)
162}
163
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000164void SkClipStack::Element::checkEmpty() const {
165 SkASSERT(fFiniteBound.isEmpty());
166 SkASSERT(kNormal_BoundsType == fFiniteBoundType);
167 SkASSERT(!fIsIntersectionOfRects);
168 SkASSERT(kEmptyGenID == fGenID);
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000169 SkASSERT(!fPath.isValid());
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000170}
reed@google.com5c3d1472011-02-22 19:12:23 +0000171
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000172bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const {
173 if (kEmpty_Type == fType &&
174 (SkRegion::kDifference_Op == op || SkRegion::kIntersect_Op == op)) {
175 return true;
176 }
177 // Only clips within the same save/restore frame (as captured by
178 // the save count) can be merged
179 return fSaveCount == saveCount &&
180 SkRegion::kIntersect_Op == op &&
181 (SkRegion::kIntersect_Op == fOp || SkRegion::kReplace_Op == fOp);
182}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000183
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000184bool SkClipStack::Element::rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
185 SkASSERT(kRect_Type == fType);
186
187 if (fDoAA == newAA) {
188 // if the AA setting is the same there is no issue
189 return true;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000190 }
191
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000192 if (!SkRect::Intersects(this->getRect(), newR)) {
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000193 // The calling code will correctly set the result to the empty clip
194 return true;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000195 }
196
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000197 if (this->getRect().contains(newR)) {
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000198 // if the new rect carves out a portion of the old one there is no
199 // issue
200 return true;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000201 }
202
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000203 // So either the two overlap in some complex manner or newR contains oldR.
204 // In the first, case the edges will require different AA. In the second,
205 // the AA setting that would be carried forward is incorrect (e.g., oldR
206 // is AA while newR is BW but since newR contains oldR, oldR will be
207 // drawn BW) since the new AA setting will predominate.
208 return false;
209}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000210
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000211// a mirror of combineBoundsRevDiff
212void SkClipStack::Element::combineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
213 switch (combination) {
214 case kInvPrev_InvCur_FillCombo:
215 // In this case the only pixels that can remain set
216 // are inside the current clip rect since the extensions
217 // to infinity of both clips cancel out and whatever
218 // is outside of the current clip is removed
robertphillips@google.com607fe072012-07-24 13:54:00 +0000219 fFiniteBoundType = kNormal_BoundsType;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000220 break;
221 case kInvPrev_Cur_FillCombo:
222 // In this case the current op is finite so the only pixels
223 // that aren't set are whatever isn't set in the previous
224 // clip and whatever this clip carves out
225 fFiniteBound.join(prevFinite);
226 fFiniteBoundType = kInsideOut_BoundsType;
227 break;
228 case kPrev_InvCur_FillCombo:
229 // In this case everything outside of this clip's bound
230 // is erased, so the only pixels that can remain set
231 // occur w/in the intersection of the two finite bounds
232 if (!fFiniteBound.intersect(prevFinite)) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000233 this->setEmpty();
234 } else {
235 fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000236 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000237 break;
238 case kPrev_Cur_FillCombo:
239 // The most conservative result bound is that of the
240 // prior clip. This could be wildly incorrect if the
241 // second clip either exactly matches the first clip
242 // (which should yield the empty set) or reduces the
243 // size of the prior bound (e.g., if the second clip
244 // exactly matched the bottom half of the prior clip).
245 // We ignore these two possibilities.
246 fFiniteBound = prevFinite;
247 break;
248 default:
249 SkDEBUGFAIL("SkClipStack::Element::combineBoundsDiff Invalid fill combination");
250 break;
251 }
252}
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000253
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000254void SkClipStack::Element::combineBoundsXOR(int combination, const SkRect& prevFinite) {
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000255
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000256 switch (combination) {
257 case kInvPrev_Cur_FillCombo: // fall through
258 case kPrev_InvCur_FillCombo:
259 // With only one of the clips inverted the result will always
260 // extend to infinity. The only pixels that may be un-writeable
261 // lie within the union of the two finite bounds
262 fFiniteBound.join(prevFinite);
263 fFiniteBoundType = kInsideOut_BoundsType;
264 break;
265 case kInvPrev_InvCur_FillCombo:
266 // The only pixels that can survive are within the
267 // union of the two bounding boxes since the extensions
268 // to infinity of both clips cancel out
269 // fall through!
270 case kPrev_Cur_FillCombo:
271 // The most conservative bound for xor is the
272 // union of the two bounds. If the two clips exactly overlapped
273 // the xor could yield the empty set. Similarly the xor
274 // could reduce the size of the original clip's bound (e.g.,
275 // if the second clip exactly matched the bottom half of the
276 // first clip). We ignore these two cases.
277 fFiniteBound.join(prevFinite);
278 fFiniteBoundType = kNormal_BoundsType;
279 break;
280 default:
281 SkDEBUGFAIL("SkClipStack::Element::combineBoundsXOR Invalid fill combination");
282 break;
283 }
284}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000285
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000286// a mirror of combineBoundsIntersection
287void SkClipStack::Element::combineBoundsUnion(int combination, const SkRect& prevFinite) {
288
289 switch (combination) {
290 case kInvPrev_InvCur_FillCombo:
291 if (!fFiniteBound.intersect(prevFinite)) {
292 fFiniteBound.setEmpty();
293 fGenID = kWideOpenGenID;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000294 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000295 fFiniteBoundType = kInsideOut_BoundsType;
296 break;
297 case kInvPrev_Cur_FillCombo:
298 // The only pixels that won't be drawable are inside
299 // the prior clip's finite bound
300 fFiniteBound = prevFinite;
301 fFiniteBoundType = kInsideOut_BoundsType;
302 break;
303 case kPrev_InvCur_FillCombo:
304 // The only pixels that won't be drawable are inside
305 // this clip's finite bound
306 break;
307 case kPrev_Cur_FillCombo:
308 fFiniteBound.join(prevFinite);
309 break;
310 default:
311 SkDEBUGFAIL("SkClipStack::Element::combineBoundsUnion Invalid fill combination");
312 break;
313 }
314}
315
316// a mirror of combineBoundsUnion
317void SkClipStack::Element::combineBoundsIntersection(int combination, const SkRect& prevFinite) {
318
319 switch (combination) {
320 case kInvPrev_InvCur_FillCombo:
321 // The only pixels that aren't writable in this case
322 // occur in the union of the two finite bounds
323 fFiniteBound.join(prevFinite);
324 fFiniteBoundType = kInsideOut_BoundsType;
325 break;
326 case kInvPrev_Cur_FillCombo:
327 // In this case the only pixels that will remain writeable
328 // are within the current clip
329 break;
330 case kPrev_InvCur_FillCombo:
331 // In this case the only pixels that will remain writeable
332 // are with the previous clip
333 fFiniteBound = prevFinite;
334 fFiniteBoundType = kNormal_BoundsType;
335 break;
336 case kPrev_Cur_FillCombo:
337 if (!fFiniteBound.intersect(prevFinite)) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000338 this->setEmpty();
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000339 }
340 break;
341 default:
342 SkDEBUGFAIL("SkClipStack::Element::combineBoundsIntersection Invalid fill combination");
343 break;
344 }
345}
346
347// a mirror of combineBoundsDiff
348void SkClipStack::Element::combineBoundsRevDiff(int combination, const SkRect& prevFinite) {
349
350 switch (combination) {
351 case kInvPrev_InvCur_FillCombo:
352 // The only pixels that can survive are in the
353 // previous bound since the extensions to infinity in
354 // both clips cancel out
355 fFiniteBound = prevFinite;
356 fFiniteBoundType = kNormal_BoundsType;
357 break;
358 case kInvPrev_Cur_FillCombo:
359 if (!fFiniteBound.intersect(prevFinite)) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000360 this->setEmpty();
361 } else {
362 fFiniteBoundType = kNormal_BoundsType;
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000363 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000364 break;
365 case kPrev_InvCur_FillCombo:
366 fFiniteBound.join(prevFinite);
367 fFiniteBoundType = kInsideOut_BoundsType;
368 break;
369 case kPrev_Cur_FillCombo:
370 // Fall through - as with the kDifference_Op case, the
371 // most conservative result bound is the bound of the
372 // current clip. The prior clip could reduce the size of this
373 // bound (as in the kDifference_Op case) but we are ignoring
374 // those cases.
375 break;
376 default:
377 SkDEBUGFAIL("SkClipStack::Element::combineBoundsRevDiff Invalid fill combination");
378 break;
379 }
380}
381
382void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
383 // We set this first here but we may overwrite it later if we determine that the clip is
384 // either wide-open or empty.
385 fGenID = GetNextGenID();
386
387 // First, optimistically update the current Element's bound information
388 // with the current clip's bound
389 fIsIntersectionOfRects = false;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000390 switch (fType) {
391 case kRect_Type:
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000392 fFiniteBound = this->getRect();
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000393 fFiniteBoundType = kNormal_BoundsType;
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000394
395 if (SkRegion::kReplace_Op == fOp ||
396 (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
397 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000398 prior->rectRectIntersectAllowed(this->getRect(), fDoAA))) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000399 fIsIntersectionOfRects = true;
400 }
401 break;
402 case kRRect_Type:
403 fFiniteBound = fRRect.getBounds();
404 fFiniteBoundType = kNormal_BoundsType;
405 break;
406 case kPath_Type:
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000407 fFiniteBound = fPath.get()->getBounds();
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000408
commit-bot@chromium.org6f954b92014-02-27 17:39:46 +0000409 if (fPath.get()->isInverseFillType()) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000410 fFiniteBoundType = kInsideOut_BoundsType;
411 } else {
412 fFiniteBoundType = kNormal_BoundsType;
413 }
414 break;
415 case kEmpty_Type:
416 SkDEBUGFAIL("We shouldn't get here with an empty element.");
417 break;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000418 }
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000419
420 if (!fDoAA) {
421 // Here we mimic a non-anti-aliased scanline system. If there is
422 // no anti-aliasing we can integerize the bounding box to exclude
423 // fractional parts that won't be rendered.
424 // Note: the left edge is handled slightly differently below. We
425 // are a bit more generous in the rounding since we don't want to
426 // risk missing the left pixels when fLeft is very close to .5
reed@google.come1ca7052013-12-17 19:22:07 +0000427 fFiniteBound.set(SkScalarFloorToScalar(fFiniteBound.fLeft+0.45f),
428 SkScalarRoundToScalar(fFiniteBound.fTop),
429 SkScalarRoundToScalar(fFiniteBound.fRight),
430 SkScalarRoundToScalar(fFiniteBound.fBottom));
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000431 }
432
433 // Now determine the previous Element's bound information taking into
434 // account that there may be no previous clip
435 SkRect prevFinite;
436 SkClipStack::BoundsType prevType;
437
438 if (NULL == prior) {
439 // no prior clip means the entire plane is writable
440 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
441 prevType = kInsideOut_BoundsType;
442 } else {
443 prevFinite = prior->fFiniteBound;
444 prevType = prior->fFiniteBoundType;
445 }
446
447 FillCombo combination = kPrev_Cur_FillCombo;
448 if (kInsideOut_BoundsType == fFiniteBoundType) {
449 combination = (FillCombo) (combination | 0x01);
450 }
451 if (kInsideOut_BoundsType == prevType) {
452 combination = (FillCombo) (combination | 0x02);
453 }
454
455 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
456 kInvPrev_Cur_FillCombo == combination ||
457 kPrev_InvCur_FillCombo == combination ||
458 kPrev_Cur_FillCombo == combination);
459
460 // Now integrate with clip with the prior clips
461 switch (fOp) {
462 case SkRegion::kDifference_Op:
463 this->combineBoundsDiff(combination, prevFinite);
464 break;
465 case SkRegion::kXOR_Op:
466 this->combineBoundsXOR(combination, prevFinite);
467 break;
468 case SkRegion::kUnion_Op:
469 this->combineBoundsUnion(combination, prevFinite);
470 break;
471 case SkRegion::kIntersect_Op:
472 this->combineBoundsIntersection(combination, prevFinite);
473 break;
474 case SkRegion::kReverseDifference_Op:
475 this->combineBoundsRevDiff(combination, prevFinite);
476 break;
477 case SkRegion::kReplace_Op:
478 // Replace just ignores everything prior
479 // The current clip's bound information is already filled in
480 // so nothing to do
481 break;
482 default:
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000483 SkDebugf("SkRegion::Op error\n");
bsalomon@google.com8a98e3b2012-11-29 21:05:13 +0000484 SkASSERT(0);
485 break;
486 }
487}
reed@google.com5c3d1472011-02-22 19:12:23 +0000488
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000489// This constant determines how many Element's are allocated together as a block in
robertphillips@google.comf9a90842012-08-17 14:25:43 +0000490// the deque. As such it needs to balance allocating too much memory vs.
491// incurring allocation/deallocation thrashing. It should roughly correspond to
492// the deepest save/restore stack we expect to see.
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000493static const int kDefaultElementAllocCnt = 8;
robertphillips@google.com46f93502012-08-07 15:38:08 +0000494
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000495SkClipStack::SkClipStack()
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000496 : fDeque(sizeof(Element), kDefaultElementAllocCnt)
robertphillips@google.com46f93502012-08-07 15:38:08 +0000497 , fSaveCount(0) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000498}
499
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000500SkClipStack::SkClipStack(const SkClipStack& b)
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000501 : fDeque(sizeof(Element), kDefaultElementAllocCnt) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000502 *this = b;
503}
504
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000505SkClipStack::SkClipStack(const SkRect& r)
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000506 : fDeque(sizeof(Element), kDefaultElementAllocCnt)
robertphillips@google.com46f93502012-08-07 15:38:08 +0000507 , fSaveCount(0) {
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000508 if (!r.isEmpty()) {
509 this->clipDevRect(r, SkRegion::kReplace_Op, false);
510 }
511}
512
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000513SkClipStack::SkClipStack(const SkIRect& r)
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000514 : fDeque(sizeof(Element), kDefaultElementAllocCnt)
robertphillips@google.com46f93502012-08-07 15:38:08 +0000515 , fSaveCount(0) {
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000516 if (!r.isEmpty()) {
517 SkRect temp;
518 temp.set(r);
519 this->clipDevRect(temp, SkRegion::kReplace_Op, false);
520 }
521}
522
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000523SkClipStack::~SkClipStack() {
524 reset();
525}
526
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000527SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
528 if (this == &b) {
529 return *this;
530 }
531 reset();
532
533 fSaveCount = b.fSaveCount;
534 SkDeque::F2BIter recIter(b.fDeque);
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000535 for (const Element* element = (const Element*)recIter.next();
536 element != NULL;
537 element = (const Element*)recIter.next()) {
538 new (fDeque.push_back()) Element(*element);
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000539 }
540
541 return *this;
542}
543
544bool SkClipStack::operator==(const SkClipStack& b) const {
commit-bot@chromium.org86b39f32014-01-06 16:54:20 +0000545 if (this->getTopmostGenID() == b.getTopmostGenID()) {
546 return true;
547 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000548 if (fSaveCount != b.fSaveCount ||
robertphillips@google.com46f93502012-08-07 15:38:08 +0000549 fDeque.count() != b.fDeque.count()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000550 return false;
551 }
552 SkDeque::F2BIter myIter(fDeque);
553 SkDeque::F2BIter bIter(b.fDeque);
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000554 const Element* myElement = (const Element*)myIter.next();
555 const Element* bElement = (const Element*)bIter.next();
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000556
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000557 while (myElement != NULL && bElement != NULL) {
558 if (*myElement != *bElement) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000559 return false;
560 }
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000561 myElement = (const Element*)myIter.next();
562 bElement = (const Element*)bIter.next();
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000563 }
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000564 return myElement == NULL && bElement == NULL;
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000565}
566
reed@google.com5c3d1472011-02-22 19:12:23 +0000567void SkClipStack::reset() {
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000568 // We used a placement new for each object in fDeque, so we're responsible
569 // for calling the destructor on each of them as well.
570 while (!fDeque.empty()) {
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000571 Element* element = (Element*)fDeque.back();
572 element->~Element();
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000573 fDeque.pop_back();
574 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000575
576 fSaveCount = 0;
577}
578
579void SkClipStack::save() {
580 fSaveCount += 1;
581}
582
583void SkClipStack::restore() {
584 fSaveCount -= 1;
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000585 restoreTo(fSaveCount);
586}
587
588void SkClipStack::restoreTo(int saveCount) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000589 while (!fDeque.empty()) {
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000590 Element* element = (Element*)fDeque.back();
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000591 if (element->fSaveCount <= saveCount) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000592 break;
593 }
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000594 element->~Element();
reed@google.com5c3d1472011-02-22 19:12:23 +0000595 fDeque.pop_back();
596 }
597}
598
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000599void SkClipStack::getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000600 BoundsType* boundType,
601 bool* isIntersectionOfRects) const {
bsalomon49f085d2014-09-05 13:34:00 -0700602 SkASSERT(canvFiniteBound && boundType);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000603
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000604 Element* element = (Element*)fDeque.back();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000605
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000606 if (NULL == element) {
robertphillips@google.com607fe072012-07-24 13:54:00 +0000607 // the clip is wide open - the infinite plane w/ no pixels un-writeable
robertphillips@google.com7b112892012-07-31 15:18:21 +0000608 canvFiniteBound->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000609 *boundType = kInsideOut_BoundsType;
bsalomon49f085d2014-09-05 13:34:00 -0700610 if (isIntersectionOfRects) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000611 *isIntersectionOfRects = false;
612 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000613 return;
614 }
615
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000616 *canvFiniteBound = element->fFiniteBound;
617 *boundType = element->fFiniteBoundType;
bsalomon49f085d2014-09-05 13:34:00 -0700618 if (isIntersectionOfRects) {
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000619 *isIntersectionOfRects = element->fIsIntersectionOfRects;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000620 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000621}
622
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000623bool SkClipStack::intersectRectWithClip(SkRect* rect) const {
bsalomon49f085d2014-09-05 13:34:00 -0700624 SkASSERT(rect);
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000625
626 SkRect bounds;
627 SkClipStack::BoundsType bt;
628 this->getBounds(&bounds, &bt);
629 if (bt == SkClipStack::kInsideOut_BoundsType) {
630 if (bounds.contains(*rect)) {
631 return false;
632 } else {
633 // If rect's x values are both within bound's x range we
634 // could clip here. Same for y. But we don't bother to check.
635 return true;
636 }
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +0000637 } else {
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000638 return rect->intersect(bounds);
639 }
640}
641
junov@chromium.org8cdf0f52012-12-12 17:58:15 +0000642bool SkClipStack::quickContains(const SkRect& rect) const {
643
644 Iter iter(*this, Iter::kTop_IterStart);
645 const Element* element = iter.prev();
646 while (element != NULL) {
647 if (SkRegion::kIntersect_Op != element->getOp() && SkRegion::kReplace_Op != element->getOp())
648 return false;
649 if (element->isInverseFilled()) {
650 // Part of 'rect' could be trimmed off by the inverse-filled clip element
651 if (SkRect::Intersects(element->getBounds(), rect)) {
652 return false;
653 }
654 } else {
655 if (!element->contains(rect)) {
656 return false;
657 }
658 }
659 if (SkRegion::kReplace_Op == element->getOp()) {
660 break;
661 }
662 element = iter.prev();
663 }
664 return true;
665}
666
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000667void SkClipStack::pushElement(const Element& element) {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000668 // Use reverse iterator instead of back because Rect path may need previous
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000669 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000670 Element* prior = (Element*) iter.prev();
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000671
bsalomon49f085d2014-09-05 13:34:00 -0700672 if (prior) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000673 if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) {
674 switch (prior->fType) {
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000675 case Element::kEmpty_Type:
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000676 SkDEBUGCODE(prior->checkEmpty();)
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000677 return;
678 case Element::kRect_Type:
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000679 if (Element::kRect_Type == element.getType()) {
680 if (prior->rectRectIntersectAllowed(element.getRect(), element.isAA())) {
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000681 SkRect isectRect;
682 if (!isectRect.intersect(prior->getRect(), element.getRect())) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000683 prior->setEmpty();
684 return;
685 }
686
commit-bot@chromium.org032a52f2014-02-21 20:09:13 +0000687 prior->fRRect.setRect(isectRect);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000688 prior->fDoAA = element.isAA();
689 Element* priorPrior = (Element*) iter.prev();
690 prior->updateBoundAndGenID(priorPrior);
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000691 return;
692 }
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000693 break;
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000694 }
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000695 // fallthrough
696 default:
697 if (!SkRect::Intersects(prior->getBounds(), element.getBounds())) {
698 prior->setEmpty();
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000699 return;
700 }
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000701 break;
702 }
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000703 } else if (SkRegion::kReplace_Op == element.getOp()) {
commit-bot@chromium.org6fbe54c2013-06-11 11:01:48 +0000704 this->restoreTo(fSaveCount - 1);
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000705 prior = (Element*) fDeque.back();
reed@google.com5c3d1472011-02-22 19:12:23 +0000706 }
707 }
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000708 Element* newElement = SkNEW_PLACEMENT_ARGS(fDeque.push_back(), Element, (element));
709 newElement->updateBoundAndGenID(prior);
710}
711
712void SkClipStack::clipDevRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
713 Element element(fSaveCount, rrect, op, doAA);
714 this->pushElement(element);
715}
716
717void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
718 Element element(fSaveCount, rect, op, doAA);
719 this->pushElement(element);
reed@google.com5c3d1472011-02-22 19:12:23 +0000720}
721
reed@google.com00177082011-10-12 14:34:30 +0000722void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000723 Element element(fSaveCount, path, op, doAA);
724 this->pushElement(element);
reed@google.com5c3d1472011-02-22 19:12:23 +0000725}
726
reed@google.com0557d9e2012-08-16 15:59:59 +0000727void SkClipStack::clipEmpty() {
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000728 Element* element = (Element*) fDeque.back();
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000729
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000730 if (element && element->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
commit-bot@chromium.orge5b2af92014-02-16 13:25:24 +0000731 element->setEmpty();
reed@google.com0557d9e2012-08-16 15:59:59 +0000732 }
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000733 new (fDeque.push_back()) Element(fSaveCount);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000734
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000735 ((Element*)fDeque.back())->fGenID = kEmptyGenID;
reed@google.com0557d9e2012-08-16 15:59:59 +0000736}
737
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000738bool SkClipStack::isWideOpen() const {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000739 return this->getTopmostGenID() == kWideOpenGenID;
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000740}
741
reed@google.com5c3d1472011-02-22 19:12:23 +0000742///////////////////////////////////////////////////////////////////////////////
743
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000744SkClipStack::Iter::Iter() : fStack(NULL) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000745}
746
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000747SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
748 : fStack(&stack) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000749 this->reset(stack, startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000750}
751
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000752const SkClipStack::Element* SkClipStack::Iter::next() {
753 return (const SkClipStack::Element*)fIter.next();
reed@google.com5c3d1472011-02-22 19:12:23 +0000754}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000755
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000756const SkClipStack::Element* SkClipStack::Iter::prev() {
757 return (const SkClipStack::Element*)fIter.prev();
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000758}
759
bsalomon@google.com8182fa02012-12-04 14:06:06 +0000760const SkClipStack::Element* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000761
762 if (NULL == fStack) {
763 return NULL;
764 }
765
766 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
767
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000768 const SkClipStack::Element* element = NULL;
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000769
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000770 for (element = (const SkClipStack::Element*) fIter.prev();
bsalomon49f085d2014-09-05 13:34:00 -0700771 element;
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000772 element = (const SkClipStack::Element*) fIter.prev()) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000773
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000774 if (op == element->fOp) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000775 // The Deque's iterator is actually one pace ahead of the
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000776 // returned value. So while "element" is the element we want to
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000777 // return, the iterator is actually pointing at (and will
778 // return on the next "next" or "prev" call) the element
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000779 // in front of it in the deque. Bump the iterator forward a
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000780 // step so we get the expected result.
781 if (NULL == fIter.next()) {
782 // The reverse iterator has run off the front of the deque
783 // (i.e., the "op" clip is the first clip) and can't
784 // recover. Reset the iterator to start at the front.
785 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
786 }
787 break;
788 }
789 }
790
bsalomon@google.com9128edc2012-11-29 18:58:19 +0000791 if (NULL == element) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000792 // There were no "op" clips
793 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
794 }
795
796 return this->next();
797}
798
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000799void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
robertphillips@google.coma6f11c42012-07-23 17:39:44 +0000800 fStack = &stack;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000801 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
bsalomon@google.comd302f142011-03-03 13:54:13 +0000802}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000803
804// helper method
805void SkClipStack::getConservativeBounds(int offsetX,
806 int offsetY,
807 int maxWidth,
808 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000809 SkRect* devBounds,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000810 bool* isIntersectionOfRects) const {
bsalomon49f085d2014-09-05 13:34:00 -0700811 SkASSERT(devBounds);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000812
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000813 devBounds->setLTRB(0, 0,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000814 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000815
816 SkRect temp;
817 SkClipStack::BoundsType boundType;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000818
robertphillips@google.com7b112892012-07-31 15:18:21 +0000819 // temp starts off in canvas space here
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000820 this->getBounds(&temp, &boundType, isIntersectionOfRects);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000821 if (SkClipStack::kInsideOut_BoundsType == boundType) {
822 return;
823 }
824
robertphillips@google.com7b112892012-07-31 15:18:21 +0000825 // but is converted to device space here
robertphillips@google.com607fe072012-07-24 13:54:00 +0000826 temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
827
robertphillips@google.com7b112892012-07-31 15:18:21 +0000828 if (!devBounds->intersect(temp)) {
829 devBounds->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000830 }
831}
robertphillips@google.com46f93502012-08-07 15:38:08 +0000832
robertphillips@google.com46f93502012-08-07 15:38:08 +0000833int32_t SkClipStack::GetNextGenID() {
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000834 // TODO: handle overflow.
robertphillips@google.com46f93502012-08-07 15:38:08 +0000835 return sk_atomic_inc(&gGenID);
836}
robertphillips@google.com73e71022012-08-09 18:10:49 +0000837
838int32_t SkClipStack::getTopmostGenID() const {
robertphillips@google.com73e71022012-08-09 18:10:49 +0000839 if (fDeque.empty()) {
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000840 return kWideOpenGenID;
robertphillips@google.com73e71022012-08-09 18:10:49 +0000841 }
842
commit-bot@chromium.orgd3e58422013-11-05 15:03:08 +0000843 const Element* back = static_cast<const Element*>(fDeque.back());
844 if (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.isEmpty()) {
845 return kWideOpenGenID;
846 }
847
848 return back->getGenID();
robertphillips@google.com73e71022012-08-09 18:10:49 +0000849}
bsalomonb6b02522014-06-09 07:59:06 -0700850
851#ifdef SK_DEVELOPER
852void SkClipStack::Element::dump() const {
853 static const char* kTypeStrings[] = {
854 "empty",
855 "rect",
856 "rrect",
857 "path"
858 };
859 SK_COMPILE_ASSERT(0 == kEmpty_Type, type_str);
860 SK_COMPILE_ASSERT(1 == kRect_Type, type_str);
861 SK_COMPILE_ASSERT(2 == kRRect_Type, type_str);
862 SK_COMPILE_ASSERT(3 == kPath_Type, type_str);
863 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kTypeStrings) == kTypeCnt, type_str);
864
865 static const char* kOpStrings[] = {
866 "difference",
867 "intersect",
868 "union",
869 "xor",
870 "reverse-difference",
871 "replace",
872 };
873 SK_COMPILE_ASSERT(0 == SkRegion::kDifference_Op, op_str);
874 SK_COMPILE_ASSERT(1 == SkRegion::kIntersect_Op, op_str);
875 SK_COMPILE_ASSERT(2 == SkRegion::kUnion_Op, op_str);
876 SK_COMPILE_ASSERT(3 == SkRegion::kXOR_Op, op_str);
877 SK_COMPILE_ASSERT(4 == SkRegion::kReverseDifference_Op, op_str);
878 SK_COMPILE_ASSERT(5 == SkRegion::kReplace_Op, op_str);
879 SK_COMPILE_ASSERT(SK_ARRAY_COUNT(kOpStrings) == SkRegion::kOpCnt, op_str);
880
881 SkDebugf("Type: %s, Op: %s, AA: %s, Save Count: %d\n", kTypeStrings[fType],
882 kOpStrings[fOp], (fDoAA ? "yes" : "no"), fSaveCount);
883 switch (fType) {
884 case kEmpty_Type:
885 SkDebugf("\n");
886 break;
887 case kRect_Type:
888 this->getRect().dump();
889 SkDebugf("\n");
890 break;
891 case kRRect_Type:
892 this->getRRect().dump();
893 SkDebugf("\n");
894 break;
895 case kPath_Type:
caryclarke9562592014-09-15 09:26:09 -0700896 this->getPath().dump(NULL, true, false);
bsalomonb6b02522014-06-09 07:59:06 -0700897 break;
898 }
899}
900
901void SkClipStack::dump() const {
902 B2TIter iter(*this);
903 const Element* e;
904 while ((e = iter.next())) {
905 e->dump();
906 SkDebugf("\n");
907 }
908}
909#endif