1 /*
2  * Copyright 2013 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "PathOpsExtendedTest.h"
8 #include "PathOpsThreadedCommon.h"
9 #include "SkCanvas.h"
10 #include "SkRandom.h"
11 #include "SkTSort.h"
12 #include "Test.h"
13 
testTightBoundsLines(PathOpsThreadState * data)14 static void testTightBoundsLines(PathOpsThreadState* data) {
15     SkRandom ran;
16     for (int index = 0; index < 1000; ++index) {
17         SkPath path;
18         int contourCount = ran.nextRangeU(1, 10);
19         for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
20             int lineCount = ran.nextRangeU(1, 10);
21             path.moveTo(ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000));
22             for (int lIndex = 0; lIndex < lineCount; ++lIndex) {
23                 path.lineTo(ran.nextRangeF(-1000, 1000), ran.nextRangeF(-1000, 1000));
24             }
25             if (ran.nextBool()) {
26                 path.close();
27             }
28         }
29         SkRect classicBounds = path.getBounds();
30         SkRect tightBounds;
31         REPORTER_ASSERT(data->fReporter, TightBounds(path, &tightBounds));
32         REPORTER_ASSERT(data->fReporter, classicBounds == tightBounds);
33     }
34 }
35 
DEF_TEST(PathOpsTightBoundsLines,reporter)36 DEF_TEST(PathOpsTightBoundsLines, reporter) {
37     initializeTests(reporter, "tightBoundsLines");
38     PathOpsThreadedTestRunner testRunner(reporter);
39     int outerCount = reporter->allowExtendedTest() ? 100 : 1;
40     for (int index = 0; index < outerCount; ++index) {
41         for (int idx2 = 0; idx2 < 10; ++idx2) {
42             *testRunner.fRunnables.append() =
43                     new PathOpsThreadedRunnable(&testTightBoundsLines, 0, 0, 0, 0, &testRunner);
44         }
45     }
46     testRunner.render();
47 }
48 
testTightBoundsQuads(PathOpsThreadState * data)49 static void testTightBoundsQuads(PathOpsThreadState* data) {
50     SkRandom ran;
51     const int bitWidth = 32;
52     const int bitHeight = 32;
53     const float pathMin = 1;
54     const float pathMax = (float) (bitHeight - 2);
55     SkBitmap& bits = *data->fBitmap;
56     if (bits.width() == 0) {
57         bits.allocN32Pixels(bitWidth, bitHeight);
58     }
59     SkCanvas canvas(bits);
60     SkPaint paint;
61     for (int index = 0; index < 100; ++index) {
62         SkPath path;
63         int contourCount = ran.nextRangeU(1, 10);
64         for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
65             int lineCount = ran.nextRangeU(1, 10);
66             path.moveTo(ran.nextRangeF(1, pathMax), ran.nextRangeF(pathMin, pathMax));
67             for (int lIndex = 0; lIndex < lineCount; ++lIndex) {
68                 if (ran.nextBool()) {
69                     path.lineTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax));
70                 } else {
71                     path.quadTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax),
72                             ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax));
73                 }
74             }
75             if (ran.nextBool()) {
76                 path.close();
77             }
78         }
79         SkRect classicBounds = path.getBounds();
80         SkRect tightBounds;
81         REPORTER_ASSERT(data->fReporter, TightBounds(path, &tightBounds));
82         REPORTER_ASSERT(data->fReporter, classicBounds.contains(tightBounds));
83         canvas.drawColor(SK_ColorWHITE);
84         canvas.drawPath(path, paint);
85         SkIRect bitsWritten = {31, 31, 0, 0};
86         for (int y = 0; y < bitHeight; ++y) {
87             uint32_t* addr1 = data->fBitmap->getAddr32(0, y);
88             bool lineWritten = false;
89             for (int x = 0; x < bitWidth; ++x) {
90                 if (addr1[x] == (uint32_t) -1) {
91                     continue;
92                 }
93                 lineWritten = true;
94                 bitsWritten.fLeft = SkTMin(bitsWritten.fLeft, x);
95                 bitsWritten.fRight = SkTMax(bitsWritten.fRight, x);
96             }
97             if (!lineWritten) {
98                 continue;
99             }
100             bitsWritten.fTop = SkTMin(bitsWritten.fTop, y);
101             bitsWritten.fBottom = SkTMax(bitsWritten.fBottom, y);
102         }
103         if (!bitsWritten.isEmpty()) {
104             SkIRect tightOut;
105             tightBounds.roundOut(&tightOut);
106             REPORTER_ASSERT(data->fReporter, tightOut.contains(bitsWritten));
107         }
108     }
109 }
110 
DEF_TEST(PathOpsTightBoundsQuads,reporter)111 DEF_TEST(PathOpsTightBoundsQuads, reporter) {
112     initializeTests(reporter, "tightBoundsQuads");
113     PathOpsThreadedTestRunner testRunner(reporter);
114     int outerCount = reporter->allowExtendedTest() ? 100 : 1;
115     for (int index = 0; index < outerCount; ++index) {
116         for (int idx2 = 0; idx2 < 10; ++idx2) {
117             *testRunner.fRunnables.append() =
118                     new PathOpsThreadedRunnable(&testTightBoundsQuads, 0, 0, 0, 0, &testRunner);
119         }
120     }
121     testRunner.render();
122 }
123 
DEF_TEST(PathOpsTightBoundsMove,reporter)124 DEF_TEST(PathOpsTightBoundsMove, reporter) {
125     SkPath path;
126     path.moveTo(10, 10);
127     path.close();
128     path.moveTo(20, 20);
129     path.lineTo(20, 20);
130     path.close();
131     path.moveTo(15, 15);
132     path.lineTo(15, 15);
133     path.close();
134     const SkRect& bounds = path.getBounds();
135     SkRect tight;
136     REPORTER_ASSERT(reporter, TightBounds(path, &tight));
137     REPORTER_ASSERT(reporter, bounds == tight);
138 }
139 
DEF_TEST(PathOpsTightBoundsMoveOne,reporter)140 DEF_TEST(PathOpsTightBoundsMoveOne, reporter) {
141     SkPath path;
142     path.moveTo(20, 20);
143     const SkRect& bounds = path.getBounds();
144     SkRect tight;
145     REPORTER_ASSERT(reporter, TightBounds(path, &tight));
146     REPORTER_ASSERT(reporter, bounds == tight);
147 }
148 
DEF_TEST(PathOpsTightBoundsMoveTwo,reporter)149 DEF_TEST(PathOpsTightBoundsMoveTwo, reporter) {
150     SkPath path;
151     path.moveTo(20, 20);
152     path.moveTo(40, 40);
153     const SkRect& bounds = path.getBounds();
154     SkRect tight;
155     REPORTER_ASSERT(reporter, TightBounds(path, &tight));
156     REPORTER_ASSERT(reporter, bounds == tight);
157 }
158 
DEF_TEST(PathOpsTightBoundsTiny,reporter)159 DEF_TEST(PathOpsTightBoundsTiny, reporter) {
160     SkPath path;
161     path.moveTo(1, 1);
162     path.quadTo(1.000001f, 1, 1, 1);
163     const SkRect& bounds = path.getBounds();
164     SkRect tight;
165     REPORTER_ASSERT(reporter, TightBounds(path, &tight));
166     SkRect moveBounds = {1, 1, 1, 1};
167     REPORTER_ASSERT(reporter, bounds != tight);
168     REPORTER_ASSERT(reporter, moveBounds == tight);
169 }
170 
DEF_TEST(PathOpsTightBoundsWellBehaved,reporter)171 DEF_TEST(PathOpsTightBoundsWellBehaved, reporter) {
172     SkPath path;
173     path.moveTo(1, 1);
174     path.quadTo(2, 3, 4, 5);
175     const SkRect& bounds = path.getBounds();
176     SkRect tight;
177     REPORTER_ASSERT(reporter, TightBounds(path, &tight));
178     REPORTER_ASSERT(reporter, bounds == tight);
179 }
180 
DEF_TEST(PathOpsTightBoundsIllBehaved,reporter)181 DEF_TEST(PathOpsTightBoundsIllBehaved, reporter) {
182     SkPath path;
183     path.moveTo(1, 1);
184     path.quadTo(4, 3, 2, 2);
185     const SkRect& bounds = path.getBounds();
186     SkRect tight;
187     REPORTER_ASSERT(reporter, TightBounds(path, &tight));
188     REPORTER_ASSERT(reporter, bounds != tight);
189 }
190 
DEF_TEST(PathOpsTightBoundsIllBehavedScaled,reporter)191 DEF_TEST(PathOpsTightBoundsIllBehavedScaled, reporter) {
192     SkPath path;
193     path.moveTo(0, 0);
194     path.quadTo(1048578, 1048577, 1048576, 1048576);
195     const SkRect& bounds = path.getBounds();
196     SkRect tight;
197     REPORTER_ASSERT(reporter, TightBounds(path, &tight));
198     REPORTER_ASSERT(reporter, bounds != tight);
199     REPORTER_ASSERT(reporter, tight.right() == 1048576);
200     REPORTER_ASSERT(reporter, tight.bottom() == 1048576);
201 }
202