ClipStackTest.cpp revision 72b2e6fff3f54c6aa80a98eab4c73f02a8cd450d
1
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 */
8#include "Test.h"
9#include "SkClipStack.h"
10#include "SkPath.h"
11#include "SkRect.h"
12
13
14
15static void test_assign_and_comparison(skiatest::Reporter* reporter) {
16    SkClipStack s;
17    bool doAA = false;
18
19    REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
20
21    // Build up a clip stack with a path, an empty clip, and a rect.
22    s.save();
23    REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
24
25    SkPath p;
26    p.moveTo(5, 6);
27    p.lineTo(7, 8);
28    p.lineTo(5, 9);
29    p.close();
30    s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
31
32    s.save();
33    REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
34
35    SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
36    s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
37    r = SkRect::MakeLTRB(10, 11, 12, 13);
38    s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
39
40    s.save();
41    REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
42
43    r = SkRect::MakeLTRB(14, 15, 16, 17);
44    s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
45
46    // Test that assignment works.
47    SkClipStack copy = s;
48    REPORTER_ASSERT(reporter, s == copy);
49
50    // Test that different save levels triggers not equal.
51    s.restore();
52    REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
53    REPORTER_ASSERT(reporter, s != copy);
54
55    // Test that an equal, but not copied version is equal.
56    s.save();
57    REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
58
59    r = SkRect::MakeLTRB(14, 15, 16, 17);
60    s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
61    REPORTER_ASSERT(reporter, s == copy);
62
63    // Test that a different op on one level triggers not equal.
64    s.restore();
65    REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
66    s.save();
67    REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
68
69    r = SkRect::MakeLTRB(14, 15, 16, 17);
70    s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
71    REPORTER_ASSERT(reporter, s != copy);
72
73    // Test that different state (clip type) triggers not equal.
74    // NO LONGER VALID: if a path contains only a rect, we turn
75    // it into a bare rect for performance reasons (working
76    // around Chromium/JavaScript bad pattern).
77/*
78    s.restore();
79    s.save();
80    SkPath rp;
81    rp.addRect(r);
82    s.clipDevPath(rp, SkRegion::kUnion_Op, doAA);
83    REPORTER_ASSERT(reporter, s != copy);
84*/
85
86    // Test that different rects triggers not equal.
87    s.restore();
88    REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
89    s.save();
90    REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
91
92    r = SkRect::MakeLTRB(24, 25, 26, 27);
93    s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
94    REPORTER_ASSERT(reporter, s != copy);
95
96    // Sanity check
97    s.restore();
98    REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
99
100    copy.restore();
101    REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
102    REPORTER_ASSERT(reporter, s == copy);
103    s.restore();
104    REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
105    copy.restore();
106    REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
107    REPORTER_ASSERT(reporter, s == copy);
108
109    // Test that different paths triggers not equal.
110    s.restore();
111    REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
112    s.save();
113    REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
114
115    p.addRect(r);
116    s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
117    REPORTER_ASSERT(reporter, s != copy);
118}
119
120static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
121                         int count) {
122    SkClipStack::B2TIter iter(stack);
123    int counter = 0;
124    while (iter.next()) {
125        counter += 1;
126    }
127    REPORTER_ASSERT(reporter, count == counter);
128}
129
130// Exercise the SkClipStack's bottom to top and bidirectional iterators
131// (including the skipToTopmost functionality)
132static void test_iterators(skiatest::Reporter* reporter) {
133    SkClipStack stack;
134
135    static const SkRect gRects[] = {
136        { 0,   0,  40,  40 },
137        { 60,  0, 100,  40 },
138        { 0,  60,  40, 100 },
139        { 60, 60, 100, 100 }
140    };
141
142    for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
143        // the union op will prevent these from being fused together
144        stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false);
145    }
146
147    assert_count(reporter, stack, 4);
148
149    // bottom to top iteration
150    {
151        const SkClipStack::B2TIter::Clip* clip = NULL;
152
153        SkClipStack::B2TIter iter(stack);
154        int i;
155
156        for (i = 0, clip = iter.next(); clip; ++i, clip = iter.next()) {
157            REPORTER_ASSERT(reporter, *clip->fRect == gRects[i]);
158        }
159
160        SkASSERT(i == 4);
161    }
162
163    // top to bottom iteration
164    {
165        const SkClipStack::Iter::Clip* clip = NULL;
166
167        SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
168        int i;
169
170        for (i = 3, clip = iter.prev(); clip; --i, clip = iter.prev()) {
171            REPORTER_ASSERT(reporter, *clip->fRect == gRects[i]);
172        }
173
174        SkASSERT(i == -1);
175    }
176
177    // skipToTopmost
178    {
179        const SkClipStack::Iter::Clip*clip = NULL;
180
181        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
182
183        clip = iter.skipToTopmost(SkRegion::kUnion_Op);
184        REPORTER_ASSERT(reporter, *clip->fRect == gRects[3]);
185    }
186}
187
188// Exercise the SkClipStack's getConservativeBounds computation
189static void test_bounds(skiatest::Reporter* reporter, bool useRects) {
190
191    static const int gNumCases = 20;
192    static const SkRect gAnswerRectsBW[gNumCases] = {
193        // A op B
194        { 40, 40, 50, 50 },
195        { 10, 10, 50, 50 },
196        { 10, 10, 80, 80 },
197        { 10, 10, 80, 80 },
198        { 40, 40, 80, 80 },
199
200        // invA op B
201        { 40, 40, 80, 80 },
202        { 0, 0, 100, 100 },
203        { 0, 0, 100, 100 },
204        { 0, 0, 100, 100 },
205        { 40, 40, 50, 50 },
206
207        // A op invB
208        { 10, 10, 50, 50 },
209        { 40, 40, 50, 50 },
210        { 0, 0, 100, 100 },
211        { 0, 0, 100, 100 },
212        { 0, 0, 100, 100 },
213
214        // invA op invB
215        { 0, 0, 100, 100 },
216        { 40, 40, 80, 80 },
217        { 0, 0, 100, 100 },
218        { 10, 10, 80, 80 },
219        { 10, 10, 50, 50 },
220    };
221
222    static const SkRegion::Op gOps[] = {
223        SkRegion::kIntersect_Op,
224        SkRegion::kDifference_Op,
225        SkRegion::kUnion_Op,
226        SkRegion::kXOR_Op,
227        SkRegion::kReverseDifference_Op
228    };
229
230    SkRect rectA, rectB;
231
232    rectA.iset(10, 10, 50, 50);
233    rectB.iset(40, 40, 80, 80);
234
235    SkPath clipA, clipB;
236
237    clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
238    clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
239
240    SkClipStack stack;
241    SkRect devClipBound;
242    bool isIntersectionOfRects = false;
243
244    int testCase = 0;
245    int numBitTests = useRects ? 1 : 4;
246    for (int invBits = 0; invBits < numBitTests; ++invBits) {
247        for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
248
249            stack.save();
250            bool doInvA = SkToBool(invBits & 1);
251            bool doInvB = SkToBool(invBits & 2);
252
253            clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
254                                       SkPath::kEvenOdd_FillType);
255            clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
256                                       SkPath::kEvenOdd_FillType);
257
258            if (useRects) {
259                stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false);
260                stack.clipDevRect(rectB, gOps[op], false);
261            } else {
262                stack.clipDevPath(clipA, SkRegion::kIntersect_Op, false);
263                stack.clipDevPath(clipB, gOps[op], false);
264            }
265
266            REPORTER_ASSERT(reporter, !stack.isWideOpen());
267
268            stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
269                                        &isIntersectionOfRects);
270
271            if (useRects) {
272                REPORTER_ASSERT(reporter, isIntersectionOfRects ==
273                        (gOps[op] == SkRegion::kIntersect_Op));
274            } else {
275                REPORTER_ASSERT(reporter, !isIntersectionOfRects);
276            }
277
278            SkASSERT(testCase < gNumCases);
279            REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
280            ++testCase;
281
282            stack.restore();
283        }
284    }
285}
286
287// Test out 'isWideOpen' entry point
288static void test_isWideOpen(skiatest::Reporter* reporter) {
289
290    SkRect rectA, rectB;
291
292    rectA.iset(10, 10, 40, 40);
293    rectB.iset(50, 50, 80, 80);
294
295    // Stack should initially be wide open
296    {
297        SkClipStack stack;
298
299        REPORTER_ASSERT(reporter, stack.isWideOpen());
300    }
301
302    // Test out case where the user specifies a union that includes everything
303    {
304        SkClipStack stack;
305
306        SkPath clipA, clipB;
307
308        clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
309        clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
310
311        clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
312        clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
313
314        stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
315        stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
316
317        REPORTER_ASSERT(reporter, stack.isWideOpen());
318    }
319
320    // Test out union w/ a wide open clip
321    {
322        SkClipStack stack;
323
324        stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
325
326        REPORTER_ASSERT(reporter, stack.isWideOpen());
327    }
328
329    // Test out empty difference from a wide open clip
330    {
331        SkClipStack stack;
332
333        SkRect emptyRect;
334        emptyRect.setEmpty();
335
336        stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
337
338        REPORTER_ASSERT(reporter, stack.isWideOpen());
339    }
340
341    // Test out return to wide open
342    {
343        SkClipStack stack;
344
345        stack.save();
346
347        stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
348
349        REPORTER_ASSERT(reporter, !stack.isWideOpen());
350
351        stack.restore();
352
353        REPORTER_ASSERT(reporter, stack.isWideOpen());
354    }
355}
356
357static int count(const SkClipStack& stack) {
358
359    SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
360
361    const SkClipStack::Iter::Clip* clip = NULL;
362    int count = 0;
363
364    for (clip = iter.prev(); clip; clip = iter.prev(), ++count) {
365        ;
366    }
367
368    return count;
369}
370
371// Test out SkClipStack's merging of rect clips. In particular exercise
372// merging of aa vs. bw rects.
373static void test_rect_merging(skiatest::Reporter* reporter) {
374
375    SkRect overlapLeft  = SkRect::MakeLTRB(10, 10, 50, 50);
376    SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
377
378    SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
379    SkRect nestedChild  = SkRect::MakeLTRB(40, 40, 60, 60);
380
381    SkRect bound;
382    SkClipStack::BoundsType type;
383    bool isIntersectionOfRects;
384
385    // all bw overlapping - should merge
386    {
387        SkClipStack stack;
388
389        stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false);
390
391        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
392
393        REPORTER_ASSERT(reporter, 1 == count(stack));
394
395        stack.getBounds(&bound, &type, &isIntersectionOfRects);
396
397        REPORTER_ASSERT(reporter, isIntersectionOfRects);
398    }
399
400    // all aa overlapping - should merge
401    {
402        SkClipStack stack;
403
404        stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
405
406        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true);
407
408        REPORTER_ASSERT(reporter, 1 == count(stack));
409
410        stack.getBounds(&bound, &type, &isIntersectionOfRects);
411
412        REPORTER_ASSERT(reporter, isIntersectionOfRects);
413    }
414
415    // mixed overlapping - should _not_ merge
416    {
417        SkClipStack stack;
418
419        stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
420
421        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
422
423        REPORTER_ASSERT(reporter, 2 == count(stack));
424
425        stack.getBounds(&bound, &type, &isIntersectionOfRects);
426
427        REPORTER_ASSERT(reporter, !isIntersectionOfRects);
428    }
429
430    // mixed nested (bw inside aa) - should merge
431    {
432        SkClipStack stack;
433
434        stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true);
435
436        stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false);
437
438        REPORTER_ASSERT(reporter, 1 == count(stack));
439
440        stack.getBounds(&bound, &type, &isIntersectionOfRects);
441
442        REPORTER_ASSERT(reporter, isIntersectionOfRects);
443    }
444
445    // mixed nested (aa inside bw) - should merge
446    {
447        SkClipStack stack;
448
449        stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false);
450
451        stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true);
452
453        REPORTER_ASSERT(reporter, 1 == count(stack));
454
455        stack.getBounds(&bound, &type, &isIntersectionOfRects);
456
457        REPORTER_ASSERT(reporter, isIntersectionOfRects);
458    }
459
460    // reverse nested (aa inside bw) - should _not_ merge
461    {
462        SkClipStack stack;
463
464        stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false);
465
466        stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true);
467
468        REPORTER_ASSERT(reporter, 2 == count(stack));
469
470        stack.getBounds(&bound, &type, &isIntersectionOfRects);
471
472        REPORTER_ASSERT(reporter, !isIntersectionOfRects);
473    }
474}
475
476
477// This is similar to the above test but tests the iterator's ability to merge rects in the
478// middle of a clip stack's sequence using nextCombined(). There is a save after every clip
479// element to prevent the clip stack from merging the rectangles as they are added.
480static void test_iter_rect_merging(skiatest::Reporter* reporter) {
481
482    SkRect overlapLeft  = SkRect::MakeLTRB(10, 10, 50, 50);
483    SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
484
485    SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
486    SkRect nestedChild  = SkRect::MakeLTRB(40, 40, 60, 60);
487
488    SkRect farAway      = SkRect::MakeLTRB(1000, 1000, 1010, 1010);
489
490    SkRect overlapIntersect;
491    overlapIntersect.intersect(overlapLeft, overlapRight);
492
493    SkPath path1, path2;
494    path1.addCircle(SkIntToScalar(30), SkIntToScalar(30), SkIntToScalar(1000));
495    path2.addOval(SkRect::MakeWH(500, 600));
496
497    const SkClipStack::Iter::Clip* clip;
498
499    // call nextCombined with an empty clip stack
500    {
501        SkClipStack stack;
502        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
503        REPORTER_ASSERT(reporter, NULL == iter.nextCombined());
504    }
505
506    // two bw overlapping - should merge, bracketed by paths
507    {
508        SkClipStack stack;
509        stack.clipDevPath(path1, SkRegion::kIntersect_Op, false); stack.save();
510
511        stack.clipDevRect(overlapLeft, SkRegion::kIntersect_Op, false); stack.save();
512
513        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); stack.save();
514
515        stack.clipDevPath(path2, SkRegion::kIntersect_Op, false); stack.save();
516
517        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
518
519        clip = iter.nextCombined();
520        REPORTER_ASSERT(reporter, *clip->fPath == path1 && !clip->fDoAA);
521
522        clip = iter.nextCombined();
523        REPORTER_ASSERT(reporter, !clip->fDoAA && *clip->fRect == overlapIntersect);
524
525        clip = iter.nextCombined();
526        REPORTER_ASSERT(reporter, *clip->fPath == path2 && !clip->fDoAA);
527
528        clip = iter.nextCombined();
529        REPORTER_ASSERT(reporter, NULL == clip);
530    }
531
532    // same as above but rects are aa and no final path.
533    {
534        SkClipStack stack;
535        stack.clipDevPath(path1, SkRegion::kIntersect_Op, false); stack.save();
536
537        stack.clipDevRect(overlapLeft, SkRegion::kIntersect_Op, true); stack.save();
538
539        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true); stack.save();
540
541        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
542
543        clip = iter.nextCombined();
544        REPORTER_ASSERT(reporter, *clip->fPath == path1 && !clip->fDoAA);
545
546        clip = iter.nextCombined();
547        REPORTER_ASSERT(reporter, clip->fDoAA && *clip->fRect == overlapIntersect);
548
549        clip = iter.nextCombined();
550        REPORTER_ASSERT(reporter, NULL == clip);
551    }
552
553    // mixed overlapping - no paths - should _not_ merge
554    {
555        SkClipStack stack;
556
557        stack.clipDevRect(overlapLeft, SkRegion::kIntersect_Op, true); stack.save();
558        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); stack.save();
559
560        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
561
562        clip = iter.nextCombined();
563        REPORTER_ASSERT(reporter, clip->fDoAA && *clip->fRect == overlapLeft);
564
565        clip = iter.nextCombined();
566        REPORTER_ASSERT(reporter, !clip->fDoAA && *clip->fRect == overlapRight);
567
568        clip = iter.nextCombined();
569        REPORTER_ASSERT(reporter, NULL == clip);
570    }
571
572    // three rects in a row where the third rect uses a non-intersect op.
573    {
574        SkClipStack stack;
575
576        stack.clipDevRect(overlapLeft, SkRegion::kIntersect_Op, true); stack.save();
577        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true); stack.save();
578        stack.clipDevRect(nestedParent, SkRegion::kXOR_Op, true); stack.save();
579
580        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
581
582        clip = iter.nextCombined();
583        REPORTER_ASSERT(reporter, clip->fDoAA && *clip->fRect == overlapIntersect);
584        clip = iter.nextCombined();
585        REPORTER_ASSERT(reporter, clip->fDoAA && *clip->fRect == nestedParent);
586        clip = iter.nextCombined();
587        REPORTER_ASSERT(reporter, NULL == clip);
588    }
589
590    // mixed nested (bw inside aa) - should merge
591    {
592        SkClipStack stack;
593        stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, false); stack.save();
594
595        stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true); stack.save();
596
597        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
598
599        clip = iter.nextCombined();
600        REPORTER_ASSERT(reporter, clip->fDoAA && *clip->fRect == nestedChild);
601
602        clip = iter.nextCombined();
603        REPORTER_ASSERT(reporter, NULL == clip);
604    }
605
606    // mixed nested (aa inside bw) - should merge
607    {
608        SkClipStack stack;
609        stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false); stack.save();
610
611        stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true); stack.save();
612
613        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
614
615        clip = iter.nextCombined();
616        REPORTER_ASSERT(reporter, !clip->fDoAA && *clip->fRect == nestedChild);
617
618        clip = iter.nextCombined();
619        REPORTER_ASSERT(reporter, NULL == clip);
620    }
621
622    // three rect intersects in a row where result is empty after the second.
623    {
624        SkClipStack stack;
625
626        stack.clipDevRect(overlapLeft, SkRegion::kIntersect_Op, false); stack.save();
627        stack.clipDevRect(farAway, SkRegion::kIntersect_Op, false); stack.save();
628        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false); stack.save();
629
630        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
631
632        clip = iter.nextCombined();
633        REPORTER_ASSERT(reporter, clip->fRect->isEmpty());
634
635        clip = iter.nextCombined();
636        REPORTER_ASSERT(reporter, *clip->fRect == overlapRight);
637
638        clip = iter.nextCombined();
639        REPORTER_ASSERT(reporter, NULL == clip);
640    }
641}
642
643static void TestClipStack(skiatest::Reporter* reporter) {
644    SkClipStack stack;
645
646    REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
647    assert_count(reporter, stack, 0);
648
649    static const SkIRect gRects[] = {
650        { 0, 0, 100, 100 },
651        { 25, 25, 125, 125 },
652        { 0, 0, 1000, 1000 },
653        { 0, 0, 75, 75 }
654    };
655    for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
656        stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
657    }
658
659    // all of the above rects should have been intersected, leaving only 1 rect
660    SkClipStack::B2TIter iter(stack);
661    const SkClipStack::B2TIter::Clip* clip = iter.next();
662    SkRect answer;
663    answer.iset(25, 25, 75, 75);
664
665    REPORTER_ASSERT(reporter, clip);
666    REPORTER_ASSERT(reporter, clip->fRect);
667    REPORTER_ASSERT(reporter, !clip->fPath);
668    REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == clip->fOp);
669    REPORTER_ASSERT(reporter, *clip->fRect == answer);
670    // now check that we only had one in our iterator
671    REPORTER_ASSERT(reporter, !iter.next());
672
673    stack.reset();
674    REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
675    assert_count(reporter, stack, 0);
676
677    test_assign_and_comparison(reporter);
678    test_iterators(reporter);
679    test_bounds(reporter, true);        // once with rects
680    test_bounds(reporter, false);       // once with paths
681    test_isWideOpen(reporter);
682    test_rect_merging(reporter);
683    test_iter_rect_merging(reporter);
684}
685
686#include "TestClassDef.h"
687DEFINE_TESTCLASS("ClipStack", TestClipStackClass, TestClipStack)
688