blob: f0fc1fd4ee7aa73968c8fa5c74ed35286d6770af [file] [log] [blame]
joshualittae5b2c62015-08-19 08:48:41 -07001/*
2 * Copyright 2015 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#ifndef GrQuad_DEFINED
9#define GrQuad_DEFINED
10
joshualittae5b2c62015-08-19 08:48:41 -070011#include "SkMatrix.h"
Brian Salomona33b67c2018-05-17 10:42:14 -040012#include "SkNx.h"
13#include "SkPoint.h"
Brian Salomonbe3c1d22018-05-21 12:54:39 -040014#include "SkPoint3.h"
Michael Ludwigc96fc372019-01-08 15:46:15 -050015#include "SkTArray.h"
joshualittae5b2c62015-08-19 08:48:41 -070016
Michael Ludwig6bee7762018-10-19 09:50:36 -040017enum class GrAAType : unsigned;
18enum class GrQuadAAFlags;
19
Michael Ludwig1f7e4382018-10-19 09:36:57 -040020// Rectangles transformed by matrices (view or local) can be classified in three ways:
21// 1. Stays a rectangle - the matrix rectStaysRect() is true, or x(0) == x(1) && x(2) == x(3)
22// and y(0) == y(2) && y(1) == y(3). Or under mirrors, x(0) == x(2) && x(1) == x(3) and
23// y(0) == y(1) && y(2) == y(3).
Michael Ludwigf995c052018-11-26 15:24:29 -050024// 2. Is rectilinear - the matrix does not have skew or perspective, but may rotate (unlike #1)
25// 3. Is a quadrilateral - the matrix does not have perspective, but may rotate or skew, or
Michael Ludwig1f7e4382018-10-19 09:36:57 -040026// ws() == all ones.
Michael Ludwigf995c052018-11-26 15:24:29 -050027// 4. Is a perspective quad - the matrix has perspective, subsuming all previous quad types.
Michael Ludwig1f7e4382018-10-19 09:36:57 -040028enum class GrQuadType {
Michael Ludwigc182b942018-11-16 10:27:51 -050029 kRect,
Michael Ludwigf995c052018-11-26 15:24:29 -050030 kRectilinear,
Michael Ludwigc182b942018-11-16 10:27:51 -050031 kStandard,
32 kPerspective,
33 kLast = kPerspective
Michael Ludwig1f7e4382018-10-19 09:36:57 -040034};
Michael Ludwigc182b942018-11-16 10:27:51 -050035static const int kGrQuadTypeCount = static_cast<int>(GrQuadType::kLast) + 1;
Michael Ludwig1f7e4382018-10-19 09:36:57 -040036
Michael Ludwig9bf37f62019-04-12 14:24:38 -040037// If an SkRect is transformed by this matrix, what class of quad is required to represent it.
Michael Ludwig1f7e4382018-10-19 09:36:57 -040038GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix);
Michael Ludwig97b94422019-04-09 10:42:39 -040039// Perform minimal analysis of 'pts' (which are suitable for MakeFromSkQuad), and determine a
40// quad type that will be as minimally general as possible.
41GrQuadType GrQuadTypeForPoints(const SkPoint pts[4], const SkMatrix& matrix);
Michael Ludwig1f7e4382018-10-19 09:36:57 -040042
Michael Ludwig6bee7762018-10-19 09:50:36 -040043// Resolve disagreements between the overall requested AA type and the per-edge quad AA flags.
Michael Ludwigc182b942018-11-16 10:27:51 -050044// knownQuadType must have come from GrQuadTypeForTransformedRect with the matrix that created the
Michael Ludwig6bee7762018-10-19 09:50:36 -040045// provided quad. Both outAAType and outEdgeFlags will be updated.
46template <typename Q>
47void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags,
48 const Q& quad, GrQuadType knownQuadType,
49 GrAAType* outAAtype, GrQuadAAFlags* outEdgeFlags);
50
joshualittae5b2c62015-08-19 08:48:41 -070051/**
Brian Salomon57caa662017-10-18 12:21:05 +000052 * GrQuad is a collection of 4 points which can be used to represent an arbitrary quadrilateral. The
53 * points make a triangle strip with CCW triangles (top-left, bottom-left, top-right, bottom-right).
joshualittae5b2c62015-08-19 08:48:41 -070054 */
55class GrQuad {
56public:
Brian Salomona33b67c2018-05-17 10:42:14 -040057 GrQuad() = default;
joshualitt8cce8f12015-08-26 06:23:39 -070058
Brian Salomona33b67c2018-05-17 10:42:14 -040059 GrQuad(const GrQuad& that) = default;
60
61 explicit GrQuad(const SkRect& rect)
62 : fX{rect.fLeft, rect.fLeft, rect.fRight, rect.fRight}
63 , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom} {}
64
Michael Ludwige9c57d32019-02-13 13:39:39 -050065 GrQuad(const Sk4f& xs, const Sk4f& ys) {
66 xs.store(fX);
67 ys.store(fY);
68 }
Brian Salomona33b67c2018-05-17 10:42:14 -040069
70 explicit GrQuad(const SkPoint pts[4])
71 : fX{pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX}
72 , fY{pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY} {}
73
Michael Ludwige9c57d32019-02-13 13:39:39 -050074 /** Sets the quad to the rect as transformed by the matrix. */
75 static GrQuad MakeFromRect(const SkRect&, const SkMatrix&);
76
Michael Ludwig009b92e2019-02-15 16:03:53 -050077 // Creates a GrQuad from the quadrilateral 'pts', transformed by the matrix. Unlike the explicit
78 // constructor, the input points array is arranged as per SkRect::toQuad (top-left, top-right,
79 // bottom-right, bottom-left). The returned instance's point order will still be CCW tri-strip
80 // order.
81 static GrQuad MakeFromSkQuad(const SkPoint pts[4], const SkMatrix&);
82
Brian Salomona33b67c2018-05-17 10:42:14 -040083 GrQuad& operator=(const GrQuad& that) = default;
84
85 SkPoint point(int i) const { return {fX[i], fY[i]}; }
86
87 SkRect bounds() const {
88 auto x = this->x4f(), y = this->y4f();
89 return {x.min(), y.min(), x.max(), y.max()};
joshualittae5b2c62015-08-19 08:48:41 -070090 }
91
Brian Salomona33b67c2018-05-17 10:42:14 -040092 float x(int i) const { return fX[i]; }
93 float y(int i) const { return fY[i]; }
joshualittae5b2c62015-08-19 08:48:41 -070094
Brian Salomona33b67c2018-05-17 10:42:14 -040095 Sk4f x4f() const { return Sk4f::Load(fX); }
96 Sk4f y4f() const { return Sk4f::Load(fY); }
joshualitt8cce8f12015-08-26 06:23:39 -070097
Michael Ludwig9bf37f62019-04-12 14:24:38 -040098 // True if anti-aliasing affects this quad. Only valid when quadType == kRect_QuadType
Michael Ludwig1f7e4382018-10-19 09:36:57 -040099 bool aaHasEffectOnRect() const;
100
joshualittae5b2c62015-08-19 08:48:41 -0700101private:
Michael Ludwigc96fc372019-01-08 15:46:15 -0500102 template<typename T>
103 friend class GrQuadListBase;
104
Brian Salomona33b67c2018-05-17 10:42:14 -0400105 float fX[4];
106 float fY[4];
joshualittae5b2c62015-08-19 08:48:41 -0700107};
108
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400109class GrPerspQuad {
110public:
111 GrPerspQuad() = default;
112
Michael Ludwige9c57d32019-02-13 13:39:39 -0500113 explicit GrPerspQuad(const SkRect& rect)
114 : fX{rect.fLeft, rect.fLeft, rect.fRight, rect.fRight}
115 , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom}
116 , fW{1.f, 1.f, 1.f, 1.f} {}
117
118 GrPerspQuad(const Sk4f& xs, const Sk4f& ys) {
119 xs.store(fX);
120 ys.store(fY);
121 fW[0] = fW[1] = fW[2] = fW[3] = 1.f;
122 }
Michael Ludwig009b92e2019-02-15 16:03:53 -0500123
Michael Ludwige9c57d32019-02-13 13:39:39 -0500124 GrPerspQuad(const Sk4f& xs, const Sk4f& ys, const Sk4f& ws) {
125 xs.store(fX);
126 ys.store(fY);
127 ws.store(fW);
128 }
129
130 static GrPerspQuad MakeFromRect(const SkRect&, const SkMatrix&);
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400131
Michael Ludwig009b92e2019-02-15 16:03:53 -0500132 // Creates a GrPerspQuad from the quadrilateral 'pts', transformed by the matrix. The input
133 // points array is arranged as per SkRect::toQuad (top-left, top-right, bottom-right,
134 // bottom-left). The returned instance's point order will still be CCW tri-strip order.
135 static GrPerspQuad MakeFromSkQuad(const SkPoint pts[4], const SkMatrix&);
136
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400137 GrPerspQuad& operator=(const GrPerspQuad&) = default;
138
139 SkPoint3 point(int i) const { return {fX[i], fY[i], fW[i]}; }
140
Michael Ludwigc96fc372019-01-08 15:46:15 -0500141 SkRect bounds(GrQuadType type) const {
Michael Ludwigc96fc372019-01-08 15:46:15 -0500142 Sk4f x = this->x4f();
143 Sk4f y = this->y4f();
144 if (type == GrQuadType::kPerspective) {
145 Sk4f iw = this->iw4f();
146 x *= iw;
147 y *= iw;
148 }
149
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400150 return {x.min(), y.min(), x.max(), y.max()};
151 }
152
153 float x(int i) const { return fX[i]; }
154 float y(int i) const { return fY[i]; }
155 float w(int i) const { return fW[i]; }
Michael Ludwigc96fc372019-01-08 15:46:15 -0500156 float iw(int i) const { return sk_ieee_float_divide(1.f, fW[i]); }
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400157
158 Sk4f x4f() const { return Sk4f::Load(fX); }
159 Sk4f y4f() const { return Sk4f::Load(fY); }
160 Sk4f w4f() const { return Sk4f::Load(fW); }
Michael Ludwigc96fc372019-01-08 15:46:15 -0500161 Sk4f iw4f() const { return this->w4f().invert(); }
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400162
Michael Ludwig1f7e4382018-10-19 09:36:57 -0400163 bool hasPerspective() const { return (w4f() != Sk4f(1.f)).anyTrue(); }
164
Michael Ludwig9bf37f62019-04-12 14:24:38 -0400165 // True if anti-aliasing affects this quad. Only valid when quadType == kRect_QuadType
Michael Ludwig1f7e4382018-10-19 09:36:57 -0400166 bool aaHasEffectOnRect() const;
167
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400168private:
Michael Ludwigc96fc372019-01-08 15:46:15 -0500169 template<typename T>
170 friend class GrQuadListBase;
171
172 // Copy 4 values from each of the arrays into the quad's components
173 GrPerspQuad(const float xs[4], const float ys[4], const float ws[4]);
174
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400175 float fX[4];
176 float fY[4];
177 float fW[4];
Michael Ludwigc96fc372019-01-08 15:46:15 -0500178};
179
180// Underlying data used by GrQuadListBase. It is defined outside of GrQuadListBase due to compiler
181// issues related to specializing member types.
182template<typename T>
183struct QuadData {
184 float fX[4];
185 float fY[4];
186 T fMetadata;
187};
188
189template<>
190struct QuadData<void> {
191 float fX[4];
192 float fY[4];
193};
194
195// A dynamic list of (possibly) perspective quads that tracks the most general quad type of all
196// added quads. It avoids storing the 3rd component if the quad type never becomes perspective.
197// Use GrQuadList subclass when only storing quads. Use GrTQuadList subclass when storing quads
198// and per-quad templated metadata (such as color or domain).
199template<typename T>
200class GrQuadListBase {
201public:
202
203 int count() const { return fXYs.count(); }
204
205 GrQuadType quadType() const { return fType; }
206
207 void reserve(int count, GrQuadType forType) {
208 fXYs.reserve(count);
209 if (forType == GrQuadType::kPerspective || fType == GrQuadType::kPerspective) {
210 fWs.reserve(4 * count);
211 }
212 }
213
214 GrPerspQuad operator[] (int i) const {
215 SkASSERT(i < this->count());
216 SkASSERT(i >= 0);
217
218 const QuadData<T>& item = fXYs[i];
219 if (fType == GrQuadType::kPerspective) {
220 // Read the explicit ws
221 return GrPerspQuad(item.fX, item.fY, fWs.begin() + 4 * i);
222 } else {
223 // Ws are implicitly 1s.
224 static constexpr float kNoPerspectiveWs[4] = {1.f, 1.f, 1.f, 1.f};
225 return GrPerspQuad(item.fX, item.fY, kNoPerspectiveWs);
226 }
227 }
228
229 // Subclasses expose push_back(const GrQuad|GrPerspQuad&, GrQuadType, [const T&]), where
230 // the metadata argument is only present in GrTQuadList's push_back definition.
231
232protected:
233 GrQuadListBase() : fType(GrQuadType::kRect) {}
234
235 void concatImpl(const GrQuadListBase<T>& that) {
236 this->upgradeType(that.fType);
237 fXYs.push_back_n(that.fXYs.count(), that.fXYs.begin());
238 if (fType == GrQuadType::kPerspective) {
239 if (that.fType == GrQuadType::kPerspective) {
240 // Copy the other's ws into the end of this list's data
241 fWs.push_back_n(that.fWs.count(), that.fWs.begin());
242 } else {
243 // This list stores ws but the appended list had implicit 1s, so add explicit 1s to
244 // fill out the total list
245 fWs.push_back_n(4 * that.count(), 1.f);
246 }
247 }
248 }
249
250 // Returns the added item data so that its metadata can be initialized if T is not void
251 QuadData<T>& pushBackImpl(const GrQuad& quad, GrQuadType type) {
Michael Ludwigc96fc372019-01-08 15:46:15 -0500252 this->upgradeType(type);
253 QuadData<T>& item = fXYs.push_back();
254 memcpy(item.fX, quad.fX, 4 * sizeof(float));
255 memcpy(item.fY, quad.fY, 4 * sizeof(float));
256 if (fType == GrQuadType::kPerspective) {
257 fWs.push_back_n(4, 1.f);
258 }
259 return item;
260 }
261
262 QuadData<T>& pushBackImpl(const GrPerspQuad& quad, GrQuadType type) {
Michael Ludwigc96fc372019-01-08 15:46:15 -0500263 this->upgradeType(type);
264 QuadData<T>& item = fXYs.push_back();
265 memcpy(item.fX, quad.fX, 4 * sizeof(float));
266 memcpy(item.fY, quad.fY, 4 * sizeof(float));
267 if (fType == GrQuadType::kPerspective) {
268 fWs.push_back_n(4, quad.fW);
269 }
270 return item;
271 }
272
273 const QuadData<T>& item(int i) const {
274 return fXYs[i];
275 }
276
277 QuadData<T>& item(int i) {
278 return fXYs[i];
279 }
280
281private:
282 void upgradeType(GrQuadType type) {
283 // Possibly upgrade the overall type tracked by the list
284 if (type > fType) {
285 fType = type;
286 if (type == GrQuadType::kPerspective) {
287 // All existing quads were 2D, so the ws array just needs to be filled with 1s
288 fWs.push_back_n(4 * this->count(), 1.f);
289 }
290 }
291 }
292
293 // Interleaves xs, ys, and per-quad metadata so that all data for a single quad is together
294 // (barring ws, which can be dropped entirely if the quad type allows it).
295 SkSTArray<1, QuadData<T>, true> fXYs;
296 // The w channel is kept separate so that it can remain empty when only dealing with 2D quads.
297 SkTArray<float, true> fWs;
298
299 GrQuadType fType;
300};
301
302// This list only stores the quad data itself.
303class GrQuadList : public GrQuadListBase<void> {
304public:
305 GrQuadList() : INHERITED() {}
306
307 void concat(const GrQuadList& that) {
308 this->concatImpl(that);
309 }
310
311 void push_back(const GrQuad& quad, GrQuadType type) {
312 this->pushBackImpl(quad, type);
313 }
314
315 void push_back(const GrPerspQuad& quad, GrQuadType type) {
316 this->pushBackImpl(quad, type);
317 }
318
319private:
320 typedef GrQuadListBase<void> INHERITED;
321};
322
323// This variant of the list allows simple metadata to be stored per quad as well, such as color
324// or texture domain.
325template<typename T>
326class GrTQuadList : public GrQuadListBase<T> {
327public:
328 GrTQuadList() : INHERITED() {}
329
330 void concat(const GrTQuadList<T>& that) {
331 this->concatImpl(that);
332 }
333
334 // Adding to the list requires metadata
335 void push_back(const GrQuad& quad, GrQuadType type, T&& metadata) {
336 QuadData<T>& item = this->pushBackImpl(quad, type);
337 item.fMetadata = std::move(metadata);
338 }
339
340 void push_back(const GrPerspQuad& quad, GrQuadType type, T&& metadata) {
341 QuadData<T>& item = this->pushBackImpl(quad, type);
342 item.fMetadata = std::move(metadata);
343 }
344
345 // And provide access to the metadata per quad
346 const T& metadata(int i) const {
347 return this->item(i).fMetadata;
348 }
349
350 T& metadata(int i) {
351 return this->item(i).fMetadata;
352 }
353
354private:
355 typedef GrQuadListBase<T> INHERITED;
Brian Salomonbe3c1d22018-05-21 12:54:39 -0400356};
357
joshualittae5b2c62015-08-19 08:48:41 -0700358#endif