blob: 891270d37a1b4c2285569a0832a1969b21f81b90 [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
bsalomon@google.comedb26fd2012-11-28 14:42:41 +000016static const int32_t kFirstUnreservedGenID = 3;
17int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
robertphillips@google.com46f93502012-08-07 15:38:08 +000018
reed@google.com5c3d1472011-02-22 19:12:23 +000019struct SkClipStack::Rec {
20 enum State {
21 kEmpty_State,
22 kRect_State,
23 kPath_State
24 };
25
26 SkPath fPath;
27 SkRect fRect;
28 int fSaveCount;
29 SkRegion::Op fOp;
30 State fState;
reed@google.com00177082011-10-12 14:34:30 +000031 bool fDoAA;
reed@google.com5c3d1472011-02-22 19:12:23 +000032
robertphillips@google.com607fe072012-07-24 13:54:00 +000033 // fFiniteBoundType and fFiniteBound are used to incrementally update
rmistry@google.comfbfcd562012-08-23 18:09:54 +000034 // the clip stack's bound. When fFiniteBoundType is kNormal_BoundsType,
35 // fFiniteBound represents the conservative bounding box of the pixels
36 // that aren't clipped (i.e., any pixels that can be drawn to are inside
37 // the bound). When fFiniteBoundType is kInsideOut_BoundsType (which occurs
38 // when a clip is inverse filled), fFiniteBound represents the
39 // conservative bounding box of the pixels that _are_ clipped (i.e., any
40 // pixels that cannot be drawn to are inside the bound). When
robertphillips@google.com607fe072012-07-24 13:54:00 +000041 // fFiniteBoundType is kInsideOut_BoundsType the actual bound is
42 // the infinite plane. This behavior of fFiniteBoundType and
43 // fFiniteBound is required so that we can capture the cancelling out
44 // of the extensions to infinity when two inverse filled clips are
45 // Booleaned together.
46 SkClipStack::BoundsType fFiniteBoundType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000047 SkRect fFiniteBound;
48 bool fIsIntersectionOfRects;
robertphillips@google.com607fe072012-07-24 13:54:00 +000049
robertphillips@google.com46f93502012-08-07 15:38:08 +000050 int fGenID;
51
rmistry@google.comfbfcd562012-08-23 18:09:54 +000052 Rec(int saveCount)
reed@google.com0557d9e2012-08-16 15:59:59 +000053 : fGenID(kInvalidGenID) {
54 fSaveCount = saveCount;
55 this->setEmpty();
56 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000057
58 Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA)
robertphillips@google.com46f93502012-08-07 15:38:08 +000059 : fRect(rect)
60 , fGenID(kInvalidGenID) {
reed@google.com5c3d1472011-02-22 19:12:23 +000061 fSaveCount = saveCount;
62 fOp = op;
63 fState = kRect_State;
reed@google.com00177082011-10-12 14:34:30 +000064 fDoAA = doAA;
bsalomon@google.comedb26fd2012-11-28 14:42:41 +000065 // bounding box members are updated in a following updateBoundAndGenID call
reed@google.com5c3d1472011-02-22 19:12:23 +000066 }
67
rmistry@google.comfbfcd562012-08-23 18:09:54 +000068 Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA)
robertphillips@google.com46f93502012-08-07 15:38:08 +000069 : fPath(path)
70 , fGenID(kInvalidGenID) {
vandebo@chromium.orge1bc2742011-06-21 22:26:39 +000071 fRect.setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +000072 fSaveCount = saveCount;
73 fOp = op;
74 fState = kPath_State;
reed@google.com00177082011-10-12 14:34:30 +000075 fDoAA = doAA;
bsalomon@google.comedb26fd2012-11-28 14:42:41 +000076 // bounding box members are updated in a following updateBoundAndGenID call
reed@google.com5c3d1472011-02-22 19:12:23 +000077 }
78
robertphillips@google.com08eacc12012-08-02 12:49:00 +000079 void setEmpty() {
80 fState = kEmpty_State;
81 fFiniteBound.setEmpty();
82 fFiniteBoundType = kNormal_BoundsType;
83 fIsIntersectionOfRects = false;
robertphillips@google.com46f93502012-08-07 15:38:08 +000084 fGenID = kEmptyGenID;
85 }
86
bsalomon@google.comedb26fd2012-11-28 14:42:41 +000087 void checkEmpty() const {
robertphillips@google.com46f93502012-08-07 15:38:08 +000088 SkASSERT(fFiniteBound.isEmpty());
89 SkASSERT(kNormal_BoundsType == fFiniteBoundType);
90 SkASSERT(!fIsIntersectionOfRects);
91 SkASSERT(kEmptyGenID == fGenID);
robertphillips@google.com08eacc12012-08-02 12:49:00 +000092 }
93
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000094 bool operator==(const Rec& b) const {
rmistry@google.comfbfcd562012-08-23 18:09:54 +000095 if (fSaveCount != b.fSaveCount ||
96 fOp != b.fOp ||
robertphillips@google.com46f93502012-08-07 15:38:08 +000097 fState != b.fState ||
98 fDoAA != b.fDoAA) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000099 return false;
100 }
101 switch (fState) {
102 case kEmpty_State:
103 return true;
104 case kRect_State:
105 return fRect == b.fRect;
106 case kPath_State:
107 return fPath == b.fPath;
108 }
109 return false; // Silence the compiler.
110 }
111
112 bool operator!=(const Rec& b) const {
113 return !(*this == b);
114 }
115
116
reed@google.com5c3d1472011-02-22 19:12:23 +0000117 /**
118 * Returns true if this Rec can be intersected in place with a new clip
119 */
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000120 bool canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000121 if (kEmpty_State == fState && (
122 SkRegion::kDifference_Op == op ||
123 SkRegion::kIntersect_Op == op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000124 return true;
125 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000126 // Only clips within the same save/restore frame (as captured by
127 // the save count) can be merged
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000128 return fSaveCount == saveCount &&
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000129 SkRegion::kIntersect_Op == op &&
130 (SkRegion::kIntersect_Op == fOp || SkRegion::kReplace_Op == fOp);
reed@google.com5c3d1472011-02-22 19:12:23 +0000131 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000132
133 /**
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000134 * This method checks to see if two rect clips can be safely merged into
135 * one. The issue here is that to be strictly correct all the edges of
136 * the resulting rect must have the same anti-aliasing.
137 */
138 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
139 SkASSERT(kRect_State == fState);
140
141 if (fDoAA == newAA) {
142 // if the AA setting is the same there is no issue
143 return true;
144 }
145
146 if (!SkRect::Intersects(fRect, newR)) {
147 // The calling code will correctly set the result to the empty clip
148 return true;
149 }
150
151 if (fRect.contains(newR)) {
152 // if the new rect carves out a portion of the old one there is no
153 // issue
154 return true;
155 }
156
157 // So either the two overlap in some complex manner or newR contains oldR.
158 // In the first, case the edges will require different AA. In the second,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000159 // the AA setting that would be carried forward is incorrect (e.g., oldR
160 // is AA while newR is BW but since newR contains oldR, oldR will be
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000161 // drawn BW) since the new AA setting will predominate.
162 return false;
163 }
164
165
166 /**
robertphillips@google.com607fe072012-07-24 13:54:00 +0000167 * The different combination of fill & inverse fill when combining
168 * bounding boxes
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000169 */
robertphillips@google.com607fe072012-07-24 13:54:00 +0000170 enum FillCombo {
171 kPrev_Cur_FillCombo,
172 kPrev_InvCur_FillCombo,
173 kInvPrev_Cur_FillCombo,
174 kInvPrev_InvCur_FillCombo
175 };
176
177 // a mirror of CombineBoundsRevDiff
178 void CombineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
179 switch (combination) {
180 case kInvPrev_InvCur_FillCombo:
181 // In this case the only pixels that can remain set
182 // are inside the current clip rect since the extensions
183 // to infinity of both clips cancel out and whatever
184 // is outside of the current clip is removed
185 fFiniteBoundType = kNormal_BoundsType;
186 break;
187 case kInvPrev_Cur_FillCombo:
188 // In this case the current op is finite so the only pixels
189 // that aren't set are whatever isn't set in the previous
190 // clip and whatever this clip carves out
191 fFiniteBound.join(prevFinite);
192 fFiniteBoundType = kInsideOut_BoundsType;
193 break;
194 case kPrev_InvCur_FillCombo:
195 // In this case everything outside of this clip's bound
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000196 // is erased, so the only pixels that can remain set
robertphillips@google.com607fe072012-07-24 13:54:00 +0000197 // occur w/in the intersection of the two finite bounds
198 if (!fFiniteBound.intersect(prevFinite)) {
199 fFiniteBound.setEmpty();
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000200 fGenID = kEmptyGenID;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000201 }
202 fFiniteBoundType = kNormal_BoundsType;
203 break;
204 case kPrev_Cur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000205 // The most conservative result bound is that of the
206 // prior clip. This could be wildly incorrect if the
207 // second clip either exactly matches the first clip
robertphillips@google.com607fe072012-07-24 13:54:00 +0000208 // (which should yield the empty set) or reduces the
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000209 // size of the prior bound (e.g., if the second clip
robertphillips@google.com607fe072012-07-24 13:54:00 +0000210 // exactly matched the bottom half of the prior clip).
211 // We ignore these two possibilities.
212 fFiniteBound = prevFinite;
213 break;
214 default:
215 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsDiff Invalid fill combination");
216 break;
217 }
218 }
219
220 void CombineBoundsXOR(int combination, const SkRect& prevFinite) {
221
222 switch (combination) {
223 case kInvPrev_Cur_FillCombo: // fall through
224 case kPrev_InvCur_FillCombo:
225 // With only one of the clips inverted the result will always
226 // extend to infinity. The only pixels that may be un-writeable
227 // lie within the union of the two finite bounds
228 fFiniteBound.join(prevFinite);
229 fFiniteBoundType = kInsideOut_BoundsType;
230 break;
231 case kInvPrev_InvCur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000232 // The only pixels that can survive are within the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000233 // union of the two bounding boxes since the extensions
234 // to infinity of both clips cancel out
235 // fall through!
236 case kPrev_Cur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000237 // The most conservative bound for xor is the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000238 // union of the two bounds. If the two clips exactly overlapped
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000239 // the xor could yield the empty set. Similarly the xor
240 // could reduce the size of the original clip's bound (e.g.,
241 // if the second clip exactly matched the bottom half of the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000242 // first clip). We ignore these two cases.
243 fFiniteBound.join(prevFinite);
244 fFiniteBoundType = kNormal_BoundsType;
245 break;
246 default:
247 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsXOR Invalid fill combination");
248 break;
249 }
250 }
251
252 // a mirror of CombineBoundsIntersection
253 void CombineBoundsUnion(int combination, const SkRect& prevFinite) {
254
255 switch (combination) {
256 case kInvPrev_InvCur_FillCombo:
257 if (!fFiniteBound.intersect(prevFinite)) {
258 fFiniteBound.setEmpty();
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000259 fGenID = kWideOpenGenID;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000260 }
261 fFiniteBoundType = kInsideOut_BoundsType;
262 break;
263 case kInvPrev_Cur_FillCombo:
264 // The only pixels that won't be drawable are inside
265 // the prior clip's finite bound
266 fFiniteBound = prevFinite;
267 fFiniteBoundType = kInsideOut_BoundsType;
268 break;
269 case kPrev_InvCur_FillCombo:
270 // The only pixels that won't be drawable are inside
271 // this clip's finite bound
272 break;
273 case kPrev_Cur_FillCombo:
274 fFiniteBound.join(prevFinite);
275 break;
276 default:
277 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsUnion Invalid fill combination");
278 break;
279 }
280 }
281
282 // a mirror of CombineBoundsUnion
283 void CombineBoundsIntersection(int combination, const SkRect& prevFinite) {
284
285 switch (combination) {
286 case kInvPrev_InvCur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000287 // The only pixels that aren't writable in this case
robertphillips@google.com607fe072012-07-24 13:54:00 +0000288 // occur in the union of the two finite bounds
289 fFiniteBound.join(prevFinite);
290 fFiniteBoundType = kInsideOut_BoundsType;
291 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000292 case kInvPrev_Cur_FillCombo:
robertphillips@google.com607fe072012-07-24 13:54:00 +0000293 // In this case the only pixels that will remain writeable
294 // are within the current clip
295 break;
296 case kPrev_InvCur_FillCombo:
297 // In this case the only pixels that will remain writeable
298 // are with the previous clip
299 fFiniteBound = prevFinite;
300 fFiniteBoundType = kNormal_BoundsType;
301 break;
302 case kPrev_Cur_FillCombo:
303 if (!fFiniteBound.intersect(prevFinite)) {
304 fFiniteBound.setEmpty();
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000305 fGenID = kEmptyGenID;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000306 }
307 break;
308 default:
309 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsIntersection Invalid fill combination");
310 break;
311 }
312 }
313
314 // a mirror of CombineBoundsDiff
315 void CombineBoundsRevDiff(int combination, const SkRect& prevFinite) {
316
317 switch (combination) {
318 case kInvPrev_InvCur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000319 // The only pixels that can survive are in the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000320 // previous bound since the extensions to infinity in
321 // both clips cancel out
322 fFiniteBound = prevFinite;
323 fFiniteBoundType = kNormal_BoundsType;
324 break;
325 case kInvPrev_Cur_FillCombo:
326 if (!fFiniteBound.intersect(prevFinite)) {
327 fFiniteBound.setEmpty();
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000328 fGenID = kEmptyGenID;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000329 }
330 fFiniteBoundType = kNormal_BoundsType;
331 break;
332 case kPrev_InvCur_FillCombo:
333 fFiniteBound.join(prevFinite);
334 fFiniteBoundType = kInsideOut_BoundsType;
335 break;
336 case kPrev_Cur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000337 // Fall through - as with the kDifference_Op case, the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000338 // most conservative result bound is the bound of the
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000339 // current clip. The prior clip could reduce the size of this
340 // bound (as in the kDifference_Op case) but we are ignoring
robertphillips@google.com607fe072012-07-24 13:54:00 +0000341 // those cases.
342 break;
343 default:
344 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsRevDiff Invalid fill combination");
345 break;
346 }
347 }
348
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000349 void updateBoundAndGenID(const Rec* prior) {
350 // We set this first here but we may overwrite it later if we determine that the clip is
351 // either wide-open or empty.
352 fGenID = GetNextGenID();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000353
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000354 // First, optimistically update the current Rec's bound information
robertphillips@google.com607fe072012-07-24 13:54:00 +0000355 // with the current clip's bound
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000356 fIsIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000357 if (kRect_State == fState) {
358 fFiniteBound = fRect;
359 fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000360
361 if (SkRegion::kReplace_Op == fOp ||
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000362 (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000363 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
364 prior->rectRectIntersectAllowed(fRect, fDoAA))) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000365 fIsIntersectionOfRects = true;
366 }
367
robertphillips@google.com607fe072012-07-24 13:54:00 +0000368 } else {
reed@google.com0557d9e2012-08-16 15:59:59 +0000369 SkASSERT(kPath_State == fState);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000370
robertphillips@google.com607fe072012-07-24 13:54:00 +0000371 fFiniteBound = fPath.getBounds();
372
373 if (fPath.isInverseFillType()) {
374 fFiniteBoundType = kInsideOut_BoundsType;
375 } else {
376 fFiniteBoundType = kNormal_BoundsType;
377 }
378 }
379
380 if (!fDoAA) {
381 // Here we mimic a non-anti-aliased scanline system. If there is
382 // no anti-aliasing we can integerize the bounding box to exclude
383 // fractional parts that won't be rendered.
384 // Note: the left edge is handled slightly differently below. We
385 // are a bit more generous in the rounding since we don't want to
386 // risk missing the left pixels when fLeft is very close to .5
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000387 fFiniteBound.set(SkIntToScalar(SkScalarFloorToInt(fFiniteBound.fLeft+0.45f)),
388 SkIntToScalar(SkScalarRound(fFiniteBound.fTop)),
389 SkIntToScalar(SkScalarRound(fFiniteBound.fRight)),
robertphillips@google.com607fe072012-07-24 13:54:00 +0000390 SkIntToScalar(SkScalarRound(fFiniteBound.fBottom)));
391 }
392
393 // Now set up the previous Rec's bound information taking into
394 // account that there may be no previous clip
395 SkRect prevFinite;
396 SkClipStack::BoundsType prevType;
397
398 if (NULL == prior) {
399 // no prior clip means the entire plane is writable
400 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
401 prevType = kInsideOut_BoundsType;
402 } else {
403 prevFinite = prior->fFiniteBound;
404 prevType = prior->fFiniteBoundType;
405 }
406
407 FillCombo combination = kPrev_Cur_FillCombo;
408 if (kInsideOut_BoundsType == fFiniteBoundType) {
409 combination = (FillCombo) (combination | 0x01);
410 }
411 if (kInsideOut_BoundsType == prevType) {
412 combination = (FillCombo) (combination | 0x02);
413 }
414
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000415 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
robertphillips@google.com607fe072012-07-24 13:54:00 +0000416 kInvPrev_Cur_FillCombo == combination ||
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000417 kPrev_InvCur_FillCombo == combination ||
robertphillips@google.com607fe072012-07-24 13:54:00 +0000418 kPrev_Cur_FillCombo == combination);
419
420 // Now integrate with clip with the prior clips
421 switch (fOp) {
422 case SkRegion::kDifference_Op:
423 this->CombineBoundsDiff(combination, prevFinite);
424 break;
425 case SkRegion::kXOR_Op:
426 this->CombineBoundsXOR(combination, prevFinite);
427 break;
428 case SkRegion::kUnion_Op:
429 this->CombineBoundsUnion(combination, prevFinite);
430 break;
431 case SkRegion::kIntersect_Op:
432 this->CombineBoundsIntersection(combination, prevFinite);
433 break;
434 case SkRegion::kReverseDifference_Op:
435 this->CombineBoundsRevDiff(combination, prevFinite);
436 break;
437 case SkRegion::kReplace_Op:
438 // Replace just ignores everything prior
439 // The current clip's bound information is already filled in
440 // so nothing to do
441 break;
442 default:
443 SkDebugf("SkRegion::Op error/n");
444 SkASSERT(0);
445 break;
446 }
447 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000448};
449
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000450// This constant determines how many Rec's are allocated together as a block in
robertphillips@google.comf9a90842012-08-17 14:25:43 +0000451// the deque. As such it needs to balance allocating too much memory vs.
452// incurring allocation/deallocation thrashing. It should roughly correspond to
453// the deepest save/restore stack we expect to see.
454static const int kDefaultRecordAllocCnt = 8;
robertphillips@google.com46f93502012-08-07 15:38:08 +0000455
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000456SkClipStack::SkClipStack()
robertphillips@google.comf9a90842012-08-17 14:25:43 +0000457 : fDeque(sizeof(Rec), kDefaultRecordAllocCnt)
robertphillips@google.com46f93502012-08-07 15:38:08 +0000458 , fSaveCount(0) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000459}
460
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000461SkClipStack::SkClipStack(const SkClipStack& b)
robertphillips@google.comf9a90842012-08-17 14:25:43 +0000462 : fDeque(sizeof(Rec), kDefaultRecordAllocCnt) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000463 *this = b;
464}
465
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000466SkClipStack::SkClipStack(const SkRect& r)
robertphillips@google.comf9a90842012-08-17 14:25:43 +0000467 : fDeque(sizeof(Rec), kDefaultRecordAllocCnt)
robertphillips@google.com46f93502012-08-07 15:38:08 +0000468 , fSaveCount(0) {
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000469 if (!r.isEmpty()) {
470 this->clipDevRect(r, SkRegion::kReplace_Op, false);
471 }
472}
473
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000474SkClipStack::SkClipStack(const SkIRect& r)
robertphillips@google.comf9a90842012-08-17 14:25:43 +0000475 : fDeque(sizeof(Rec), kDefaultRecordAllocCnt)
robertphillips@google.com46f93502012-08-07 15:38:08 +0000476 , fSaveCount(0) {
robertphillips@google.com641f8b12012-07-31 19:15:58 +0000477 if (!r.isEmpty()) {
478 SkRect temp;
479 temp.set(r);
480 this->clipDevRect(temp, SkRegion::kReplace_Op, false);
481 }
482}
483
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000484SkClipStack::~SkClipStack() {
485 reset();
486}
487
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000488SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
489 if (this == &b) {
490 return *this;
491 }
492 reset();
493
494 fSaveCount = b.fSaveCount;
495 SkDeque::F2BIter recIter(b.fDeque);
496 for (const Rec* rec = (const Rec*)recIter.next();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000497 rec != NULL;
498 rec = (const Rec*)recIter.next()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000499 new (fDeque.push_back()) Rec(*rec);
500 }
501
502 return *this;
503}
504
505bool SkClipStack::operator==(const SkClipStack& b) const {
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000506 if (fSaveCount != b.fSaveCount ||
robertphillips@google.com46f93502012-08-07 15:38:08 +0000507 fDeque.count() != b.fDeque.count()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000508 return false;
509 }
510 SkDeque::F2BIter myIter(fDeque);
511 SkDeque::F2BIter bIter(b.fDeque);
512 const Rec* myRec = (const Rec*)myIter.next();
513 const Rec* bRec = (const Rec*)bIter.next();
514
515 while (myRec != NULL && bRec != NULL) {
516 if (*myRec != *bRec) {
517 return false;
518 }
519 myRec = (const Rec*)myIter.next();
520 bRec = (const Rec*)bIter.next();
521 }
522 return myRec == NULL && bRec == NULL;
523}
524
reed@google.com5c3d1472011-02-22 19:12:23 +0000525void SkClipStack::reset() {
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000526 // We used a placement new for each object in fDeque, so we're responsible
527 // for calling the destructor on each of them as well.
528 while (!fDeque.empty()) {
529 Rec* rec = (Rec*)fDeque.back();
530 rec->~Rec();
531 fDeque.pop_back();
532 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000533
534 fSaveCount = 0;
535}
536
537void SkClipStack::save() {
538 fSaveCount += 1;
539}
540
541void SkClipStack::restore() {
542 fSaveCount -= 1;
543 while (!fDeque.empty()) {
544 Rec* rec = (Rec*)fDeque.back();
545 if (rec->fSaveCount <= fSaveCount) {
546 break;
547 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000548 this->purgeClip(rec);
reed@google.com5c3d1472011-02-22 19:12:23 +0000549 rec->~Rec();
550 fDeque.pop_back();
551 }
552}
553
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000554void SkClipStack::getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000555 BoundsType* boundType,
556 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000557 SkASSERT(NULL != canvFiniteBound && NULL != boundType);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000558
559 Rec* rec = (Rec*)fDeque.back();
560
561 if (NULL == rec) {
562 // the clip is wide open - the infinite plane w/ no pixels un-writeable
robertphillips@google.com7b112892012-07-31 15:18:21 +0000563 canvFiniteBound->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000564 *boundType = kInsideOut_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000565 if (NULL != isIntersectionOfRects) {
566 *isIntersectionOfRects = false;
567 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000568 return;
569 }
570
robertphillips@google.com7b112892012-07-31 15:18:21 +0000571 *canvFiniteBound = rec->fFiniteBound;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000572 *boundType = rec->fFiniteBoundType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000573 if (NULL != isIntersectionOfRects) {
574 *isIntersectionOfRects = rec->fIsIntersectionOfRects;
575 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000576}
577
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000578bool SkClipStack::intersectRectWithClip(SkRect* rect) const {
579 SkASSERT(NULL != rect);
580
581 SkRect bounds;
582 SkClipStack::BoundsType bt;
583 this->getBounds(&bounds, &bt);
584 if (bt == SkClipStack::kInsideOut_BoundsType) {
585 if (bounds.contains(*rect)) {
586 return false;
587 } else {
588 // If rect's x values are both within bound's x range we
589 // could clip here. Same for y. But we don't bother to check.
590 return true;
591 }
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +0000592 } else {
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000593 return rect->intersect(bounds);
594 }
595}
596
reed@google.com00177082011-10-12 14:34:30 +0000597void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000598
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000599 // Use reverse iterator instead of back because Rect path may need previous
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000600 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
601 Rec* rec = (Rec*) iter.prev();
602
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000603 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000604 switch (rec->fState) {
605 case Rec::kEmpty_State:
robertphillips@google.com46f93502012-08-07 15:38:08 +0000606 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000607 return;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000608 case Rec::kRect_State:
609 if (rec->rectRectIntersectAllowed(rect, doAA)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000610 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000611 if (!rec->fRect.intersect(rect)) {
612 rec->setEmpty();
613 return;
614 }
615
616 rec->fDoAA = doAA;
617 Rec* prev = (Rec*) iter.prev();
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000618 rec->updateBoundAndGenID(prev);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000619 return;
reed@google.com5c3d1472011-02-22 19:12:23 +0000620 }
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000621 break;
reed@google.com5c3d1472011-02-22 19:12:23 +0000622 case Rec::kPath_State:
623 if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000624 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000625 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000626 return;
627 }
628 break;
629 }
630 }
reed@google.com00177082011-10-12 14:34:30 +0000631 new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000632 ((Rec*) fDeque.back())->updateBoundAndGenID(rec);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000633
634 if (rec && rec->fSaveCount == fSaveCount) {
635 this->purgeClip(rec);
636 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000637}
638
reed@google.com00177082011-10-12 14:34:30 +0000639void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
tomhudson@google.com4c433722012-03-09 16:48:20 +0000640 SkRect alt;
641 if (path.isRect(&alt)) {
642 return this->clipDevRect(alt, op, doAA);
643 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000644
reed@google.com5c3d1472011-02-22 19:12:23 +0000645 Rec* rec = (Rec*)fDeque.back();
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000646 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000647 const SkRect& pathBounds = path.getBounds();
648 switch (rec->fState) {
649 case Rec::kEmpty_State:
robertphillips@google.com46f93502012-08-07 15:38:08 +0000650 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000651 return;
652 case Rec::kRect_State:
653 if (!SkRect::Intersects(rec->fRect, pathBounds)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000654 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000655 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000656 return;
657 }
658 break;
659 case Rec::kPath_State:
660 if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000661 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000662 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000663 return;
664 }
665 break;
666 }
667 }
reed@google.com00177082011-10-12 14:34:30 +0000668 new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000669 ((Rec*) fDeque.back())->updateBoundAndGenID(rec);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000670
671 if (rec && rec->fSaveCount == fSaveCount) {
672 this->purgeClip(rec);
673 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000674}
675
reed@google.com0557d9e2012-08-16 15:59:59 +0000676void SkClipStack::clipEmpty() {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000677
678 Rec* rec = (Rec*) fDeque.back();
679
reed@google.com0557d9e2012-08-16 15:59:59 +0000680 if (rec && rec->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
681 switch (rec->fState) {
682 case Rec::kEmpty_State:
683 rec->checkEmpty();
684 return;
685 case Rec::kRect_State:
686 case Rec::kPath_State:
687 this->purgeClip(rec);
688 rec->setEmpty();
689 return;
690 }
691 }
692 new (fDeque.push_back()) Rec(fSaveCount);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000693
reed@google.com0557d9e2012-08-16 15:59:59 +0000694 if (rec && rec->fSaveCount == fSaveCount) {
695 this->purgeClip(rec);
696 }
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000697 ((Rec*)fDeque.back())->fGenID = kEmptyGenID;
reed@google.com0557d9e2012-08-16 15:59:59 +0000698}
699
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000700bool SkClipStack::isWideOpen() const {
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000701 if (0 == fDeque.count()) {
702 return true;
703 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000704
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000705 const Rec* back = (const Rec*) fDeque.back();
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000706 return kWideOpenGenID == back->fGenID ||
707 (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.isEmpty());
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000708}
709
reed@google.com5c3d1472011-02-22 19:12:23 +0000710///////////////////////////////////////////////////////////////////////////////
711
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000712SkClipStack::Iter::Iter() : fStack(NULL) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000713}
714
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000715bool operator==(const SkClipStack::Iter::Clip& a,
716 const SkClipStack::Iter::Clip& b) {
reed@google.com00177082011-10-12 14:34:30 +0000717 return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
vandebo@chromium.orge03c6522011-06-21 20:45:51 +0000718 ((a.fRect == NULL && b.fRect == NULL) ||
719 (a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
720 ((a.fPath == NULL && b.fPath == NULL) ||
721 (a.fPath != NULL && b.fPath != NULL && *a.fPath == *b.fPath));
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000722}
723
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000724bool operator!=(const SkClipStack::Iter::Clip& a,
725 const SkClipStack::Iter::Clip& b) {
vandebo@chromium.org8887ede2011-05-25 01:27:52 +0000726 return !(a == b);
727}
728
bsalomon@google.com7b7cdd12012-11-07 16:17:24 +0000729const SkRect& SkClipStack::Iter::Clip::getBounds() const {
730 if (NULL != fRect) {
731 return *fRect;
732 } else if (NULL != fPath) {
733 return fPath->getBounds();
734 } else {
735 static const SkRect kEmpty = {0, 0, 0, 0};
736 return kEmpty;
737 }
738}
739
bsalomon@google.com51a62862012-11-26 21:19:43 +0000740bool SkClipStack::Iter::Clip::contains(const SkRect& rect) const {
741 if (NULL != fRect) {
742 return fRect->contains(rect);
743 } else if (NULL != fPath) {
744 return fPath->conservativelyContainsRect(rect);
745 } else {
746 return false;
747 }
748}
749
750bool SkClipStack::Iter::Clip::isInverseFilled() const {
751 return NULL != fPath && fPath->isInverseFillType();
752}
753
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000754SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
755 : fStack(&stack) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000756 this->reset(stack, startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000757}
758
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000759const SkClipStack::Iter::Clip* SkClipStack::Iter::updateClip(
760 const SkClipStack::Rec* rec) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000761 switch (rec->fState) {
762 case SkClipStack::Rec::kEmpty_State:
763 fClip.fRect = NULL;
764 fClip.fPath = NULL;
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000765 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000766 break;
767 case SkClipStack::Rec::kRect_State:
768 fClip.fRect = &rec->fRect;
769 fClip.fPath = NULL;
770 break;
771 case SkClipStack::Rec::kPath_State:
772 fClip.fRect = NULL;
773 fClip.fPath = &rec->fPath;
774 break;
775 }
776 fClip.fOp = rec->fOp;
reed@google.com00177082011-10-12 14:34:30 +0000777 fClip.fDoAA = rec->fDoAA;
robertphillips@google.com73e71022012-08-09 18:10:49 +0000778 fClip.fGenID = rec->fGenID;
reed@google.com5c3d1472011-02-22 19:12:23 +0000779 return &fClip;
780}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000781
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000782const SkClipStack::Iter::Clip* SkClipStack::Iter::next() {
783 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
784 if (NULL == rec) {
785 return NULL;
786 }
787
788 return this->updateClip(rec);
789}
790
791const SkClipStack::Iter::Clip* SkClipStack::Iter::prev() {
792 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.prev();
793 if (NULL == rec) {
794 return NULL;
795 }
796
797 return this->updateClip(rec);
798}
799
robertphillips@google.com80214e22012-07-20 15:33:18 +0000800const SkClipStack::Iter::Clip* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000801
802 if (NULL == fStack) {
803 return NULL;
804 }
805
806 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
807
808 const SkClipStack::Rec* rec = NULL;
809
810 for (rec = (const SkClipStack::Rec*) fIter.prev();
811 NULL != rec;
812 rec = (const SkClipStack::Rec*) fIter.prev()) {
813
814 if (op == rec->fOp) {
815 // The Deque's iterator is actually one pace ahead of the
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000816 // returned value. So while "rec" is the element we want to
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000817 // return, the iterator is actually pointing at (and will
818 // return on the next "next" or "prev" call) the element
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000819 // in front of it in the deque. Bump the iterator forward a
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000820 // step so we get the expected result.
821 if (NULL == fIter.next()) {
822 // The reverse iterator has run off the front of the deque
823 // (i.e., the "op" clip is the first clip) and can't
824 // recover. Reset the iterator to start at the front.
825 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
826 }
827 break;
828 }
829 }
830
831 if (NULL == rec) {
832 // There were no "op" clips
833 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
834 }
835
836 return this->next();
837}
838
bsalomon@google.come8ca6c62012-11-07 21:19:10 +0000839const SkClipStack::Iter::Clip* SkClipStack::Iter::nextCombined() {
840 const Clip* clip;
841
842 if (NULL != (clip = this->next()) &&
843 SkRegion::kIntersect_Op == clip->fOp &&
844 NULL != clip->fRect) {
845 fCombinedRect = *clip->fRect;
846 bool doAA = clip->fDoAA;
847
848 while(NULL != (clip = this->next()) &&
849 SkRegion::kIntersect_Op == clip->fOp &&
850 NULL != clip->fRect) { // backup if non-null
851 /**
852 * If the AA settings don't match on consecutive rects we can still continue if
853 * either contains the other. Otherwise, we must stop.
854 */
855 if (doAA != clip->fDoAA) {
856 if (fCombinedRect.contains(*clip->fRect)) {
857 fCombinedRect = *clip->fRect;
858 doAA = clip->fDoAA;
859 } else if (!clip->fRect->contains(fCombinedRect)) {
860 break;
861 }
862 } else if (!fCombinedRect.intersect(*fClip.fRect)) {
863 fCombinedRect.setEmpty();
864 clip = NULL; // prevents unnecessary rewind below.
865 break;
866 }
867 }
868 // If we got here and clip is non-NULL then we got an element that we weren't able to
869 // combine. We need to backup one to ensure that the callers next next() call returns it.
870 if (NULL != clip) {
871 // If next() above returned the last element then due to Iter's internal workings prev()
872 // will return NULL. In that case we reset to the last element.
873 if (NULL == this->prev()) {
874 this->reset(*fStack, SkClipStack::Iter::kTop_IterStart);
875 }
876 }
877
878 // Must do this last because it is overwritten in the above backup.
879 fClip.fRect = &fCombinedRect;
880 fClip.fPath = NULL;
881 fClip.fOp = SkRegion::kIntersect_Op;
882 fClip.fDoAA = doAA;
883 fClip.fGenID = kInvalidGenID;
884 return &fClip;
885 } else {
886 return clip;
887 }
888}
889
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000890void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
robertphillips@google.coma6f11c42012-07-23 17:39:44 +0000891 fStack = &stack;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000892 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
bsalomon@google.comd302f142011-03-03 13:54:13 +0000893}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000894
895// helper method
896void SkClipStack::getConservativeBounds(int offsetX,
897 int offsetY,
898 int maxWidth,
899 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000900 SkRect* devBounds,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000901 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000902 SkASSERT(NULL != devBounds);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000903
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000904 devBounds->setLTRB(0, 0,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000905 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000906
907 SkRect temp;
908 SkClipStack::BoundsType boundType;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000909
robertphillips@google.com7b112892012-07-31 15:18:21 +0000910 // temp starts off in canvas space here
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000911 this->getBounds(&temp, &boundType, isIntersectionOfRects);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000912 if (SkClipStack::kInsideOut_BoundsType == boundType) {
913 return;
914 }
915
robertphillips@google.com7b112892012-07-31 15:18:21 +0000916 // but is converted to device space here
robertphillips@google.com607fe072012-07-24 13:54:00 +0000917 temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
918
robertphillips@google.com7b112892012-07-31 15:18:21 +0000919 if (!devBounds->intersect(temp)) {
920 devBounds->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000921 }
922}
robertphillips@google.com46f93502012-08-07 15:38:08 +0000923
924void SkClipStack::addPurgeClipCallback(PFPurgeClipCB callback, void* data) const {
925 ClipCallbackData temp = { callback, data };
926 fCallbackData.append(1, &temp);
927}
928
929void SkClipStack::removePurgeClipCallback(PFPurgeClipCB callback, void* data) const {
930 ClipCallbackData temp = { callback, data };
931 int index = fCallbackData.find(temp);
932 if (index >= 0) {
933 fCallbackData.removeShuffle(index);
934 }
935}
936
937// The clip state represented by 'rec' will never be used again. Purge it.
938void SkClipStack::purgeClip(Rec* rec) {
939 SkASSERT(NULL != rec);
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000940 if (rec->fGenID >= 0 && rec->fGenID < kFirstUnreservedGenID) {
941 return;
942 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000943
944 for (int i = 0; i < fCallbackData.count(); ++i) {
945 (*fCallbackData[i].fCallback)(rec->fGenID, fCallbackData[i].fData);
946 }
947
948 // Invalidate rec's gen ID so handlers can detect already handled records
949 rec->fGenID = kInvalidGenID;
950}
951
952int32_t SkClipStack::GetNextGenID() {
bsalomon@google.comedb26fd2012-11-28 14:42:41 +0000953 // TODO: handle overflow.
robertphillips@google.com46f93502012-08-07 15:38:08 +0000954 return sk_atomic_inc(&gGenID);
955}
robertphillips@google.com73e71022012-08-09 18:10:49 +0000956
957int32_t SkClipStack::getTopmostGenID() const {
958
959 if (fDeque.empty()) {
960 return kInvalidGenID;
961 }
962
963 Rec* rec = (Rec*)fDeque.back();
964 return rec->fGenID;
965}