blob: 2807632fb99b9699c71cb4a96598904890ed24c8 [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
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050029 DrawQuad quad = {GrQuad::MakeFromRect(kDrawRect, viewMatrix),
30 GrQuad::MakeFromRect(kDrawRect, localMatrix ? *localMatrix : SkMatrix::I()),
31 clipAA == GrAA::kYes ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll};
Michael Ludwig61328202019-06-19 14:48:58 +000032
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050033 bool exact = GrQuadUtils::CropToRect(clipRect, clipAA, &quad, /* calc. locals */ !!localMatrix);
Michael Ludwig61328202019-06-19 14:48:58 +000034 ASSERTF(exact, "Expected exact crop");
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050035 ASSERTF(quad.fDevice.quadType() == GrQuad::Type::kAxisAligned,
Michael Ludwig61328202019-06-19 14:48:58 +000036 "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
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050042 SkRect actualBounds = quad.fDevice.bounds();
Michael Ludwig61328202019-06-19 14:48:58 +000043 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) {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050056 SkPoint expectedPoint = quad.fDevice.point(p);
Michael Ludwig61328202019-06-19 14:48:58 +000057 toLocal.mapPoints(&expectedPoint, 1);
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050058 SkPoint actualPoint = quad.fLocal.point(p);
Michael Ludwig61328202019-06-19 14:48:58 +000059
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) {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050070 ASSERTF(quad.fEdgeFlags & GrQuadAAFlags::kLeft, "Expected left edge AA set");
Michael Ludwig61328202019-06-19 14:48:58 +000071 } else {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050072 ASSERTF(!(quad.fEdgeFlags & GrQuadAAFlags::kLeft), "Expected left edge AA unset");
Michael Ludwig61328202019-06-19 14:48:58 +000073 }
74 }
75 if (drawClip.fRight < kDrawRect.fRight) {
76 if (clipAA == GrAA::kYes) {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050077 ASSERTF(quad.fEdgeFlags & GrQuadAAFlags::kRight, "Expected right edge AA set");
Michael Ludwig61328202019-06-19 14:48:58 +000078 } else {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050079 ASSERTF(!(quad.fEdgeFlags & GrQuadAAFlags::kRight), "Expected right edge AA unset");
Michael Ludwig61328202019-06-19 14:48:58 +000080 }
81 }
82 if (drawClip.fTop > kDrawRect.fTop) {
83 if (clipAA == GrAA::kYes) {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050084 ASSERTF(quad.fEdgeFlags & GrQuadAAFlags::kTop, "Expected top edge AA set");
Michael Ludwig61328202019-06-19 14:48:58 +000085 } else {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050086 ASSERTF(!(quad.fEdgeFlags & GrQuadAAFlags::kTop), "Expected top edge AA unset");
Michael Ludwig61328202019-06-19 14:48:58 +000087 }
88 }
89 if (drawClip.fBottom < kDrawRect.fBottom) {
90 if (clipAA == GrAA::kYes) {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050091 ASSERTF(quad.fEdgeFlags & GrQuadAAFlags::kBottom, "Expected bottom edge AA set");
Michael Ludwig61328202019-06-19 14:48:58 +000092 } else {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -050093 ASSERTF(!(quad.fEdgeFlags & GrQuadAAFlags::kBottom), "Expected bottom edge AA unset");
Michael Ludwig61328202019-06-19 14:48:58 +000094 }
95 }
96}
97
98static 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
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500112 DrawQuad quad = {GrQuad::MakeFromRect(drawRect, viewMatrix),
113 GrQuad::MakeFromRect(drawRect, localMatrix ? *localMatrix : SkMatrix::I()),
114 clipAA == GrAA::kYes ? GrQuadAAFlags::kNone : GrQuadAAFlags::kAll};
Michael Ludwig61328202019-06-19 14:48:58 +0000115
116 if (localMatrix) {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500117 DrawQuad originalQuad = quad;
Michael Ludwig61328202019-06-19 14:48:58 +0000118
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500119 bool exact = GrQuadUtils::CropToRect(kDrawRect, clipAA, &quad);
Michael Ludwig61328202019-06-19 14:48:58 +0000120 // 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");
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500123 ASSERTF(quad.fEdgeFlags == originalQuad.fEdgeFlags,
124 "Expected edge flags not to be modified");
Michael Ludwig61328202019-06-19 14:48:58 +0000125
126 for (int i = 0; i < 4; ++i) {
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500127 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));
Michael Ludwig61328202019-06-19 14:48:58 +0000130
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500131 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));
Michael Ludwig61328202019-06-19 14:48:58 +0000134 }
135 } else {
136 // Since no local coordinates were provided, and the input draw geometry is known to
Michael Ludwig65299902021-05-13 12:02:23 -0400137 // fully cover the crop rect, the quad should be updated to match cropRect exactly,
138 // unless it's perspective in which case we don't do anything since the code isn't
139 // numerically robust enough.
140 DrawQuad originalQuad = quad;
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500141 bool exact = GrQuadUtils::CropToRect(kDrawRect, clipAA, &quad, /* calc. local */ false);
Michael Ludwig65299902021-05-13 12:02:23 -0400142 if (originalQuad.fDevice.quadType() == GrQuad::Type::kPerspective) {
143 ASSERTF(!exact, "Expected no change for perspective");
144 for (int i = 0; i < 4; ++i) {
145 ASSERTF(originalQuad.fDevice.x(i) == quad.fDevice.x(i));
146 ASSERTF(originalQuad.fDevice.y(i) == quad.fDevice.y(i));
147 ASSERTF(originalQuad.fDevice.w(i) == quad.fDevice.w(i));
148 }
149 return;
150 }
Michael Ludwig61328202019-06-19 14:48:58 +0000151
Michael Ludwig65299902021-05-13 12:02:23 -0400152 ASSERTF(exact, "Expected crop to be exact");
Michael Ludwig61328202019-06-19 14:48:58 +0000153 GrQuadAAFlags expectedFlags = clipAA == GrAA::kYes ? GrQuadAAFlags::kAll
154 : GrQuadAAFlags::kNone;
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500155 ASSERTF(expectedFlags == quad.fEdgeFlags,
156 "Expected edge flags do not match clip AA setting");
157 ASSERTF(quad.fDevice.quadType() == GrQuad::Type::kAxisAligned, "Unexpected quad type");
Michael Ludwig61328202019-06-19 14:48:58 +0000158
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500159 ASSERT_NEARLY_EQUAL(kDrawRect.fLeft, quad.fDevice.x(0));
160 ASSERT_NEARLY_EQUAL(kDrawRect.fTop, quad.fDevice.y(0));
161 ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(0));
Michael Ludwig61328202019-06-19 14:48:58 +0000162
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500163 ASSERT_NEARLY_EQUAL(kDrawRect.fLeft, quad.fDevice.x(1));
164 ASSERT_NEARLY_EQUAL(kDrawRect.fBottom, quad.fDevice.y(1));
165 ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(1));
Michael Ludwig61328202019-06-19 14:48:58 +0000166
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500167 ASSERT_NEARLY_EQUAL(kDrawRect.fRight, quad.fDevice.x(2));
168 ASSERT_NEARLY_EQUAL(kDrawRect.fTop, quad.fDevice.y(2));
169 ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(2));
Michael Ludwig61328202019-06-19 14:48:58 +0000170
Michael Ludwig6b45c5d2020-02-07 09:56:38 -0500171 ASSERT_NEARLY_EQUAL(kDrawRect.fRight, quad.fDevice.x(3));
172 ASSERT_NEARLY_EQUAL(kDrawRect.fBottom, quad.fDevice.y(3));
173 ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(3));
Michael Ludwig61328202019-06-19 14:48:58 +0000174 }
175}
176
177static void test_axis_aligned_all_clips(skiatest::Reporter* r, const SkMatrix& viewMatrix,
178 const SkMatrix* localMatrix) {
179 static const float kInsideEdge = SkScalarAbs(kDrawRect.fLeft) - 1.f;
180 static const float kOutsideEdge = SkScalarAbs(kDrawRect.fBottom) + 1.f;
181 static const float kIntersectEdge = SkScalarAbs(kDrawRect.fTop) + 1.f;
182
183 static const SkRect kInsideClipRect = SkRect::MakeLTRB(-kInsideEdge, -kInsideEdge,
184 kInsideEdge, kInsideEdge);
185 static const SkRect kContainsClipRect = SkRect::MakeLTRB(-kOutsideEdge, -kOutsideEdge,
186 kOutsideEdge, kOutsideEdge);
187 static const SkRect kXYAxesClipRect = SkRect::MakeLTRB(-kIntersectEdge, -kIntersectEdge,
188 kIntersectEdge, kIntersectEdge);
189 static const SkRect kXAxisClipRect = SkRect::MakeLTRB(-kIntersectEdge, -kOutsideEdge,
190 kIntersectEdge, kOutsideEdge);
191 static const SkRect kYAxisClipRect = SkRect::MakeLTRB(-kOutsideEdge, -kIntersectEdge,
192 kOutsideEdge, kIntersectEdge);
193
194 run_crop_axis_aligned_test(r, kInsideClipRect, GrAA::kNo, viewMatrix, localMatrix);
195 run_crop_axis_aligned_test(r, kContainsClipRect, GrAA::kNo, viewMatrix, localMatrix);
196 run_crop_axis_aligned_test(r, kXYAxesClipRect, GrAA::kNo, viewMatrix, localMatrix);
197 run_crop_axis_aligned_test(r, kXAxisClipRect, GrAA::kNo, viewMatrix, localMatrix);
198 run_crop_axis_aligned_test(r, kYAxisClipRect, GrAA::kNo, viewMatrix, localMatrix);
199
200 run_crop_axis_aligned_test(r, kInsideClipRect, GrAA::kYes, viewMatrix, localMatrix);
201 run_crop_axis_aligned_test(r, kContainsClipRect, GrAA::kYes, viewMatrix, localMatrix);
202 run_crop_axis_aligned_test(r, kXYAxesClipRect, GrAA::kYes, viewMatrix, localMatrix);
203 run_crop_axis_aligned_test(r, kXAxisClipRect, GrAA::kYes, viewMatrix, localMatrix);
204 run_crop_axis_aligned_test(r, kYAxisClipRect, GrAA::kYes, viewMatrix, localMatrix);
205}
206
207static void test_axis_aligned(skiatest::Reporter* r, const SkMatrix& viewMatrix) {
208 test_axis_aligned_all_clips(r, viewMatrix, nullptr);
209
Mike Reed2ac6ce82021-01-15 12:26:22 -0500210 SkMatrix normalized = SkMatrix::RectToRect(kDrawRect, SkRect::MakeWH(1.f, 1.f));
Michael Ludwig61328202019-06-19 14:48:58 +0000211 test_axis_aligned_all_clips(r, viewMatrix, &normalized);
212
213 SkMatrix rotated;
214 rotated.setRotate(45.f);
215 test_axis_aligned_all_clips(r, viewMatrix, &rotated);
216
217 SkMatrix perspective;
218 perspective.setPerspY(0.001f);
219 perspective.setSkewX(8.f / 25.f);
220 test_axis_aligned_all_clips(r, viewMatrix, &perspective);
221}
222
223static void test_crop_fully_covered(skiatest::Reporter* r, const SkMatrix& viewMatrix) {
224 run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, nullptr);
225 run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, nullptr);
226
Mike Reed2ac6ce82021-01-15 12:26:22 -0500227 SkMatrix normalized = SkMatrix::RectToRect(kDrawRect, SkRect::MakeWH(1.f, 1.f));
Michael Ludwig61328202019-06-19 14:48:58 +0000228 run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &normalized);
229 run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &normalized);
230
231 SkMatrix rotated;
232 rotated.setRotate(45.f);
233 run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &rotated);
234 run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &rotated);
235
236 SkMatrix perspective;
237 perspective.setPerspY(0.001f);
238 perspective.setSkewX(8.f / 25.f);
239 run_crop_fully_covered_test(r, GrAA::kNo, viewMatrix, &perspective);
240 run_crop_fully_covered_test(r, GrAA::kYes, viewMatrix, &perspective);
241}
242
243TEST(AxisAligned) {
244 test_axis_aligned(r, SkMatrix::I());
Mike Reed1f607332020-05-21 12:11:27 -0400245 test_axis_aligned(r, SkMatrix::Scale(-1.f, 1.f));
246 test_axis_aligned(r, SkMatrix::Scale(1.f, -1.f));
Michael Ludwig61328202019-06-19 14:48:58 +0000247
248 SkMatrix rotation;
249 rotation.setRotate(90.f);
250 test_axis_aligned(r, rotation);
251 rotation.setRotate(180.f);
252 test_axis_aligned(r, rotation);
253 rotation.setRotate(270.f);
254 test_axis_aligned(r, rotation);
255}
256
257TEST(FullyCovered) {
258 SkMatrix rotation;
259 rotation.setRotate(34.f);
260 test_crop_fully_covered(r, rotation);
261
262 SkMatrix skew;
263 skew.setSkewX(0.3f);
264 skew.setSkewY(0.04f);
265 test_crop_fully_covered(r, skew);
266
267 SkMatrix perspective;
268 perspective.setPerspX(0.001f);
269 perspective.setSkewY(8.f / 25.f);
270 test_crop_fully_covered(r, perspective);
271}