blob: 52b12cf7795639aa773c8427dc7525c3c59493d6 [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 ||
89 fGenID != b.fGenID ||
90 fOp != b.fOp ||
91 fState != b.fState ||
92 fDoAA != b.fDoAA) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000093 return false;
94 }
95 switch (fState) {
96 case kEmpty_State:
97 return true;
98 case kRect_State:
99 return fRect == b.fRect;
100 case kPath_State:
101 return fPath == b.fPath;
102 }
103 return false; // Silence the compiler.
104 }
105
106 bool operator!=(const Rec& b) const {
107 return !(*this == b);
108 }
109
110
reed@google.com5c3d1472011-02-22 19:12:23 +0000111 /**
112 * Returns true if this Rec can be intersected in place with a new clip
113 */
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000114 bool canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000115 if (kEmpty_State == fState && (
116 SkRegion::kDifference_Op == op ||
117 SkRegion::kIntersect_Op == op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000118 return true;
119 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000120 // Only clips within the same save/restore frame (as captured by
121 // the save count) can be merged
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000122 return fSaveCount == saveCount &&
123 SkRegion::kIntersect_Op == op &&
124 (SkRegion::kIntersect_Op == fOp || SkRegion::kReplace_Op == fOp);
reed@google.com5c3d1472011-02-22 19:12:23 +0000125 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000126
127 /**
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000128 * This method checks to see if two rect clips can be safely merged into
129 * one. The issue here is that to be strictly correct all the edges of
130 * the resulting rect must have the same anti-aliasing.
131 */
132 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
133 SkASSERT(kRect_State == fState);
134
135 if (fDoAA == newAA) {
136 // if the AA setting is the same there is no issue
137 return true;
138 }
139
140 if (!SkRect::Intersects(fRect, newR)) {
141 // The calling code will correctly set the result to the empty clip
142 return true;
143 }
144
145 if (fRect.contains(newR)) {
146 // if the new rect carves out a portion of the old one there is no
147 // issue
148 return true;
149 }
150
151 // So either the two overlap in some complex manner or newR contains oldR.
152 // In the first, case the edges will require different AA. In the second,
153 // the AA setting that would be carried forward is incorrect (e.g., oldR
154 // is AA while newR is BW but since newR contains oldR, oldR will be
155 // drawn BW) since the new AA setting will predominate.
156 return false;
157 }
158
159
160 /**
robertphillips@google.com607fe072012-07-24 13:54:00 +0000161 * The different combination of fill & inverse fill when combining
162 * bounding boxes
163 */
164 enum FillCombo {
165 kPrev_Cur_FillCombo,
166 kPrev_InvCur_FillCombo,
167 kInvPrev_Cur_FillCombo,
168 kInvPrev_InvCur_FillCombo
169 };
170
171 // a mirror of CombineBoundsRevDiff
172 void CombineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
173 switch (combination) {
174 case kInvPrev_InvCur_FillCombo:
175 // In this case the only pixels that can remain set
176 // are inside the current clip rect since the extensions
177 // to infinity of both clips cancel out and whatever
178 // is outside of the current clip is removed
179 fFiniteBoundType = kNormal_BoundsType;
180 break;
181 case kInvPrev_Cur_FillCombo:
182 // In this case the current op is finite so the only pixels
183 // that aren't set are whatever isn't set in the previous
184 // clip and whatever this clip carves out
185 fFiniteBound.join(prevFinite);
186 fFiniteBoundType = kInsideOut_BoundsType;
187 break;
188 case kPrev_InvCur_FillCombo:
189 // In this case everything outside of this clip's bound
190 // is erased, so the only pixels that can remain set
191 // occur w/in the intersection of the two finite bounds
192 if (!fFiniteBound.intersect(prevFinite)) {
193 fFiniteBound.setEmpty();
194 }
195 fFiniteBoundType = kNormal_BoundsType;
196 break;
197 case kPrev_Cur_FillCombo:
198 // The most conservative result bound is that of the
199 // prior clip. This could be wildly incorrect if the
200 // second clip either exactly matches the first clip
201 // (which should yield the empty set) or reduces the
202 // size of the prior bound (e.g., if the second clip
203 // exactly matched the bottom half of the prior clip).
204 // We ignore these two possibilities.
205 fFiniteBound = prevFinite;
206 break;
207 default:
208 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsDiff Invalid fill combination");
209 break;
210 }
211 }
212
213 void CombineBoundsXOR(int combination, const SkRect& prevFinite) {
214
215 switch (combination) {
216 case kInvPrev_Cur_FillCombo: // fall through
217 case kPrev_InvCur_FillCombo:
218 // With only one of the clips inverted the result will always
219 // extend to infinity. The only pixels that may be un-writeable
220 // lie within the union of the two finite bounds
221 fFiniteBound.join(prevFinite);
222 fFiniteBoundType = kInsideOut_BoundsType;
223 break;
224 case kInvPrev_InvCur_FillCombo:
225 // The only pixels that can survive are within the
226 // union of the two bounding boxes since the extensions
227 // to infinity of both clips cancel out
228 // fall through!
229 case kPrev_Cur_FillCombo:
230 // The most conservative bound for xor is the
231 // union of the two bounds. If the two clips exactly overlapped
232 // the xor could yield the empty set. Similarly the xor
233 // could reduce the size of the original clip's bound (e.g.,
234 // if the second clip exactly matched the bottom half of the
235 // first clip). We ignore these two cases.
236 fFiniteBound.join(prevFinite);
237 fFiniteBoundType = kNormal_BoundsType;
238 break;
239 default:
240 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsXOR Invalid fill combination");
241 break;
242 }
243 }
244
245 // a mirror of CombineBoundsIntersection
246 void CombineBoundsUnion(int combination, const SkRect& prevFinite) {
247
248 switch (combination) {
249 case kInvPrev_InvCur_FillCombo:
250 if (!fFiniteBound.intersect(prevFinite)) {
251 fFiniteBound.setEmpty();
252 }
253 fFiniteBoundType = kInsideOut_BoundsType;
254 break;
255 case kInvPrev_Cur_FillCombo:
256 // The only pixels that won't be drawable are inside
257 // the prior clip's finite bound
258 fFiniteBound = prevFinite;
259 fFiniteBoundType = kInsideOut_BoundsType;
260 break;
261 case kPrev_InvCur_FillCombo:
262 // The only pixels that won't be drawable are inside
263 // this clip's finite bound
264 break;
265 case kPrev_Cur_FillCombo:
266 fFiniteBound.join(prevFinite);
267 break;
268 default:
269 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsUnion Invalid fill combination");
270 break;
271 }
272 }
273
274 // a mirror of CombineBoundsUnion
275 void CombineBoundsIntersection(int combination, const SkRect& prevFinite) {
276
277 switch (combination) {
278 case kInvPrev_InvCur_FillCombo:
279 // The only pixels that aren't writable in this case
280 // occur in the union of the two finite bounds
281 fFiniteBound.join(prevFinite);
282 fFiniteBoundType = kInsideOut_BoundsType;
283 break;
284 case kInvPrev_Cur_FillCombo:
285 // In this case the only pixels that will remain writeable
286 // are within the current clip
287 break;
288 case kPrev_InvCur_FillCombo:
289 // In this case the only pixels that will remain writeable
290 // are with the previous clip
291 fFiniteBound = prevFinite;
292 fFiniteBoundType = kNormal_BoundsType;
293 break;
294 case kPrev_Cur_FillCombo:
295 if (!fFiniteBound.intersect(prevFinite)) {
296 fFiniteBound.setEmpty();
297 }
298 break;
299 default:
300 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsIntersection Invalid fill combination");
301 break;
302 }
303 }
304
305 // a mirror of CombineBoundsDiff
306 void CombineBoundsRevDiff(int combination, const SkRect& prevFinite) {
307
308 switch (combination) {
309 case kInvPrev_InvCur_FillCombo:
310 // The only pixels that can survive are in the
311 // previous bound since the extensions to infinity in
312 // both clips cancel out
313 fFiniteBound = prevFinite;
314 fFiniteBoundType = kNormal_BoundsType;
315 break;
316 case kInvPrev_Cur_FillCombo:
317 if (!fFiniteBound.intersect(prevFinite)) {
318 fFiniteBound.setEmpty();
319 }
320 fFiniteBoundType = kNormal_BoundsType;
321 break;
322 case kPrev_InvCur_FillCombo:
323 fFiniteBound.join(prevFinite);
324 fFiniteBoundType = kInsideOut_BoundsType;
325 break;
326 case kPrev_Cur_FillCombo:
327 // Fall through - as with the kDifference_Op case, the
328 // most conservative result bound is the bound of the
329 // current clip. The prior clip could reduce the size of this
330 // bound (as in the kDifference_Op case) but we are ignoring
331 // those cases.
332 break;
333 default:
334 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsRevDiff Invalid fill combination");
335 break;
336 }
337 }
338
339 void updateBound(const Rec* prior) {
340
341 // First, optimistically update the current Rec's bound information
342 // with the current clip's bound
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000343 fIsIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000344 if (kRect_State == fState) {
345 fFiniteBound = fRect;
346 fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000347
348 if (SkRegion::kReplace_Op == fOp ||
349 (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000350 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
351 prior->rectRectIntersectAllowed(fRect, fDoAA))) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000352 fIsIntersectionOfRects = true;
353 }
354
robertphillips@google.com607fe072012-07-24 13:54:00 +0000355 } else {
356 fFiniteBound = fPath.getBounds();
357
358 if (fPath.isInverseFillType()) {
359 fFiniteBoundType = kInsideOut_BoundsType;
360 } else {
361 fFiniteBoundType = kNormal_BoundsType;
362 }
363 }
364
365 if (!fDoAA) {
366 // Here we mimic a non-anti-aliased scanline system. If there is
367 // no anti-aliasing we can integerize the bounding box to exclude
368 // fractional parts that won't be rendered.
369 // Note: the left edge is handled slightly differently below. We
370 // are a bit more generous in the rounding since we don't want to
371 // risk missing the left pixels when fLeft is very close to .5
372 fFiniteBound.set(SkIntToScalar(SkScalarFloorToInt(fFiniteBound.fLeft+0.45f)),
373 SkIntToScalar(SkScalarRound(fFiniteBound.fTop)),
374 SkIntToScalar(SkScalarRound(fFiniteBound.fRight)),
375 SkIntToScalar(SkScalarRound(fFiniteBound.fBottom)));
376 }
377
378 // Now set up the previous Rec's bound information taking into
379 // account that there may be no previous clip
380 SkRect prevFinite;
381 SkClipStack::BoundsType prevType;
382
383 if (NULL == prior) {
384 // no prior clip means the entire plane is writable
385 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
386 prevType = kInsideOut_BoundsType;
387 } else {
388 prevFinite = prior->fFiniteBound;
389 prevType = prior->fFiniteBoundType;
390 }
391
392 FillCombo combination = kPrev_Cur_FillCombo;
393 if (kInsideOut_BoundsType == fFiniteBoundType) {
394 combination = (FillCombo) (combination | 0x01);
395 }
396 if (kInsideOut_BoundsType == prevType) {
397 combination = (FillCombo) (combination | 0x02);
398 }
399
400 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
401 kInvPrev_Cur_FillCombo == combination ||
402 kPrev_InvCur_FillCombo == combination ||
403 kPrev_Cur_FillCombo == combination);
404
405 // Now integrate with clip with the prior clips
406 switch (fOp) {
407 case SkRegion::kDifference_Op:
408 this->CombineBoundsDiff(combination, prevFinite);
409 break;
410 case SkRegion::kXOR_Op:
411 this->CombineBoundsXOR(combination, prevFinite);
412 break;
413 case SkRegion::kUnion_Op:
414 this->CombineBoundsUnion(combination, prevFinite);
415 break;
416 case SkRegion::kIntersect_Op:
417 this->CombineBoundsIntersection(combination, prevFinite);
418 break;
419 case SkRegion::kReverseDifference_Op:
420 this->CombineBoundsRevDiff(combination, prevFinite);
421 break;
422 case SkRegion::kReplace_Op:
423 // Replace just ignores everything prior
424 // The current clip's bound information is already filled in
425 // so nothing to do
426 break;
427 default:
428 SkDebugf("SkRegion::Op error/n");
429 SkASSERT(0);
430 break;
431 }
432 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000433};
434
robertphillips@google.com46f93502012-08-07 15:38:08 +0000435
436SkClipStack::SkClipStack()
437 : fDeque(sizeof(Rec))
438 , fSaveCount(0) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000439}
440
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000441SkClipStack::SkClipStack(const SkClipStack& b) : fDeque(sizeof(Rec)) {
442 *this = b;
443}
444
robertphillips@google.com46f93502012-08-07 15:38:08 +0000445SkClipStack::SkClipStack(const SkRect& r)
446 : fDeque(sizeof(Rec))
447 , fSaveCount(0) {
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000448 if (!r.isEmpty()) {
449 this->clipDevRect(r, SkRegion::kReplace_Op, false);
450 }
451}
452
robertphillips@google.com46f93502012-08-07 15:38:08 +0000453SkClipStack::SkClipStack(const SkIRect& r)
454 : fDeque(sizeof(Rec))
455 , fSaveCount(0) {
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000456 if (!r.isEmpty()) {
457 SkRect temp;
458 temp.set(r);
459 this->clipDevRect(temp, SkRegion::kReplace_Op, false);
460 }
461}
462
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000463SkClipStack::~SkClipStack() {
464 reset();
465}
466
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000467SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
468 if (this == &b) {
469 return *this;
470 }
471 reset();
472
473 fSaveCount = b.fSaveCount;
474 SkDeque::F2BIter recIter(b.fDeque);
475 for (const Rec* rec = (const Rec*)recIter.next();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000476 rec != NULL;
477 rec = (const Rec*)recIter.next()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000478 new (fDeque.push_back()) Rec(*rec);
479 }
480
481 return *this;
482}
483
484bool SkClipStack::operator==(const SkClipStack& b) const {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000485 if (fSaveCount != b.fSaveCount ||
486 fDeque.count() != b.fDeque.count()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000487 return false;
488 }
489 SkDeque::F2BIter myIter(fDeque);
490 SkDeque::F2BIter bIter(b.fDeque);
491 const Rec* myRec = (const Rec*)myIter.next();
492 const Rec* bRec = (const Rec*)bIter.next();
493
494 while (myRec != NULL && bRec != NULL) {
495 if (*myRec != *bRec) {
496 return false;
497 }
498 myRec = (const Rec*)myIter.next();
499 bRec = (const Rec*)bIter.next();
500 }
501 return myRec == NULL && bRec == NULL;
502}
503
reed@google.com5c3d1472011-02-22 19:12:23 +0000504void SkClipStack::reset() {
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000505 // We used a placement new for each object in fDeque, so we're responsible
506 // for calling the destructor on each of them as well.
507 while (!fDeque.empty()) {
508 Rec* rec = (Rec*)fDeque.back();
509 rec->~Rec();
510 fDeque.pop_back();
511 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000512
513 fSaveCount = 0;
514}
515
516void SkClipStack::save() {
517 fSaveCount += 1;
518}
519
520void SkClipStack::restore() {
521 fSaveCount -= 1;
522 while (!fDeque.empty()) {
523 Rec* rec = (Rec*)fDeque.back();
524 if (rec->fSaveCount <= fSaveCount) {
525 break;
526 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000527 this->purgeClip(rec);
reed@google.com5c3d1472011-02-22 19:12:23 +0000528 rec->~Rec();
529 fDeque.pop_back();
530 }
531}
532
robertphillips@google.com7b112892012-07-31 15:18:21 +0000533void SkClipStack::getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000534 BoundsType* boundType,
535 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000536 SkASSERT(NULL != canvFiniteBound && NULL != boundType);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000537
538 Rec* rec = (Rec*)fDeque.back();
539
540 if (NULL == rec) {
541 // the clip is wide open - the infinite plane w/ no pixels un-writeable
robertphillips@google.com7b112892012-07-31 15:18:21 +0000542 canvFiniteBound->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000543 *boundType = kInsideOut_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000544 if (NULL != isIntersectionOfRects) {
545 *isIntersectionOfRects = false;
546 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000547 return;
548 }
549
robertphillips@google.com7b112892012-07-31 15:18:21 +0000550 *canvFiniteBound = rec->fFiniteBound;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000551 *boundType = rec->fFiniteBoundType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000552 if (NULL != isIntersectionOfRects) {
553 *isIntersectionOfRects = rec->fIsIntersectionOfRects;
554 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000555}
556
reed@google.com00177082011-10-12 14:34:30 +0000557void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000558
robertphillips@google.com46f93502012-08-07 15:38:08 +0000559 int32_t genID = GetNextGenID();
560
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000561 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
562 Rec* rec = (Rec*) iter.prev();
563
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000564 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000565 switch (rec->fState) {
566 case Rec::kEmpty_State:
robertphillips@google.com46f93502012-08-07 15:38:08 +0000567 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000568 return;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000569 case Rec::kRect_State:
570 if (rec->rectRectIntersectAllowed(rect, doAA)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000571 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000572 if (!rec->fRect.intersect(rect)) {
573 rec->setEmpty();
574 return;
575 }
576
577 rec->fDoAA = doAA;
578 Rec* prev = (Rec*) iter.prev();
579 rec->updateBound(prev);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000580 rec->fGenID = genID;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000581 return;
reed@google.com5c3d1472011-02-22 19:12:23 +0000582 }
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000583 break;
reed@google.com5c3d1472011-02-22 19:12:23 +0000584 case Rec::kPath_State:
585 if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000586 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000587 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000588 return;
589 }
590 break;
591 }
592 }
reed@google.com00177082011-10-12 14:34:30 +0000593 new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000594 ((Rec*) fDeque.back())->updateBound(rec);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000595 ((Rec*) fDeque.back())->fGenID = genID;
596
597 if (rec && rec->fSaveCount == fSaveCount) {
598 this->purgeClip(rec);
599 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000600}
601
reed@google.com00177082011-10-12 14:34:30 +0000602void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
tomhudson@google.com4c433722012-03-09 16:48:20 +0000603 SkRect alt;
604 if (path.isRect(&alt)) {
605 return this->clipDevRect(alt, op, doAA);
606 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000607
608 int32_t genID = GetNextGenID();
609
reed@google.com5c3d1472011-02-22 19:12:23 +0000610 Rec* rec = (Rec*)fDeque.back();
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000611 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000612 const SkRect& pathBounds = path.getBounds();
613 switch (rec->fState) {
614 case Rec::kEmpty_State:
robertphillips@google.com46f93502012-08-07 15:38:08 +0000615 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000616 return;
617 case Rec::kRect_State:
618 if (!SkRect::Intersects(rec->fRect, pathBounds)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000619 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000620 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000621 return;
622 }
623 break;
624 case Rec::kPath_State:
625 if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000626 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000627 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000628 return;
629 }
630 break;
631 }
632 }
reed@google.com00177082011-10-12 14:34:30 +0000633 new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000634 ((Rec*) fDeque.back())->updateBound(rec);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000635 ((Rec*) fDeque.back())->fGenID = genID;
636
637 if (rec && rec->fSaveCount == fSaveCount) {
638 this->purgeClip(rec);
639 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000640}
641
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000642bool SkClipStack::isWideOpen() const {
643 if (0 == fDeque.count()) {
644 return true;
645 }
646
647 const Rec* back = (const Rec*) fDeque.back();
648 return kInsideOut_BoundsType == back->fFiniteBoundType &&
649 back->fFiniteBound.isEmpty();
650}
651
reed@google.com5c3d1472011-02-22 19:12:23 +0000652///////////////////////////////////////////////////////////////////////////////
653
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000654SkClipStack::Iter::Iter() : fStack(NULL) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000655}
656
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000657bool operator==(const SkClipStack::Iter::Clip& a,
658 const SkClipStack::Iter::Clip& b) {
reed@google.com00177082011-10-12 14:34:30 +0000659 return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
vandebo@chromium.orge03c6522011-06-21 20:45:51 +0000660 ((a.fRect == NULL && b.fRect == NULL) ||
661 (a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
662 ((a.fPath == NULL && b.fPath == NULL) ||
663 (a.fPath != NULL && b.fPath != NULL && *a.fPath == *b.fPath));
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000664}
665
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000666bool operator!=(const SkClipStack::Iter::Clip& a,
667 const SkClipStack::Iter::Clip& b) {
vandebo@chromium.org8887ede2011-05-25 01:27:52 +0000668 return !(a == b);
669}
670
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000671SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
672 : fStack(&stack) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000673 this->reset(stack, startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000674}
675
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000676const SkClipStack::Iter::Clip* SkClipStack::Iter::updateClip(
677 const SkClipStack::Rec* rec) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000678 switch (rec->fState) {
679 case SkClipStack::Rec::kEmpty_State:
680 fClip.fRect = NULL;
681 fClip.fPath = NULL;
682 break;
683 case SkClipStack::Rec::kRect_State:
684 fClip.fRect = &rec->fRect;
685 fClip.fPath = NULL;
686 break;
687 case SkClipStack::Rec::kPath_State:
688 fClip.fRect = NULL;
689 fClip.fPath = &rec->fPath;
690 break;
691 }
692 fClip.fOp = rec->fOp;
reed@google.com00177082011-10-12 14:34:30 +0000693 fClip.fDoAA = rec->fDoAA;
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}