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