blob: d2c2035befb6959f30d5114008e76907e5909490 [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
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000061 bool operator==(const Rec& b) const {
reed@google.com00177082011-10-12 14:34:30 +000062 if (fSaveCount != b.fSaveCount || fOp != b.fOp || fState != b.fState ||
63 fDoAA != b.fDoAA) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000064 return false;
65 }
66 switch (fState) {
67 case kEmpty_State:
68 return true;
69 case kRect_State:
70 return fRect == b.fRect;
71 case kPath_State:
72 return fPath == b.fPath;
73 }
74 return false; // Silence the compiler.
75 }
76
77 bool operator!=(const Rec& b) const {
78 return !(*this == b);
79 }
80
81
reed@google.com5c3d1472011-02-22 19:12:23 +000082 /**
83 * Returns true if this Rec can be intersected in place with a new clip
84 */
85 bool canBeIntersected(int saveCount, SkRegion::Op op) const {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000086 if (kEmpty_State == fState && (
87 SkRegion::kDifference_Op == op ||
88 SkRegion::kIntersect_Op == op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +000089 return true;
90 }
91 return fSaveCount == saveCount &&
92 SkRegion::kIntersect_Op == fOp &&
93 SkRegion::kIntersect_Op == op;
94 }
robertphillips@google.com607fe072012-07-24 13:54:00 +000095
96 /**
97 * The different combination of fill & inverse fill when combining
98 * bounding boxes
99 */
100 enum FillCombo {
101 kPrev_Cur_FillCombo,
102 kPrev_InvCur_FillCombo,
103 kInvPrev_Cur_FillCombo,
104 kInvPrev_InvCur_FillCombo
105 };
106
107 // a mirror of CombineBoundsRevDiff
108 void CombineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
109 switch (combination) {
110 case kInvPrev_InvCur_FillCombo:
111 // In this case the only pixels that can remain set
112 // are inside the current clip rect since the extensions
113 // to infinity of both clips cancel out and whatever
114 // is outside of the current clip is removed
115 fFiniteBoundType = kNormal_BoundsType;
116 break;
117 case kInvPrev_Cur_FillCombo:
118 // In this case the current op is finite so the only pixels
119 // that aren't set are whatever isn't set in the previous
120 // clip and whatever this clip carves out
121 fFiniteBound.join(prevFinite);
122 fFiniteBoundType = kInsideOut_BoundsType;
123 break;
124 case kPrev_InvCur_FillCombo:
125 // In this case everything outside of this clip's bound
126 // is erased, so the only pixels that can remain set
127 // occur w/in the intersection of the two finite bounds
128 if (!fFiniteBound.intersect(prevFinite)) {
129 fFiniteBound.setEmpty();
130 }
131 fFiniteBoundType = kNormal_BoundsType;
132 break;
133 case kPrev_Cur_FillCombo:
134 // The most conservative result bound is that of the
135 // prior clip. This could be wildly incorrect if the
136 // second clip either exactly matches the first clip
137 // (which should yield the empty set) or reduces the
138 // size of the prior bound (e.g., if the second clip
139 // exactly matched the bottom half of the prior clip).
140 // We ignore these two possibilities.
141 fFiniteBound = prevFinite;
142 break;
143 default:
144 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsDiff Invalid fill combination");
145 break;
146 }
147 }
148
149 void CombineBoundsXOR(int combination, const SkRect& prevFinite) {
150
151 switch (combination) {
152 case kInvPrev_Cur_FillCombo: // fall through
153 case kPrev_InvCur_FillCombo:
154 // With only one of the clips inverted the result will always
155 // extend to infinity. The only pixels that may be un-writeable
156 // lie within the union of the two finite bounds
157 fFiniteBound.join(prevFinite);
158 fFiniteBoundType = kInsideOut_BoundsType;
159 break;
160 case kInvPrev_InvCur_FillCombo:
161 // The only pixels that can survive are within the
162 // union of the two bounding boxes since the extensions
163 // to infinity of both clips cancel out
164 // fall through!
165 case kPrev_Cur_FillCombo:
166 // The most conservative bound for xor is the
167 // union of the two bounds. If the two clips exactly overlapped
168 // the xor could yield the empty set. Similarly the xor
169 // could reduce the size of the original clip's bound (e.g.,
170 // if the second clip exactly matched the bottom half of the
171 // first clip). We ignore these two cases.
172 fFiniteBound.join(prevFinite);
173 fFiniteBoundType = kNormal_BoundsType;
174 break;
175 default:
176 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsXOR Invalid fill combination");
177 break;
178 }
179 }
180
181 // a mirror of CombineBoundsIntersection
182 void CombineBoundsUnion(int combination, const SkRect& prevFinite) {
183
184 switch (combination) {
185 case kInvPrev_InvCur_FillCombo:
186 if (!fFiniteBound.intersect(prevFinite)) {
187 fFiniteBound.setEmpty();
188 }
189 fFiniteBoundType = kInsideOut_BoundsType;
190 break;
191 case kInvPrev_Cur_FillCombo:
192 // The only pixels that won't be drawable are inside
193 // the prior clip's finite bound
194 fFiniteBound = prevFinite;
195 fFiniteBoundType = kInsideOut_BoundsType;
196 break;
197 case kPrev_InvCur_FillCombo:
198 // The only pixels that won't be drawable are inside
199 // this clip's finite bound
200 break;
201 case kPrev_Cur_FillCombo:
202 fFiniteBound.join(prevFinite);
203 break;
204 default:
205 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsUnion Invalid fill combination");
206 break;
207 }
208 }
209
210 // a mirror of CombineBoundsUnion
211 void CombineBoundsIntersection(int combination, const SkRect& prevFinite) {
212
213 switch (combination) {
214 case kInvPrev_InvCur_FillCombo:
215 // The only pixels that aren't writable in this case
216 // occur in the union of the two finite bounds
217 fFiniteBound.join(prevFinite);
218 fFiniteBoundType = kInsideOut_BoundsType;
219 break;
220 case kInvPrev_Cur_FillCombo:
221 // In this case the only pixels that will remain writeable
222 // are within the current clip
223 break;
224 case kPrev_InvCur_FillCombo:
225 // In this case the only pixels that will remain writeable
226 // are with the previous clip
227 fFiniteBound = prevFinite;
228 fFiniteBoundType = kNormal_BoundsType;
229 break;
230 case kPrev_Cur_FillCombo:
231 if (!fFiniteBound.intersect(prevFinite)) {
232 fFiniteBound.setEmpty();
233 }
234 break;
235 default:
236 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsIntersection Invalid fill combination");
237 break;
238 }
239 }
240
241 // a mirror of CombineBoundsDiff
242 void CombineBoundsRevDiff(int combination, const SkRect& prevFinite) {
243
244 switch (combination) {
245 case kInvPrev_InvCur_FillCombo:
246 // The only pixels that can survive are in the
247 // previous bound since the extensions to infinity in
248 // both clips cancel out
249 fFiniteBound = prevFinite;
250 fFiniteBoundType = kNormal_BoundsType;
251 break;
252 case kInvPrev_Cur_FillCombo:
253 if (!fFiniteBound.intersect(prevFinite)) {
254 fFiniteBound.setEmpty();
255 }
256 fFiniteBoundType = kNormal_BoundsType;
257 break;
258 case kPrev_InvCur_FillCombo:
259 fFiniteBound.join(prevFinite);
260 fFiniteBoundType = kInsideOut_BoundsType;
261 break;
262 case kPrev_Cur_FillCombo:
263 // Fall through - as with the kDifference_Op case, the
264 // most conservative result bound is the bound of the
265 // current clip. The prior clip could reduce the size of this
266 // bound (as in the kDifference_Op case) but we are ignoring
267 // those cases.
268 break;
269 default:
270 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsRevDiff Invalid fill combination");
271 break;
272 }
273 }
274
275 void updateBound(const Rec* prior) {
276
277 // First, optimistically update the current Rec's bound information
278 // with the current clip's bound
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000279 fIsIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000280 if (kRect_State == fState) {
281 fFiniteBound = fRect;
282 fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000283
284 if (SkRegion::kReplace_Op == fOp ||
285 (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
286 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects)) {
287 fIsIntersectionOfRects = true;
288 }
289
robertphillips@google.com607fe072012-07-24 13:54:00 +0000290 } else {
291 fFiniteBound = fPath.getBounds();
292
293 if (fPath.isInverseFillType()) {
294 fFiniteBoundType = kInsideOut_BoundsType;
295 } else {
296 fFiniteBoundType = kNormal_BoundsType;
297 }
298 }
299
300 if (!fDoAA) {
301 // Here we mimic a non-anti-aliased scanline system. If there is
302 // no anti-aliasing we can integerize the bounding box to exclude
303 // fractional parts that won't be rendered.
304 // Note: the left edge is handled slightly differently below. We
305 // are a bit more generous in the rounding since we don't want to
306 // risk missing the left pixels when fLeft is very close to .5
307 fFiniteBound.set(SkIntToScalar(SkScalarFloorToInt(fFiniteBound.fLeft+0.45f)),
308 SkIntToScalar(SkScalarRound(fFiniteBound.fTop)),
309 SkIntToScalar(SkScalarRound(fFiniteBound.fRight)),
310 SkIntToScalar(SkScalarRound(fFiniteBound.fBottom)));
311 }
312
313 // Now set up the previous Rec's bound information taking into
314 // account that there may be no previous clip
315 SkRect prevFinite;
316 SkClipStack::BoundsType prevType;
317
318 if (NULL == prior) {
319 // no prior clip means the entire plane is writable
320 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
321 prevType = kInsideOut_BoundsType;
322 } else {
323 prevFinite = prior->fFiniteBound;
324 prevType = prior->fFiniteBoundType;
325 }
326
327 FillCombo combination = kPrev_Cur_FillCombo;
328 if (kInsideOut_BoundsType == fFiniteBoundType) {
329 combination = (FillCombo) (combination | 0x01);
330 }
331 if (kInsideOut_BoundsType == prevType) {
332 combination = (FillCombo) (combination | 0x02);
333 }
334
335 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
336 kInvPrev_Cur_FillCombo == combination ||
337 kPrev_InvCur_FillCombo == combination ||
338 kPrev_Cur_FillCombo == combination);
339
340 // Now integrate with clip with the prior clips
341 switch (fOp) {
342 case SkRegion::kDifference_Op:
343 this->CombineBoundsDiff(combination, prevFinite);
344 break;
345 case SkRegion::kXOR_Op:
346 this->CombineBoundsXOR(combination, prevFinite);
347 break;
348 case SkRegion::kUnion_Op:
349 this->CombineBoundsUnion(combination, prevFinite);
350 break;
351 case SkRegion::kIntersect_Op:
352 this->CombineBoundsIntersection(combination, prevFinite);
353 break;
354 case SkRegion::kReverseDifference_Op:
355 this->CombineBoundsRevDiff(combination, prevFinite);
356 break;
357 case SkRegion::kReplace_Op:
358 // Replace just ignores everything prior
359 // The current clip's bound information is already filled in
360 // so nothing to do
361 break;
362 default:
363 SkDebugf("SkRegion::Op error/n");
364 SkASSERT(0);
365 break;
366 }
367 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000368};
369
370SkClipStack::SkClipStack() : fDeque(sizeof(Rec)) {
371 fSaveCount = 0;
372}
373
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000374SkClipStack::SkClipStack(const SkClipStack& b) : fDeque(sizeof(Rec)) {
375 *this = b;
376}
377
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000378SkClipStack::SkClipStack(const SkRect& r) : fDeque(sizeof(Rec)) {
379 if (!r.isEmpty()) {
380 this->clipDevRect(r, SkRegion::kReplace_Op, false);
381 }
382}
383
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000384SkClipStack::~SkClipStack() {
385 reset();
386}
387
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000388SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
389 if (this == &b) {
390 return *this;
391 }
392 reset();
393
394 fSaveCount = b.fSaveCount;
395 SkDeque::F2BIter recIter(b.fDeque);
396 for (const Rec* rec = (const Rec*)recIter.next();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000397 rec != NULL;
398 rec = (const Rec*)recIter.next()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000399 new (fDeque.push_back()) Rec(*rec);
400 }
401
402 return *this;
403}
404
405bool SkClipStack::operator==(const SkClipStack& b) const {
406 if (fSaveCount != b.fSaveCount || fDeque.count() != b.fDeque.count()) {
407 return false;
408 }
409 SkDeque::F2BIter myIter(fDeque);
410 SkDeque::F2BIter bIter(b.fDeque);
411 const Rec* myRec = (const Rec*)myIter.next();
412 const Rec* bRec = (const Rec*)bIter.next();
413
414 while (myRec != NULL && bRec != NULL) {
415 if (*myRec != *bRec) {
416 return false;
417 }
418 myRec = (const Rec*)myIter.next();
419 bRec = (const Rec*)bIter.next();
420 }
421 return myRec == NULL && bRec == NULL;
422}
423
reed@google.com5c3d1472011-02-22 19:12:23 +0000424void SkClipStack::reset() {
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000425 // We used a placement new for each object in fDeque, so we're responsible
426 // for calling the destructor on each of them as well.
427 while (!fDeque.empty()) {
428 Rec* rec = (Rec*)fDeque.back();
429 rec->~Rec();
430 fDeque.pop_back();
431 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000432
433 fSaveCount = 0;
434}
435
436void SkClipStack::save() {
437 fSaveCount += 1;
438}
439
440void SkClipStack::restore() {
441 fSaveCount -= 1;
442 while (!fDeque.empty()) {
443 Rec* rec = (Rec*)fDeque.back();
444 if (rec->fSaveCount <= fSaveCount) {
445 break;
446 }
447 rec->~Rec();
448 fDeque.pop_back();
449 }
450}
451
robertphillips@google.com7b112892012-07-31 15:18:21 +0000452void SkClipStack::getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000453 BoundsType* boundType,
454 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000455 SkASSERT(NULL != canvFiniteBound && NULL != boundType);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000456
457 Rec* rec = (Rec*)fDeque.back();
458
459 if (NULL == rec) {
460 // the clip is wide open - the infinite plane w/ no pixels un-writeable
robertphillips@google.com7b112892012-07-31 15:18:21 +0000461 canvFiniteBound->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000462 *boundType = kInsideOut_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000463 if (NULL != isIntersectionOfRects) {
464 *isIntersectionOfRects = false;
465 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000466 return;
467 }
468
robertphillips@google.com7b112892012-07-31 15:18:21 +0000469 *canvFiniteBound = rec->fFiniteBound;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000470 *boundType = rec->fFiniteBoundType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000471 if (NULL != isIntersectionOfRects) {
472 *isIntersectionOfRects = rec->fIsIntersectionOfRects;
473 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000474}
475
reed@google.com00177082011-10-12 14:34:30 +0000476void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000477
478 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
479 Rec* rec = (Rec*) iter.prev();
480
reed@google.com5c3d1472011-02-22 19:12:23 +0000481 if (rec && rec->canBeIntersected(fSaveCount, op)) {
482 switch (rec->fState) {
483 case Rec::kEmpty_State:
robertphillips@google.com607fe072012-07-24 13:54:00 +0000484 SkASSERT(rec->fFiniteBound.isEmpty());
485 SkASSERT(kNormal_BoundsType == rec->fFiniteBoundType);
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000486 SkASSERT(!rec->fIsIntersectionOfRects);
reed@google.com5c3d1472011-02-22 19:12:23 +0000487 return;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000488 case Rec::kRect_State: {
reed@google.com5c3d1472011-02-22 19:12:23 +0000489 if (!rec->fRect.intersect(rect)) {
490 rec->fState = Rec::kEmpty_State;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000491 rec->fFiniteBound.setEmpty();
492 rec->fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000493 rec->fIsIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000494 return;
reed@google.com5c3d1472011-02-22 19:12:23 +0000495 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000496
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000497 Rec* prev = (Rec*) iter.prev();
498 rec->updateBound(prev);
reed@google.com5c3d1472011-02-22 19:12:23 +0000499 return;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000500 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000501 case Rec::kPath_State:
502 if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
503 rec->fState = Rec::kEmpty_State;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000504 rec->fFiniteBound.setEmpty();
505 rec->fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000506 rec->fIsIntersectionOfRects = false;
reed@google.com5c3d1472011-02-22 19:12:23 +0000507 return;
508 }
509 break;
510 }
511 }
reed@google.com00177082011-10-12 14:34:30 +0000512 new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000513 ((Rec*) fDeque.back())->updateBound(rec);
reed@google.com5c3d1472011-02-22 19:12:23 +0000514}
515
reed@google.com00177082011-10-12 14:34:30 +0000516void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
tomhudson@google.com4c433722012-03-09 16:48:20 +0000517 SkRect alt;
518 if (path.isRect(&alt)) {
519 return this->clipDevRect(alt, op, doAA);
520 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000521 Rec* rec = (Rec*)fDeque.back();
522 if (rec && rec->canBeIntersected(fSaveCount, op)) {
523 const SkRect& pathBounds = path.getBounds();
524 switch (rec->fState) {
525 case Rec::kEmpty_State:
robertphillips@google.com607fe072012-07-24 13:54:00 +0000526 SkASSERT(rec->fFiniteBound.isEmpty());
527 SkASSERT(kNormal_BoundsType == rec->fFiniteBoundType);
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000528 SkASSERT(!rec->fIsIntersectionOfRects);
reed@google.com5c3d1472011-02-22 19:12:23 +0000529 return;
530 case Rec::kRect_State:
531 if (!SkRect::Intersects(rec->fRect, pathBounds)) {
532 rec->fState = Rec::kEmpty_State;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000533 rec->fFiniteBound.setEmpty();
534 rec->fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000535 rec->fIsIntersectionOfRects = false;
reed@google.com5c3d1472011-02-22 19:12:23 +0000536 return;
537 }
538 break;
539 case Rec::kPath_State:
540 if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
541 rec->fState = Rec::kEmpty_State;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000542 rec->fFiniteBound.setEmpty();
543 rec->fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000544 rec->fIsIntersectionOfRects = false;
reed@google.com5c3d1472011-02-22 19:12:23 +0000545 return;
546 }
547 break;
548 }
549 }
reed@google.com00177082011-10-12 14:34:30 +0000550 new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000551 ((Rec*) fDeque.back())->updateBound(rec);
reed@google.com5c3d1472011-02-22 19:12:23 +0000552}
553
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000554bool SkClipStack::isWideOpen() const {
555 if (0 == fDeque.count()) {
556 return true;
557 }
558
559 const Rec* back = (const Rec*) fDeque.back();
560 return kInsideOut_BoundsType == back->fFiniteBoundType &&
561 back->fFiniteBound.isEmpty();
562}
563
reed@google.com5c3d1472011-02-22 19:12:23 +0000564///////////////////////////////////////////////////////////////////////////////
565
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000566SkClipStack::Iter::Iter() : fStack(NULL) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000567}
568
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000569bool operator==(const SkClipStack::Iter::Clip& a,
570 const SkClipStack::Iter::Clip& b) {
reed@google.com00177082011-10-12 14:34:30 +0000571 return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
vandebo@chromium.orge03c6522011-06-21 20:45:51 +0000572 ((a.fRect == NULL && b.fRect == NULL) ||
573 (a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
574 ((a.fPath == NULL && b.fPath == NULL) ||
575 (a.fPath != NULL && b.fPath != NULL && *a.fPath == *b.fPath));
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000576}
577
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000578bool operator!=(const SkClipStack::Iter::Clip& a,
579 const SkClipStack::Iter::Clip& b) {
vandebo@chromium.org8887ede2011-05-25 01:27:52 +0000580 return !(a == b);
581}
582
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000583SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
584 : fStack(&stack) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000585 this->reset(stack, startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000586}
587
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000588const SkClipStack::Iter::Clip* SkClipStack::Iter::updateClip(
589 const SkClipStack::Rec* rec) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000590 switch (rec->fState) {
591 case SkClipStack::Rec::kEmpty_State:
592 fClip.fRect = NULL;
593 fClip.fPath = NULL;
594 break;
595 case SkClipStack::Rec::kRect_State:
596 fClip.fRect = &rec->fRect;
597 fClip.fPath = NULL;
598 break;
599 case SkClipStack::Rec::kPath_State:
600 fClip.fRect = NULL;
601 fClip.fPath = &rec->fPath;
602 break;
603 }
604 fClip.fOp = rec->fOp;
reed@google.com00177082011-10-12 14:34:30 +0000605 fClip.fDoAA = rec->fDoAA;
reed@google.com5c3d1472011-02-22 19:12:23 +0000606 return &fClip;
607}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000608
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000609const SkClipStack::Iter::Clip* SkClipStack::Iter::next() {
610 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
611 if (NULL == rec) {
612 return NULL;
613 }
614
615 return this->updateClip(rec);
616}
617
618const SkClipStack::Iter::Clip* SkClipStack::Iter::prev() {
619 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.prev();
620 if (NULL == rec) {
621 return NULL;
622 }
623
624 return this->updateClip(rec);
625}
626
robertphillips@google.com80214e22012-07-20 15:33:18 +0000627const SkClipStack::Iter::Clip* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000628
629 if (NULL == fStack) {
630 return NULL;
631 }
632
633 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
634
635 const SkClipStack::Rec* rec = NULL;
636
637 for (rec = (const SkClipStack::Rec*) fIter.prev();
638 NULL != rec;
639 rec = (const SkClipStack::Rec*) fIter.prev()) {
640
641 if (op == rec->fOp) {
642 // The Deque's iterator is actually one pace ahead of the
643 // returned value. So while "rec" is the element we want to
644 // return, the iterator is actually pointing at (and will
645 // return on the next "next" or "prev" call) the element
646 // in front of it in the deque. Bump the iterator forward a
647 // step so we get the expected result.
648 if (NULL == fIter.next()) {
649 // The reverse iterator has run off the front of the deque
650 // (i.e., the "op" clip is the first clip) and can't
651 // recover. Reset the iterator to start at the front.
652 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
653 }
654 break;
655 }
656 }
657
658 if (NULL == rec) {
659 // There were no "op" clips
660 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
661 }
662
663 return this->next();
664}
665
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000666void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
robertphillips@google.coma6f11c42012-07-23 17:39:44 +0000667 fStack = &stack;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000668 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
bsalomon@google.comd302f142011-03-03 13:54:13 +0000669}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000670
671// helper method
672void SkClipStack::getConservativeBounds(int offsetX,
673 int offsetY,
674 int maxWidth,
675 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000676 SkRect* devBounds,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000677 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000678 SkASSERT(NULL != devBounds);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000679
robertphillips@google.com7b112892012-07-31 15:18:21 +0000680 devBounds->setLTRB(0, 0,
681 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000682
683 SkRect temp;
684 SkClipStack::BoundsType boundType;
685
robertphillips@google.com7b112892012-07-31 15:18:21 +0000686 // temp starts off in canvas space here
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000687 this->getBounds(&temp, &boundType, isIntersectionOfRects);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000688 if (SkClipStack::kInsideOut_BoundsType == boundType) {
689 return;
690 }
691
robertphillips@google.com7b112892012-07-31 15:18:21 +0000692 // but is converted to device space here
robertphillips@google.com607fe072012-07-24 13:54:00 +0000693 temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
694
robertphillips@google.com7b112892012-07-31 15:18:21 +0000695 if (!devBounds->intersect(temp)) {
696 devBounds->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000697 }
698}