Flutter Engine
The Flutter Engine
GrQuadCropTest.cpp
Go to the documentation of this file.
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
10#include "include/core/SkRect.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.
27static const SkRect kDrawRect = SkRect::MakeLTRB(-5.f, -6.f, 10.f, 11.f);
28
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()),
37
38 bool exact = GrQuadUtils::CropToRect(clipRect, clipAA, &quad, /* calc. locals */ !!localMatrix);
39 ASSERTF(exact, "Expected exact crop");
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
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()),
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
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
166 ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(0));
167
170 ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(1));
171
174 ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(2));
175
178 ASSERT_NEARLY_EQUAL(1.f, quad.fDevice.w(3));
179 }
180}
181
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
212static 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
228static 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
248TEST(AxisAligned) {
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
262TEST(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);
271
272 SkMatrix perspective;
273 perspective.setPerspX(0.001f);
274 perspective.setSkewY(8.f / 25.f);
275 test_crop_fully_covered(r, perspective);
276}
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
static void test_axis_aligned_all_clips(skiatest::Reporter *r, const SkMatrix &viewMatrix, const SkMatrix *localMatrix)
static const SkRect kDrawRect
#define ASSERTF(cond,...)
static void run_crop_fully_covered_test(skiatest::Reporter *r, GrAA clipAA, const SkMatrix &viewMatrix, const SkMatrix *localMatrix)
static void test_crop_fully_covered(skiatest::Reporter *r, const SkMatrix &viewMatrix)
#define TEST(name)
static void test_axis_aligned(skiatest::Reporter *r, const SkMatrix &viewMatrix)
static void run_crop_axis_aligned_test(skiatest::Reporter *r, const SkRect &clipRect, GrAA clipAA, const SkMatrix &viewMatrix, const SkMatrix *localMatrix)
#define ASSERT_NEARLY_EQUAL(expected, actual)
GrQuadAAFlags
Definition: GrTypesPriv.h:247
GrAA
Definition: GrTypesPriv.h:173
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define SkScalarAbs(x)
Definition: SkScalar.h:39
float y(int i) const
Definition: GrQuad.h:109
Type quadType() const
Definition: GrQuad.h:118
static GrQuad MakeFromRect(const SkRect &, const SkMatrix &)
Definition: GrQuad.cpp:107
float w(int i) const
Definition: GrQuad.h:110
float x(int i) const
Definition: GrQuad.h:108
SkPoint point(int i) const
Definition: GrQuad.h:69
SkRect bounds() const
Definition: GrQuad.h:81
static SkMatrix Scale(SkScalar sx, SkScalar sy)
Definition: SkMatrix.h:75
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition: SkMatrix.h:157
SkMatrix & setSkewX(SkScalar v)
Definition: SkMatrix.h:518
SkMatrix & setPerspX(SkScalar v)
Definition: SkMatrix.h:537
void mapPoints(SkPoint dst[], const SkPoint src[], int count) const
Definition: SkMatrix.cpp:770
static SkMatrix Concat(const SkMatrix &a, const SkMatrix &b)
Definition: SkMatrix.h:1775
bool invert(SkMatrix *inverse) const
Definition: SkMatrix.h:1206
bool rectStaysRect() const
Definition: SkMatrix.h:271
SkMatrix & setRotate(SkScalar degrees, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:452
SkMatrix & setPerspY(SkScalar v)
Definition: SkMatrix.h:544
static const SkMatrix & I()
Definition: SkMatrix.cpp:1544
bool mapRect(SkRect *dst, const SkRect &src, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition: SkMatrix.cpp:1141
SkMatrix & setSkewY(SkScalar v)
Definition: SkMatrix.h:512
bool CropToRect(const SkRect &cropRect, GrAA cropAA, DrawQuad *quad, bool computeLocal)
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
GrQuad fLocal
Definition: GrQuad.h:186
GrQuad fDevice
Definition: GrQuad.h:185
GrQuadAAFlags fEdgeFlags
Definition: GrQuad.h:187
float fX
x-axis value
Definition: SkPoint_impl.h:164
float fY
y-axis value
Definition: SkPoint_impl.h:165
SkScalar fBottom
larger y-axis bounds
Definition: extension.cpp:17
bool intersect(const SkRect &r)
Definition: SkRect.cpp:114
SkScalar fLeft
smaller x-axis bounds
Definition: extension.cpp:14
void outset(float dx, float dy)
Definition: SkRect.h:1077
SkScalar fRight
larger x-axis bounds
Definition: extension.cpp:16
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646
SkScalar fTop
smaller y-axis bounds
Definition: extension.cpp:15