blob: 3c9705dcae79264f80d206e497e9b4f5c8c5c216 [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
reed@google.com00177082011-10-12 14:34:30 +0000570void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000571
robertphillips@google.com46f93502012-08-07 15:38:08 +0000572 int32_t genID = GetNextGenID();
573
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000574 // Use reverse iterator instead of back because Rect path may need previous
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000575 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
576 Rec* rec = (Rec*) iter.prev();
577
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000578 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000579 switch (rec->fState) {
580 case Rec::kEmpty_State:
robertphillips@google.com46f93502012-08-07 15:38:08 +0000581 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000582 return;
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000583 case Rec::kRect_State:
584 if (rec->rectRectIntersectAllowed(rect, doAA)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000585 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000586 if (!rec->fRect.intersect(rect)) {
587 rec->setEmpty();
588 return;
589 }
590
591 rec->fDoAA = doAA;
592 Rec* prev = (Rec*) iter.prev();
593 rec->updateBound(prev);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000594 rec->fGenID = genID;
robertphillips@google.com607fe072012-07-24 13:54:00 +0000595 return;
reed@google.com5c3d1472011-02-22 19:12:23 +0000596 }
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000597 break;
reed@google.com5c3d1472011-02-22 19:12:23 +0000598 case Rec::kPath_State:
599 if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000600 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000601 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000602 return;
603 }
604 break;
605 }
606 }
reed@google.com00177082011-10-12 14:34:30 +0000607 new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000608 ((Rec*) fDeque.back())->updateBound(rec);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000609 ((Rec*) fDeque.back())->fGenID = genID;
610
611 if (rec && rec->fSaveCount == fSaveCount) {
612 this->purgeClip(rec);
613 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000614}
615
reed@google.com00177082011-10-12 14:34:30 +0000616void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
tomhudson@google.com4c433722012-03-09 16:48:20 +0000617 SkRect alt;
618 if (path.isRect(&alt)) {
619 return this->clipDevRect(alt, op, doAA);
620 }
robertphillips@google.com46f93502012-08-07 15:38:08 +0000621
622 int32_t genID = GetNextGenID();
623
reed@google.com5c3d1472011-02-22 19:12:23 +0000624 Rec* rec = (Rec*)fDeque.back();
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000625 if (rec && rec->canBeIntersectedInPlace(fSaveCount, op)) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000626 const SkRect& pathBounds = path.getBounds();
627 switch (rec->fState) {
628 case Rec::kEmpty_State:
robertphillips@google.com46f93502012-08-07 15:38:08 +0000629 rec->checkEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000630 return;
631 case Rec::kRect_State:
632 if (!SkRect::Intersects(rec->fRect, pathBounds)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000633 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000634 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000635 return;
636 }
637 break;
638 case Rec::kPath_State:
639 if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
robertphillips@google.com46f93502012-08-07 15:38:08 +0000640 this->purgeClip(rec);
robertphillips@google.com08eacc12012-08-02 12:49:00 +0000641 rec->setEmpty();
reed@google.com5c3d1472011-02-22 19:12:23 +0000642 return;
643 }
644 break;
645 }
646 }
reed@google.com00177082011-10-12 14:34:30 +0000647 new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000648 ((Rec*) fDeque.back())->updateBound(rec);
robertphillips@google.com46f93502012-08-07 15:38:08 +0000649 ((Rec*) fDeque.back())->fGenID = genID;
650
651 if (rec && rec->fSaveCount == fSaveCount) {
652 this->purgeClip(rec);
653 }
reed@google.com5c3d1472011-02-22 19:12:23 +0000654}
655
reed@google.com0557d9e2012-08-16 15:59:59 +0000656void SkClipStack::clipEmpty() {
robertphillips@google.com63ae1cf2012-08-17 13:53:05 +0000657
658 Rec* rec = (Rec*) fDeque.back();
659
reed@google.com0557d9e2012-08-16 15:59:59 +0000660 if (rec && rec->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
661 switch (rec->fState) {
662 case Rec::kEmpty_State:
663 rec->checkEmpty();
664 return;
665 case Rec::kRect_State:
666 case Rec::kPath_State:
667 this->purgeClip(rec);
668 rec->setEmpty();
669 return;
670 }
671 }
672 new (fDeque.push_back()) Rec(fSaveCount);
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000673
reed@google.com0557d9e2012-08-16 15:59:59 +0000674 if (rec && rec->fSaveCount == fSaveCount) {
675 this->purgeClip(rec);
676 }
677}
678
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000679bool SkClipStack::isWideOpen() const {
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000680 if (0 == fDeque.count()) {
681 return true;
682 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000683
robertphillips@google.comcc6493b2012-07-26 18:39:13 +0000684 const Rec* back = (const Rec*) fDeque.back();
685 return kInsideOut_BoundsType == back->fFiniteBoundType &&
686 back->fFiniteBound.isEmpty();
687}
688
reed@google.com5c3d1472011-02-22 19:12:23 +0000689///////////////////////////////////////////////////////////////////////////////
690
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000691SkClipStack::Iter::Iter() : fStack(NULL) {
bsalomon@google.comd302f142011-03-03 13:54:13 +0000692}
693
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000694bool operator==(const SkClipStack::Iter::Clip& a,
695 const SkClipStack::Iter::Clip& b) {
reed@google.com00177082011-10-12 14:34:30 +0000696 return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
vandebo@chromium.orge03c6522011-06-21 20:45:51 +0000697 ((a.fRect == NULL && b.fRect == NULL) ||
698 (a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
699 ((a.fPath == NULL && b.fPath == NULL) ||
700 (a.fPath != NULL && b.fPath != NULL && *a.fPath == *b.fPath));
vandebo@chromium.org9fbdf872011-05-09 07:55:58 +0000701}
702
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000703bool operator!=(const SkClipStack::Iter::Clip& a,
704 const SkClipStack::Iter::Clip& b) {
vandebo@chromium.org8887ede2011-05-25 01:27:52 +0000705 return !(a == b);
706}
707
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000708SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
709 : fStack(&stack) {
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000710 this->reset(stack, startLoc);
reed@google.com5c3d1472011-02-22 19:12:23 +0000711}
712
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000713const SkClipStack::Iter::Clip* SkClipStack::Iter::updateClip(
714 const SkClipStack::Rec* rec) {
reed@google.com5c3d1472011-02-22 19:12:23 +0000715 switch (rec->fState) {
716 case SkClipStack::Rec::kEmpty_State:
717 fClip.fRect = NULL;
718 fClip.fPath = NULL;
719 break;
720 case SkClipStack::Rec::kRect_State:
721 fClip.fRect = &rec->fRect;
722 fClip.fPath = NULL;
723 break;
724 case SkClipStack::Rec::kPath_State:
725 fClip.fRect = NULL;
726 fClip.fPath = &rec->fPath;
727 break;
728 }
729 fClip.fOp = rec->fOp;
reed@google.com00177082011-10-12 14:34:30 +0000730 fClip.fDoAA = rec->fDoAA;
robertphillips@google.com73e71022012-08-09 18:10:49 +0000731 fClip.fGenID = rec->fGenID;
reed@google.com5c3d1472011-02-22 19:12:23 +0000732 return &fClip;
733}
bsalomon@google.comd302f142011-03-03 13:54:13 +0000734
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000735const SkClipStack::Iter::Clip* SkClipStack::Iter::next() {
736 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
737 if (NULL == rec) {
738 return NULL;
739 }
740
741 return this->updateClip(rec);
742}
743
744const SkClipStack::Iter::Clip* SkClipStack::Iter::prev() {
745 const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.prev();
746 if (NULL == rec) {
747 return NULL;
748 }
749
750 return this->updateClip(rec);
751}
752
robertphillips@google.com80214e22012-07-20 15:33:18 +0000753const SkClipStack::Iter::Clip* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000754
755 if (NULL == fStack) {
756 return NULL;
757 }
758
759 fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
760
761 const SkClipStack::Rec* rec = NULL;
762
763 for (rec = (const SkClipStack::Rec*) fIter.prev();
764 NULL != rec;
765 rec = (const SkClipStack::Rec*) fIter.prev()) {
766
767 if (op == rec->fOp) {
768 // The Deque's iterator is actually one pace ahead of the
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000769 // returned value. So while "rec" is the element we want to
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000770 // return, the iterator is actually pointing at (and will
771 // return on the next "next" or "prev" call) the element
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000772 // in front of it in the deque. Bump the iterator forward a
robertphillips@google.com5836b6d2012-07-18 12:06:15 +0000773 // step so we get the expected result.
774 if (NULL == fIter.next()) {
775 // The reverse iterator has run off the front of the deque
776 // (i.e., the "op" clip is the first clip) and can't
777 // recover. Reset the iterator to start at the front.
778 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
779 }
780 break;
781 }
782 }
783
784 if (NULL == rec) {
785 // There were no "op" clips
786 fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
787 }
788
789 return this->next();
790}
791
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000792void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
robertphillips@google.coma6f11c42012-07-23 17:39:44 +0000793 fStack = &stack;
robertphillips@google.com52cb2c72012-07-16 18:52:29 +0000794 fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
bsalomon@google.comd302f142011-03-03 13:54:13 +0000795}
robertphillips@google.com607fe072012-07-24 13:54:00 +0000796
797// helper method
798void SkClipStack::getConservativeBounds(int offsetX,
799 int offsetY,
800 int maxWidth,
801 int maxHeight,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000802 SkRect* devBounds,
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000803 bool* isIntersectionOfRects) const {
robertphillips@google.com7b112892012-07-31 15:18:21 +0000804 SkASSERT(NULL != devBounds);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000805
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000806 devBounds->setLTRB(0, 0,
robertphillips@google.com7b112892012-07-31 15:18:21 +0000807 SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
robertphillips@google.com607fe072012-07-24 13:54:00 +0000808
809 SkRect temp;
810 SkClipStack::BoundsType boundType;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000811
robertphillips@google.com7b112892012-07-31 15:18:21 +0000812 // temp starts off in canvas space here
robertphillips@google.com4c2a2f72012-07-24 22:07:50 +0000813 this->getBounds(&temp, &boundType, isIntersectionOfRects);
robertphillips@google.com607fe072012-07-24 13:54:00 +0000814 if (SkClipStack::kInsideOut_BoundsType == boundType) {
815 return;
816 }
817
robertphillips@google.com7b112892012-07-31 15:18:21 +0000818 // but is converted to device space here
robertphillips@google.com607fe072012-07-24 13:54:00 +0000819 temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
820
robertphillips@google.com7b112892012-07-31 15:18:21 +0000821 if (!devBounds->intersect(temp)) {
822 devBounds->setEmpty();
robertphillips@google.com607fe072012-07-24 13:54:00 +0000823 }
824}
robertphillips@google.com46f93502012-08-07 15:38:08 +0000825
826void SkClipStack::addPurgeClipCallback(PFPurgeClipCB callback, void* data) const {
827 ClipCallbackData temp = { callback, data };
828 fCallbackData.append(1, &temp);
829}
830
831void SkClipStack::removePurgeClipCallback(PFPurgeClipCB callback, void* data) const {
832 ClipCallbackData temp = { callback, data };
833 int index = fCallbackData.find(temp);
834 if (index >= 0) {
835 fCallbackData.removeShuffle(index);
836 }
837}
838
839// The clip state represented by 'rec' will never be used again. Purge it.
840void SkClipStack::purgeClip(Rec* rec) {
841 SkASSERT(NULL != rec);
842
843 for (int i = 0; i < fCallbackData.count(); ++i) {
844 (*fCallbackData[i].fCallback)(rec->fGenID, fCallbackData[i].fData);
845 }
846
847 // Invalidate rec's gen ID so handlers can detect already handled records
848 rec->fGenID = kInvalidGenID;
849}
850
851int32_t SkClipStack::GetNextGenID() {
852 return sk_atomic_inc(&gGenID);
853}
robertphillips@google.com73e71022012-08-09 18:10:49 +0000854
855int32_t SkClipStack::getTopmostGenID() const {
856
857 if (fDeque.empty()) {
858 return kInvalidGenID;
859 }
860
861 Rec* rec = (Rec*)fDeque.back();
862 return rec->fGenID;
863}