blob: 36fafe4899732931e29bcf8c4a064164690bdc94 [file] [log] [blame]
Michael Ludwig61328202019-06-19 14:48:58 +00001/*
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.
22static const SkRect kDrawRect = SkRect::MakeLTRB(-5.f, -6.f, 10.f, 11.f);
23
24static 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
99static 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
167static 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
197static 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
214static 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
235TEST(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
249TEST(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}