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
14static 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
36DEF_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
49static 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
111DEF_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
124DEF_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
140DEF_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
149DEF_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
159DEF_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
171DEF_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
181DEF_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
191DEF_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