• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
11 #include "SkMatrix.h"
12 #include "SkNx.h"
13 #include "SkPoint.h"
14 #include "SkPoint3.h"
15 #include "SkTArray.h"
16 
17 enum class GrAAType : unsigned;
18 enum class GrQuadAAFlags;
19 
20 // 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).
24 //  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
26 //     ws() == all ones.
27 //  4. Is a perspective quad - the matrix has perspective, subsuming all previous quad types.
28 enum class GrQuadType {
29     kRect,
30     kRectilinear,
31     kStandard,
32     kPerspective,
33     kLast = kPerspective
34 };
35 static const int kGrQuadTypeCount = static_cast<int>(GrQuadType::kLast) + 1;
36 
37 // If an SkRect is transformed by this matrix, what class of quad is required to represent it. Since
38 // quadType() is only provided on Gr[Persp]Quad in debug builds, production code should use this
39 // to efficiently determine quad types.
40 GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix);
41 
42 // Resolve disagreements between the overall requested AA type and the per-edge quad AA flags.
43 // knownQuadType must have come from GrQuadTypeForTransformedRect with the matrix that created the
44 // provided quad. Both outAAType and outEdgeFlags will be updated.
45 template <typename Q>
46 void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags,
47                             const Q& quad, GrQuadType knownQuadType,
48                             GrAAType* outAAtype, GrQuadAAFlags* outEdgeFlags);
49 
50 /**
51  * GrQuad is a collection of 4 points which can be used to represent an arbitrary quadrilateral. The
52  * points make a triangle strip with CCW triangles (top-left, bottom-left, top-right, bottom-right).
53  */
54 class GrQuad {
55 public:
56     GrQuad() = default;
57 
58     GrQuad(const GrQuad& that) = default;
59 
GrQuad(const SkRect & rect)60     explicit GrQuad(const SkRect& rect)
61             : fX{rect.fLeft, rect.fLeft, rect.fRight, rect.fRight}
62             , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom} {}
63 
GrQuad(const Sk4f & xs,const Sk4f & ys)64     GrQuad(const Sk4f& xs, const Sk4f& ys) {
65         xs.store(fX);
66         ys.store(fY);
67     }
68 
GrQuad(const SkPoint pts[4])69     explicit GrQuad(const SkPoint pts[4])
70             : fX{pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX}
71             , fY{pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY} {}
72 
73     /** Sets the quad to the rect as transformed by the matrix. */
74     static GrQuad MakeFromRect(const SkRect&, const SkMatrix&);
75 
76     // Creates a GrQuad from the quadrilateral 'pts', transformed by the matrix. Unlike the explicit
77     // constructor, the input points array is arranged as per SkRect::toQuad (top-left, top-right,
78     // bottom-right, bottom-left). The returned instance's point order will still be CCW tri-strip
79     // order.
80     static GrQuad MakeFromSkQuad(const SkPoint pts[4], const SkMatrix&);
81 
82     GrQuad& operator=(const GrQuad& that) = default;
83 
point(int i)84     SkPoint point(int i) const { return {fX[i], fY[i]}; }
85 
bounds()86     SkRect bounds() const {
87         auto x = this->x4f(), y = this->y4f();
88         return {x.min(), y.min(), x.max(), y.max()};
89     }
90 
x(int i)91     float x(int i) const { return fX[i]; }
y(int i)92     float y(int i) const { return fY[i]; }
93 
x4f()94     Sk4f x4f() const { return Sk4f::Load(fX); }
y4f()95     Sk4f y4f() const { return Sk4f::Load(fY); }
96 
97     // True if anti-aliasing affects this quad. Requires quadType() == kRect_QuadType
98     bool aaHasEffectOnRect() const;
99 
100 #ifdef SK_DEBUG
101     GrQuadType quadType() const;
102 #endif
103 
104 private:
105     template<typename T>
106     friend class GrQuadListBase;
107 
108     float fX[4];
109     float fY[4];
110 };
111 
112 class GrPerspQuad {
113 public:
114     GrPerspQuad() = default;
115 
GrPerspQuad(const SkRect & rect)116     explicit GrPerspQuad(const SkRect& rect)
117             : fX{rect.fLeft, rect.fLeft, rect.fRight, rect.fRight}
118             , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom}
119             , fW{1.f, 1.f, 1.f, 1.f} {}
120 
GrPerspQuad(const Sk4f & xs,const Sk4f & ys)121     GrPerspQuad(const Sk4f& xs, const Sk4f& ys) {
122         xs.store(fX);
123         ys.store(fY);
124         fW[0] = fW[1] = fW[2] = fW[3] = 1.f;
125     }
126 
GrPerspQuad(const Sk4f & xs,const Sk4f & ys,const Sk4f & ws)127     GrPerspQuad(const Sk4f& xs, const Sk4f& ys, const Sk4f& ws) {
128         xs.store(fX);
129         ys.store(fY);
130         ws.store(fW);
131     }
132 
133     static GrPerspQuad MakeFromRect(const SkRect&, const SkMatrix&);
134 
135     // Creates a GrPerspQuad from the quadrilateral 'pts', transformed by the matrix. The input
136     // points array is arranged as per SkRect::toQuad (top-left, top-right, bottom-right,
137     // bottom-left). The returned instance's point order will still be CCW tri-strip order.
138     static GrPerspQuad MakeFromSkQuad(const SkPoint pts[4], const SkMatrix&);
139 
140     GrPerspQuad& operator=(const GrPerspQuad&) = default;
141 
point(int i)142     SkPoint3 point(int i) const { return {fX[i], fY[i], fW[i]}; }
143 
bounds(GrQuadType type)144     SkRect bounds(GrQuadType type) const {
145         SkASSERT(this->quadType() <= type);
146 
147         Sk4f x = this->x4f();
148         Sk4f y = this->y4f();
149         if (type == GrQuadType::kPerspective) {
150             Sk4f iw = this->iw4f();
151             x *= iw;
152             y *= iw;
153         }
154 
155         return {x.min(), y.min(), x.max(), y.max()};
156     }
157 
x(int i)158     float x(int i) const { return fX[i]; }
y(int i)159     float y(int i) const { return fY[i]; }
w(int i)160     float w(int i) const { return fW[i]; }
iw(int i)161     float iw(int i) const { return sk_ieee_float_divide(1.f, fW[i]); }
162 
x4f()163     Sk4f x4f() const { return Sk4f::Load(fX); }
y4f()164     Sk4f y4f() const { return Sk4f::Load(fY); }
w4f()165     Sk4f w4f() const { return Sk4f::Load(fW); }
iw4f()166     Sk4f iw4f() const { return this->w4f().invert(); }
167 
hasPerspective()168     bool hasPerspective() const { return (w4f() != Sk4f(1.f)).anyTrue(); }
169 
170     // True if anti-aliasing affects this quad. Requires quadType() == kRect_QuadType
171     bool aaHasEffectOnRect() const;
172 
173 #ifdef SK_DEBUG
174     GrQuadType quadType() const;
175 #endif
176 
177 private:
178     template<typename T>
179     friend class GrQuadListBase;
180 
181     // Copy 4 values from each of the arrays into the quad's components
182     GrPerspQuad(const float xs[4], const float ys[4], const float ws[4]);
183 
184     float fX[4];
185     float fY[4];
186     float fW[4];
187 };
188 
189 // Underlying data used by GrQuadListBase. It is defined outside of GrQuadListBase due to compiler
190 // issues related to specializing member types.
191 template<typename T>
192 struct QuadData {
193     float fX[4];
194     float fY[4];
195     T fMetadata;
196 };
197 
198 template<>
199 struct QuadData<void> {
200     float fX[4];
201     float fY[4];
202 };
203 
204 // A dynamic list of (possibly) perspective quads that tracks the most general quad type of all
205 // added quads. It avoids storing the 3rd component if the quad type never becomes perspective.
206 // Use GrQuadList subclass when only storing quads. Use GrTQuadList subclass when storing quads
207 // and per-quad templated metadata (such as color or domain).
208 template<typename T>
209 class GrQuadListBase {
210 public:
211 
212     int count() const { return fXYs.count(); }
213 
214     GrQuadType quadType() const { return fType; }
215 
216     void reserve(int count, GrQuadType forType) {
217         fXYs.reserve(count);
218         if (forType == GrQuadType::kPerspective || fType == GrQuadType::kPerspective) {
219             fWs.reserve(4 * count);
220         }
221     }
222 
223     GrPerspQuad operator[] (int i) const {
224         SkASSERT(i < this->count());
225         SkASSERT(i >= 0);
226 
227         const QuadData<T>& item = fXYs[i];
228         if (fType == GrQuadType::kPerspective) {
229             // Read the explicit ws
230             return GrPerspQuad(item.fX, item.fY, fWs.begin() + 4 * i);
231         } else {
232             // Ws are implicitly 1s.
233             static constexpr float kNoPerspectiveWs[4] = {1.f, 1.f, 1.f, 1.f};
234             return GrPerspQuad(item.fX, item.fY, kNoPerspectiveWs);
235         }
236     }
237 
238     // Subclasses expose push_back(const GrQuad|GrPerspQuad&, GrQuadType, [const T&]), where
239     // the metadata argument is only present in GrTQuadList's push_back definition.
240 
241 protected:
242     GrQuadListBase() : fType(GrQuadType::kRect) {}
243 
244     void concatImpl(const GrQuadListBase<T>& that) {
245         this->upgradeType(that.fType);
246         fXYs.push_back_n(that.fXYs.count(), that.fXYs.begin());
247         if (fType == GrQuadType::kPerspective) {
248             if (that.fType == GrQuadType::kPerspective) {
249                 // Copy the other's ws into the end of this list's data
250                 fWs.push_back_n(that.fWs.count(), that.fWs.begin());
251             } else {
252                 // This list stores ws but the appended list had implicit 1s, so add explicit 1s to
253                 // fill out the total list
254                 fWs.push_back_n(4 * that.count(), 1.f);
255             }
256         }
257     }
258 
259     // Returns the added item data so that its metadata can be initialized if T is not void
260     QuadData<T>& pushBackImpl(const GrQuad& quad, GrQuadType type) {
261         SkASSERT(quad.quadType() <= type);
262 
263         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, 1.f);
269         }
270         return item;
271     }
272 
273     QuadData<T>& pushBackImpl(const GrPerspQuad& quad, GrQuadType type) {
274         SkASSERT(quad.quadType() <= type);
275 
276         this->upgradeType(type);
277         QuadData<T>& item = fXYs.push_back();
278         memcpy(item.fX, quad.fX, 4 * sizeof(float));
279         memcpy(item.fY, quad.fY, 4 * sizeof(float));
280         if (fType == GrQuadType::kPerspective) {
281             fWs.push_back_n(4, quad.fW);
282         }
283         return item;
284     }
285 
286     const QuadData<T>& item(int i) const {
287         return fXYs[i];
288     }
289 
290     QuadData<T>& item(int i) {
291         return fXYs[i];
292     }
293 
294 private:
295     void upgradeType(GrQuadType type) {
296         // Possibly upgrade the overall type tracked by the list
297         if (type > fType) {
298             fType = type;
299             if (type == GrQuadType::kPerspective) {
300                 // All existing quads were 2D, so the ws array just needs to be filled with 1s
301                 fWs.push_back_n(4 * this->count(), 1.f);
302             }
303         }
304     }
305 
306     // Interleaves xs, ys, and per-quad metadata so that all data for a single quad is together
307     // (barring ws, which can be dropped entirely if the quad type allows it).
308     SkSTArray<1, QuadData<T>, true> fXYs;
309     // The w channel is kept separate so that it can remain empty when only dealing with 2D quads.
310     SkTArray<float, true> fWs;
311 
312     GrQuadType fType;
313 };
314 
315 // This list only stores the quad data itself.
316 class GrQuadList : public GrQuadListBase<void> {
317 public:
318     GrQuadList() : INHERITED() {}
319 
320     void concat(const GrQuadList& that) {
321         this->concatImpl(that);
322     }
323 
324     void push_back(const GrQuad& quad, GrQuadType type) {
325         this->pushBackImpl(quad, type);
326     }
327 
328     void push_back(const GrPerspQuad& quad, GrQuadType type) {
329         this->pushBackImpl(quad, type);
330     }
331 
332 private:
333     typedef GrQuadListBase<void> INHERITED;
334 };
335 
336 // This variant of the list allows simple metadata to be stored per quad as well, such as color
337 // or texture domain.
338 template<typename T>
339 class GrTQuadList : public GrQuadListBase<T> {
340 public:
341     GrTQuadList() : INHERITED() {}
342 
343     void concat(const GrTQuadList<T>& that) {
344         this->concatImpl(that);
345     }
346 
347     // Adding to the list requires metadata
348     void push_back(const GrQuad& quad, GrQuadType type, T&& metadata) {
349         QuadData<T>& item = this->pushBackImpl(quad, type);
350         item.fMetadata = std::move(metadata);
351     }
352 
353     void push_back(const GrPerspQuad& quad, GrQuadType type, T&& metadata) {
354         QuadData<T>& item = this->pushBackImpl(quad, type);
355         item.fMetadata = std::move(metadata);
356     }
357 
358     // And provide access to the metadata per quad
359     const T& metadata(int i) const {
360         return this->item(i).fMetadata;
361     }
362 
363     T& metadata(int i) {
364         return this->item(i).fMetadata;
365     }
366 
367 private:
368     typedef GrQuadListBase<T> INHERITED;
369 };
370 
371 #endif
372