• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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 
8 #include "include/utils/SkRandom.h"
9 #include "src/gpu/ccpr/GrOctoBounds.h"
10 #include "tests/Test.h"
11 
12 using namespace skiatest;
13 
14 constexpr static float kEpsilon = 1e-3f;
15 
16 static int numClipsOut = 0;
17 static int numIntersectClips = 0;
18 
19 // Ensures devBounds and devBounds45 are valid. Namely, that they are both tight bounding boxes
20 // around a valid octagon.
validate_octo_bounds(Reporter * reporter,const SkIRect & clipRect,const GrOctoBounds & octoBounds)21 static void validate_octo_bounds(
22         Reporter* reporter, const SkIRect& clipRect, const GrOctoBounds& octoBounds) {
23     // Verify dev bounds are inside the clip rect.
24     REPORTER_ASSERT(reporter, octoBounds.left() >= (float)clipRect.left() - kEpsilon);
25     REPORTER_ASSERT(reporter, octoBounds.top() >= (float)clipRect.top() - kEpsilon);
26     REPORTER_ASSERT(reporter, octoBounds.right() <= (float)clipRect.right() + kEpsilon);
27     REPORTER_ASSERT(reporter, octoBounds.bottom() <= (float)clipRect.bottom() + kEpsilon);
28 
29     octoBounds.validateBoundsAreTight([reporter](
30             bool cond, const char* file, int line, const char* code) {
31         if (!cond) {
32             reporter->reportFailedWithContext(skiatest::Failure(file, line, code, SkString()));
33         }
34     });
35 }
36 
37 // This is a variant of SkRandom::nextRangeU that can handle negative numbers. As currently written,
38 // and assuming two's compliment, it would probably work to just call the existing nextRangeU
39 // implementation with negative value(s), but we go through this method as an extra precaution.
next_range_i(SkRandom * rand,int min,int max)40 static int next_range_i(SkRandom* rand, int min, int max) {
41     int u = rand->nextRangeU(0, max - min);
42     return u + min;
43 }
44 
test_octagon(Reporter * reporter,SkRandom * rand,float l,float t,float r,float b)45 static void test_octagon(Reporter* reporter, SkRandom* rand, float l, float t, float r, float b) {
46     for (int i = 0; i < 20; ++i) {
47         float minL45 = GrOctoBounds::Get_x45(l,b);
48         float maxL45 = std::min(GrOctoBounds::Get_x45(r,b), GrOctoBounds::Get_x45(l,t));
49         float minT45 = GrOctoBounds::Get_y45(l,t);
50         float maxT45 = std::min(GrOctoBounds::Get_y45(l,b), GrOctoBounds::Get_y45(r,t));
51         float minR45 = std::max(GrOctoBounds::Get_x45(l,t), GrOctoBounds::Get_x45(r,b));
52         float maxR45 = GrOctoBounds::Get_x45(r,t);
53         float minB45 = std::max(GrOctoBounds::Get_y45(r,t), GrOctoBounds::Get_y45(l,b));
54         float maxB45 = GrOctoBounds::Get_y45(r,b);
55 
56         // Pick somewhat valid 45 degree bounds.
57         float l45 = rand->nextRangeF(minL45, maxL45);
58         float t45 = rand->nextRangeF(minT45, maxT45);
59         float r45 = rand->nextRangeF(minR45, maxR45);
60         float b45 = rand->nextRangeF(minB45, maxB45);
61 
62         // Grow out diagonal corners if too tight, making 45 bounds valid.
63         std::function<void()> growOutDiagonals[4] = {
64             [&]() {  // Push top-left diagonal corner outside left edge.
65                 float miss = GrOctoBounds::Get_x(l45,t45) - l;
66                 if (miss > 0) {
67                     // x = (x45 + y45)/2
68                     l45 -= miss;
69                     if (l45 < minL45) {
70                         t45 -= minL45 - l45;
71                         l45 = minL45;
72                     }
73                     t45 -= miss;
74                     if (t45 < minT45) {
75                         l45 -= minT45 - t45;
76                         t45 = minT45;
77                     }
78                 }
79             },
80             [&]() {  // Push top-right diagonal corner outside top edge.
81                 float miss = GrOctoBounds::Get_y(r45,t45) - t;
82                 if (miss > 0) {
83                     // y = (y45 - x45)/2
84                     r45 += miss;
85                     if (r45 > maxR45) {
86                         t45 -= r45 - maxR45;
87                         r45 = maxR45;
88                     }
89                     t45 -= miss;
90                     if (t45 < minT45) {
91                         r45 += minT45 - t45;
92                         t45 = minT45;
93                     }
94                 }
95             },
96             [&]() {  // Push bottom-right diagonal corner outside right edge.
97                 float miss = r - GrOctoBounds::Get_x(r45,b45);
98                 if (miss > 0) {
99                     // x = (x45 + y45)/2
100                     r45 += miss;
101                     if (r45 > maxR45) {
102                         b45 += r45 - maxR45;
103                         r45 = maxR45;
104                     }
105                     b45 += miss;
106                     if (b45 > maxB45) {
107                         r45 += b45 - maxB45;
108                         b45 = maxB45;
109                     }
110                 }
111             },
112             [&]() {  // Push bottom-left diagonal corner outside bottom edge.
113                 float miss = b - GrOctoBounds::Get_y(l45,b45);
114                 if (miss > 0) {
115                     // y = (y45 - x45)/2
116                     l45 -= miss;
117                     if (l45 < minL45) {
118                         b45 += minL45 - l45;
119                         l45 = minL45;
120                     }
121                     b45 += miss;
122                     if (b45 > maxB45) {
123                         l45 -= b45 - maxB45;
124                         b45 = maxB45;
125                     }
126                 }
127             },
128         };
129         // Shuffle.
130         for (int i = 0; i < 10; ++i) {
131             std::swap(growOutDiagonals[rand->nextRangeU(0, 3)],
132                       growOutDiagonals[rand->nextRangeU(0, 3)]);
133         }
134         for (const auto& f : growOutDiagonals) {
135             f();
136         }
137 
138         GrOctoBounds octoBounds(SkRect::MakeLTRB(l,t,r,b), SkRect::MakeLTRB(l45,t45,r45,b45));
139 
140         SkIRect devIBounds;
141         octoBounds.roundOut(&devIBounds);
142 
143         // Test a clip rect that completely encloses the octagon.
144         bool clipSuccess = octoBounds.clip(devIBounds);
145         REPORTER_ASSERT(reporter, clipSuccess);
146         // Should not have clipped anything.
147         REPORTER_ASSERT(reporter, octoBounds == GrOctoBounds({l,t,r,b}, {l45,t45,r45,b45}));
148         validate_octo_bounds(reporter, devIBounds, octoBounds);
149 
150         // Test a bunch of random clip rects.
151         for (int j = 0; j < 20; ++j) {
152             SkIRect clipRect;
153             do {
154                 clipRect.fLeft = next_range_i(rand, devIBounds.left(), devIBounds.right() - 1);
155                 clipRect.fTop = next_range_i(rand, devIBounds.top(), devIBounds.bottom() - 1);
156                 clipRect.fRight = next_range_i(rand, clipRect.left() + 1, devIBounds.right());
157                 clipRect.fBottom = next_range_i(rand, clipRect.top() + 1, devIBounds.bottom());
158             } while (clipRect == devIBounds);
159 
160             GrOctoBounds octoBoundsClipped = octoBounds;
161             if (!octoBoundsClipped.clip(clipRect)) {
162                 // Ensure clipRect is completely outside one of the diagonals.
163                 float il = (float)clipRect.left();
164                 float it = (float)clipRect.top();
165                 float ir = (float)clipRect.right();
166                 float ib = (float)clipRect.bottom();
167                 REPORTER_ASSERT(reporter,
168                         GrOctoBounds::Get_x45(ir,it) <= l45 + kEpsilon ||
169                         GrOctoBounds::Get_y45(ir,ib) <= t45 + kEpsilon ||
170                         GrOctoBounds::Get_x45(il,ib) >= r45 - kEpsilon ||
171                         GrOctoBounds::Get_y45(il,it) >= b45 - kEpsilon);
172                 ++numClipsOut;
173             } else {
174                 validate_octo_bounds(reporter, clipRect, octoBoundsClipped);
175                 ++numIntersectClips;
176             }
177         }
178     }
179 
180 }
181 
DEF_TEST(OctoBounds,reporter)182 DEF_TEST(OctoBounds, reporter) {
183     numClipsOut = 0;
184     numIntersectClips = 0;
185 
186     SkRandom rand;
187     test_octagon(reporter, &rand, 0, 0, 100, 100);
188     test_octagon(reporter, &rand, -2, 0, 2, 100);
189     test_octagon(reporter, &rand, 0, -10, 100, 0);
190     // We can't test Infs or NaNs because they trigger internal asserts when setting GrOctoBounds.
191 
192     // Sanity check on our random clip testing.. Just make we hit both types of clip.
193     REPORTER_ASSERT(reporter, numClipsOut > 0);
194     REPORTER_ASSERT(reporter, numIntersectClips > 0);
195 }
196