Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(589)

Side by Side Diff: src/core/SkClipStack.cpp

Issue 163683002: Store SkRRects in SkClipStack (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: fix typo in last upload Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 1
2 /* 2 /*
3 * Copyright 2011 Google Inc. 3 * Copyright 2011 Google Inc.
4 * 4 *
5 * Use of this source code is governed by a BSD-style license that can be 5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file. 6 * found in the LICENSE file.
7 */ 7 */
8 #include "SkClipStack.h" 8 #include "SkClipStack.h"
9 #include "SkPath.h" 9 #include "SkPath.h"
10 #include "SkThread.h" 10 #include "SkThread.h"
11 11
12 #include <new> 12 #include <new>
13 13
14 14
15 // 0-2 are reserved for invalid, empty & wide-open 15 // 0-2 are reserved for invalid, empty & wide-open
16 static const int32_t kFirstUnreservedGenID = 3; 16 static const int32_t kFirstUnreservedGenID = 3;
17 int32_t SkClipStack::gGenID = kFirstUnreservedGenID; 17 int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
18 18
19 bool SkClipStack::Element::operator== (const Element& element) const {
20 if (this == &element) {
21 return true;
22 }
23 if (fOp != element.fOp ||
24 fType != element.fType ||
25 fDoAA != element.fDoAA ||
26 fSaveCount != element.fSaveCount) {
27 return false;
28 }
29 switch (fType) {
30 case kPath_Type:
31 return fPath == element.fPath;
32 case kRRect_Type:
33 return fRRect == element.fRRect;
34 case kRect_Type:
35 return fRect == element.fRect;
36 case kEmpty_Type:
37 return true;
38 default:
39 SkDEBUGFAIL("Unexpected type.");
40 return false;
41 }
42 }
43
19 void SkClipStack::Element::invertShapeFillType() { 44 void SkClipStack::Element::invertShapeFillType() {
20 switch (fType) { 45 switch (fType) {
21 case kRect_Type: 46 case kRect_Type:
22 fPath.reset(); 47 fPath.reset();
23 fPath.addRect(fRect); 48 fPath.addRect(fRect);
24 fPath.setFillType(SkPath::kInverseWinding_FillType); 49 fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
robertphillips 2014/02/14 20:36:01 Do we really care about setting fRect to be empty?
bsalomon 2014/02/14 21:20:57 I guess not
50 fRect.setEmpty();
51 fType = kPath_Type;
52 break;
53 case kRRect_Type:
54 fPath.reset();
55 fPath.addRRect(fRRect);
56 fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
robertphillips 2014/02/14 20:36:01 Same for rrect?
bsalomon 2014/02/14 21:20:57 Done.
57 fRRect.setEmpty();
25 fType = kPath_Type; 58 fType = kPath_Type;
26 break; 59 break;
27 case kPath_Type: 60 case kPath_Type:
28 fPath.toggleInverseFillType(); 61 fPath.toggleInverseFillType();
29 case kEmpty_Type: 62 case kEmpty_Type:
robertphillips 2014/02/14 20:36:01 Probably
bsalomon 2014/02/14 21:20:57 Agreed, but don't want to conflate any regressions
63 // Should this set to an empty, inverse filled path?
30 break; 64 break;
31 } 65 }
32 } 66 }
67
68 void SkClipStack::Element::initPath(int saveCount, const SkPath& path, SkRegion: :Op op,
69 bool doAA) {
70 if (!path.isInverseFillType()) {
71 if (SkPath::kNone_PathAsRect != path.asRect()) {
72 this->initRect(saveCount, path.getBounds(), op, doAA);
73 return;
74 }
75 SkRect ovalRect;
76 if (path.isOval(&ovalRect)) {
77 SkRRect rrect;
78 rrect.setOval(ovalRect);
79 this->initRRect(saveCount, rrect, op, doAA);
80 return;
81 }
82 }
83 fPath = path;
84 fType = kPath_Type;
85 this->initCommon(saveCount, op, doAA);
86 }
87
88 void SkClipStack::Element::asPath(SkPath* path) const {
89 switch (fType) {
90 case kEmpty_Type:
91 path->reset();
92 break;
93 case kRect_Type:
94 path->reset();
95 path->addRect(fRect);
96 break;
97 case kRRect_Type:
98 path->reset();
99 path->addRRect(fRRect);
100 break;
101 case kPath_Type:
102 *path = fPath;
103 break;
104 }
105 }
33 106
34 void SkClipStack::Element::checkEmpty() const { 107 void SkClipStack::Element::checkEmpty() const {
35 SkASSERT(fFiniteBound.isEmpty()); 108 SkASSERT(fFiniteBound.isEmpty());
36 SkASSERT(kNormal_BoundsType == fFiniteBoundType); 109 SkASSERT(kNormal_BoundsType == fFiniteBoundType);
37 SkASSERT(!fIsIntersectionOfRects); 110 SkASSERT(!fIsIntersectionOfRects);
38 SkASSERT(kEmptyGenID == fGenID); 111 SkASSERT(kEmptyGenID == fGenID);
39 SkASSERT(fPath.isEmpty()); 112 SkASSERT(fPath.isEmpty());
40 } 113 }
41 114
42 bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkRegion::Op o p) const { 115 bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkRegion::Op o p) const {
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
93 // that aren't set are whatever isn't set in the previous 166 // that aren't set are whatever isn't set in the previous
94 // clip and whatever this clip carves out 167 // clip and whatever this clip carves out
95 fFiniteBound.join(prevFinite); 168 fFiniteBound.join(prevFinite);
96 fFiniteBoundType = kInsideOut_BoundsType; 169 fFiniteBoundType = kInsideOut_BoundsType;
97 break; 170 break;
98 case kPrev_InvCur_FillCombo: 171 case kPrev_InvCur_FillCombo:
99 // In this case everything outside of this clip's bound 172 // In this case everything outside of this clip's bound
100 // is erased, so the only pixels that can remain set 173 // is erased, so the only pixels that can remain set
101 // occur w/in the intersection of the two finite bounds 174 // occur w/in the intersection of the two finite bounds
102 if (!fFiniteBound.intersect(prevFinite)) { 175 if (!fFiniteBound.intersect(prevFinite)) {
103 fFiniteBound.setEmpty(); 176 this->setEmpty();
104 fGenID = kEmptyGenID; 177 } else {
178 fFiniteBoundType = kNormal_BoundsType;
105 } 179 }
106 fFiniteBoundType = kNormal_BoundsType;
107 break; 180 break;
108 case kPrev_Cur_FillCombo: 181 case kPrev_Cur_FillCombo:
109 // The most conservative result bound is that of the 182 // The most conservative result bound is that of the
110 // prior clip. This could be wildly incorrect if the 183 // prior clip. This could be wildly incorrect if the
111 // second clip either exactly matches the first clip 184 // second clip either exactly matches the first clip
112 // (which should yield the empty set) or reduces the 185 // (which should yield the empty set) or reduces the
113 // size of the prior bound (e.g., if the second clip 186 // size of the prior bound (e.g., if the second clip
114 // exactly matched the bottom half of the prior clip). 187 // exactly matched the bottom half of the prior clip).
115 // We ignore these two possibilities. 188 // We ignore these two possibilities.
116 fFiniteBound = prevFinite; 189 fFiniteBound = prevFinite;
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 // are within the current clip 271 // are within the current clip
199 break; 272 break;
200 case kPrev_InvCur_FillCombo: 273 case kPrev_InvCur_FillCombo:
201 // In this case the only pixels that will remain writeable 274 // In this case the only pixels that will remain writeable
202 // are with the previous clip 275 // are with the previous clip
203 fFiniteBound = prevFinite; 276 fFiniteBound = prevFinite;
204 fFiniteBoundType = kNormal_BoundsType; 277 fFiniteBoundType = kNormal_BoundsType;
205 break; 278 break;
206 case kPrev_Cur_FillCombo: 279 case kPrev_Cur_FillCombo:
207 if (!fFiniteBound.intersect(prevFinite)) { 280 if (!fFiniteBound.intersect(prevFinite)) {
208 fFiniteBound.setEmpty(); 281 this->setEmpty();
209 fGenID = kEmptyGenID;
210 } 282 }
211 break; 283 break;
212 default: 284 default:
213 SkDEBUGFAIL("SkClipStack::Element::combineBoundsIntersection Invalid fill combination"); 285 SkDEBUGFAIL("SkClipStack::Element::combineBoundsIntersection Invalid fill combination");
214 break; 286 break;
215 } 287 }
216 } 288 }
217 289
218 // a mirror of combineBoundsDiff 290 // a mirror of combineBoundsDiff
219 void SkClipStack::Element::combineBoundsRevDiff(int combination, const SkRect& p revFinite) { 291 void SkClipStack::Element::combineBoundsRevDiff(int combination, const SkRect& p revFinite) {
220 292
221 switch (combination) { 293 switch (combination) {
222 case kInvPrev_InvCur_FillCombo: 294 case kInvPrev_InvCur_FillCombo:
223 // The only pixels that can survive are in the 295 // The only pixels that can survive are in the
224 // previous bound since the extensions to infinity in 296 // previous bound since the extensions to infinity in
225 // both clips cancel out 297 // both clips cancel out
226 fFiniteBound = prevFinite; 298 fFiniteBound = prevFinite;
227 fFiniteBoundType = kNormal_BoundsType; 299 fFiniteBoundType = kNormal_BoundsType;
228 break; 300 break;
229 case kInvPrev_Cur_FillCombo: 301 case kInvPrev_Cur_FillCombo:
230 if (!fFiniteBound.intersect(prevFinite)) { 302 if (!fFiniteBound.intersect(prevFinite)) {
231 fFiniteBound.setEmpty(); 303 this->setEmpty();
232 fGenID = kEmptyGenID; 304 } else {
305 fFiniteBoundType = kNormal_BoundsType;
233 } 306 }
234 fFiniteBoundType = kNormal_BoundsType;
235 break; 307 break;
236 case kPrev_InvCur_FillCombo: 308 case kPrev_InvCur_FillCombo:
237 fFiniteBound.join(prevFinite); 309 fFiniteBound.join(prevFinite);
238 fFiniteBoundType = kInsideOut_BoundsType; 310 fFiniteBoundType = kInsideOut_BoundsType;
239 break; 311 break;
240 case kPrev_Cur_FillCombo: 312 case kPrev_Cur_FillCombo:
241 // Fall through - as with the kDifference_Op case, the 313 // Fall through - as with the kDifference_Op case, the
242 // most conservative result bound is the bound of the 314 // most conservative result bound is the bound of the
243 // current clip. The prior clip could reduce the size of this 315 // current clip. The prior clip could reduce the size of this
244 // bound (as in the kDifference_Op case) but we are ignoring 316 // bound (as in the kDifference_Op case) but we are ignoring
245 // those cases. 317 // those cases.
246 break; 318 break;
247 default: 319 default:
248 SkDEBUGFAIL("SkClipStack::Element::combineBoundsRevDiff Invalid fill combination"); 320 SkDEBUGFAIL("SkClipStack::Element::combineBoundsRevDiff Invalid fill combination");
249 break; 321 break;
250 } 322 }
251 } 323 }
252 324
253 void SkClipStack::Element::updateBoundAndGenID(const Element* prior) { 325 void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
254 // We set this first here but we may overwrite it later if we determine that the clip is 326 // We set this first here but we may overwrite it later if we determine that the clip is
255 // either wide-open or empty. 327 // either wide-open or empty.
256 fGenID = GetNextGenID(); 328 fGenID = GetNextGenID();
257 329
258 // First, optimistically update the current Element's bound information 330 // First, optimistically update the current Element's bound information
259 // with the current clip's bound 331 // with the current clip's bound
260 fIsIntersectionOfRects = false; 332 fIsIntersectionOfRects = false;
261 if (kRect_Type == fType) { 333 switch (fType) {
262 fFiniteBound = fRect; 334 case kRect_Type:
263 fFiniteBoundType = kNormal_BoundsType; 335 fFiniteBound = fRect;
336 fFiniteBoundType = kNormal_BoundsType;
264 337
265 if (SkRegion::kReplace_Op == fOp || 338 if (SkRegion::kReplace_Op == fOp ||
266 (SkRegion::kIntersect_Op == fOp && NULL == prior) || 339 (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
267 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects && 340 (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
268 prior->rectRectIntersectAllowed(fRect, fDoAA))) { 341 prior->rectRectIntersectAllowed(fRect, fDoAA))) {
269 fIsIntersectionOfRects = true; 342 fIsIntersectionOfRects = true;
270 } 343 }
344 break;
345 case kRRect_Type:
346 fFiniteBound = fRRect.getBounds();
347 fFiniteBoundType = kNormal_BoundsType;
348 break;
349 case kPath_Type:
350 fFiniteBound = fPath.getBounds();
271 351
272 } else { 352 if (fPath.isInverseFillType()) {
273 SkASSERT(kPath_Type == fType); 353 fFiniteBoundType = kInsideOut_BoundsType;
274 354 } else {
275 fFiniteBound = fPath.getBounds(); 355 fFiniteBoundType = kNormal_BoundsType;
276 356 }
277 if (fPath.isInverseFillType()) { 357 break;
278 fFiniteBoundType = kInsideOut_BoundsType; 358 case kEmpty_Type:
279 } else { 359 SkDEBUGFAIL("We shouldn't get here with an empty element.");
280 fFiniteBoundType = kNormal_BoundsType; 360 break;
281 }
282 } 361 }
283 362
284 if (!fDoAA) { 363 if (!fDoAA) {
285 // Here we mimic a non-anti-aliased scanline system. If there is 364 // Here we mimic a non-anti-aliased scanline system. If there is
286 // no anti-aliasing we can integerize the bounding box to exclude 365 // no anti-aliasing we can integerize the bounding box to exclude
287 // fractional parts that won't be rendered. 366 // fractional parts that won't be rendered.
288 // Note: the left edge is handled slightly differently below. We 367 // Note: the left edge is handled slightly differently below. We
289 // are a bit more generous in the rounding since we don't want to 368 // are a bit more generous in the rounding since we don't want to
290 // risk missing the left pixels when fLeft is very close to .5 369 // risk missing the left pixels when fLeft is very close to .5
291 fFiniteBound.set(SkScalarFloorToScalar(fFiniteBound.fLeft+0.45f), 370 fFiniteBound.set(SkScalarFloorToScalar(fFiniteBound.fLeft+0.45f),
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
337 break; 416 break;
338 case SkRegion::kReverseDifference_Op: 417 case SkRegion::kReverseDifference_Op:
339 this->combineBoundsRevDiff(combination, prevFinite); 418 this->combineBoundsRevDiff(combination, prevFinite);
340 break; 419 break;
341 case SkRegion::kReplace_Op: 420 case SkRegion::kReplace_Op:
342 // Replace just ignores everything prior 421 // Replace just ignores everything prior
343 // The current clip's bound information is already filled in 422 // The current clip's bound information is already filled in
344 // so nothing to do 423 // so nothing to do
345 break; 424 break;
346 default: 425 default:
347 SkDebugf("SkRegion::Op error/n"); 426 SkDebugf("SkRegion::Op error\n");
348 SkASSERT(0); 427 SkASSERT(0);
349 break; 428 break;
350 } 429 }
351 } 430 }
352 431
353 // This constant determines how many Element's are allocated together as a block in 432 // This constant determines how many Element's are allocated together as a block in
354 // the deque. As such it needs to balance allocating too much memory vs. 433 // the deque. As such it needs to balance allocating too much memory vs.
355 // incurring allocation/deallocation thrashing. It should roughly correspond to 434 // incurring allocation/deallocation thrashing. It should roughly correspond to
356 // the deepest save/restore stack we expect to see. 435 // the deepest save/restore stack we expect to see.
357 static const int kDefaultElementAllocCnt = 8; 436 static const int kDefaultElementAllocCnt = 8;
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
521 } 600 }
522 } 601 }
523 if (SkRegion::kReplace_Op == element->getOp()) { 602 if (SkRegion::kReplace_Op == element->getOp()) {
524 break; 603 break;
525 } 604 }
526 element = iter.prev(); 605 element = iter.prev();
527 } 606 }
528 return true; 607 return true;
529 } 608 }
530 609
531 void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) { 610 void SkClipStack::pushElement(const Element& element) {
532
533 // Use reverse iterator instead of back because Rect path may need previous 611 // Use reverse iterator instead of back because Rect path may need previous
534 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart); 612 SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
535 Element* element = (Element*) iter.prev(); 613 Element* prior = (Element*) iter.prev();
536 614
537 if (NULL != element) { 615 if (NULL != prior) {
538 if (element->canBeIntersectedInPlace(fSaveCount, op)) { 616 if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) {
539 switch (element->fType) { 617 switch (prior->fType) {
540 case Element::kEmpty_Type: 618 case Element::kEmpty_Type:
541 element->checkEmpty(); 619 prior->checkEmpty();
542 return; 620 return;
543 case Element::kRect_Type: 621 case Element::kRect_Type:
544 if (element->rectRectIntersectAllowed(rect, doAA)) { 622 if (Element::kRect_Type == element.getType()) {
545 if (!element->fRect.intersect(rect)) { 623 if (prior->rectRectIntersectAllowed(element.getRect(), e lement.isAA())) {
546 element->setEmpty(); 624 if (!prior->fRect.intersect(element.getRect())) {
625 prior->setEmpty();
626 return;
627 }
628
629 prior->fDoAA = element.isAA();
630 Element* priorPrior = (Element*) iter.prev();
631 prior->updateBoundAndGenID(priorPrior);
547 return; 632 return;
548 } 633 }
549 634 break;
550 element->fDoAA = doAA;
551 Element* prev = (Element*) iter.prev();
552 element->updateBoundAndGenID(prev);
553 return;
554 } 635 }
555 break; 636 // fallthrough
556 case Element::kPath_Type: 637 default:
557 if (!SkRect::Intersects(element->fPath.getBounds(), rect)) { 638 if (!SkRect::Intersects(prior->getBounds(), element.getBound s())) {
558 element->setEmpty(); 639 prior->setEmpty();
559 return; 640 return;
560 } 641 }
561 break; 642 break;
562 } 643 }
563 } else if (SkRegion::kReplace_Op == op) { 644 } else if (SkRegion::kReplace_Op == element.getOp()) {
564 this->restoreTo(fSaveCount - 1); 645 this->restoreTo(fSaveCount - 1);
565 element = (Element*) fDeque.back(); 646 prior = (Element*) fDeque.back();
566 } 647 }
567 } 648 }
568 new (fDeque.push_back()) Element(fSaveCount, rect, op, doAA); 649 Element* newElement = SkNEW_PLACEMENT_ARGS(fDeque.push_back(), Element, (ele ment));
569 ((Element*) fDeque.back())->updateBoundAndGenID(element); 650 newElement->updateBoundAndGenID(prior);
651 }
652
653 void SkClipStack::clipDevRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
654 Element element(fSaveCount, rrect, op, doAA);
655 this->pushElement(element);
656 }
657
658 void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
659 Element element(fSaveCount, rect, op, doAA);
660 this->pushElement(element);
570 } 661 }
571 662
572 void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) { 663 void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
573 SkRect alt; 664 Element element(fSaveCount, path, op, doAA);
574 if (path.isRect(&alt) && !path.isInverseFillType()) { 665 this->pushElement(element);
575 return this->clipDevRect(alt, op, doAA);
576 }
577
578 Element* element = (Element*)fDeque.back();
579 if (NULL != element) {
580 if (element->canBeIntersectedInPlace(fSaveCount, op)) {
581 const SkRect& pathBounds = path.getBounds();
582 switch (element->fType) {
583 case Element::kEmpty_Type:
584 element->checkEmpty();
585 return;
586 case Element::kRect_Type:
587 if (!SkRect::Intersects(element->fRect, pathBounds)) {
588 element->setEmpty();
589 return;
590 }
591 break;
592 case Element::kPath_Type:
593 if (!SkRect::Intersects(element->fPath.getBounds(), pathBoun ds)) {
594 element->setEmpty();
595 return;
596 }
597 break;
598 }
599 } else if (SkRegion::kReplace_Op == op) {
600 this->restoreTo(fSaveCount - 1);
601 element = (Element*) fDeque.back();
602 }
603 }
604 new (fDeque.push_back()) Element(fSaveCount, path, op, doAA);
605 ((Element*) fDeque.back())->updateBoundAndGenID(element);
606 } 666 }
607 667
608 void SkClipStack::clipEmpty() { 668 void SkClipStack::clipEmpty() {
609 669
610 Element* element = (Element*) fDeque.back(); 670 Element* element = (Element*) fDeque.back();
611 671
612 if (element && element->canBeIntersectedInPlace(fSaveCount, SkRegion::kInter sect_Op)) { 672 if (element && element->canBeIntersectedInPlace(fSaveCount, SkRegion::kInter sect_Op)) {
673 element->setEmpty();
robertphillips 2014/02/14 20:36:01 Seems like we don't really need this switch any mo
bsalomon 2014/02/14 21:20:57 Done.
613 switch (element->fType) { 674 switch (element->fType) {
614 case Element::kEmpty_Type: 675 case Element::kEmpty_Type:
615 element->checkEmpty(); 676 element->checkEmpty();
616 return; 677 return;
617 case Element::kRect_Type: 678 default:
618 case Element::kPath_Type:
619 element->setEmpty(); 679 element->setEmpty();
620 return; 680 return;
621 } 681 }
622 } 682 }
623 new (fDeque.push_back()) Element(fSaveCount); 683 new (fDeque.push_back()) Element(fSaveCount);
624 684
625 ((Element*)fDeque.back())->fGenID = kEmptyGenID; 685 ((Element*)fDeque.back())->fGenID = kEmptyGenID;
626 } 686 }
627 687
628 bool SkClipStack::isWideOpen() const { 688 bool SkClipStack::isWideOpen() const {
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
730 return kWideOpenGenID; 790 return kWideOpenGenID;
731 } 791 }
732 792
733 const Element* back = static_cast<const Element*>(fDeque.back()); 793 const Element* back = static_cast<const Element*>(fDeque.back());
734 if (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.is Empty()) { 794 if (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.is Empty()) {
735 return kWideOpenGenID; 795 return kWideOpenGenID;
736 } 796 }
737 797
738 return back->getGenID(); 798 return back->getGenID();
739 } 799 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698