xref: /aosp_15_r20/external/skia/tests/GrQuadCropTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2019 Google LLC
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/core/SkMatrix.h"
9 #include "include/core/SkPoint.h"
10 #include "include/core/SkRect.h"
11 #include "include/core/SkScalar.h"
12 #include "include/core/SkTypes.h"
13 #include "include/private/gpu/ganesh/GrTypesPriv.h"
14 #include "src/gpu/ganesh/geometry/GrQuad.h"
15 #include "src/gpu/ganesh/geometry/GrQuadUtils.h"
16 #include "tests/Test.h"
17 
18 #define ASSERT(cond) REPORTER_ASSERT(r, cond)
19 #define ASSERTF(cond, ...) REPORTER_ASSERT(r, cond, __VA_ARGS__)
20 #define TEST(name) DEF_TEST(GrQuadCrop##name, r)
21 #define ASSERT_NEARLY_EQUAL(expected, actual) \
22     ASSERTF(SkScalarNearlyEqual(expected, actual), "expected: %f, actual: %f", \
23             expected, actual)
24 
25 // Make the base rect contain the origin and have unique edge values so that each transform
26 // produces a different axis-aligned rectangle.
27 static const SkRect kDrawRect = SkRect::MakeLTRB(-5.f, -6.f, 10.f, 11.f);
28 
run_crop_axis_aligned_test(skiatest::Reporter * r,const SkRect & clipRect,GrAA clipAA,const SkMatrix & viewMatrix,const SkMatrix * localMatrix)29 static void run_crop_axis_aligned_test(skiatest::Reporter* r, const SkRect& clipRect, GrAA clipAA,
30                                        const SkMatrix& viewMatrix, const SkMatrix* localMatrix) {
31     // Should use run_crop_fully_covers_test for non-rect matrices
32     SkASSERT(viewMatrix.rectStaysRect());
33 
34     DrawQuad quad = {GrQuad::MakeFromRect(kDrawRect, viewMatrix),
35                      GrQuad::MakeFromRect(kDrawRect, localMatrix ? *localMatrix : SkMatrix::I()),
36                      clipAA == GrAA::kYes ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll};
37 
38     bool exact = GrQuadUtils::CropToRect(clipRect, clipAA, &quad, /* calc. locals */ !!localMatrix);
39     ASSERTF(exact, "Expected exact crop");
40     ASSERTF(quad.fDevice.quadType() == GrQuad::Type::kAxisAligned,
41             "Expected quad to remain axis-aligned");
42 
43     // Since we remained a rectangle, the bounds will exactly match the coordinates
44     SkRect expectedBounds = viewMatrix.mapRect(kDrawRect);
45     SkAssertResult(expectedBounds.intersect(clipRect));
46 
47     SkRect actualBounds = quad.fDevice.bounds();
48     ASSERT_NEARLY_EQUAL(expectedBounds.fLeft, actualBounds.fLeft);
49     ASSERT_NEARLY_EQUAL(expectedBounds.fTop, actualBounds.fTop);
50     ASSERT_NEARLY_EQUAL(expectedBounds.fRight, actualBounds.fRight);
51     ASSERT_NEARLY_EQUAL(expectedBounds.fBottom, actualBounds.fBottom);
52 
53     // Confirm that local coordinates match up with clipped edges and the transform
54     SkMatrix invViewMatrix;
55     SkAssertResult(viewMatrix.invert(&invViewMatrix));
56 
57     if (localMatrix) {
58         SkMatrix toLocal = SkMatrix::Concat(*localMatrix, invViewMatrix);
59 
60         for (int p = 0; p < 4; ++p) {
61             SkPoint expectedPoint = quad.fDevice.point(p);
62             toLocal.mapPoints(&expectedPoint, 1);
63             SkPoint actualPoint = quad.fLocal.point(p);
64 
65             ASSERT_NEARLY_EQUAL(expectedPoint.fX, actualPoint.fX);
66             ASSERT_NEARLY_EQUAL(expectedPoint.fY, actualPoint.fY);
67         }
68     }
69 
70     // Confirm that the edge flags match, by mapping clip rect to drawRect space and
71     // comparing to the original draw rect edges
72     SkRect drawClip = invViewMatrix.mapRect(clipRect);
73     if (drawClip.fLeft > kDrawRect.fLeft) {
74         if (clipAA == GrAA::kYes) {
75             ASSERTF(quad.fEdgeFlags & GrQuadAAFlags::kLeft, "Expected left edge AA set");
76         } else {
77             ASSERTF(!(quad.fEdgeFlags & GrQuadAAFlags::kLeft), "Expected left edge AA unset");
78         }
79     }
80     if (drawClip.fRight < kDrawRect.fRight) {
81         if (clipAA == GrAA::kYes) {
82             ASSERTF(quad.fEdgeFlags & GrQuadAAFlags::kRight, "Expected right edge AA set");
83         } else {
84             ASSERTF(!(quad.fEdgeFlags & GrQuadAAFlags::kRight),  "Expected right edge AA unset");
85         }
86     }
87     if (drawClip.fTop > kDrawRect.fTop) {
88         if (clipAA == GrAA::kYes) {
89             ASSERTF(quad.fEdgeFlags & GrQuadAAFlags::kTop, "Expected top edge AA set");
90         } else {
91             ASSERTF(!(quad.fEdgeFlags & GrQuadAAFlags::kTop), "Expected top edge AA unset");
92         }
93     }
94     if (drawClip.fBottom < kDrawRect.fBottom) {
95         if (clipAA == GrAA::kYes) {
96             ASSERTF(quad.fEdgeFlags & GrQuadAAFlags::kBottom, "Expected bottom edge AA set");
97         } else {
98             ASSERTF(!(quad.fEdgeFlags & GrQuadAAFlags::kBottom), "Expected bottom edge AA unset");
99         }
100     }
101 }
102 
run_crop_fully_covered_test(skiatest::Reporter * r,GrAA clipAA,const SkMatrix & viewMatrix,const SkMatrix * localMatrix)103 static void run_crop_fully_covered_test(skiatest::Reporter* r, GrAA clipAA,
104                                         const SkMatrix& viewMatrix, const SkMatrix* localMatrix) {
105     // Should use run_crop_axis_aligned for rect transforms since that verifies more behavior
106     SkASSERT(!viewMatrix.rectStaysRect());
107 
108     // Test what happens when the geometry fully covers the crop rect. Given a fixed crop,
109     // use the provided view matrix to derive the "input" geometry that we know covers the crop.
110     SkMatrix invViewMatrix;
111     SkAssertResult(viewMatrix.invert(&invViewMatrix));
112 
113     SkRect containsCrop = kDrawRect; // Use kDrawRect as the crop rect for this test
114     containsCrop.outset(10.f, 10.f);
115     SkRect drawRect = invViewMatrix.mapRect(containsCrop);
116 
117     DrawQuad quad = {GrQuad::MakeFromRect(drawRect, viewMatrix),
118                      GrQuad::MakeFromRect(drawRect, localMatrix ? *localMatrix : SkMatrix::I()),
119                      clipAA == GrAA::kYes ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll};
120 
121     if (localMatrix) {
122         DrawQuad originalQuad = quad;
123 
124         bool exact = GrQuadUtils::CropToRect(kDrawRect, clipAA, &quad);
125         // Currently non-rect matrices don't know how to update local coordinates, so the crop
126         // doesn't know how to restrict itself and should leave the inputs unmodified
127         ASSERTF(!exact, "Expected crop to be not exact");
128         ASSERTF(quad.fEdgeFlags == originalQuad.fEdgeFlags,
129                 "Expected edge flags not to be modified");
130 
131         for (int i = 0; i < 4; ++i) {
132             ASSERT_NEARLY_EQUAL(originalQuad.fDevice.x(i), quad.fDevice.x(i));
133             ASSERT_NEARLY_EQUAL(originalQuad.fDevice.y(i), quad.fDevice.y(i));
134             ASSERT_NEARLY_EQUAL(originalQuad.fDevice.w(i), quad.fDevice.w(i));
135 
136             ASSERT_NEARLY_EQUAL(originalQuad.fLocal.x(i), quad.fLocal.x(i));
137             ASSERT_NEARLY_EQUAL(originalQuad.fLocal.y(i), quad.fLocal.y(i));
138             ASSERT_NEARLY_EQUAL(originalQuad.fLocal.w(i), quad.fLocal.w(i));
139         }
140     } else {
141         // Since no local coordinates were provided, and the input draw geometry is known to
142         // fully cover the crop rect, the quad should be updated to match cropRect exactly,
143         // unless it's perspective in which case we don't do anything since the code isn't
144         // numerically robust enough.
145         DrawQuad originalQuad = quad;
146         bool exact = GrQuadUtils::CropToRect(kDrawRect, clipAA, &quad, /* calc. local */ false);
147         if (originalQuad.fDevice.quadType() == GrQuad::Type::kPerspective) {
148             ASSERTF(!exact, "Expected no change for perspective");
149             for (int i = 0; i < 4; ++i) {
150                 ASSERTF(originalQuad.fDevice.x(i) == quad.fDevice.x(i));
151                 ASSERTF(originalQuad.fDevice.y(i) == quad.fDevice.y(i));
152                 ASSERTF(originalQuad.fDevice.w(i) == quad.fDevice.w(i));
153             }
154             return;
155         }
156 
157         ASSERTF(exact, "Expected crop to be exact");
158         GrQuadAAFlags expectedFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kAll
159                                                            : GrQuadAAFlags::kNone;
160         ASSERTF(expectedFlags == quad.fEdgeFlags,
161                 "Expected edge flags do not match clip AA setting");
162         ASSERTF(quad.fDevice.quadType() == GrQuad::Type::kAxisAligned, "Unexpected quad type");
163 
164         ASSERT_NEARLY_EQUAL(kDrawRect.fLeft, quad.fDevice.x(0));
165         ASSERT_NEARLY_EQUAL(kDrawRect.fTop, quad.fDevice.y(0));
166         ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(0));
167 
168         ASSERT_NEARLY_EQUAL(kDrawRect.fLeft, quad.fDevice.x(1));
169         ASSERT_NEARLY_EQUAL(kDrawRect.fBottom, quad.fDevice.y(1));
170         ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(1));
171 
172         ASSERT_NEARLY_EQUAL(kDrawRect.fRight, quad.fDevice.x(2));
173         ASSERT_NEARLY_EQUAL(kDrawRect.fTop, quad.fDevice.y(2));
174         ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(2));
175 
176         ASSERT_NEARLY_EQUAL(kDrawRect.fRight, quad.fDevice.x(3));
177         ASSERT_NEARLY_EQUAL(kDrawRect.fBottom, quad.fDevice.y(3));
178         ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(3));
179     }
180 }
181 
test_axis_aligned_all_clips(skiatest::Reporter * r,const SkMatrix & viewMatrix,const SkMatrix * localMatrix)182 static void test_axis_aligned_all_clips(skiatest::Reporter* r, const SkMatrix& viewMatrix,
183                                         const SkMatrix* localMatrix) {
184     static const float kInsideEdge = SkScalarAbs(kDrawRect.fLeft) - 1.f;
185     static const float kOutsideEdge = SkScalarAbs(kDrawRect.fBottom) + 1.f;
186     static const float kIntersectEdge = SkScalarAbs(kDrawRect.fTop) + 1.f;
187 
188     static const SkRect kInsideClipRect = SkRect::MakeLTRB(-kInsideEdge, -kInsideEdge,
189                                                            kInsideEdge, kInsideEdge);
190     static const SkRect kContainsClipRect = SkRect::MakeLTRB(-kOutsideEdge, -kOutsideEdge,
191                                                              kOutsideEdge, kOutsideEdge);
192     static const SkRect kXYAxesClipRect = SkRect::MakeLTRB(-kIntersectEdge, -kIntersectEdge,
193                                                            kIntersectEdge, kIntersectEdge);
194     static const SkRect kXAxisClipRect = SkRect::MakeLTRB(-kIntersectEdge, -kOutsideEdge,
195                                                           kIntersectEdge, kOutsideEdge);
196     static const SkRect kYAxisClipRect = SkRect::MakeLTRB(-kOutsideEdge, -kIntersectEdge,
197                                                           kOutsideEdge, kIntersectEdge);
198 
199     run_crop_axis_aligned_test(r, kInsideClipRect, GrAA::kNo, viewMatrix, localMatrix);
200     run_crop_axis_aligned_test(r, kContainsClipRect, GrAA::kNo, viewMatrix, localMatrix);
201     run_crop_axis_aligned_test(r, kXYAxesClipRect, GrAA::kNo, viewMatrix, localMatrix);
202     run_crop_axis_aligned_test(r, kXAxisClipRect, GrAA::kNo, viewMatrix, localMatrix);
203     run_crop_axis_aligned_test(r, kYAxisClipRect, GrAA::kNo, viewMatrix, localMatrix);
204 
205     run_crop_axis_aligned_test(r, kInsideClipRect, GrAA::kYes, viewMatrix, localMatrix);
206     run_crop_axis_aligned_test(r, kContainsClipRect, GrAA::kYes, viewMatrix, localMatrix);
207     run_crop_axis_aligned_test(r, kXYAxesClipRect, GrAA::kYes, viewMatrix, localMatrix);
208     run_crop_axis_aligned_test(r, kXAxisClipRect, GrAA::kYes, viewMatrix, localMatrix);
209     run_crop_axis_aligned_test(r, kYAxisClipRect, GrAA::kYes, viewMatrix, localMatrix);
210 }
211 
test_axis_aligned(skiatest::Reporter * r,const SkMatrix & viewMatrix)212 static void test_axis_aligned(skiatest::Reporter* r, const SkMatrix& viewMatrix) {
213     test_axis_aligned_all_clips(r, viewMatrix, nullptr);
214 
215     SkMatrix normalized = SkMatrix::RectToRect(kDrawRect, SkRect::MakeWH(1.f, 1.f));
216     test_axis_aligned_all_clips(r, viewMatrix, &normalized);
217 
218     SkMatrix rotated;
219     rotated.setRotate(45.f);
220     test_axis_aligned_all_clips(r, viewMatrix, &rotated);
221 
222     SkMatrix perspective;
223     perspective.setPerspY(0.001f);
224     perspective.setSkewX(8.f / 25.f);
225     test_axis_aligned_all_clips(r, viewMatrix, &perspective);
226 }
227 
test_crop_fully_covered(skiatest::Reporter * r,const SkMatrix & viewMatrix)228 static void test_crop_fully_covered(skiatest::Reporter* r, const SkMatrix& viewMatrix) {
229     run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, nullptr);
230     run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, nullptr);
231 
232     SkMatrix normalized = SkMatrix::RectToRect(kDrawRect, SkRect::MakeWH(1.f, 1.f));
233     run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &normalized);
234     run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &normalized);
235 
236     SkMatrix rotated;
237     rotated.setRotate(45.f);
238     run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &rotated);
239     run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &rotated);
240 
241     SkMatrix perspective;
242     perspective.setPerspY(0.001f);
243     perspective.setSkewX(8.f / 25.f);
244     run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &perspective);
245     run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &perspective);
246 }
247 
TEST(AxisAligned)248 TEST(AxisAligned) {
249     test_axis_aligned(r, SkMatrix::I());
250     test_axis_aligned(r, SkMatrix::Scale(-1.f, 1.f));
251     test_axis_aligned(r, SkMatrix::Scale(1.f, -1.f));
252 
253     SkMatrix rotation;
254     rotation.setRotate(90.f);
255     test_axis_aligned(r, rotation);
256     rotation.setRotate(180.f);
257     test_axis_aligned(r, rotation);
258     rotation.setRotate(270.f);
259     test_axis_aligned(r, rotation);
260 }
261 
TEST(FullyCovered)262 TEST(FullyCovered) {
263     SkMatrix rotation;
264     rotation.setRotate(34.f);
265     test_crop_fully_covered(r, rotation);
266 
267     SkMatrix skew;
268     skew.setSkewX(0.3f);
269     skew.setSkewY(0.04f);
270     test_crop_fully_covered(r, skew);
271 
272     SkMatrix perspective;
273     perspective.setPerspX(0.001f);
274     perspective.setSkewY(8.f / 25.f);
275     test_crop_fully_covered(r, perspective);
276 }
277