blob: 38856e586ab446876539ab41c01b4078eb3f1bdf [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 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 */
reed@google.com5c3d1472011-02-22 19:12:23 +00008#include "SkClipStack.h"
9#include "SkPath.h"
10#include <new>
11
robertphillips@google.com607fe072012-07-24 13:54:00 +000012
reed@google.com5c3d1472011-02-22 19:12:23 +000013struct SkClipStack::Rec {
14 enum State {
15 kEmpty_State,
16 kRect_State,
17 kPath_State
18 };
19
20 SkPath fPath;
21 SkRect fRect;
22 int fSaveCount;
23 SkRegion::Op fOp;
24 State fState;
reed@google.com00177082011-10-12 14:34:30 +000025 bool fDoAA;
reed@google.com5c3d1472011-02-22 19:12:23 +000026
robertphillips@google.com607fe072012-07-24 13:54:00 +000027 // fFiniteBoundType and fFiniteBound are used to incrementally update
28 // the clip stack's bound. When fFiniteBoundType is kNormal_BoundsType,
29 // fFiniteBound represents the conservative bounding box of the pixels
30 // that aren't clipped (i.e., any pixels that can be drawn to are inside
31 // the bound). When fFiniteBoundType is kInsideOut_BoundsType (which occurs
32 // when a clip is inverse filled), fFiniteBound represents the
33 // conservative bounding box of the pixels that _are_ clipped (i.e., any
34 // pixels that cannot be drawn to are inside the bound). When
35 // fFiniteBoundType is kInsideOut_BoundsType the actual bound is
36 // the infinite plane. This behavior of fFiniteBoundType and
37 // fFiniteBound is required so that we can capture the cancelling out
38 // of the extensions to infinity when two inverse filled clips are
39 // Booleaned together.
40 SkClipStack::BoundsType fFiniteBoundType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000041 SkRect fFiniteBound;
42 bool fIsIntersectionOfRects;
robertphillips@google.com607fe072012-07-24 13:54:00 +000043
reed@google.com00177082011-10-12 14:34:30 +000044 Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) : fRect(rect) {
reed@google.com5c3d1472011-02-22 19:12:23 +000045 fSaveCount = saveCount;
46 fOp = op;
47 fState = kRect_State;
reed@google.com00177082011-10-12 14:34:30 +000048 fDoAA = doAA;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000049 // bounding box members are updated in a following updateBound call
reed@google.com5c3d1472011-02-22 19:12:23 +000050 }
51
reed@google.com00177082011-10-12 14:34:30 +000052 Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) : fPath(path) {
vandebo@chromium.orge1bc2742011-06-21 22:26:39 +000053 fRect.setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +000054 fSaveCount = saveCount;
55 fOp = op;
56 fState = kPath_State;
reed@google.com00177082011-10-12 14:34:30 +000057 fDoAA = doAA;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000058 // bounding box members are updated in a following updateBound call
reed@google.com5c3d1472011-02-22 19:12:23 +000059 }
60
robertphillips@google.com08eacc12012-08-02 12:49:00 +000061 void setEmpty() {
62 fState = kEmpty_State;
63 fFiniteBound.setEmpty();
64 fFiniteBoundType = kNormal_BoundsType;
65 fIsIntersectionOfRects = false;
66 }
67
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000068 bool operator==(const Rec& b) const {
reed@google.com00177082011-10-12 14:34:30 +000069 if (fSaveCount != b.fSaveCount || fOp != b.fOp || fState != b.fState ||
70 fDoAA != b.fDoAA) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000071 return false;
72 }
73 switch (fState) {
74 case kEmpty_State:
75 return true;
76 case kRect_State:
77 return fRect == b.fRect;
78 case kPath_State:
79 return fPath == b.fPath;
80 }
81 return false; // Silence the compiler.
82 }
83
84 bool operator!=(const Rec& b) const {
85 return !(*this == b);
86 }
87
88
reed@google.com5c3d1472011-02-22 19:12:23 +000089 /**
90 * Returns true if this Rec can be intersected in place with a new clip
91 */
robertphillips@google.com08eacc12012-08-02 12:49:00 +000092 bool canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000093 if (kEmpty_State == fState && (
94 SkRegion::kDifference_Op == op ||
95 SkRegion::kIntersect_Op == op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +000096 return true;
97 }
robertphillips@google.com08eacc12012-08-02 12:49:00 +000098 return fSaveCount == saveCount &&
99 SkRegion::kIntersect_Op == op &&
100 (SkRegion::kIntersect_Op == fOp || SkRegion::kReplace_Op == fOp);
reed@google.com5c3d1472011-02-22 19:12:23 +0000101 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000102
103 /**
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000104 * This method checks to see if two rect clips can be safely merged into
105 * one. The issue here is that to be strictly correct all the edges of
106 * the resulting rect must have the same anti-aliasing.
107 */
108 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
109 SkASSERT(kRect_State == fState);
110
111 if (fDoAA == newAA) {
112 // if the AA setting is the same there is no issue
113 return true;
114 }
115
116 if (!SkRect::Intersects(fRect, newR)) {
117 // The calling code will correctly set the result to the empty clip
118 return true;
119 }
120
121 if (fRect.contains(newR)) {
122 // if the new rect carves out a portion of the old one there is no
123 // issue
124 return true;
125 }
126
127 // So either the two overlap in some complex manner or newR contains oldR.
128 // In the first, case the edges will require different AA. In the second,
129 // the AA setting that would be carried forward is incorrect (e.g., oldR
130 // is AA while newR is BW but since newR contains oldR, oldR will be
131 // drawn BW) since the new AA setting will predominate.
132 return false;
133 }
134
135
136 /**
robertphillips@google.com607fe072012-07-24 13:54:00 +0000137 * The different combination of fill & inverse fill when combining
138 * bounding boxes
139 */
140 enum FillCombo {
141 kPrev_Cur_FillCombo,
142 kPrev_InvCur_FillCombo,
143 kInvPrev_Cur_FillCombo,
144 kInvPrev_InvCur_FillCombo
145 };
146
147 // a mirror of CombineBoundsRevDiff
148 void CombineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
149 switch (combination) {
150 case kInvPrev_InvCur_FillCombo:
151 // In this case the only pixels that can remain set
152 // are inside the current clip rect since the extensions
153 // to infinity of both clips cancel out and whatever
154 // is outside of the current clip is removed
155 fFiniteBoundType = kNormal_BoundsType;
156 break;
157 case kInvPrev_Cur_FillCombo:
158 // In this case the current op is finite so the only pixels
159 // that aren't set are whatever isn't set in the previous
160 // clip and whatever this clip carves out
161 fFiniteBound.join(prevFinite);
162 fFiniteBoundType = kInsideOut_BoundsType;
163 break;
164 case kPrev_InvCur_FillCombo:
165 // In this case everything outside of this clip's bound
166 // is erased, so the only pixels that can remain set
167 // occur w/in the intersection of the two finite bounds
168 if (!fFiniteBound.intersect(prevFinite)) {
169 fFiniteBound.setEmpty();
170 }
171 fFiniteBoundType = kNormal_BoundsType;
172 break;
173 case kPrev_Cur_FillCombo:
174 // The most conservative result bound is that of the
175 // prior clip. This could be wildly incorrect if the
176 // second clip either exactly matches the first clip
177 // (which should yield the empty set) or reduces the
178 // size of the prior bound (e.g., if the second clip
179 // exactly matched the bottom half of the prior clip).
180 // We ignore these two possibilities.
181 fFiniteBound = prevFinite;
182 break;
183 default:
184 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsDiff Invalid fill combination");
185 break;
186 }
187 }
188
189 void CombineBoundsXOR(int combination, const SkRect& prevFinite) {
190
191 switch (combination) {
192 case kInvPrev_Cur_FillCombo: // fall through
193 case kPrev_InvCur_FillCombo:
194 // With only one of the clips inverted the result will always
195 // extend to infinity. The only pixels that may be un-writeable
196 // lie within the union of the two finite bounds
197 fFiniteBound.join(prevFinite);
198 fFiniteBoundType = kInsideOut_BoundsType;
199 break;
200 case kInvPrev_InvCur_FillCombo:
201 // The only pixels that can survive are within the
202 // union of the two bounding boxes since the extensions
203 // to infinity of both clips cancel out
204 // fall through!
205 case kPrev_Cur_FillCombo:
206 // The most conservative bound for xor is the
207 // union of the two bounds. If the two clips exactly overlapped
208 // the xor could yield the empty set. Similarly the xor
209 // could reduce the size of the original clip's bound (e.g.,
210 // if the second clip exactly matched the bottom half of the
211 // first clip). We ignore these two cases.
212 fFiniteBound.join(prevFinite);
213 fFiniteBoundType = kNormal_BoundsType;
214 break;
215 default:
216 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsXOR Invalid fill combination");
217 break;
218 }
219 }
220
221 // a mirror of CombineBoundsIntersection
222 void CombineBoundsUnion(int combination, const SkRect& prevFinite) {
223
224 switch (combination) {
225 case kInvPrev_InvCur_FillCombo:
226 if (!fFiniteBound.intersect(prevFinite)) {
227 fFiniteBound.setEmpty();
228 }
229 fFiniteBoundType = kInsideOut_BoundsType;
230 break;
231 case kInvPrev_Cur_FillCombo:
232 // The only pixels that won't be drawable are inside
233 // the prior clip's finite bound
234 fFiniteBound = prevFinite;
235 fFiniteBoundType = kInsideOut_BoundsType;
236 break;
237 case kPrev_InvCur_FillCombo:
238 // The only pixels that won't be drawable are inside
239 // this clip's finite bound
240 break;
241 case kPrev_Cur_FillCombo:
242 fFiniteBound.join(prevFinite);
243 break;
244 default:
245 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsUnion Invalid fill combination");
246 break;
247 }
248 }
249
250 // a mirror of CombineBoundsUnion
251 void CombineBoundsIntersection(int combination, const SkRect& prevFinite) {
252
253 switch (combination) {
254 case kInvPrev_InvCur_FillCombo:
255 // The only pixels that aren't writable in this case
256 // occur in the union of the two finite bounds
257 fFiniteBound.join(prevFinite);
258 fFiniteBoundType = kInsideOut_BoundsType;
259 break;
260 case kInvPrev_Cur_FillCombo:
261 // In this case the only pixels that will remain writeable
262 // are within the current clip
263 break;
264 case kPrev_InvCur_FillCombo:
265 // In this case the only pixels that will remain writeable
266 // are with the previous clip
267 fFiniteBound = prevFinite;
268 fFiniteBoundType = kNormal_BoundsType;
269 break;
270 case kPrev_Cur_FillCombo:
271 if (!fFiniteBound.intersect(prevFinite)) {
272 fFiniteBound.setEmpty();
273 }
274 break;
275 default:
276 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsIntersection Invalid fill combination");
277 break;
278 }
279 }
280
281 // a mirror of CombineBoundsDiff
282 void CombineBoundsRevDiff(int combination, const SkRect& prevFinite) {
283
284 switch (combination) {
285 case kInvPrev_InvCur_FillCombo:
286 // The only pixels that can survive are in the
287 // previous bound since the extensions to infinity in
288 // both clips cancel out
289 fFiniteBound = prevFinite;
290 fFiniteBoundType = kNormal_BoundsType;
291 break;
292 case kInvPrev_Cur_FillCombo:
293 if (!fFiniteBound.intersect(prevFinite)) {
294 fFiniteBound.setEmpty();
295 }
296 fFiniteBoundType = kNormal_BoundsType;
297 break;
298 case kPrev_InvCur_FillCombo:
299 fFiniteBound.join(prevFinite);
300 fFiniteBoundType = kInsideOut_BoundsType;
301 break;
302 case kPrev_Cur_FillCombo:
303 // Fall through - as with the kDifference_Op case, the
304 // most conservative result bound is the bound of the
305 // current clip. The prior clip could reduce the size of this
306 // bound (as in the kDifference_Op case) but we are ignoring
307 // those cases.
308 break;
309 default:
310 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsRevDiff Invalid fill combination");
311 break;
312 }
313 }
314
315 void updateBound(const Rec* prior) {
316
317 // First, optimistically update the current Rec's bound information
318 // with the current clip's bound
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000319 fIsIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000320 if (kRect_State == fState) {
321 fFiniteBound = fRect;
322 fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000323
324 if (SkRegion::kReplace_Op == fOp ||
325 (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000326 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
327 prior->rectRectIntersectAllowed(fRect, fDoAA))) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000328 fIsIntersectionOfRects = true;
329 }
330
robertphillips@google.com607fe072012-07-24 13:54:00 +0000331 } else {
332 fFiniteBound = fPath.getBounds();
333
334 if (fPath.isInverseFillType()) {
335 fFiniteBoundType = kInsideOut_BoundsType;
336 } else {
337 fFiniteBoundType = kNormal_BoundsType;
338 }
339 }
340
341 if (!fDoAA) {
342 // Here we mimic a non-anti-aliased scanline system. If there is
343 // no anti-aliasing we can integerize the bounding box to exclude
344 // fractional parts that won't be rendered.
345 // Note: the left edge is handled slightly differently below. We
346 // are a bit more generous in the rounding since we don't want to
347 // risk missing the left pixels when fLeft is very close to .5
348 fFiniteBound.set(SkIntToScalar(SkScalarFloorToInt(fFiniteBound.fLeft+0.45f)),
349 SkIntToScalar(SkScalarRound(fFiniteBound.fTop)),
350 SkIntToScalar(SkScalarRound(fFiniteBound.fRight)),
351 SkIntToScalar(SkScalarRound(fFiniteBound.fBottom)));
352 }
353
354 // Now set up the previous Rec's bound information taking into
355 // account that there may be no previous clip
356 SkRect prevFinite;
357 SkClipStack::BoundsType prevType;
358
359 if (NULL == prior) {
360 // no prior clip means the entire plane is writable
361 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
362 prevType = kInsideOut_BoundsType;
363 } else {
364 prevFinite = prior->fFiniteBound;
365 prevType = prior->fFiniteBoundType;
366 }
367
368 FillCombo combination = kPrev_Cur_FillCombo;
369 if (kInsideOut_BoundsType == fFiniteBoundType) {
370 combination = (FillCombo) (combination | 0x01);
371 }
372 if (kInsideOut_BoundsType == prevType) {
373 combination = (FillCombo) (combination | 0x02);
374 }
375
376 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
377 kInvPrev_Cur_FillCombo == combination ||
378 kPrev_InvCur_FillCombo == combination ||
379 kPrev_Cur_FillCombo == combination);
380
381 // Now integrate with clip with the prior clips
382 switch (fOp) {
383 case SkRegion::kDifference_Op:
384 this->CombineBoundsDiff(combination, prevFinite);
385 break;
386 case SkRegion::kXOR_Op:
387 this->CombineBoundsXOR(combination, prevFinite);
388 break;
389 case SkRegion::kUnion_Op:
390 this->CombineBoundsUnion(combination, prevFinite);
391 break;
392 case SkRegion::kIntersect_Op:
393 this->CombineBoundsIntersection(combination, prevFinite);
394 break;
395 case SkRegion::kReverseDifference_Op:
396 this->CombineBoundsRevDiff(combination, prevFinite);
397 break;
398 case SkRegion::kReplace_Op:
399 // Replace just ignores everything prior
400 // The current clip's bound information is already filled in
401 // so nothing to do
402 break;
403 default:
404 SkDebugf("SkRegion::Op error/n");
405 SkASSERT(0);
406 break;
407 }
408 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000409};
410
411SkClipStack::SkClipStack() : fDeque(sizeof(Rec)) {
412 fSaveCount = 0;
413}
414
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000415SkClipStack::SkClipStack(const SkClipStack& b) : fDeque(sizeof(Rec)) {
416 *this = b;
417}
418
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000419SkClipStack::SkClipStack(const SkRect& r) : fDeque(sizeof(Rec)) {
420 if (!r.isEmpty()) {
421 this->clipDevRect(r, SkRegion::kReplace_Op, false);
422 }
423}
424
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000425SkClipStack::SkClipStack(const SkIRect& r) : fDeque(sizeof(Rec)) {
426 if (!r.isEmpty()) {
427 SkRect temp;
428 temp.set(r);
429 this->clipDevRect(temp, SkRegion::kReplace_Op, false);
430 }
431}
432
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000433SkClipStack::~SkClipStack() {
434 reset();
435}
436
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000437SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
438 if (this == &b) {
439 return *this;
440 }
441 reset();
442
443 fSaveCount = b.fSaveCount;
444 SkDeque::F2BIter recIter(b.fDeque);
445 for (const Rec* rec = (const Rec*)recIter.next();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000446 rec != NULL;
447 rec = (const Rec*)recIter.next()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000448 new (fDeque.push_back()) Rec(*rec);
449 }
450
451 return *this;
452}
453
454bool SkClipStack::operator==(const SkClipStack& b) const {
455 if (fSaveCount != b.fSaveCount || fDeque.count() != b.fDeque.count()) {
456 return false;
457 }
458 SkDeque::F2BIter myIter(fDeque);
459 SkDeque::F2BIter bIter(b.fDeque);
460 const Rec* myRec = (const Rec*)myIter.next();
461 const Rec* bRec = (const Rec*)bIter.next();
462
463 while (myRec != NULL && bRec != NULL) {
464 if (*myRec != *bRec) {
465 return false;
466 }
467 myRec = (const Rec*)myIter.next();
468 bRec = (const Rec*)bIter.next();
469 }
470 return myRec == NULL && bRec == NULL;
471}
472
reed@google.com5c3d1472011-02-22 19:12:23 +0000473void SkClipStack::reset() {
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000474 // We used a placement new for each object in fDeque, so we're responsible
475 // for calling the destructor on each of them as well.
476 while (!fDeque.empty()) {
477 Rec* rec = (Rec*)fDeque.back();
478 rec->~Rec();
479 fDeque.pop_back();
480 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000481
482 fSaveCount = 0;
483}
484
485void SkClipStack::save() {
486 fSaveCount += 1;
487}
488
489void SkClipStack::restore() {
490 fSaveCount -= 1;
491 while (!fDeque.empty()) {
492 Rec* rec = (Rec*)fDeque.back();
493 if (rec->fSaveCount <= fSaveCount) {
494 break;
495 }
496 rec->~Rec();
497 fDeque.pop_back();
498 }
499}
500
robertphillips@google.com7b112892012-07-31 15:18:21 +0000501void SkClipStack::getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000502 BoundsType* boundType,
503 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000504 SkASSERT(NULL != canvFiniteBound && NULL != boundType);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000505
506 Rec* rec = (Rec*)fDeque.back();
507
508 if (NULL == rec) {
509 // the clip is wide open - the infinite plane w/ no pixels un-writeable
robertphillips@google.com7b112892012-07-31 15:18:21 +0000510 canvFiniteBound->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000511 *boundType = kInsideOut_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000512 if (NULL != isIntersectionOfRects) {
513 *isIntersectionOfRects = false;
514 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000515 return;
516 }
517
robertphillips@google.com7b112892012-07-31 15:18:21 +0000518 *canvFiniteBound = rec->fFiniteBound;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000519 *boundType = rec->fFiniteBoundType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000520 if (NULL != isIntersectionOfRects) {
521 *isIntersectionOfRects = rec->fIsIntersectionOfRects;
522 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000523}
524
reed@google.com00177082011-10-12 14:34:30 +0000525void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000526
527 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
528 Rec* rec = (Rec*) iter.prev();
529
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000530 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000531 switch (rec->fState) {
532 case Rec::kEmpty_State:
robertphillips@google.com607fe072012-07-24 13:54:00 +0000533 SkASSERT(rec->fFiniteBound.isEmpty());
534 SkASSERT(kNormal_BoundsType == rec->fFiniteBoundType);
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000535 SkASSERT(!rec->fIsIntersectionOfRects);
reed@google.com5c3d1472011-02-22 19:12:23 +0000536 return;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000537 case Rec::kRect_State:
538 if (rec->rectRectIntersectAllowed(rect, doAA)) {
539 if (!rec->fRect.intersect(rect)) {
540 rec->setEmpty();
541 return;
542 }
543
544 rec->fDoAA = doAA;
545 Rec* prev = (Rec*) iter.prev();
546 rec->updateBound(prev);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000547 return;
reed@google.com5c3d1472011-02-22 19:12:23 +0000548 }
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000549 break;
reed@google.com5c3d1472011-02-22 19:12:23 +0000550 case Rec::kPath_State:
551 if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000552 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000553 return;
554 }
555 break;
556 }
557 }
reed@google.com00177082011-10-12 14:34:30 +0000558 new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000559 ((Rec*) fDeque.back())->updateBound(rec);
reed@google.com5c3d1472011-02-22 19:12:23 +0000560}
561
reed@google.com00177082011-10-12 14:34:30 +0000562void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
tomhudson@google.com4c433722012-03-09 16:48:20 +0000563 SkRect alt;
564 if (path.isRect(&alt)) {
565 return this->clipDevRect(alt, op, doAA);
566 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000567 Rec* rec = (Rec*)fDeque.back();
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000568 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000569 const SkRect& pathBounds = path.getBounds();
570 switch (rec->fState) {
571 case Rec::kEmpty_State:
robertphillips@google.com607fe072012-07-24 13:54:00 +0000572 SkASSERT(rec->fFiniteBound.isEmpty());
573 SkASSERT(kNormal_BoundsType == rec->fFiniteBoundType);
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000574 SkASSERT(!rec->fIsIntersectionOfRects);
reed@google.com5c3d1472011-02-22 19:12:23 +0000575 return;
576 case Rec::kRect_State:
577 if (!SkRect::Intersects(rec->fRect, pathBounds)) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000578 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000579 return;
580 }
581 break;
582 case Rec::kPath_State:
583 if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000584 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000585 return;
586 }
587 break;
588 }
589 }
reed@google.com00177082011-10-12 14:34:30 +0000590 new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000591 ((Rec*) fDeque.back())->updateBound(rec);
reed@google.com5c3d1472011-02-22 19:12:23 +0000592}
593
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000594bool SkClipStack::isWideOpen() const {
595 if (0 == fDeque.count()) {
596 return true;
597 }
598
599 const Rec* back = (const Rec*) fDeque.back();
600 return kInsideOut_BoundsType == back->fFiniteBoundType &&
601 back->fFiniteBound.isEmpty();
602}
603
reed@google.com5c3d1472011-02-22 19:12:23 +0000604///////////////////////////////////////////////////////////////////////////////
605
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000606SkClipStack::Iter::Iter() : fStack(NULL) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000607}
608
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000609bool operator==(const SkClipStack::Iter::Clip& a,
610 const SkClipStack::Iter::Clip& b) {
reed@google.com00177082011-10-12 14:34:30 +0000611 return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
vandebo@chromium.orge03c6522011-06-21 20:45:51 +0000612 ((a.fRect == NULL && b.fRect == NULL) ||
613 (a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
614 ((a.fPath == NULL && b.fPath == NULL) ||
615 (a.fPath != NULL && b.fPath != NULL && *a.fPath == *b.fPath));
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000616}
617
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000618bool operator!=(const SkClipStack::Iter::Clip& a,
619 const SkClipStack::Iter::Clip& b) {
vandebo@chromium.org8887ede2011-05-25 01:27:52 +0000620 return !(a == b);
621}
622
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000623SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
624 : fStack(&stack) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000625 this->reset(stack, startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000626}
627
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000628const SkClipStack::Iter::Clip* SkClipStack::Iter::updateClip(
629 const SkClipStack::Rec* rec) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000630 switch (rec->fState) {
631 case SkClipStack::Rec::kEmpty_State:
632 fClip.fRect = NULL;
633 fClip.fPath = NULL;
634 break;
635 case SkClipStack::Rec::kRect_State:
636 fClip.fRect = &rec->fRect;
637 fClip.fPath = NULL;
638 break;
639 case SkClipStack::Rec::kPath_State:
640 fClip.fRect = NULL;
641 fClip.fPath = &rec->fPath;
642 break;
643 }
644 fClip.fOp = rec->fOp;
reed@google.com00177082011-10-12 14:34:30 +0000645 fClip.fDoAA = rec->fDoAA;
reed@google.com5c3d1472011-02-22 19:12:23 +0000646 return &fClip;
647}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000648
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000649const SkClipStack::Iter::Clip* SkClipStack::Iter::next() {
650 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
651 if (NULL == rec) {
652 return NULL;
653 }
654
655 return this->updateClip(rec);
656}
657
658const SkClipStack::Iter::Clip* SkClipStack::Iter::prev() {
659 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.prev();
660 if (NULL == rec) {
661 return NULL;
662 }
663
664 return this->updateClip(rec);
665}
666
robertphillips@google.com80214e22012-07-20 15:33:18 +0000667const SkClipStack::Iter::Clip* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000668
669 if (NULL == fStack) {
670 return NULL;
671 }
672
673 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
674
675 const SkClipStack::Rec* rec = NULL;
676
677 for (rec = (const SkClipStack::Rec*) fIter.prev();
678 NULL != rec;
679 rec = (const SkClipStack::Rec*) fIter.prev()) {
680
681 if (op == rec->fOp) {
682 // The Deque's iterator is actually one pace ahead of the
683 // returned value. So while "rec" is the element we want to
684 // return, the iterator is actually pointing at (and will
685 // return on the next "next" or "prev" call) the element
686 // in front of it in the deque. Bump the iterator forward a
687 // step so we get the expected result.
688 if (NULL == fIter.next()) {
689 // The reverse iterator has run off the front of the deque
690 // (i.e., the "op" clip is the first clip) and can't
691 // recover. Reset the iterator to start at the front.
692 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
693 }
694 break;
695 }
696 }
697
698 if (NULL == rec) {
699 // There were no "op" clips
700 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
701 }
702
703 return this->next();
704}
705
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000706void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
robertphillips@google.coma6f11c42012-07-23 17:39:44 +0000707 fStack = &stack;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000708 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
bsalomon@google.comd302f142011-03-03 13:54:13 +0000709}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000710
711// helper method
712void SkClipStack::getConservativeBounds(int offsetX,
713 int offsetY,
714 int maxWidth,
715 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000716 SkRect* devBounds,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000717 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000718 SkASSERT(NULL != devBounds);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000719
robertphillips@google.com7b112892012-07-31 15:18:21 +0000720 devBounds->setLTRB(0, 0,
721 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000722
723 SkRect temp;
724 SkClipStack::BoundsType boundType;
725
robertphillips@google.com7b112892012-07-31 15:18:21 +0000726 // temp starts off in canvas space here
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000727 this->getBounds(&temp, &boundType, isIntersectionOfRects);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000728 if (SkClipStack::kInsideOut_BoundsType == boundType) {
729 return;
730 }
731
robertphillips@google.com7b112892012-07-31 15:18:21 +0000732 // but is converted to device space here
robertphillips@google.com607fe072012-07-24 13:54:00 +0000733 temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
734
robertphillips@google.com7b112892012-07-31 15:18:21 +0000735 if (!devBounds->intersect(temp)) {
736 devBounds->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000737 }
738}