blob: a5b591e72a0068bafbc15c377f9eb2ea7cf11119 [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
rmistry@google.comfbfcd562012-08-23 18:09:54 +000016int32_t SkClipStack::gGenID = 3;
robertphillips@google.com46f93502012-08-07 15:38:08 +000017
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
rmistry@google.comfbfcd562012-08-23 18:09:54 +000033 // 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
robertphillips@google.com607fe072012-07-24 13:54:00 +000040 // 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
rmistry@google.comfbfcd562012-08-23 18:09:54 +000051 Rec(int saveCount)
reed@google.com0557d9e2012-08-16 15:59:59 +000052 : fGenID(kInvalidGenID) {
53 fSaveCount = saveCount;
54 this->setEmpty();
55 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +000056
57 Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA)
robertphillips@google.com46f93502012-08-07 15:38:08 +000058 : fRect(rect)
59 , fGenID(kInvalidGenID) {
reed@google.com5c3d1472011-02-22 19:12:23 +000060 fSaveCount = saveCount;
61 fOp = op;
62 fState = kRect_State;
reed@google.com00177082011-10-12 14:34:30 +000063 fDoAA = doAA;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000064 // bounding box members are updated in a following updateBound call
reed@google.com5c3d1472011-02-22 19:12:23 +000065 }
66
rmistry@google.comfbfcd562012-08-23 18:09:54 +000067 Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA)
robertphillips@google.com46f93502012-08-07 15:38:08 +000068 : fPath(path)
69 , fGenID(kInvalidGenID) {
vandebo@chromium.orge1bc2742011-06-21 22:26:39 +000070 fRect.setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +000071 fSaveCount = saveCount;
72 fOp = op;
73 fState = kPath_State;
reed@google.com00177082011-10-12 14:34:30 +000074 fDoAA = doAA;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +000075 // bounding box members are updated in a following updateBound call
reed@google.com5c3d1472011-02-22 19:12:23 +000076 }
77
robertphillips@google.com08eacc12012-08-02 12:49:00 +000078 void setEmpty() {
79 fState = kEmpty_State;
80 fFiniteBound.setEmpty();
81 fFiniteBoundType = kNormal_BoundsType;
82 fIsIntersectionOfRects = false;
robertphillips@google.com46f93502012-08-07 15:38:08 +000083 fGenID = kEmptyGenID;
84 }
85
86 void checkEmpty() {
87 SkASSERT(fFiniteBound.isEmpty());
88 SkASSERT(kNormal_BoundsType == fFiniteBoundType);
89 SkASSERT(!fIsIntersectionOfRects);
90 SkASSERT(kEmptyGenID == fGenID);
robertphillips@google.com08eacc12012-08-02 12:49:00 +000091 }
92
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000093 bool operator==(const Rec& b) const {
rmistry@google.comfbfcd562012-08-23 18:09:54 +000094 if (fSaveCount != b.fSaveCount ||
95 fOp != b.fOp ||
robertphillips@google.com46f93502012-08-07 15:38:08 +000096 fState != b.fState ||
97 fDoAA != b.fDoAA) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +000098 return false;
99 }
100 switch (fState) {
101 case kEmpty_State:
102 return true;
103 case kRect_State:
104 return fRect == b.fRect;
105 case kPath_State:
106 return fPath == b.fPath;
107 }
108 return false; // Silence the compiler.
109 }
110
111 bool operator!=(const Rec& b) const {
112 return !(*this == b);
113 }
114
115
reed@google.com5c3d1472011-02-22 19:12:23 +0000116 /**
117 * Returns true if this Rec can be intersected in place with a new clip
118 */
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000119 bool canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000120 if (kEmpty_State == fState && (
121 SkRegion::kDifference_Op == op ||
122 SkRegion::kIntersect_Op == op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000123 return true;
124 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000125 // Only clips within the same save/restore frame (as captured by
126 // the save count) can be merged
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000127 return fSaveCount == saveCount &&
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000128 SkRegion::kIntersect_Op == op &&
129 (SkRegion::kIntersect_Op == fOp || SkRegion::kReplace_Op == fOp);
reed@google.com5c3d1472011-02-22 19:12:23 +0000130 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000131
132 /**
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000133 * This method checks to see if two rect clips can be safely merged into
134 * one. The issue here is that to be strictly correct all the edges of
135 * the resulting rect must have the same anti-aliasing.
136 */
137 bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
138 SkASSERT(kRect_State == fState);
139
140 if (fDoAA == newAA) {
141 // if the AA setting is the same there is no issue
142 return true;
143 }
144
145 if (!SkRect::Intersects(fRect, newR)) {
146 // The calling code will correctly set the result to the empty clip
147 return true;
148 }
149
150 if (fRect.contains(newR)) {
151 // if the new rect carves out a portion of the old one there is no
152 // issue
153 return true;
154 }
155
156 // So either the two overlap in some complex manner or newR contains oldR.
157 // In the first, case the edges will require different AA. In the second,
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000158 // the AA setting that would be carried forward is incorrect (e.g., oldR
159 // is AA while newR is BW but since newR contains oldR, oldR will be
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000160 // drawn BW) since the new AA setting will predominate.
161 return false;
162 }
163
164
165 /**
robertphillips@google.com607fe072012-07-24 13:54:00 +0000166 * The different combination of fill & inverse fill when combining
167 * bounding boxes
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000168 */
robertphillips@google.com607fe072012-07-24 13:54:00 +0000169 enum FillCombo {
170 kPrev_Cur_FillCombo,
171 kPrev_InvCur_FillCombo,
172 kInvPrev_Cur_FillCombo,
173 kInvPrev_InvCur_FillCombo
174 };
175
176 // a mirror of CombineBoundsRevDiff
177 void CombineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
178 switch (combination) {
179 case kInvPrev_InvCur_FillCombo:
180 // In this case the only pixels that can remain set
181 // are inside the current clip rect since the extensions
182 // to infinity of both clips cancel out and whatever
183 // is outside of the current clip is removed
184 fFiniteBoundType = kNormal_BoundsType;
185 break;
186 case kInvPrev_Cur_FillCombo:
187 // In this case the current op is finite so the only pixels
188 // that aren't set are whatever isn't set in the previous
189 // clip and whatever this clip carves out
190 fFiniteBound.join(prevFinite);
191 fFiniteBoundType = kInsideOut_BoundsType;
192 break;
193 case kPrev_InvCur_FillCombo:
194 // In this case everything outside of this clip's bound
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000195 // is erased, so the only pixels that can remain set
robertphillips@google.com607fe072012-07-24 13:54:00 +0000196 // occur w/in the intersection of the two finite bounds
197 if (!fFiniteBound.intersect(prevFinite)) {
198 fFiniteBound.setEmpty();
199 }
200 fFiniteBoundType = kNormal_BoundsType;
201 break;
202 case kPrev_Cur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000203 // The most conservative result bound is that of the
204 // prior clip. This could be wildly incorrect if the
205 // second clip either exactly matches the first clip
robertphillips@google.com607fe072012-07-24 13:54:00 +0000206 // (which should yield the empty set) or reduces the
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000207 // size of the prior bound (e.g., if the second clip
robertphillips@google.com607fe072012-07-24 13:54:00 +0000208 // exactly matched the bottom half of the prior clip).
209 // We ignore these two possibilities.
210 fFiniteBound = prevFinite;
211 break;
212 default:
213 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsDiff Invalid fill combination");
214 break;
215 }
216 }
217
218 void CombineBoundsXOR(int combination, const SkRect& prevFinite) {
219
220 switch (combination) {
221 case kInvPrev_Cur_FillCombo: // fall through
222 case kPrev_InvCur_FillCombo:
223 // With only one of the clips inverted the result will always
224 // extend to infinity. The only pixels that may be un-writeable
225 // lie within the union of the two finite bounds
226 fFiniteBound.join(prevFinite);
227 fFiniteBoundType = kInsideOut_BoundsType;
228 break;
229 case kInvPrev_InvCur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000230 // The only pixels that can survive are within the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000231 // union of the two bounding boxes since the extensions
232 // to infinity of both clips cancel out
233 // fall through!
234 case kPrev_Cur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000235 // The most conservative bound for xor is the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000236 // union of the two bounds. If the two clips exactly overlapped
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000237 // the xor could yield the empty set. Similarly the xor
238 // could reduce the size of the original clip's bound (e.g.,
239 // if the second clip exactly matched the bottom half of the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000240 // first clip). We ignore these two cases.
241 fFiniteBound.join(prevFinite);
242 fFiniteBoundType = kNormal_BoundsType;
243 break;
244 default:
245 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsXOR Invalid fill combination");
246 break;
247 }
248 }
249
250 // a mirror of CombineBoundsIntersection
251 void CombineBoundsUnion(int combination, const SkRect& prevFinite) {
252
253 switch (combination) {
254 case kInvPrev_InvCur_FillCombo:
255 if (!fFiniteBound.intersect(prevFinite)) {
256 fFiniteBound.setEmpty();
257 }
258 fFiniteBoundType = kInsideOut_BoundsType;
259 break;
260 case kInvPrev_Cur_FillCombo:
261 // The only pixels that won't be drawable are inside
262 // the prior clip's finite bound
263 fFiniteBound = prevFinite;
264 fFiniteBoundType = kInsideOut_BoundsType;
265 break;
266 case kPrev_InvCur_FillCombo:
267 // The only pixels that won't be drawable are inside
268 // this clip's finite bound
269 break;
270 case kPrev_Cur_FillCombo:
271 fFiniteBound.join(prevFinite);
272 break;
273 default:
274 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsUnion Invalid fill combination");
275 break;
276 }
277 }
278
279 // a mirror of CombineBoundsUnion
280 void CombineBoundsIntersection(int combination, const SkRect& prevFinite) {
281
282 switch (combination) {
283 case kInvPrev_InvCur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000284 // The only pixels that aren't writable in this case
robertphillips@google.com607fe072012-07-24 13:54:00 +0000285 // occur in the union of the two finite bounds
286 fFiniteBound.join(prevFinite);
287 fFiniteBoundType = kInsideOut_BoundsType;
288 break;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000289 case kInvPrev_Cur_FillCombo:
robertphillips@google.com607fe072012-07-24 13:54:00 +0000290 // In this case the only pixels that will remain writeable
291 // are within the current clip
292 break;
293 case kPrev_InvCur_FillCombo:
294 // In this case the only pixels that will remain writeable
295 // are with the previous clip
296 fFiniteBound = prevFinite;
297 fFiniteBoundType = kNormal_BoundsType;
298 break;
299 case kPrev_Cur_FillCombo:
300 if (!fFiniteBound.intersect(prevFinite)) {
301 fFiniteBound.setEmpty();
302 }
303 break;
304 default:
305 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsIntersection Invalid fill combination");
306 break;
307 }
308 }
309
310 // a mirror of CombineBoundsDiff
311 void CombineBoundsRevDiff(int combination, const SkRect& prevFinite) {
312
313 switch (combination) {
314 case kInvPrev_InvCur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000315 // The only pixels that can survive are in the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000316 // previous bound since the extensions to infinity in
317 // both clips cancel out
318 fFiniteBound = prevFinite;
319 fFiniteBoundType = kNormal_BoundsType;
320 break;
321 case kInvPrev_Cur_FillCombo:
322 if (!fFiniteBound.intersect(prevFinite)) {
323 fFiniteBound.setEmpty();
324 }
325 fFiniteBoundType = kNormal_BoundsType;
326 break;
327 case kPrev_InvCur_FillCombo:
328 fFiniteBound.join(prevFinite);
329 fFiniteBoundType = kInsideOut_BoundsType;
330 break;
331 case kPrev_Cur_FillCombo:
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000332 // Fall through - as with the kDifference_Op case, the
robertphillips@google.com607fe072012-07-24 13:54:00 +0000333 // most conservative result bound is the bound of the
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000334 // current clip. The prior clip could reduce the size of this
335 // bound (as in the kDifference_Op case) but we are ignoring
robertphillips@google.com607fe072012-07-24 13:54:00 +0000336 // those cases.
337 break;
338 default:
339 SkDEBUGFAIL("SkClipStack::Rec::CombineBoundsRevDiff Invalid fill combination");
340 break;
341 }
342 }
343
344 void updateBound(const Rec* prior) {
345
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000346 // First, optimistically update the current Rec's bound information
robertphillips@google.com607fe072012-07-24 13:54:00 +0000347 // with the current clip's bound
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000348 fIsIntersectionOfRects = false;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000349 if (kRect_State == fState) {
350 fFiniteBound = fRect;
351 fFiniteBoundType = kNormal_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000352
353 if (SkRegion::kReplace_Op == fOp ||
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000354 (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000355 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
356 prior->rectRectIntersectAllowed(fRect, fDoAA))) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000357 fIsIntersectionOfRects = true;
358 }
359
robertphillips@google.com607fe072012-07-24 13:54:00 +0000360 } else {
reed@google.com0557d9e2012-08-16 15:59:59 +0000361 SkASSERT(kPath_State == fState);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000362
robertphillips@google.com607fe072012-07-24 13:54:00 +0000363 fFiniteBound = fPath.getBounds();
364
365 if (fPath.isInverseFillType()) {
366 fFiniteBoundType = kInsideOut_BoundsType;
367 } else {
368 fFiniteBoundType = kNormal_BoundsType;
369 }
370 }
371
372 if (!fDoAA) {
373 // Here we mimic a non-anti-aliased scanline system. If there is
374 // no anti-aliasing we can integerize the bounding box to exclude
375 // fractional parts that won't be rendered.
376 // Note: the left edge is handled slightly differently below. We
377 // are a bit more generous in the rounding since we don't want to
378 // risk missing the left pixels when fLeft is very close to .5
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000379 fFiniteBound.set(SkIntToScalar(SkScalarFloorToInt(fFiniteBound.fLeft+0.45f)),
380 SkIntToScalar(SkScalarRound(fFiniteBound.fTop)),
381 SkIntToScalar(SkScalarRound(fFiniteBound.fRight)),
robertphillips@google.com607fe072012-07-24 13:54:00 +0000382 SkIntToScalar(SkScalarRound(fFiniteBound.fBottom)));
383 }
384
385 // Now set up the previous Rec's bound information taking into
386 // account that there may be no previous clip
387 SkRect prevFinite;
388 SkClipStack::BoundsType prevType;
389
390 if (NULL == prior) {
391 // no prior clip means the entire plane is writable
392 prevFinite.setEmpty(); // there are no pixels that cannot be drawn to
393 prevType = kInsideOut_BoundsType;
394 } else {
395 prevFinite = prior->fFiniteBound;
396 prevType = prior->fFiniteBoundType;
397 }
398
399 FillCombo combination = kPrev_Cur_FillCombo;
400 if (kInsideOut_BoundsType == fFiniteBoundType) {
401 combination = (FillCombo) (combination | 0x01);
402 }
403 if (kInsideOut_BoundsType == prevType) {
404 combination = (FillCombo) (combination | 0x02);
405 }
406
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000407 SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
robertphillips@google.com607fe072012-07-24 13:54:00 +0000408 kInvPrev_Cur_FillCombo == combination ||
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000409 kPrev_InvCur_FillCombo == combination ||
robertphillips@google.com607fe072012-07-24 13:54:00 +0000410 kPrev_Cur_FillCombo == combination);
411
412 // Now integrate with clip with the prior clips
413 switch (fOp) {
414 case SkRegion::kDifference_Op:
415 this->CombineBoundsDiff(combination, prevFinite);
416 break;
417 case SkRegion::kXOR_Op:
418 this->CombineBoundsXOR(combination, prevFinite);
419 break;
420 case SkRegion::kUnion_Op:
421 this->CombineBoundsUnion(combination, prevFinite);
422 break;
423 case SkRegion::kIntersect_Op:
424 this->CombineBoundsIntersection(combination, prevFinite);
425 break;
426 case SkRegion::kReverseDifference_Op:
427 this->CombineBoundsRevDiff(combination, prevFinite);
428 break;
429 case SkRegion::kReplace_Op:
430 // Replace just ignores everything prior
431 // The current clip's bound information is already filled in
432 // so nothing to do
433 break;
434 default:
435 SkDebugf("SkRegion::Op error/n");
436 SkASSERT(0);
437 break;
438 }
439 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000440};
441
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000442// This constant determines how many Rec's are allocated together as a block in
robertphillips@google.comf9a90842012-08-17 14:25:43 +0000443// the deque. As such it needs to balance allocating too much memory vs.
444// incurring allocation/deallocation thrashing. It should roughly correspond to
445// the deepest save/restore stack we expect to see.
446static const int kDefaultRecordAllocCnt = 8;
robertphillips@google.com46f93502012-08-07 15:38:08 +0000447
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000448SkClipStack::SkClipStack()
robertphillips@google.comf9a90842012-08-17 14:25:43 +0000449 : fDeque(sizeof(Rec), kDefaultRecordAllocCnt)
robertphillips@google.com46f93502012-08-07 15:38:08 +0000450 , fSaveCount(0) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000451}
452
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000453SkClipStack::SkClipStack(const SkClipStack& b)
robertphillips@google.comf9a90842012-08-17 14:25:43 +0000454 : fDeque(sizeof(Rec), kDefaultRecordAllocCnt) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000455 *this = b;
456}
457
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000458SkClipStack::SkClipStack(const SkRect& r)
robertphillips@google.comf9a90842012-08-17 14:25:43 +0000459 : fDeque(sizeof(Rec), kDefaultRecordAllocCnt)
robertphillips@google.com46f93502012-08-07 15:38:08 +0000460 , fSaveCount(0) {
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000461 if (!r.isEmpty()) {
462 this->clipDevRect(r, SkRegion::kReplace_Op, false);
463 }
464}
465
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000466SkClipStack::SkClipStack(const SkIRect& 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.com641f8b12012-07-31 19:15:58 +0000469 if (!r.isEmpty()) {
470 SkRect temp;
471 temp.set(r);
472 this->clipDevRect(temp, SkRegion::kReplace_Op, false);
473 }
474}
475
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000476SkClipStack::~SkClipStack() {
477 reset();
478}
479
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000480SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
481 if (this == &b) {
482 return *this;
483 }
484 reset();
485
486 fSaveCount = b.fSaveCount;
487 SkDeque::F2BIter recIter(b.fDeque);
488 for (const Rec* rec = (const Rec*)recIter.next();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000489 rec != NULL;
490 rec = (const Rec*)recIter.next()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000491 new (fDeque.push_back()) Rec(*rec);
492 }
493
494 return *this;
495}
496
497bool SkClipStack::operator==(const SkClipStack& b) const {
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000498 if (fSaveCount != b.fSaveCount ||
robertphillips@google.com46f93502012-08-07 15:38:08 +0000499 fDeque.count() != b.fDeque.count()) {
vandebo@chromium.org1e1c36f2011-05-03 16:26:09 +0000500 return false;
501 }
502 SkDeque::F2BIter myIter(fDeque);
503 SkDeque::F2BIter bIter(b.fDeque);
504 const Rec* myRec = (const Rec*)myIter.next();
505 const Rec* bRec = (const Rec*)bIter.next();
506
507 while (myRec != NULL && bRec != NULL) {
508 if (*myRec != *bRec) {
509 return false;
510 }
511 myRec = (const Rec*)myIter.next();
512 bRec = (const Rec*)bIter.next();
513 }
514 return myRec == NULL && bRec == NULL;
515}
516
reed@google.com5c3d1472011-02-22 19:12:23 +0000517void SkClipStack::reset() {
vandebo@chromium.org610f7162012-03-14 18:34:15 +0000518 // We used a placement new for each object in fDeque, so we're responsible
519 // for calling the destructor on each of them as well.
520 while (!fDeque.empty()) {
521 Rec* rec = (Rec*)fDeque.back();
522 rec->~Rec();
523 fDeque.pop_back();
524 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000525
526 fSaveCount = 0;
527}
528
529void SkClipStack::save() {
530 fSaveCount += 1;
531}
532
533void SkClipStack::restore() {
534 fSaveCount -= 1;
535 while (!fDeque.empty()) {
536 Rec* rec = (Rec*)fDeque.back();
537 if (rec->fSaveCount <= fSaveCount) {
538 break;
539 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000540 this->purgeClip(rec);
reed@google.com5c3d1472011-02-22 19:12:23 +0000541 rec->~Rec();
542 fDeque.pop_back();
543 }
544}
545
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000546void SkClipStack::getBounds(SkRect* canvFiniteBound,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000547 BoundsType* boundType,
548 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000549 SkASSERT(NULL != canvFiniteBound && NULL != boundType);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000550
551 Rec* rec = (Rec*)fDeque.back();
552
553 if (NULL == rec) {
554 // the clip is wide open - the infinite plane w/ no pixels un-writeable
robertphillips@google.com7b112892012-07-31 15:18:21 +0000555 canvFiniteBound->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000556 *boundType = kInsideOut_BoundsType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000557 if (NULL != isIntersectionOfRects) {
558 *isIntersectionOfRects = false;
559 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000560 return;
561 }
562
robertphillips@google.com7b112892012-07-31 15:18:21 +0000563 *canvFiniteBound = rec->fFiniteBound;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000564 *boundType = rec->fFiniteBoundType;
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000565 if (NULL != isIntersectionOfRects) {
566 *isIntersectionOfRects = rec->fIsIntersectionOfRects;
567 }
robertphillips@google.com607fe072012-07-24 13:54:00 +0000568}
569
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000570bool SkClipStack::intersectRectWithClip(SkRect* rect) const {
571 SkASSERT(NULL != rect);
572
573 SkRect bounds;
574 SkClipStack::BoundsType bt;
575 this->getBounds(&bounds, &bt);
576 if (bt == SkClipStack::kInsideOut_BoundsType) {
577 if (bounds.contains(*rect)) {
578 return false;
579 } else {
580 // If rect's x values are both within bound's x range we
581 // could clip here. Same for y. But we don't bother to check.
582 return true;
583 }
skia.committer@gmail.com5b6f9162012-10-12 02:01:15 +0000584 } else {
bsalomon@google.com3ab43d52012-10-11 19:39:09 +0000585 return rect->intersect(bounds);
586 }
587}
588
reed@google.com00177082011-10-12 14:34:30 +0000589void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000590
robertphillips@google.com46f93502012-08-07 15:38:08 +0000591 int32_t genID = GetNextGenID();
592
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000593 // Use reverse iterator instead of back because Rect path may need previous
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000594 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
595 Rec* rec = (Rec*) iter.prev();
596
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000597 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000598 switch (rec->fState) {
599 case Rec::kEmpty_State:
robertphillips@google.com46f93502012-08-07 15:38:08 +0000600 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000601 return;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000602 case Rec::kRect_State:
603 if (rec->rectRectIntersectAllowed(rect, doAA)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000604 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000605 if (!rec->fRect.intersect(rect)) {
606 rec->setEmpty();
607 return;
608 }
609
610 rec->fDoAA = doAA;
611 Rec* prev = (Rec*) iter.prev();
612 rec->updateBound(prev);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000613 rec->fGenID = genID;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000614 return;
reed@google.com5c3d1472011-02-22 19:12:23 +0000615 }
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000616 break;
reed@google.com5c3d1472011-02-22 19:12:23 +0000617 case Rec::kPath_State:
618 if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
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 }
625 }
reed@google.com00177082011-10-12 14:34:30 +0000626 new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000627 ((Rec*) fDeque.back())->updateBound(rec);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000628 ((Rec*) fDeque.back())->fGenID = genID;
629
630 if (rec && rec->fSaveCount == fSaveCount) {
631 this->purgeClip(rec);
632 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000633}
634
reed@google.com00177082011-10-12 14:34:30 +0000635void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
tomhudson@google.com4c433722012-03-09 16:48:20 +0000636 SkRect alt;
637 if (path.isRect(&alt)) {
638 return this->clipDevRect(alt, op, doAA);
639 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000640
641 int32_t genID = GetNextGenID();
642
reed@google.com5c3d1472011-02-22 19:12:23 +0000643 Rec* rec = (Rec*)fDeque.back();
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000644 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000645 const SkRect& pathBounds = path.getBounds();
646 switch (rec->fState) {
647 case Rec::kEmpty_State:
robertphillips@google.com46f93502012-08-07 15:38:08 +0000648 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000649 return;
650 case Rec::kRect_State:
651 if (!SkRect::Intersects(rec->fRect, pathBounds)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000652 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000653 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000654 return;
655 }
656 break;
657 case Rec::kPath_State:
658 if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000659 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000660 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000661 return;
662 }
663 break;
664 }
665 }
reed@google.com00177082011-10-12 14:34:30 +0000666 new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000667 ((Rec*) fDeque.back())->updateBound(rec);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000668 ((Rec*) fDeque.back())->fGenID = genID;
669
670 if (rec && rec->fSaveCount == fSaveCount) {
671 this->purgeClip(rec);
672 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000673}
674
reed@google.com0557d9e2012-08-16 15:59:59 +0000675void SkClipStack::clipEmpty() {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000676
677 Rec* rec = (Rec*) fDeque.back();
678
reed@google.com0557d9e2012-08-16 15:59:59 +0000679 if (rec && rec->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
680 switch (rec->fState) {
681 case Rec::kEmpty_State:
682 rec->checkEmpty();
683 return;
684 case Rec::kRect_State:
685 case Rec::kPath_State:
686 this->purgeClip(rec);
687 rec->setEmpty();
688 return;
689 }
690 }
691 new (fDeque.push_back()) Rec(fSaveCount);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000692
reed@google.com0557d9e2012-08-16 15:59:59 +0000693 if (rec && rec->fSaveCount == fSaveCount) {
694 this->purgeClip(rec);
695 }
696}
697
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000698bool SkClipStack::isWideOpen() const {
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000699 if (0 == fDeque.count()) {
700 return true;
701 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000702
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000703 const Rec* back = (const Rec*) fDeque.back();
704 return kInsideOut_BoundsType == back->fFiniteBoundType &&
705 back->fFiniteBound.isEmpty();
706}
707
reed@google.com5c3d1472011-02-22 19:12:23 +0000708///////////////////////////////////////////////////////////////////////////////
709
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000710SkClipStack::Iter::Iter() : fStack(NULL) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000711}
712
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000713bool operator==(const SkClipStack::Iter::Clip& a,
714 const SkClipStack::Iter::Clip& b) {
reed@google.com00177082011-10-12 14:34:30 +0000715 return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
vandebo@chromium.orge03c6522011-06-21 20:45:51 +0000716 ((a.fRect == NULL && b.fRect == NULL) ||
717 (a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
718 ((a.fPath == NULL && b.fPath == NULL) ||
719 (a.fPath != NULL && b.fPath != NULL && *a.fPath == *b.fPath));
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000720}
721
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000722bool operator!=(const SkClipStack::Iter::Clip& a,
723 const SkClipStack::Iter::Clip& b) {
vandebo@chromium.org8887ede2011-05-25 01:27:52 +0000724 return !(a == b);
725}
726
bsalomon@google.com7b7cdd12012-11-07 16:17:24 +0000727const SkRect& SkClipStack::Iter::Clip::getBounds() const {
728 if (NULL != fRect) {
729 return *fRect;
730 } else if (NULL != fPath) {
731 return fPath->getBounds();
732 } else {
733 static const SkRect kEmpty = {0, 0, 0, 0};
734 return kEmpty;
735 }
736}
737
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000738SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
739 : fStack(&stack) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000740 this->reset(stack, startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000741}
742
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000743const SkClipStack::Iter::Clip* SkClipStack::Iter::updateClip(
744 const SkClipStack::Rec* rec) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000745 switch (rec->fState) {
746 case SkClipStack::Rec::kEmpty_State:
747 fClip.fRect = NULL;
748 fClip.fPath = NULL;
749 break;
750 case SkClipStack::Rec::kRect_State:
751 fClip.fRect = &rec->fRect;
752 fClip.fPath = NULL;
753 break;
754 case SkClipStack::Rec::kPath_State:
755 fClip.fRect = NULL;
756 fClip.fPath = &rec->fPath;
757 break;
758 }
759 fClip.fOp = rec->fOp;
reed@google.com00177082011-10-12 14:34:30 +0000760 fClip.fDoAA = rec->fDoAA;
robertphillips@google.com73e71022012-08-09 18:10:49 +0000761 fClip.fGenID = rec->fGenID;
reed@google.com5c3d1472011-02-22 19:12:23 +0000762 return &fClip;
763}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000764
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000765const SkClipStack::Iter::Clip* SkClipStack::Iter::next() {
766 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
767 if (NULL == rec) {
768 return NULL;
769 }
770
771 return this->updateClip(rec);
772}
773
774const SkClipStack::Iter::Clip* SkClipStack::Iter::prev() {
775 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.prev();
776 if (NULL == rec) {
777 return NULL;
778 }
779
780 return this->updateClip(rec);
781}
782
robertphillips@google.com80214e22012-07-20 15:33:18 +0000783const SkClipStack::Iter::Clip* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000784
785 if (NULL == fStack) {
786 return NULL;
787 }
788
789 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
790
791 const SkClipStack::Rec* rec = NULL;
792
793 for (rec = (const SkClipStack::Rec*) fIter.prev();
794 NULL != rec;
795 rec = (const SkClipStack::Rec*) fIter.prev()) {
796
797 if (op == rec->fOp) {
798 // The Deque's iterator is actually one pace ahead of the
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000799 // returned value. So while "rec" is the element we want to
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000800 // return, the iterator is actually pointing at (and will
801 // return on the next "next" or "prev" call) the element
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000802 // in front of it in the deque. Bump the iterator forward a
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000803 // step so we get the expected result.
804 if (NULL == fIter.next()) {
805 // The reverse iterator has run off the front of the deque
806 // (i.e., the "op" clip is the first clip) and can't
807 // recover. Reset the iterator to start at the front.
808 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
809 }
810 break;
811 }
812 }
813
814 if (NULL == rec) {
815 // There were no "op" clips
816 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
817 }
818
819 return this->next();
820}
821
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000822void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
robertphillips@google.coma6f11c42012-07-23 17:39:44 +0000823 fStack = &stack;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000824 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
bsalomon@google.comd302f142011-03-03 13:54:13 +0000825}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000826
827// helper method
828void SkClipStack::getConservativeBounds(int offsetX,
829 int offsetY,
830 int maxWidth,
831 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000832 SkRect* devBounds,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000833 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000834 SkASSERT(NULL != devBounds);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000835
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000836 devBounds->setLTRB(0, 0,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000837 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000838
839 SkRect temp;
840 SkClipStack::BoundsType boundType;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000841
robertphillips@google.com7b112892012-07-31 15:18:21 +0000842 // temp starts off in canvas space here
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000843 this->getBounds(&temp, &boundType, isIntersectionOfRects);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000844 if (SkClipStack::kInsideOut_BoundsType == boundType) {
845 return;
846 }
847
robertphillips@google.com7b112892012-07-31 15:18:21 +0000848 // but is converted to device space here
robertphillips@google.com607fe072012-07-24 13:54:00 +0000849 temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
850
robertphillips@google.com7b112892012-07-31 15:18:21 +0000851 if (!devBounds->intersect(temp)) {
852 devBounds->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000853 }
854}
robertphillips@google.com46f93502012-08-07 15:38:08 +0000855
856void SkClipStack::addPurgeClipCallback(PFPurgeClipCB callback, void* data) const {
857 ClipCallbackData temp = { callback, data };
858 fCallbackData.append(1, &temp);
859}
860
861void SkClipStack::removePurgeClipCallback(PFPurgeClipCB callback, void* data) const {
862 ClipCallbackData temp = { callback, data };
863 int index = fCallbackData.find(temp);
864 if (index >= 0) {
865 fCallbackData.removeShuffle(index);
866 }
867}
868
869// The clip state represented by 'rec' will never be used again. Purge it.
870void SkClipStack::purgeClip(Rec* rec) {
871 SkASSERT(NULL != rec);
872
873 for (int i = 0; i < fCallbackData.count(); ++i) {
874 (*fCallbackData[i].fCallback)(rec->fGenID, fCallbackData[i].fData);
875 }
876
877 // Invalidate rec's gen ID so handlers can detect already handled records
878 rec->fGenID = kInvalidGenID;
879}
880
881int32_t SkClipStack::GetNextGenID() {
882 return sk_atomic_inc(&gGenID);
883}
robertphillips@google.com73e71022012-08-09 18:10:49 +0000884
885int32_t SkClipStack::getTopmostGenID() const {
886
887 if (fDeque.empty()) {
888 return kInvalidGenID;
889 }
890
891 Rec* rec = (Rec*)fDeque.back();
892 return rec->fGenID;
893}