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