blob: 303230778faabf45d75a28fec139de89f48ff117 [file] [log] [blame]
Brian Salomona33b67c2018-05-17 10:42:14 -04001/*
2 * Copyright 2018 Google Inc.
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 "GrQuad.h"
9
Michael Ludwig6bee7762018-10-19 09:50:36 -040010#include "GrTypesPriv.h"
11
Michael Ludwig1f7e4382018-10-19 09:36:57 -040012///////////////////////////////////////////////////////////////////////////////////////////////////
13// Functions for identifying the quad type from its coordinates, which are kept debug-only since
14// production code should rely on the matrix to derive the quad type more efficiently. These are
15// useful in asserts that the quad type is as expected.
16///////////////////////////////////////////////////////////////////////////////////////////////////
17
18#ifdef SK_DEBUG
19// Allow some tolerance from floating point matrix transformations, but SkScalarNearlyEqual doesn't
20// support comparing infinity, and coords_form_rect should return true for infinite edges
21#define NEARLY_EQUAL(f1, f2) (f1 == f2 || SkScalarNearlyEqual(f1, f2, 1e-5f))
Michael Ludwig69858532018-11-28 15:34:34 -050022// Similarly, support infinite rectangles by looking at the sign of infinities
23static bool dot_nearly_zero(const SkVector& e1, const SkVector& e2) {
24 static constexpr auto dot = SkPoint::DotProduct;
25 static constexpr auto sign = SkScalarSignAsScalar;
26
27 SkScalar dotValue = dot(e1, e2);
28 if (SkScalarIsNaN(dotValue)) {
29 // Form vectors from the signs of infinities, and check their dot product
30 dotValue = dot({sign(e1.fX), sign(e1.fY)}, {sign(e2.fX), sign(e2.fY)});
31 }
32
Michael Ludwig94633de2019-01-22 10:24:55 -050033 return SkScalarNearlyZero(dotValue, 1e-3f);
Michael Ludwig69858532018-11-28 15:34:34 -050034}
Michael Ludwig1f7e4382018-10-19 09:36:57 -040035
36// This is not the most performance critical function; code using GrQuad should rely on the faster
37// quad type from matrix path, so this will only be called as part of SkASSERT.
38static bool coords_form_rect(const float xs[4], const float ys[4]) {
39 return (NEARLY_EQUAL(xs[0], xs[1]) && NEARLY_EQUAL(xs[2], xs[3]) &&
40 NEARLY_EQUAL(ys[0], ys[2]) && NEARLY_EQUAL(ys[1], ys[3])) ||
41 (NEARLY_EQUAL(xs[0], xs[2]) && NEARLY_EQUAL(xs[1], xs[3]) &&
42 NEARLY_EQUAL(ys[0], ys[1]) && NEARLY_EQUAL(ys[2], ys[3]));
43}
44
Michael Ludwigf995c052018-11-26 15:24:29 -050045static bool coords_rectilinear(const float xs[4], const float ys[4]) {
Michael Ludwig7a8d08d2019-01-02 14:36:15 -050046 SkVector e0{xs[1] - xs[0], ys[1] - ys[0]}; // connects to e1 and e2(repeat)
Michael Ludwigf995c052018-11-26 15:24:29 -050047 SkVector e1{xs[3] - xs[1], ys[3] - ys[1]}; // connects to e0(repeat) and e3
48 SkVector e2{xs[0] - xs[2], ys[0] - ys[2]}; // connects to e0 and e3(repeat)
49 SkVector e3{xs[2] - xs[3], ys[2] - ys[3]}; // connects to e1(repeat) and e2
50
Michael Ludwig7a8d08d2019-01-02 14:36:15 -050051 e0.normalize();
52 e1.normalize();
53 e2.normalize();
54 e3.normalize();
55
Michael Ludwig69858532018-11-28 15:34:34 -050056 return dot_nearly_zero(e0, e1) && dot_nearly_zero(e1, e3) &&
57 dot_nearly_zero(e2, e0) && dot_nearly_zero(e3, e2);
Michael Ludwigf995c052018-11-26 15:24:29 -050058}
59
Michael Ludwig1f7e4382018-10-19 09:36:57 -040060GrQuadType GrQuad::quadType() const {
61 // Since GrQuad applies any perspective information at construction time, there's only two
62 // types to choose from.
Michael Ludwigf995c052018-11-26 15:24:29 -050063 if (coords_form_rect(fX, fY)) {
64 return GrQuadType::kRect;
65 } else if (coords_rectilinear(fX, fY)) {
66 return GrQuadType::kRectilinear;
67 } else {
68 return GrQuadType::kStandard;
69 }
Michael Ludwig1f7e4382018-10-19 09:36:57 -040070}
71
72GrQuadType GrPerspQuad::quadType() const {
73 if (this->hasPerspective()) {
Michael Ludwigc182b942018-11-16 10:27:51 -050074 return GrQuadType::kPerspective;
Michael Ludwig1f7e4382018-10-19 09:36:57 -040075 } else {
76 // Rect or standard quad, can ignore w since they are all ones
Michael Ludwigf995c052018-11-26 15:24:29 -050077 if (coords_form_rect(fX, fY)) {
78 return GrQuadType::kRect;
79 } else if (coords_rectilinear(fX, fY)) {
80 return GrQuadType::kRectilinear;
81 } else {
82 return GrQuadType::kStandard;
83 }
Michael Ludwig1f7e4382018-10-19 09:36:57 -040084 }
85}
86#endif
87
88///////////////////////////////////////////////////////////////////////////////////////////////////
89
90static bool aa_affects_rect(float ql, float qt, float qr, float qb) {
91 return !SkScalarIsInt(ql) || !SkScalarIsInt(qr) || !SkScalarIsInt(qt) || !SkScalarIsInt(qb);
92}
93
Michael Ludwige9c57d32019-02-13 13:39:39 -050094static void map_rect_translate_scale(const SkRect& rect, const SkMatrix& m,
95 Sk4f* xs, Sk4f* ys) {
96 SkMatrix::TypeMask tm = m.getType();
97 SkASSERT(tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask));
98
99 Sk4f r = Sk4f::Load(&rect);
100 if (tm > SkMatrix::kIdentity_Mask) {
101 const Sk4f t(m.getTranslateX(), m.getTranslateY(), m.getTranslateX(), m.getTranslateY());
102 if (tm <= SkMatrix::kTranslate_Mask) {
103 r += t;
104 } else {
105 const Sk4f s(m.getScaleX(), m.getScaleY(), m.getScaleX(), m.getScaleY());
106 r = r * s + t;
107 }
108 }
109 *xs = SkNx_shuffle<0, 0, 2, 2>(r);
110 *ys = SkNx_shuffle<1, 3, 1, 3>(r);
111}
112
113static void map_quad_general(const Sk4f& qx, const Sk4f& qy, const SkMatrix& m,
114 Sk4f* xs, Sk4f* ys, Sk4f* ws) {
115 static constexpr auto fma = SkNx_fma<4, float>;
116 *xs = fma(m.getScaleX(), qx, fma(m.getSkewX(), qy, m.getTranslateX()));
117 *ys = fma(m.getSkewY(), qx, fma(m.getScaleY(), qy, m.getTranslateY()));
118 if (m.hasPerspective()) {
119 Sk4f w = fma(m.getPerspX(), qx, fma(m.getPerspY(), qy, m.get(SkMatrix::kMPersp2)));
120 if (ws) {
121 // Output the calculated w coordinates
122 *ws = w;
123 } else {
124 // Apply perspective division immediately
125 Sk4f iw = w.invert();
126 *xs *= iw;
127 *ys *= iw;
128 }
129 } else if (ws) {
130 *ws = 1.f;
131 }
132}
133
134static void map_rect_general(const SkRect& rect, const SkMatrix& matrix,
135 Sk4f* xs, Sk4f* ys, Sk4f* ws) {
136 Sk4f rx(rect.fLeft, rect.fLeft, rect.fRight, rect.fRight);
137 Sk4f ry(rect.fTop, rect.fBottom, rect.fTop, rect.fBottom);
138 map_quad_general(rx, ry, matrix, xs, ys, ws);
139}
140
Michael Ludwig009b92e2019-02-15 16:03:53 -0500141// Rearranges (top-left, top-right, bottom-right, bottom-left) ordered skQuadPts into xs and ys
142// ordered (top-left, bottom-left, top-right, bottom-right)
143static void rearrange_sk_to_gr_points(const SkPoint skQuadPts[4], Sk4f* xs, Sk4f* ys) {
144 *xs = Sk4f(skQuadPts[0].fX, skQuadPts[3].fX, skQuadPts[1].fX, skQuadPts[2].fX);
145 *ys = Sk4f(skQuadPts[0].fY, skQuadPts[3].fY, skQuadPts[1].fY, skQuadPts[2].fY);
146}
147
Michael Ludwig6bee7762018-10-19 09:50:36 -0400148template <typename Q>
149void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags,
150 const Q& quad, GrQuadType knownType,
151 GrAAType* outAAType, GrQuadAAFlags* outEdgeFlags) {
152 // Most cases will keep the requested types unchanged
153 *outAAType = requestedAAType;
154 *outEdgeFlags = requestedEdgeFlags;
155
156 switch (requestedAAType) {
157 // When aa type is coverage, disable AA if the edge configuration doesn't actually need it
158 case GrAAType::kCoverage:
159 if (requestedEdgeFlags == GrQuadAAFlags::kNone) {
160 // Turn off anti-aliasing
161 *outAAType = GrAAType::kNone;
162 } else {
163 // For coverage AA, if the quad is a rect and it lines up with pixel boundaries
164 // then overall aa and per-edge aa can be completely disabled
Michael Ludwigc182b942018-11-16 10:27:51 -0500165 if (knownType == GrQuadType::kRect && !quad.aaHasEffectOnRect()) {
Michael Ludwig6bee7762018-10-19 09:50:36 -0400166 *outAAType = GrAAType::kNone;
167 *outEdgeFlags = GrQuadAAFlags::kNone;
168 }
169 }
170 break;
171 // For no or msaa anti aliasing, override the edge flags since edge flags only make sense
172 // when coverage aa is being used.
173 case GrAAType::kNone:
174 *outEdgeFlags = GrQuadAAFlags::kNone;
175 break;
176 case GrAAType::kMSAA:
177 *outEdgeFlags = GrQuadAAFlags::kAll;
178 break;
179 case GrAAType::kMixedSamples:
180 SK_ABORT("Should not use mixed sample AA with edge AA flags");
181 break;
182 }
183};
184
185// Instantiate GrResolve... for GrQuad and GrPerspQuad
186template void GrResolveAATypeForQuad(GrAAType, GrQuadAAFlags, const GrQuad&, GrQuadType,
187 GrAAType*, GrQuadAAFlags*);
188template void GrResolveAATypeForQuad(GrAAType, GrQuadAAFlags, const GrPerspQuad&, GrQuadType,
189 GrAAType*, GrQuadAAFlags*);
190
Michael Ludwig1f7e4382018-10-19 09:36:57 -0400191GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix) {
192 if (matrix.rectStaysRect()) {
Michael Ludwigc182b942018-11-16 10:27:51 -0500193 return GrQuadType::kRect;
Michael Ludwigf995c052018-11-26 15:24:29 -0500194 } else if (matrix.preservesRightAngles()) {
195 return GrQuadType::kRectilinear;
Michael Ludwig1f7e4382018-10-19 09:36:57 -0400196 } else if (matrix.hasPerspective()) {
Michael Ludwigc182b942018-11-16 10:27:51 -0500197 return GrQuadType::kPerspective;
Michael Ludwig1f7e4382018-10-19 09:36:57 -0400198 } else {
Michael Ludwigc182b942018-11-16 10:27:51 -0500199 return GrQuadType::kStandard;
Michael Ludwig1f7e4382018-10-19 09:36:57 -0400200 }
201}
202
Michael Ludwige9c57d32019-02-13 13:39:39 -0500203GrQuad GrQuad::MakeFromRect(const SkRect& rect, const SkMatrix& m) {
204 Sk4f x, y;
Brian Salomona33b67c2018-05-17 10:42:14 -0400205 SkMatrix::TypeMask tm = m.getType();
206 if (tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
Michael Ludwige9c57d32019-02-13 13:39:39 -0500207 map_rect_translate_scale(rect, m, &x, &y);
Brian Salomona33b67c2018-05-17 10:42:14 -0400208 } else {
Michael Ludwige9c57d32019-02-13 13:39:39 -0500209 map_rect_general(rect, m, &x, &y, nullptr);
Brian Salomona33b67c2018-05-17 10:42:14 -0400210 }
Michael Ludwige9c57d32019-02-13 13:39:39 -0500211 return GrQuad(x, y);
Brian Salomona33b67c2018-05-17 10:42:14 -0400212}
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400213
Michael Ludwig009b92e2019-02-15 16:03:53 -0500214GrQuad GrQuad::MakeFromSkQuad(const SkPoint pts[4], const SkMatrix& matrix) {
215 Sk4f xs, ys;
216 rearrange_sk_to_gr_points(pts, &xs, &ys);
217 if (matrix.isIdentity()) {
218 return GrQuad(xs, ys);
219 } else {
220 Sk4f mx, my;
221 map_quad_general(xs, ys, matrix, &mx, &my, nullptr);
222 return GrQuad(mx, my);
223 }
224}
225
Michael Ludwig1f7e4382018-10-19 09:36:57 -0400226bool GrQuad::aaHasEffectOnRect() const {
Michael Ludwigc182b942018-11-16 10:27:51 -0500227 SkASSERT(this->quadType() == GrQuadType::kRect);
Michael Ludwig1f7e4382018-10-19 09:36:57 -0400228 return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]);
229}
230
Michael Ludwigc96fc372019-01-08 15:46:15 -0500231// Private constructor used by GrQuadList to quickly fill in a quad's values from the channel arrays
232GrPerspQuad::GrPerspQuad(const float* xs, const float* ys, const float* ws) {
233 memcpy(fX, xs, 4 * sizeof(float));
234 memcpy(fY, ys, 4 * sizeof(float));
235 memcpy(fW, ws, 4 * sizeof(float));
236}
237
Michael Ludwige9c57d32019-02-13 13:39:39 -0500238GrPerspQuad GrPerspQuad::MakeFromRect(const SkRect& rect, const SkMatrix& m) {
239 Sk4f x, y, w;
240 SkMatrix::TypeMask tm = m.getType();
241 if (tm <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
242 map_rect_translate_scale(rect, m, &x, &y);
243 w = 1.f;
244 } else {
245 map_rect_general(rect, m, &x, &y, &w);
246 }
247 return GrPerspQuad(x, y, w);
248}
249
Michael Ludwig009b92e2019-02-15 16:03:53 -0500250GrPerspQuad GrPerspQuad::MakeFromSkQuad(const SkPoint pts[4], const SkMatrix& matrix) {
251 Sk4f xs, ys;
252 rearrange_sk_to_gr_points(pts, &xs, &ys);
253 if (matrix.isIdentity()) {
254 return GrPerspQuad(xs, ys, 1.f);
255 } else {
256 Sk4f mx, my, mw;
257 map_quad_general(xs, ys, matrix, &mx, &my, &mw);
258 return GrPerspQuad(mx, my, mw);
259 }
260}
261
Michael Ludwig1f7e4382018-10-19 09:36:57 -0400262bool GrPerspQuad::aaHasEffectOnRect() const {
Michael Ludwigc182b942018-11-16 10:27:51 -0500263 SkASSERT(this->quadType() == GrQuadType::kRect);
Michael Ludwig1f7e4382018-10-19 09:36:57 -0400264 // If rect, ws must all be 1s so no need to divide
265 return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]);
266}