blob: ff771c1cbe8c92c6fe72eba6d7c77b4361f493a8 [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"
robertphillips@google.com46f93502012-08-07 15:38:08 +000010#include "SkThread.h"
11
reed@google.com5c3d1472011-02-22 19:12:23 +000012#include <new>
13
robertphillips@google.com607fe072012-07-24 13:54:00 +000014
robertphillips@google.com46f93502012-08-07 15:38:08 +000015// 0-2 are reserved for invalid, empty & wide-open
16int32_t SkClipStack::gGenID = 3;
17
reed@google.com5c3d1472011-02-22 19:12:23 +000018struct SkClipStack::Rec {
19 enum State {
20 kEmpty_State,
21 kRect_State,
22 kPath_State
23 };
24
25 SkPath fPath;
26 SkRect fRect;
27 int fSaveCount;
28 SkRegion::Op fOp;
29 State fState;
reed@google.com00177082011-10-12 14:34:30 +000030 bool fDoAA;
reed@google.com5c3d1472011-02-22 19:12:23 +000031
robertphillips@google.com607fe072012-07-24 13:54:00 +000032 // fFiniteBoundType and fFiniteBound are used to incrementally update
33 // the clip stack's bound. When fFiniteBoundType is kNormal_BoundsType,
34 // fFiniteBound represents the conservative bounding box of the pixels
35 // that aren't clipped (i.e., any pixels that can be drawn to are inside
36 // the bound). When fFiniteBoundType is kInsideOut_BoundsType (which occurs
37 // when a clip is inverse filled), fFiniteBound represents the
38 // conservative bounding box of the pixels that _are_ clipped (i.e., any
39 // pixels that cannot be drawn to are inside the bound). When
40 // fFiniteBoundType is kInsideOut_BoundsType the actual bound is
41 // the infinite plane. This behavior of fFiniteBoundType and
42 // fFiniteBound is required so that we can capture the cancelling out
43 // of the extensions to infinity when two inverse filled clips are
44 // Booleaned together.
45 SkClipStack::BoundsType fFiniteBoundType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000046 SkRect fFiniteBound;
47 bool fIsIntersectionOfRects;
robertphillips@google.com607fe072012-07-24 13:54:00 +000048
robertphillips@google.com46f93502012-08-07 15:38:08 +000049 int fGenID;
50
51 Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA)
52 : fRect(rect)
53 , fGenID(kInvalidGenID) {
reed@google.com5c3d1472011-02-22 19:12:23 +000054 fSaveCount = saveCount;
55 fOp = op;
56 fState = kRect_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.com46f93502012-08-07 15:38:08 +000061 Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA)
62 : fPath(path)
63 , fGenID(kInvalidGenID) {
vandebo@chromium.orge1bc2742011-06-21 22:26:39 +000064 fRect.setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +000065 fSaveCount = saveCount;
66 fOp = op;
67 fState = kPath_State;
reed@google.com00177082011-10-12 14:34:30 +000068 fDoAA = doAA;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000069 // bounding box members are updated in a following updateBound call
reed@google.com5c3d1472011-02-22 19:12:23 +000070 }
71
robertphillips@google.com08eacc12012-08-02 12:49:00 +000072 void setEmpty() {
73 fState = kEmpty_State;
74 fFiniteBound.setEmpty();
75 fFiniteBoundType = kNormal_BoundsType;
76 fIsIntersectionOfRects = false;
robertphillips@google.com46f93502012-08-07 15:38:08 +000077 fGenID = kEmptyGenID;
78 }
79
80 void checkEmpty() {
81 SkASSERT(fFiniteBound.isEmpty());
82 SkASSERT(kNormal_BoundsType == fFiniteBoundType);
83 SkASSERT(!fIsIntersectionOfRects);
84 SkASSERT(kEmptyGenID == fGenID);
robertphillips@google.com08eacc12012-08-02 12:49:00 +000085 }
86
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000087 bool operator==(const Rec& b) const {
robertphillips@google.com46f93502012-08-07 15:38:08 +000088 if (fSaveCount != b.fSaveCount ||
robertphillips@google.com46f93502012-08-07 15:38:08 +000089 fOp != b.fOp ||
90 fState != b.fState ||
91 fDoAA != b.fDoAA) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000092 return false;
93 }
94 switch (fState) {
95 case kEmpty_State:
96 return true;
97 case kRect_State:
98 return fRect == b.fRect;
99 case kPath_State:
100 return fPath == b.fPath;
101 }
102 return false; // Silence the compiler.
103 }
104
105 bool operator!=(const Rec& b) const {
106 return !(*this == b);
107 }
108
109
reed@google.com5c3d1472011-02-22 19:12:23 +0000110 /**
111 * Returns true if this Rec can be intersected in place with a new clip
112 */
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000113 bool canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000114 if (kEmpty_State == fState && (
115 SkRegion::kDifference_Op == op ||
116 SkRegion::kIntersect_Op == op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000117 return true;
118 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000119 // Only clips within the same save/restore frame (as captured by
120 // the save count) can be merged
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000121 return fSaveCount == saveCount &&
122 SkRegion::kIntersect_Op == op &&
123 (SkRegion::kIntersect_Op == fOp || SkRegion::kReplace_Op == fOp);
reed@google.com5c3d1472011-02-22 19:12:23 +0000124 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000125
126 /**
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000127 * This method checks to see if two rect clips can be safely merged into
128 * one. The issue here is that to be strictly correct all the edges of
129 * the resulting rect must have the same anti-aliasing.
130 */
131 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
132 SkASSERT(kRect_State == fState);
133
134 if (fDoAA == newAA) {
135 // if the AA setting is the same there is no issue
136 return true;
137 }
138
139 if (!SkRect::Intersects(fRect, newR)) {
140 // The calling code will correctly set the result to the empty clip
141 return true;
142 }
143
144 if (fRect.contains(newR)) {
145 // if the new rect carves out a portion of the old one there is no
146 // issue
147 return true;
148 }
149
150 // So either the two overlap in some complex manner or newR contains oldR.
151 // In the first, case the edges will require different AA. In the second,
152 // the AA setting that would be carried forward is incorrect (e.g., oldR
153 // is AA while newR is BW but since newR contains oldR, oldR will be
154 // drawn BW) since the new AA setting will predominate.
155 return false;
156 }
157
158
159 /**
robertphillips@google.com607fe072012-07-24 13:54:00 +0000160 * The different combination of fill & inverse fill when combining
161 * bounding boxes
162 */
163 enum FillCombo {
164 kPrev_Cur_FillCombo,
165 kPrev_InvCur_FillCombo,
166 kInvPrev_Cur_FillCombo,
167 kInvPrev_InvCur_FillCombo
168 };
169
170 // a mirror of CombineBoundsRevDiff
171 void CombineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
172 switch (combination) {
173 case kInvPrev_InvCur_FillCombo:
174 // In this case the only pixels that can remain set
175 // are inside the current clip rect since the extensions
176 // to infinity of both clips cancel out and whatever
177 // is outside of the current clip is removed
178 fFiniteBoundType = kNormal_BoundsType;
179 break;
180 case kInvPrev_Cur_FillCombo:
181 // In this case the current op is finite so the only pixels
182 // that aren't set are whatever isn't set in the previous
183 // clip and whatever this clip carves out
184 fFiniteBound.join(prevFinite);
185 fFiniteBoundType = kInsideOut_BoundsType;
186 break;
187 case kPrev_InvCur_FillCombo:
188 // In this case everything outside of this clip's bound
189 // is erased, so the only pixels that can remain set
190 // occur w/in the intersection of the two finite bounds
191 if (!fFiniteBound.intersect(prevFinite)) {
192 fFiniteBound.setEmpty();
193 }
194 fFiniteBoundType = kNormal_BoundsType;
195 break;
196 case kPrev_Cur_FillCombo:
197 // The most conservative result bound is that of the
198 // prior clip. This could be wildly incorrect if the
199 // second clip either exactly matches the first clip
200 // (which should yield the empty set) or reduces the
201 // size of the prior bound (e.g., if the second clip
202 // exactly matched the bottom half of the prior clip).
203 // We ignore these two possibilities.
204 fFiniteBound = prevFinite;
205 break;
206 default:
207 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsDiff Invalid fill combination");
208 break;
209 }
210 }
211
212 void CombineBoundsXOR(int combination, const SkRect& prevFinite) {
213
214 switch (combination) {
215 case kInvPrev_Cur_FillCombo: // fall through
216 case kPrev_InvCur_FillCombo:
217 // With only one of the clips inverted the result will always
218 // extend to infinity. The only pixels that may be un-writeable
219 // lie within the union of the two finite bounds
220 fFiniteBound.join(prevFinite);
221 fFiniteBoundType = kInsideOut_BoundsType;
222 break;
223 case kInvPrev_InvCur_FillCombo:
224 // The only pixels that can survive are within the
225 // union of the two bounding boxes since the extensions
226 // to infinity of both clips cancel out
227 // fall through!
228 case kPrev_Cur_FillCombo:
229 // The most conservative bound for xor is the
230 // union of the two bounds. If the two clips exactly overlapped
231 // the xor could yield the empty set. Similarly the xor
232 // could reduce the size of the original clip's bound (e.g.,
233 // if the second clip exactly matched the bottom half of the
234 // first clip). We ignore these two cases.
235 fFiniteBound.join(prevFinite);
236 fFiniteBoundType = kNormal_BoundsType;
237 break;
238 default:
239 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsXOR Invalid fill combination");
240 break;
241 }
242 }
243
244 // a mirror of CombineBoundsIntersection
245 void CombineBoundsUnion(int combination, const SkRect& prevFinite) {
246
247 switch (combination) {
248 case kInvPrev_InvCur_FillCombo:
249 if (!fFiniteBound.intersect(prevFinite)) {
250 fFiniteBound.setEmpty();
251 }
252 fFiniteBoundType = kInsideOut_BoundsType;
253 break;
254 case kInvPrev_Cur_FillCombo:
255 // The only pixels that won't be drawable are inside
256 // the prior clip's finite bound
257 fFiniteBound = prevFinite;
258 fFiniteBoundType = kInsideOut_BoundsType;
259 break;
260 case kPrev_InvCur_FillCombo:
261 // The only pixels that won't be drawable are inside
262 // this clip's finite bound
263 break;
264 case kPrev_Cur_FillCombo:
265 fFiniteBound.join(prevFinite);
266 break;
267 default:
268 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsUnion Invalid fill combination");
269 break;
270 }
271 }
272
273 // a mirror of CombineBoundsUnion
274 void CombineBoundsIntersection(int combination, const SkRect& prevFinite) {
275
276 switch (combination) {
277 case kInvPrev_InvCur_FillCombo:
278 // The only pixels that aren't writable in this case
279 // occur in the union of the two finite bounds
280 fFiniteBound.join(prevFinite);
281 fFiniteBoundType = kInsideOut_BoundsType;
282 break;
283 case kInvPrev_Cur_FillCombo:
284 // In this case the only pixels that will remain writeable
285 // are within the current clip
286 break;
287 case kPrev_InvCur_FillCombo:
288 // In this case the only pixels that will remain writeable
289 // are with the previous clip
290 fFiniteBound = prevFinite;
291 fFiniteBoundType = kNormal_BoundsType;
292 break;
293 case kPrev_Cur_FillCombo:
294 if (!fFiniteBound.intersect(prevFinite)) {
295 fFiniteBound.setEmpty();
296 }
297 break;
298 default:
299 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsIntersection Invalid fill combination");
300 break;
301 }
302 }
303
304 // a mirror of CombineBoundsDiff
305 void CombineBoundsRevDiff(int combination, const SkRect& prevFinite) {
306
307 switch (combination) {
308 case kInvPrev_InvCur_FillCombo:
309 // The only pixels that can survive are in the
310 // previous bound since the extensions to infinity in
311 // both clips cancel out
312 fFiniteBound = prevFinite;
313 fFiniteBoundType = kNormal_BoundsType;
314 break;
315 case kInvPrev_Cur_FillCombo:
316 if (!fFiniteBound.intersect(prevFinite)) {
317 fFiniteBound.setEmpty();
318 }
319 fFiniteBoundType = kNormal_BoundsType;
320 break;
321 case kPrev_InvCur_FillCombo:
322 fFiniteBound.join(prevFinite);
323 fFiniteBoundType = kInsideOut_BoundsType;
324 break;
325 case kPrev_Cur_FillCombo:
326 // Fall through - as with the kDifference_Op case, the
327 // most conservative result bound is the bound of the
328 // current clip. The prior clip could reduce the size of this
329 // bound (as in the kDifference_Op case) but we are ignoring
330 // those cases.
331 break;
332 default:
333 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsRevDiff Invalid fill combination");
334 break;
335 }
336 }
337
338 void updateBound(const Rec* prior) {
339
340 // First, optimistically update the current Rec's bound information
341 // with the current clip's bound
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000342 fIsIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000343 if (kRect_State == fState) {
344 fFiniteBound = fRect;
345 fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000346
347 if (SkRegion::kReplace_Op == fOp ||
348 (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000349 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
350 prior->rectRectIntersectAllowed(fRect, fDoAA))) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000351 fIsIntersectionOfRects = true;
352 }
353
robertphillips@google.com607fe072012-07-24 13:54:00 +0000354 } else {
355 fFiniteBound = fPath.getBounds();
356
357 if (fPath.isInverseFillType()) {
358 fFiniteBoundType = kInsideOut_BoundsType;
359 } else {
360 fFiniteBoundType = kNormal_BoundsType;
361 }
362 }
363
364 if (!fDoAA) {
365 // Here we mimic a non-anti-aliased scanline system. If there is
366 // no anti-aliasing we can integerize the bounding box to exclude
367 // fractional parts that won't be rendered.
368 // Note: the left edge is handled slightly differently below. We
369 // are a bit more generous in the rounding since we don't want to
370 // risk missing the left pixels when fLeft is very close to .5
371 fFiniteBound.set(SkIntToScalar(SkScalarFloorToInt(fFiniteBound.fLeft+0.45f)),
372 SkIntToScalar(SkScalarRound(fFiniteBound.fTop)),
373 SkIntToScalar(SkScalarRound(fFiniteBound.fRight)),
374 SkIntToScalar(SkScalarRound(fFiniteBound.fBottom)));
375 }
376
377 // Now set up the previous Rec's bound information taking into
378 // account that there may be no previous clip
379 SkRect prevFinite;
380 SkClipStack::BoundsType prevType;
381
382 if (NULL == prior) {
383 // no prior clip means the entire plane is writable
384 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
385 prevType = kInsideOut_BoundsType;
386 } else {
387 prevFinite = prior->fFiniteBound;
388 prevType = prior->fFiniteBoundType;
389 }
390
391 FillCombo combination = kPrev_Cur_FillCombo;
392 if (kInsideOut_BoundsType == fFiniteBoundType) {
393 combination = (FillCombo) (combination | 0x01);
394 }
395 if (kInsideOut_BoundsType == prevType) {
396 combination = (FillCombo) (combination | 0x02);
397 }
398
399 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
400 kInvPrev_Cur_FillCombo == combination ||
401 kPrev_InvCur_FillCombo == combination ||
402 kPrev_Cur_FillCombo == combination);
403
404 // Now integrate with clip with the prior clips
405 switch (fOp) {
406 case SkRegion::kDifference_Op:
407 this->CombineBoundsDiff(combination, prevFinite);
408 break;
409 case SkRegion::kXOR_Op:
410 this->CombineBoundsXOR(combination, prevFinite);
411 break;
412 case SkRegion::kUnion_Op:
413 this->CombineBoundsUnion(combination, prevFinite);
414 break;
415 case SkRegion::kIntersect_Op:
416 this->CombineBoundsIntersection(combination, prevFinite);
417 break;
418 case SkRegion::kReverseDifference_Op:
419 this->CombineBoundsRevDiff(combination, prevFinite);
420 break;
421 case SkRegion::kReplace_Op:
422 // Replace just ignores everything prior
423 // The current clip's bound information is already filled in
424 // so nothing to do
425 break;
426 default:
427 SkDebugf("SkRegion::Op error/n");
428 SkASSERT(0);
429 break;
430 }
431 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000432};
433
robertphillips@google.com46f93502012-08-07 15:38:08 +0000434
435SkClipStack::SkClipStack()
436 : fDeque(sizeof(Rec))
437 , fSaveCount(0) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000438}
439
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000440SkClipStack::SkClipStack(const SkClipStack& b) : fDeque(sizeof(Rec)) {
441 *this = b;
442}
443
robertphillips@google.com46f93502012-08-07 15:38:08 +0000444SkClipStack::SkClipStack(const SkRect& r)
445 : fDeque(sizeof(Rec))
446 , fSaveCount(0) {
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000447 if (!r.isEmpty()) {
448 this->clipDevRect(r, SkRegion::kReplace_Op, false);
449 }
450}
451
robertphillips@google.com46f93502012-08-07 15:38:08 +0000452SkClipStack::SkClipStack(const SkIRect& r)
453 : fDeque(sizeof(Rec))
454 , fSaveCount(0) {
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000455 if (!r.isEmpty()) {
456 SkRect temp;
457 temp.set(r);
458 this->clipDevRect(temp, SkRegion::kReplace_Op, false);
459 }
460}
461
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000462SkClipStack::~SkClipStack() {
463 reset();
464}
465
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000466SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
467 if (this == &b) {
468 return *this;
469 }
470 reset();
471
472 fSaveCount = b.fSaveCount;
473 SkDeque::F2BIter recIter(b.fDeque);
474 for (const Rec* rec = (const Rec*)recIter.next();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000475 rec != NULL;
476 rec = (const Rec*)recIter.next()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000477 new (fDeque.push_back()) Rec(*rec);
478 }
479
480 return *this;
481}
482
483bool SkClipStack::operator==(const SkClipStack& b) const {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000484 if (fSaveCount != b.fSaveCount ||
485 fDeque.count() != b.fDeque.count()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000486 return false;
487 }
488 SkDeque::F2BIter myIter(fDeque);
489 SkDeque::F2BIter bIter(b.fDeque);
490 const Rec* myRec = (const Rec*)myIter.next();
491 const Rec* bRec = (const Rec*)bIter.next();
492
493 while (myRec != NULL && bRec != NULL) {
494 if (*myRec != *bRec) {
495 return false;
496 }
497 myRec = (const Rec*)myIter.next();
498 bRec = (const Rec*)bIter.next();
499 }
500 return myRec == NULL && bRec == NULL;
501}
502
reed@google.com5c3d1472011-02-22 19:12:23 +0000503void SkClipStack::reset() {
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000504 // We used a placement new for each object in fDeque, so we're responsible
505 // for calling the destructor on each of them as well.
506 while (!fDeque.empty()) {
507 Rec* rec = (Rec*)fDeque.back();
508 rec->~Rec();
509 fDeque.pop_back();
510 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000511
512 fSaveCount = 0;
513}
514
515void SkClipStack::save() {
516 fSaveCount += 1;
517}
518
519void SkClipStack::restore() {
520 fSaveCount -= 1;
521 while (!fDeque.empty()) {
522 Rec* rec = (Rec*)fDeque.back();
523 if (rec->fSaveCount <= fSaveCount) {
524 break;
525 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000526 this->purgeClip(rec);
reed@google.com5c3d1472011-02-22 19:12:23 +0000527 rec->~Rec();
528 fDeque.pop_back();
529 }
530}
531
robertphillips@google.com7b112892012-07-31 15:18:21 +0000532void SkClipStack::getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000533 BoundsType* boundType,
534 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000535 SkASSERT(NULL != canvFiniteBound && NULL != boundType);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000536
537 Rec* rec = (Rec*)fDeque.back();
538
539 if (NULL == rec) {
540 // the clip is wide open - the infinite plane w/ no pixels un-writeable
robertphillips@google.com7b112892012-07-31 15:18:21 +0000541 canvFiniteBound->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000542 *boundType = kInsideOut_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000543 if (NULL != isIntersectionOfRects) {
544 *isIntersectionOfRects = false;
545 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000546 return;
547 }
548
robertphillips@google.com7b112892012-07-31 15:18:21 +0000549 *canvFiniteBound = rec->fFiniteBound;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000550 *boundType = rec->fFiniteBoundType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000551 if (NULL != isIntersectionOfRects) {
552 *isIntersectionOfRects = rec->fIsIntersectionOfRects;
553 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000554}
555
reed@google.com00177082011-10-12 14:34:30 +0000556void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000557
robertphillips@google.com46f93502012-08-07 15:38:08 +0000558 int32_t genID = GetNextGenID();
559
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000560 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
561 Rec* rec = (Rec*) iter.prev();
562
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000563 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000564 switch (rec->fState) {
565 case Rec::kEmpty_State:
robertphillips@google.com46f93502012-08-07 15:38:08 +0000566 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000567 return;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000568 case Rec::kRect_State:
569 if (rec->rectRectIntersectAllowed(rect, doAA)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000570 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000571 if (!rec->fRect.intersect(rect)) {
572 rec->setEmpty();
573 return;
574 }
575
576 rec->fDoAA = doAA;
577 Rec* prev = (Rec*) iter.prev();
578 rec->updateBound(prev);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000579 rec->fGenID = genID;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000580 return;
reed@google.com5c3d1472011-02-22 19:12:23 +0000581 }
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000582 break;
reed@google.com5c3d1472011-02-22 19:12:23 +0000583 case Rec::kPath_State:
584 if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000585 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000586 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000587 return;
588 }
589 break;
590 }
591 }
reed@google.com00177082011-10-12 14:34:30 +0000592 new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000593 ((Rec*) fDeque.back())->updateBound(rec);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000594 ((Rec*) fDeque.back())->fGenID = genID;
595
596 if (rec && rec->fSaveCount == fSaveCount) {
597 this->purgeClip(rec);
598 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000599}
600
reed@google.com00177082011-10-12 14:34:30 +0000601void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
tomhudson@google.com4c433722012-03-09 16:48:20 +0000602 SkRect alt;
603 if (path.isRect(&alt)) {
604 return this->clipDevRect(alt, op, doAA);
605 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000606
607 int32_t genID = GetNextGenID();
608
reed@google.com5c3d1472011-02-22 19:12:23 +0000609 Rec* rec = (Rec*)fDeque.back();
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000610 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000611 const SkRect& pathBounds = path.getBounds();
612 switch (rec->fState) {
613 case Rec::kEmpty_State:
robertphillips@google.com46f93502012-08-07 15:38:08 +0000614 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000615 return;
616 case Rec::kRect_State:
617 if (!SkRect::Intersects(rec->fRect, pathBounds)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000618 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000619 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000620 return;
621 }
622 break;
623 case Rec::kPath_State:
624 if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000625 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000626 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000627 return;
628 }
629 break;
630 }
631 }
reed@google.com00177082011-10-12 14:34:30 +0000632 new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000633 ((Rec*) fDeque.back())->updateBound(rec);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000634 ((Rec*) fDeque.back())->fGenID = genID;
635
636 if (rec && rec->fSaveCount == fSaveCount) {
637 this->purgeClip(rec);
638 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000639}
640
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000641bool SkClipStack::isWideOpen() const {
642 if (0 == fDeque.count()) {
643 return true;
644 }
645
646 const Rec* back = (const Rec*) fDeque.back();
647 return kInsideOut_BoundsType == back->fFiniteBoundType &&
648 back->fFiniteBound.isEmpty();
649}
650
reed@google.com5c3d1472011-02-22 19:12:23 +0000651///////////////////////////////////////////////////////////////////////////////
652
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000653SkClipStack::Iter::Iter() : fStack(NULL) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000654}
655
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000656bool operator==(const SkClipStack::Iter::Clip& a,
657 const SkClipStack::Iter::Clip& b) {
reed@google.com00177082011-10-12 14:34:30 +0000658 return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
vandebo@chromium.orge03c6522011-06-21 20:45:51 +0000659 ((a.fRect == NULL && b.fRect == NULL) ||
660 (a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
661 ((a.fPath == NULL && b.fPath == NULL) ||
662 (a.fPath != NULL && b.fPath != NULL && *a.fPath == *b.fPath));
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000663}
664
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000665bool operator!=(const SkClipStack::Iter::Clip& a,
666 const SkClipStack::Iter::Clip& b) {
vandebo@chromium.org8887ede2011-05-25 01:27:52 +0000667 return !(a == b);
668}
669
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000670SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
671 : fStack(&stack) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000672 this->reset(stack, startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000673}
674
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000675const SkClipStack::Iter::Clip* SkClipStack::Iter::updateClip(
676 const SkClipStack::Rec* rec) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000677 switch (rec->fState) {
678 case SkClipStack::Rec::kEmpty_State:
679 fClip.fRect = NULL;
680 fClip.fPath = NULL;
681 break;
682 case SkClipStack::Rec::kRect_State:
683 fClip.fRect = &rec->fRect;
684 fClip.fPath = NULL;
685 break;
686 case SkClipStack::Rec::kPath_State:
687 fClip.fRect = NULL;
688 fClip.fPath = &rec->fPath;
689 break;
690 }
691 fClip.fOp = rec->fOp;
reed@google.com00177082011-10-12 14:34:30 +0000692 fClip.fDoAA = rec->fDoAA;
robertphillips@google.com73e71022012-08-09 18:10:49 +0000693 fClip.fGenID = rec->fGenID;
reed@google.com5c3d1472011-02-22 19:12:23 +0000694 return &fClip;
695}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000696
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000697const SkClipStack::Iter::Clip* SkClipStack::Iter::next() {
698 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
699 if (NULL == rec) {
700 return NULL;
701 }
702
703 return this->updateClip(rec);
704}
705
706const SkClipStack::Iter::Clip* SkClipStack::Iter::prev() {
707 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.prev();
708 if (NULL == rec) {
709 return NULL;
710 }
711
712 return this->updateClip(rec);
713}
714
robertphillips@google.com80214e22012-07-20 15:33:18 +0000715const SkClipStack::Iter::Clip* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000716
717 if (NULL == fStack) {
718 return NULL;
719 }
720
721 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
722
723 const SkClipStack::Rec* rec = NULL;
724
725 for (rec = (const SkClipStack::Rec*) fIter.prev();
726 NULL != rec;
727 rec = (const SkClipStack::Rec*) fIter.prev()) {
728
729 if (op == rec->fOp) {
730 // The Deque's iterator is actually one pace ahead of the
731 // returned value. So while "rec" is the element we want to
732 // return, the iterator is actually pointing at (and will
733 // return on the next "next" or "prev" call) the element
734 // in front of it in the deque. Bump the iterator forward a
735 // step so we get the expected result.
736 if (NULL == fIter.next()) {
737 // The reverse iterator has run off the front of the deque
738 // (i.e., the "op" clip is the first clip) and can't
739 // recover. Reset the iterator to start at the front.
740 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
741 }
742 break;
743 }
744 }
745
746 if (NULL == rec) {
747 // There were no "op" clips
748 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
749 }
750
751 return this->next();
752}
753
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000754void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
robertphillips@google.coma6f11c42012-07-23 17:39:44 +0000755 fStack = &stack;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000756 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
bsalomon@google.comd302f142011-03-03 13:54:13 +0000757}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000758
759// helper method
760void SkClipStack::getConservativeBounds(int offsetX,
761 int offsetY,
762 int maxWidth,
763 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000764 SkRect* devBounds,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000765 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000766 SkASSERT(NULL != devBounds);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000767
robertphillips@google.com7b112892012-07-31 15:18:21 +0000768 devBounds->setLTRB(0, 0,
769 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000770
771 SkRect temp;
772 SkClipStack::BoundsType boundType;
773
robertphillips@google.com7b112892012-07-31 15:18:21 +0000774 // temp starts off in canvas space here
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000775 this->getBounds(&temp, &boundType, isIntersectionOfRects);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000776 if (SkClipStack::kInsideOut_BoundsType == boundType) {
777 return;
778 }
779
robertphillips@google.com7b112892012-07-31 15:18:21 +0000780 // but is converted to device space here
robertphillips@google.com607fe072012-07-24 13:54:00 +0000781 temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
782
robertphillips@google.com7b112892012-07-31 15:18:21 +0000783 if (!devBounds->intersect(temp)) {
784 devBounds->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000785 }
786}
robertphillips@google.com46f93502012-08-07 15:38:08 +0000787
788void SkClipStack::addPurgeClipCallback(PFPurgeClipCB callback, void* data) const {
789 ClipCallbackData temp = { callback, data };
790 fCallbackData.append(1, &temp);
791}
792
793void SkClipStack::removePurgeClipCallback(PFPurgeClipCB callback, void* data) const {
794 ClipCallbackData temp = { callback, data };
795 int index = fCallbackData.find(temp);
796 if (index >= 0) {
797 fCallbackData.removeShuffle(index);
798 }
799}
800
801// The clip state represented by 'rec' will never be used again. Purge it.
802void SkClipStack::purgeClip(Rec* rec) {
803 SkASSERT(NULL != rec);
804
805 for (int i = 0; i < fCallbackData.count(); ++i) {
806 (*fCallbackData[i].fCallback)(rec->fGenID, fCallbackData[i].fData);
807 }
808
809 // Invalidate rec's gen ID so handlers can detect already handled records
810 rec->fGenID = kInvalidGenID;
811}
812
813int32_t SkClipStack::GetNextGenID() {
814 return sk_atomic_inc(&gGenID);
815}
robertphillips@google.com73e71022012-08-09 18:10:49 +0000816
817int32_t SkClipStack::getTopmostGenID() const {
818
819 if (fDeque.empty()) {
820 return kInvalidGenID;
821 }
822
823 Rec* rec = (Rec*)fDeque.back();
824 return rec->fGenID;
825}