blob: 2676f3b07883b924b7c1af3ca3be2e072eede501 [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;
41 SkRect fFiniteBound;
42
reed@google.com00177082011-10-12 14:34:30 +000043 Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) : fRect(rect) {
reed@google.com5c3d1472011-02-22 19:12:23 +000044 fSaveCount = saveCount;
45 fOp = op;
46 fState = kRect_State;
reed@google.com00177082011-10-12 14:34:30 +000047 fDoAA = doAA;
reed@google.com5c3d1472011-02-22 19:12:23 +000048 }
49
reed@google.com00177082011-10-12 14:34:30 +000050 Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) : fPath(path) {
vandebo@chromium.orge1bc2742011-06-21 22:26:39 +000051 fRect.setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +000052 fSaveCount = saveCount;
53 fOp = op;
54 fState = kPath_State;
reed@google.com00177082011-10-12 14:34:30 +000055 fDoAA = doAA;
reed@google.com5c3d1472011-02-22 19:12:23 +000056 }
57
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000058 bool operator==(const Rec& b) const {
reed@google.com00177082011-10-12 14:34:30 +000059 if (fSaveCount != b.fSaveCount || fOp != b.fOp || fState != b.fState ||
60 fDoAA != b.fDoAA) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000061 return false;
62 }
63 switch (fState) {
64 case kEmpty_State:
65 return true;
66 case kRect_State:
67 return fRect == b.fRect;
68 case kPath_State:
69 return fPath == b.fPath;
70 }
71 return false; // Silence the compiler.
72 }
73
74 bool operator!=(const Rec& b) const {
75 return !(*this == b);
76 }
77
78
reed@google.com5c3d1472011-02-22 19:12:23 +000079 /**
80 * Returns true if this Rec can be intersected in place with a new clip
81 */
82 bool canBeIntersected(int saveCount, SkRegion::Op op) const {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000083 if (kEmpty_State == fState && (
84 SkRegion::kDifference_Op == op ||
85 SkRegion::kIntersect_Op == op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +000086 return true;
87 }
88 return fSaveCount == saveCount &&
89 SkRegion::kIntersect_Op == fOp &&
90 SkRegion::kIntersect_Op == op;
91 }
robertphillips@google.com607fe072012-07-24 13:54:00 +000092
93 /**
94 * The different combination of fill & inverse fill when combining
95 * bounding boxes
96 */
97 enum FillCombo {
98 kPrev_Cur_FillCombo,
99 kPrev_InvCur_FillCombo,
100 kInvPrev_Cur_FillCombo,
101 kInvPrev_InvCur_FillCombo
102 };
103
104 // a mirror of CombineBoundsRevDiff
105 void CombineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
106 switch (combination) {
107 case kInvPrev_InvCur_FillCombo:
108 // In this case the only pixels that can remain set
109 // are inside the current clip rect since the extensions
110 // to infinity of both clips cancel out and whatever
111 // is outside of the current clip is removed
112 fFiniteBoundType = kNormal_BoundsType;
113 break;
114 case kInvPrev_Cur_FillCombo:
115 // In this case the current op is finite so the only pixels
116 // that aren't set are whatever isn't set in the previous
117 // clip and whatever this clip carves out
118 fFiniteBound.join(prevFinite);
119 fFiniteBoundType = kInsideOut_BoundsType;
120 break;
121 case kPrev_InvCur_FillCombo:
122 // In this case everything outside of this clip's bound
123 // is erased, so the only pixels that can remain set
124 // occur w/in the intersection of the two finite bounds
125 if (!fFiniteBound.intersect(prevFinite)) {
126 fFiniteBound.setEmpty();
127 }
128 fFiniteBoundType = kNormal_BoundsType;
129 break;
130 case kPrev_Cur_FillCombo:
131 // The most conservative result bound is that of the
132 // prior clip. This could be wildly incorrect if the
133 // second clip either exactly matches the first clip
134 // (which should yield the empty set) or reduces the
135 // size of the prior bound (e.g., if the second clip
136 // exactly matched the bottom half of the prior clip).
137 // We ignore these two possibilities.
138 fFiniteBound = prevFinite;
139 break;
140 default:
141 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsDiff Invalid fill combination");
142 break;
143 }
144 }
145
146 void CombineBoundsXOR(int combination, const SkRect& prevFinite) {
147
148 switch (combination) {
149 case kInvPrev_Cur_FillCombo: // fall through
150 case kPrev_InvCur_FillCombo:
151 // With only one of the clips inverted the result will always
152 // extend to infinity. The only pixels that may be un-writeable
153 // lie within the union of the two finite bounds
154 fFiniteBound.join(prevFinite);
155 fFiniteBoundType = kInsideOut_BoundsType;
156 break;
157 case kInvPrev_InvCur_FillCombo:
158 // The only pixels that can survive are within the
159 // union of the two bounding boxes since the extensions
160 // to infinity of both clips cancel out
161 // fall through!
162 case kPrev_Cur_FillCombo:
163 // The most conservative bound for xor is the
164 // union of the two bounds. If the two clips exactly overlapped
165 // the xor could yield the empty set. Similarly the xor
166 // could reduce the size of the original clip's bound (e.g.,
167 // if the second clip exactly matched the bottom half of the
168 // first clip). We ignore these two cases.
169 fFiniteBound.join(prevFinite);
170 fFiniteBoundType = kNormal_BoundsType;
171 break;
172 default:
173 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsXOR Invalid fill combination");
174 break;
175 }
176 }
177
178 // a mirror of CombineBoundsIntersection
179 void CombineBoundsUnion(int combination, const SkRect& prevFinite) {
180
181 switch (combination) {
182 case kInvPrev_InvCur_FillCombo:
183 if (!fFiniteBound.intersect(prevFinite)) {
184 fFiniteBound.setEmpty();
185 }
186 fFiniteBoundType = kInsideOut_BoundsType;
187 break;
188 case kInvPrev_Cur_FillCombo:
189 // The only pixels that won't be drawable are inside
190 // the prior clip's finite bound
191 fFiniteBound = prevFinite;
192 fFiniteBoundType = kInsideOut_BoundsType;
193 break;
194 case kPrev_InvCur_FillCombo:
195 // The only pixels that won't be drawable are inside
196 // this clip's finite bound
197 break;
198 case kPrev_Cur_FillCombo:
199 fFiniteBound.join(prevFinite);
200 break;
201 default:
202 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsUnion Invalid fill combination");
203 break;
204 }
205 }
206
207 // a mirror of CombineBoundsUnion
208 void CombineBoundsIntersection(int combination, const SkRect& prevFinite) {
209
210 switch (combination) {
211 case kInvPrev_InvCur_FillCombo:
212 // The only pixels that aren't writable in this case
213 // occur in the union of the two finite bounds
214 fFiniteBound.join(prevFinite);
215 fFiniteBoundType = kInsideOut_BoundsType;
216 break;
217 case kInvPrev_Cur_FillCombo:
218 // In this case the only pixels that will remain writeable
219 // are within the current clip
220 break;
221 case kPrev_InvCur_FillCombo:
222 // In this case the only pixels that will remain writeable
223 // are with the previous clip
224 fFiniteBound = prevFinite;
225 fFiniteBoundType = kNormal_BoundsType;
226 break;
227 case kPrev_Cur_FillCombo:
228 if (!fFiniteBound.intersect(prevFinite)) {
229 fFiniteBound.setEmpty();
230 }
231 break;
232 default:
233 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsIntersection Invalid fill combination");
234 break;
235 }
236 }
237
238 // a mirror of CombineBoundsDiff
239 void CombineBoundsRevDiff(int combination, const SkRect& prevFinite) {
240
241 switch (combination) {
242 case kInvPrev_InvCur_FillCombo:
243 // The only pixels that can survive are in the
244 // previous bound since the extensions to infinity in
245 // both clips cancel out
246 fFiniteBound = prevFinite;
247 fFiniteBoundType = kNormal_BoundsType;
248 break;
249 case kInvPrev_Cur_FillCombo:
250 if (!fFiniteBound.intersect(prevFinite)) {
251 fFiniteBound.setEmpty();
252 }
253 fFiniteBoundType = kNormal_BoundsType;
254 break;
255 case kPrev_InvCur_FillCombo:
256 fFiniteBound.join(prevFinite);
257 fFiniteBoundType = kInsideOut_BoundsType;
258 break;
259 case kPrev_Cur_FillCombo:
260 // Fall through - as with the kDifference_Op case, the
261 // most conservative result bound is the bound of the
262 // current clip. The prior clip could reduce the size of this
263 // bound (as in the kDifference_Op case) but we are ignoring
264 // those cases.
265 break;
266 default:
267 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsRevDiff Invalid fill combination");
268 break;
269 }
270 }
271
272 void updateBound(const Rec* prior) {
273
274 // First, optimistically update the current Rec's bound information
275 // with the current clip's bound
276 if (kRect_State == fState) {
277 fFiniteBound = fRect;
278 fFiniteBoundType = kNormal_BoundsType;
279 } else {
280 fFiniteBound = fPath.getBounds();
281
282 if (fPath.isInverseFillType()) {
283 fFiniteBoundType = kInsideOut_BoundsType;
284 } else {
285 fFiniteBoundType = kNormal_BoundsType;
286 }
287 }
288
289 if (!fDoAA) {
290 // Here we mimic a non-anti-aliased scanline system. If there is
291 // no anti-aliasing we can integerize the bounding box to exclude
292 // fractional parts that won't be rendered.
293 // Note: the left edge is handled slightly differently below. We
294 // are a bit more generous in the rounding since we don't want to
295 // risk missing the left pixels when fLeft is very close to .5
296 fFiniteBound.set(SkIntToScalar(SkScalarFloorToInt(fFiniteBound.fLeft+0.45f)),
297 SkIntToScalar(SkScalarRound(fFiniteBound.fTop)),
298 SkIntToScalar(SkScalarRound(fFiniteBound.fRight)),
299 SkIntToScalar(SkScalarRound(fFiniteBound.fBottom)));
300 }
301
302 // Now set up the previous Rec's bound information taking into
303 // account that there may be no previous clip
304 SkRect prevFinite;
305 SkClipStack::BoundsType prevType;
306
307 if (NULL == prior) {
308 // no prior clip means the entire plane is writable
309 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
310 prevType = kInsideOut_BoundsType;
311 } else {
312 prevFinite = prior->fFiniteBound;
313 prevType = prior->fFiniteBoundType;
314 }
315
316 FillCombo combination = kPrev_Cur_FillCombo;
317 if (kInsideOut_BoundsType == fFiniteBoundType) {
318 combination = (FillCombo) (combination | 0x01);
319 }
320 if (kInsideOut_BoundsType == prevType) {
321 combination = (FillCombo) (combination | 0x02);
322 }
323
324 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
325 kInvPrev_Cur_FillCombo == combination ||
326 kPrev_InvCur_FillCombo == combination ||
327 kPrev_Cur_FillCombo == combination);
328
329 // Now integrate with clip with the prior clips
330 switch (fOp) {
331 case SkRegion::kDifference_Op:
332 this->CombineBoundsDiff(combination, prevFinite);
333 break;
334 case SkRegion::kXOR_Op:
335 this->CombineBoundsXOR(combination, prevFinite);
336 break;
337 case SkRegion::kUnion_Op:
338 this->CombineBoundsUnion(combination, prevFinite);
339 break;
340 case SkRegion::kIntersect_Op:
341 this->CombineBoundsIntersection(combination, prevFinite);
342 break;
343 case SkRegion::kReverseDifference_Op:
344 this->CombineBoundsRevDiff(combination, prevFinite);
345 break;
346 case SkRegion::kReplace_Op:
347 // Replace just ignores everything prior
348 // The current clip's bound information is already filled in
349 // so nothing to do
350 break;
351 default:
352 SkDebugf("SkRegion::Op error/n");
353 SkASSERT(0);
354 break;
355 }
356 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000357};
358
359SkClipStack::SkClipStack() : fDeque(sizeof(Rec)) {
360 fSaveCount = 0;
361}
362
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000363SkClipStack::SkClipStack(const SkClipStack& b) : fDeque(sizeof(Rec)) {
364 *this = b;
365}
366
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000367SkClipStack::~SkClipStack() {
368 reset();
369}
370
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000371SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
372 if (this == &b) {
373 return *this;
374 }
375 reset();
376
377 fSaveCount = b.fSaveCount;
378 SkDeque::F2BIter recIter(b.fDeque);
379 for (const Rec* rec = (const Rec*)recIter.next();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000380 rec != NULL;
381 rec = (const Rec*)recIter.next()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000382 new (fDeque.push_back()) Rec(*rec);
383 }
384
385 return *this;
386}
387
388bool SkClipStack::operator==(const SkClipStack& b) const {
389 if (fSaveCount != b.fSaveCount || fDeque.count() != b.fDeque.count()) {
390 return false;
391 }
392 SkDeque::F2BIter myIter(fDeque);
393 SkDeque::F2BIter bIter(b.fDeque);
394 const Rec* myRec = (const Rec*)myIter.next();
395 const Rec* bRec = (const Rec*)bIter.next();
396
397 while (myRec != NULL && bRec != NULL) {
398 if (*myRec != *bRec) {
399 return false;
400 }
401 myRec = (const Rec*)myIter.next();
402 bRec = (const Rec*)bIter.next();
403 }
404 return myRec == NULL && bRec == NULL;
405}
406
reed@google.com5c3d1472011-02-22 19:12:23 +0000407void SkClipStack::reset() {
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000408 // We used a placement new for each object in fDeque, so we're responsible
409 // for calling the destructor on each of them as well.
410 while (!fDeque.empty()) {
411 Rec* rec = (Rec*)fDeque.back();
412 rec->~Rec();
413 fDeque.pop_back();
414 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000415
416 fSaveCount = 0;
417}
418
419void SkClipStack::save() {
420 fSaveCount += 1;
421}
422
423void SkClipStack::restore() {
424 fSaveCount -= 1;
425 while (!fDeque.empty()) {
426 Rec* rec = (Rec*)fDeque.back();
427 if (rec->fSaveCount <= fSaveCount) {
428 break;
429 }
430 rec->~Rec();
431 fDeque.pop_back();
432 }
433}
434
robertphillips@google.com607fe072012-07-24 13:54:00 +0000435void SkClipStack::getBounds(SkRect* finiteBound, BoundsType* boundType) const {
436 SkASSERT(NULL != finiteBound && NULL != boundType);
437
438 Rec* rec = (Rec*)fDeque.back();
439
440 if (NULL == rec) {
441 // the clip is wide open - the infinite plane w/ no pixels un-writeable
442 finiteBound->setEmpty();
443 *boundType = kInsideOut_BoundsType;
444 return;
445 }
446
447 *finiteBound = rec->fFiniteBound;
448 *boundType = rec->fFiniteBoundType;
449}
450
reed@google.com00177082011-10-12 14:34:30 +0000451void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000452 Rec* rec = (Rec*)fDeque.back();
453 if (rec && rec->canBeIntersected(fSaveCount, op)) {
454 switch (rec->fState) {
455 case Rec::kEmpty_State:
robertphillips@google.com607fe072012-07-24 13:54:00 +0000456 SkASSERT(rec->fFiniteBound.isEmpty());
457 SkASSERT(kNormal_BoundsType == rec->fFiniteBoundType);
reed@google.com5c3d1472011-02-22 19:12:23 +0000458 return;
459 case Rec::kRect_State:
460 if (!rec->fRect.intersect(rect)) {
461 rec->fState = Rec::kEmpty_State;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000462 rec->fFiniteBound.setEmpty();
463 rec->fFiniteBoundType = kNormal_BoundsType;
464 return;
reed@google.com5c3d1472011-02-22 19:12:23 +0000465 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000466
467 rec->updateBound(NULL);
reed@google.com5c3d1472011-02-22 19:12:23 +0000468 return;
469 case Rec::kPath_State:
470 if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
471 rec->fState = Rec::kEmpty_State;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000472 rec->fFiniteBound.setEmpty();
473 rec->fFiniteBoundType = kNormal_BoundsType;
reed@google.com5c3d1472011-02-22 19:12:23 +0000474 return;
475 }
476 break;
477 }
478 }
reed@google.com00177082011-10-12 14:34:30 +0000479 new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000480 ((Rec*) fDeque.back())->updateBound(rec);
reed@google.com5c3d1472011-02-22 19:12:23 +0000481}
482
reed@google.com00177082011-10-12 14:34:30 +0000483void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
tomhudson@google.com4c433722012-03-09 16:48:20 +0000484 SkRect alt;
485 if (path.isRect(&alt)) {
486 return this->clipDevRect(alt, op, doAA);
487 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000488 Rec* rec = (Rec*)fDeque.back();
489 if (rec && rec->canBeIntersected(fSaveCount, op)) {
490 const SkRect& pathBounds = path.getBounds();
491 switch (rec->fState) {
492 case Rec::kEmpty_State:
robertphillips@google.com607fe072012-07-24 13:54:00 +0000493 SkASSERT(rec->fFiniteBound.isEmpty());
494 SkASSERT(kNormal_BoundsType == rec->fFiniteBoundType);
reed@google.com5c3d1472011-02-22 19:12:23 +0000495 return;
496 case Rec::kRect_State:
497 if (!SkRect::Intersects(rec->fRect, pathBounds)) {
498 rec->fState = Rec::kEmpty_State;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000499 rec->fFiniteBound.setEmpty();
500 rec->fFiniteBoundType = kNormal_BoundsType;
reed@google.com5c3d1472011-02-22 19:12:23 +0000501 return;
502 }
503 break;
504 case Rec::kPath_State:
505 if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
506 rec->fState = Rec::kEmpty_State;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000507 rec->fFiniteBound.setEmpty();
508 rec->fFiniteBoundType = kNormal_BoundsType;
reed@google.com5c3d1472011-02-22 19:12:23 +0000509 return;
510 }
511 break;
512 }
513 }
reed@google.com00177082011-10-12 14:34:30 +0000514 new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000515 ((Rec*) fDeque.back())->updateBound(rec);
reed@google.com5c3d1472011-02-22 19:12:23 +0000516}
517
518///////////////////////////////////////////////////////////////////////////////
519
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000520SkClipStack::Iter::Iter() : fStack(NULL) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000521}
522
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000523bool operator==(const SkClipStack::Iter::Clip& a,
524 const SkClipStack::Iter::Clip& b) {
reed@google.com00177082011-10-12 14:34:30 +0000525 return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
vandebo@chromium.orge03c6522011-06-21 20:45:51 +0000526 ((a.fRect == NULL && b.fRect == NULL) ||
527 (a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
528 ((a.fPath == NULL && b.fPath == NULL) ||
529 (a.fPath != NULL && b.fPath != NULL && *a.fPath == *b.fPath));
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000530}
531
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000532bool operator!=(const SkClipStack::Iter::Clip& a,
533 const SkClipStack::Iter::Clip& b) {
vandebo@chromium.org8887ede2011-05-25 01:27:52 +0000534 return !(a == b);
535}
536
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000537SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
538 : fStack(&stack) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000539 this->reset(stack, startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000540}
541
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000542const SkClipStack::Iter::Clip* SkClipStack::Iter::updateClip(
543 const SkClipStack::Rec* rec) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000544 switch (rec->fState) {
545 case SkClipStack::Rec::kEmpty_State:
546 fClip.fRect = NULL;
547 fClip.fPath = NULL;
548 break;
549 case SkClipStack::Rec::kRect_State:
550 fClip.fRect = &rec->fRect;
551 fClip.fPath = NULL;
552 break;
553 case SkClipStack::Rec::kPath_State:
554 fClip.fRect = NULL;
555 fClip.fPath = &rec->fPath;
556 break;
557 }
558 fClip.fOp = rec->fOp;
reed@google.com00177082011-10-12 14:34:30 +0000559 fClip.fDoAA = rec->fDoAA;
reed@google.com5c3d1472011-02-22 19:12:23 +0000560 return &fClip;
561}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000562
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000563const SkClipStack::Iter::Clip* SkClipStack::Iter::next() {
564 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
565 if (NULL == rec) {
566 return NULL;
567 }
568
569 return this->updateClip(rec);
570}
571
572const SkClipStack::Iter::Clip* SkClipStack::Iter::prev() {
573 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.prev();
574 if (NULL == rec) {
575 return NULL;
576 }
577
578 return this->updateClip(rec);
579}
580
robertphillips@google.com80214e22012-07-20 15:33:18 +0000581const SkClipStack::Iter::Clip* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000582
583 if (NULL == fStack) {
584 return NULL;
585 }
586
587 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
588
589 const SkClipStack::Rec* rec = NULL;
590
591 for (rec = (const SkClipStack::Rec*) fIter.prev();
592 NULL != rec;
593 rec = (const SkClipStack::Rec*) fIter.prev()) {
594
595 if (op == rec->fOp) {
596 // The Deque's iterator is actually one pace ahead of the
597 // returned value. So while "rec" is the element we want to
598 // return, the iterator is actually pointing at (and will
599 // return on the next "next" or "prev" call) the element
600 // in front of it in the deque. Bump the iterator forward a
601 // step so we get the expected result.
602 if (NULL == fIter.next()) {
603 // The reverse iterator has run off the front of the deque
604 // (i.e., the "op" clip is the first clip) and can't
605 // recover. Reset the iterator to start at the front.
606 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
607 }
608 break;
609 }
610 }
611
612 if (NULL == rec) {
613 // There were no "op" clips
614 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
615 }
616
617 return this->next();
618}
619
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000620void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
robertphillips@google.coma6f11c42012-07-23 17:39:44 +0000621 fStack = &stack;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000622 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
bsalomon@google.comd302f142011-03-03 13:54:13 +0000623}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000624
625// helper method
626void SkClipStack::getConservativeBounds(int offsetX,
627 int offsetY,
628 int maxWidth,
629 int maxHeight,
630 SkRect* bounds) const {
631 SkASSERT(NULL != bounds);
632
633 bounds->setLTRB(0, 0,
634 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
635
636 SkRect temp;
637 SkClipStack::BoundsType boundType;
638
639 this->getBounds(&temp, &boundType);
640 if (SkClipStack::kInsideOut_BoundsType == boundType) {
641 return;
642 }
643
644 temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
645
646 if (!bounds->intersect(temp)) {
647 bounds->setEmpty();
648 }
649}