ClipStackTest.cpp revision cc6493bbef7c9c2adf4b1ed8701e2ed015ae745d
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
130static void test_iterators(skiatest::Reporter* reporter) {
131    SkClipStack stack;
132
133    static const SkRect gRects[] = {
134        { 0,   0,  40,  40 },
135        { 60,  0, 100,  40 },
136        { 0,  60,  40, 100 },
137        { 60, 60, 100, 100 }
138    };
139
140    for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
141        // the union op will prevent these from being fused together
142        stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false);
143    }
144
145    assert_count(reporter, stack, 4);
146
147    // bottom to top iteration
148    {
149        const SkClipStack::B2TIter::Clip* clip = NULL;
150
151        SkClipStack::B2TIter iter(stack);
152        int i;
153
154        for (i = 0, clip = iter.next(); clip; ++i, clip = iter.next()) {
155            REPORTER_ASSERT(reporter, *clip->fRect == gRects[i]);
156        }
157
158        SkASSERT(i == 4);
159    }
160
161    // top to bottom iteration
162    {
163        const SkClipStack::Iter::Clip* clip = NULL;
164
165        SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
166        int i;
167
168        for (i = 3, clip = iter.prev(); clip; --i, clip = iter.prev()) {
169            REPORTER_ASSERT(reporter, *clip->fRect == gRects[i]);
170        }
171
172        SkASSERT(i == -1);
173    }
174
175    // skipToTopmost
176    {
177        const SkClipStack::Iter::Clip*clip = NULL;
178
179        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
180
181        clip = iter.skipToTopmost(SkRegion::kUnion_Op);
182        REPORTER_ASSERT(reporter, *clip->fRect == gRects[3]);
183    }
184}
185
186static void test_bounds(skiatest::Reporter* reporter, bool useRects) {
187
188    static const int gNumCases = 20;
189    static const SkRect gAnswerRectsBW[gNumCases] = {
190        // A op B
191        { 40, 40, 50, 50 },
192        { 10, 10, 50, 50 },
193        { 10, 10, 80, 80 },
194        { 10, 10, 80, 80 },
195        { 40, 40, 80, 80 },
196
197        // invA op B
198        { 40, 40, 80, 80 },
199        { 0, 0, 100, 100 },
200        { 0, 0, 100, 100 },
201        { 0, 0, 100, 100 },
202        { 40, 40, 50, 50 },
203
204        // A op invB
205        { 10, 10, 50, 50 },
206        { 40, 40, 50, 50 },
207        { 0, 0, 100, 100 },
208        { 0, 0, 100, 100 },
209        { 0, 0, 100, 100 },
210
211        // invA op invB
212        { 0, 0, 100, 100 },
213        { 40, 40, 80, 80 },
214        { 0, 0, 100, 100 },
215        { 10, 10, 80, 80 },
216        { 10, 10, 50, 50 },
217    };
218
219    static const SkRegion::Op gOps[] = {
220        SkRegion::kIntersect_Op,
221        SkRegion::kDifference_Op,
222        SkRegion::kUnion_Op,
223        SkRegion::kXOR_Op,
224        SkRegion::kReverseDifference_Op
225    };
226
227    SkRect rectA, rectB;
228
229    rectA.iset(10, 10, 50, 50);
230    rectB.iset(40, 40, 80, 80);
231
232    SkPath clipA, clipB;
233
234    clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
235    clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
236
237    SkClipStack stack;
238    SkRect bound;
239    bool isIntersectionOfRects = false;
240
241    int testCase = 0;
242    int numBitTests = useRects ? 1 : 4;
243    for (int invBits = 0; invBits < numBitTests; ++invBits) {
244        for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
245
246            stack.save();
247            bool doInvA = SkToBool(invBits & 1);
248            bool doInvB = SkToBool(invBits & 2);
249
250            clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
251                                       SkPath::kEvenOdd_FillType);
252            clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
253                                       SkPath::kEvenOdd_FillType);
254
255            if (useRects) {
256                stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false);
257                stack.clipDevRect(rectB, gOps[op], false);
258            } else {
259                stack.clipDevPath(clipA, SkRegion::kIntersect_Op, false);
260                stack.clipDevPath(clipB, gOps[op], false);
261            }
262
263            REPORTER_ASSERT(reporter, !stack.isWideOpen());
264
265            stack.getConservativeBounds(0, 0, 100, 100, &bound,
266                                        &isIntersectionOfRects);
267
268            if (useRects) {
269                REPORTER_ASSERT(reporter, isIntersectionOfRects ==
270                        (gOps[op] == SkRegion::kIntersect_Op));
271            } else {
272                REPORTER_ASSERT(reporter, !isIntersectionOfRects);
273            }
274
275            SkASSERT(testCase < gNumCases);
276            REPORTER_ASSERT(reporter, bound == gAnswerRectsBW[testCase]);
277            ++testCase;
278
279            stack.restore();
280        }
281    }
282}
283
284// Test out 'isWideOpen' entry point
285static void test_isWideOpen(skiatest::Reporter* reporter) {
286
287    SkRect rectA, rectB;
288
289    rectA.iset(10, 10, 40, 40);
290    rectB.iset(50, 50, 80, 80);
291
292    // Stack should initially be wide open
293    {
294        SkClipStack stack;
295
296        REPORTER_ASSERT(reporter, stack.isWideOpen());
297    }
298
299    // Test out case where the user specifies a union that includes everything
300    {
301        SkClipStack stack;
302
303        SkPath clipA, clipB;
304
305        clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
306        clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
307
308        clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
309        clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
310
311        stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
312        stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
313
314        REPORTER_ASSERT(reporter, stack.isWideOpen());
315    }
316
317    // Test out union w/ a wide open clip
318    {
319        SkClipStack stack;
320
321        stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
322
323        REPORTER_ASSERT(reporter, stack.isWideOpen());
324    }
325
326    // Test out empty difference from a wide open clip
327    {
328        SkClipStack stack;
329
330        SkRect emptyRect;
331        emptyRect.setEmpty();
332
333        stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
334
335        REPORTER_ASSERT(reporter, stack.isWideOpen());
336    }
337
338    // Test out return to wide open
339    {
340        SkClipStack stack;
341
342        stack.save();
343
344        stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
345
346        REPORTER_ASSERT(reporter, !stack.isWideOpen());
347
348        stack.restore();
349
350        REPORTER_ASSERT(reporter, stack.isWideOpen());
351    }
352}
353
354
355static void TestClipStack(skiatest::Reporter* reporter) {
356    SkClipStack stack;
357
358    REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
359    assert_count(reporter, stack, 0);
360
361    static const SkIRect gRects[] = {
362        { 0, 0, 100, 100 },
363        { 25, 25, 125, 125 },
364        { 0, 0, 1000, 1000 },
365        { 0, 0, 75, 75 }
366    };
367    for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
368        stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
369    }
370
371    // all of the above rects should have been intersected, leaving only 1 rect
372    SkClipStack::B2TIter iter(stack);
373    const SkClipStack::B2TIter::Clip* clip = iter.next();
374    SkRect answer;
375    answer.iset(25, 25, 75, 75);
376
377    REPORTER_ASSERT(reporter, clip);
378    REPORTER_ASSERT(reporter, clip->fRect);
379    REPORTER_ASSERT(reporter, !clip->fPath);
380    REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == clip->fOp);
381    REPORTER_ASSERT(reporter, *clip->fRect == answer);
382    // now check that we only had one in our iterator
383    REPORTER_ASSERT(reporter, !iter.next());
384
385    stack.reset();
386    REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
387    assert_count(reporter, stack, 0);
388
389    test_assign_and_comparison(reporter);
390    test_iterators(reporter);
391    test_bounds(reporter, true);
392    test_bounds(reporter, false);
393    test_isWideOpen(reporter);
394}
395
396#include "TestClassDef.h"
397DEFINE_TESTCLASS("ClipStack", TestClipStackClass, TestClipStack)
398