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 "include/core/SkMatrix.h" |
12 | #include "include/core/SkPoint.h" |
13 | #include "include/core/SkPoint3.h" |
14 | #include "include/private/SkVx.h" |
15 | |
16 | enum class GrQuadAAFlags; |
17 | |
18 | /** |
19 | * GrQuad is a collection of 4 points which can be used to represent an arbitrary quadrilateral. The |
20 | * points make a triangle strip with CCW triangles (top-left, bottom-left, top-right, bottom-right). |
21 | */ |
22 | class GrQuad { |
23 | public: |
24 | // Quadrilaterals can be classified in several useful ways that assist AA tessellation and other |
25 | // analysis when drawing, in particular, knowing if it was originally a rectangle transformed by |
26 | // certain types of matrices: |
27 | enum class Type { |
28 | // The 4 points remain an axis-aligned rectangle; their logical indices may not respect |
29 | // TL, BL, TR, BR ordering if the transform was a 90 degree rotation or mirror. |
30 | kAxisAligned, |
31 | // The 4 points represent a rectangle subjected to a rotation, its corners are right angles. |
32 | kRectilinear, |
33 | // Arbitrary 2D quadrilateral; may have been a rectangle transformed with skew or some |
34 | // clipped polygon. Its w coordinates will all be 1. |
35 | kGeneral, |
36 | // Even more general-purpose than kGeneral, this allows the w coordinates to be non-unity. |
37 | kPerspective, |
38 | kLast = kPerspective |
39 | }; |
40 | static const int kTypeCount = static_cast<int>(Type::kLast) + 1; |
41 | |
42 | // This enforces W == 1 for non-perspective quads, but does not initialize X or Y. |
43 | GrQuad() = default; |
44 | |
45 | explicit GrQuad(const SkRect& rect) |
46 | : fX{rect.fLeft, rect.fLeft, rect.fRight, rect.fRight} |
47 | , fY{rect.fTop, rect.fBottom, rect.fTop, rect.fBottom} {} |
48 | |
49 | static GrQuad MakeFromRect(const SkRect&, const SkMatrix&); |
50 | |
51 | // Creates a GrQuad from the quadrilateral 'pts', transformed by the matrix. The input |
52 | // points array is arranged as per SkRect::toQuad (top-left, top-right, bottom-right, |
53 | // bottom-left). The returned instance's point order will still be CCW tri-strip order. |
54 | static GrQuad MakeFromSkQuad(const SkPoint pts[4], const SkMatrix&); |
55 | |
56 | GrQuad& operator=(const GrQuad&) = default; |
57 | |
58 | SkPoint3 point3(int i) const { return {fX[i], fY[i], fW[i]}; } |
59 | |
60 | SkPoint point(int i) const { |
61 | if (fType == Type::kPerspective) { |
62 | return {fX[i] / fW[i], fY[i] / fW[i]}; |
63 | } else { |
64 | return {fX[i], fY[i]}; |
65 | } |
66 | } |
67 | |
68 | SkRect bounds() const { |
69 | if (fType == GrQuad::Type::kPerspective) { |
70 | return this->projectedBounds(); |
71 | } |
72 | // Calculate min/max directly on the 4 floats, instead of loading/unloading into SIMD. Since |
73 | // there's no horizontal min/max, it's not worth it. Defining non-perspective case in header |
74 | // also leads to substantial performance boost due to inlining. |
75 | auto min = [](const float c[4]) { return std::min(std::min(c[0], c[1]), |
76 | std::min(c[2], c[3]));}; |
77 | auto max = [](const float c[4]) { return std::max(std::max(c[0], c[1]), |
78 | std::max(c[2], c[3]));}; |
79 | return { min(fX), min(fY), max(fX), max(fY) }; |
80 | } |
81 | |
82 | bool isFinite() const { |
83 | // If any coordinate is infinity or NaN, then multiplying it with 0 will make accum NaN |
84 | float accum = 0; |
85 | for (int i = 0; i < 4; ++i) { |
86 | accum *= fX[i]; |
87 | accum *= fY[i]; |
88 | accum *= fW[i]; |
89 | } |
90 | SkASSERT(0 == accum || SkScalarIsNaN(accum)); |
91 | return !SkScalarIsNaN(accum); |
92 | } |
93 | |
94 | float x(int i) const { return fX[i]; } |
95 | float y(int i) const { return fY[i]; } |
96 | float w(int i) const { return fW[i]; } |
97 | float iw(int i) const { return sk_ieee_float_divide(1.f, fW[i]); } |
98 | |
99 | skvx::Vec<4, float> x4f() const { return skvx::Vec<4, float>::Load(fX); } |
100 | skvx::Vec<4, float> y4f() const { return skvx::Vec<4, float>::Load(fY); } |
101 | skvx::Vec<4, float> w4f() const { return skvx::Vec<4, float>::Load(fW); } |
102 | skvx::Vec<4, float> iw4f() const { return 1.f / this->w4f(); } |
103 | |
104 | Type quadType() const { return fType; } |
105 | |
106 | bool hasPerspective() const { return fType == Type::kPerspective; } |
107 | |
108 | // True if anti-aliasing affects this quad. Only valid when quadType == kAxisAligned |
109 | bool aaHasEffectOnRect() const; |
110 | |
111 | // True if this quad is axis-aligned and still has its top-left corner at v0. Equivalently, |
112 | // quad == GrQuad(quad->bounds()). Axis-aligned quads with flips and rotations may exactly |
113 | // fill their bounds, but their vertex order will not match TL BL TR BR anymore. |
114 | bool asRect(SkRect* rect) const; |
115 | |
116 | // The non-const pointers are provided to support modifying a GrQuad in-place, but care must be |
117 | // taken to keep its quad type aligned with the geometric nature of the new coordinates. |
118 | const float* xs() const { return fX; } |
119 | float* xs() { return fX; } |
120 | const float* ys() const { return fY; } |
121 | float* ys() { return fY; } |
122 | const float* ws() const { return fW; } |
123 | float* ws() { return fW; } |
124 | |
125 | // Automatically ensures ws are 1 if new type is not perspective. |
126 | void setQuadType(Type newType) { |
127 | if (newType != Type::kPerspective && fType == Type::kPerspective) { |
128 | fW[0] = fW[1] = fW[2] = fW[3] = 1.f; |
129 | } |
130 | SkASSERT(newType == Type::kPerspective || |
131 | (SkScalarNearlyEqual(fW[0], 1.f) && SkScalarNearlyEqual(fW[1], 1.f) && |
132 | SkScalarNearlyEqual(fW[2], 1.f) && SkScalarNearlyEqual(fW[3], 1.f))); |
133 | |
134 | fType = newType; |
135 | } |
136 | private: |
137 | template<typename T> |
138 | friend class GrQuadListBase; // for access to fX, fY, fW |
139 | |
140 | GrQuad(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys, Type type) |
141 | : fType(type) { |
142 | SkASSERT(type != Type::kPerspective); |
143 | xs.store(fX); |
144 | ys.store(fY); |
145 | } |
146 | |
147 | GrQuad(const skvx::Vec<4, float>& xs, const skvx::Vec<4, float>& ys, |
148 | const skvx::Vec<4, float>& ws, Type type) |
149 | : fW{} // Include fW in member initializer to avoid redundant default initializer |
150 | , fType(type) { |
151 | xs.store(fX); |
152 | ys.store(fY); |
153 | ws.store(fW); |
154 | } |
155 | |
156 | // Defined in GrQuadUtils.cpp to share the coord clipping code |
157 | SkRect projectedBounds() const; |
158 | |
159 | float fX[4]; |
160 | float fY[4]; |
161 | float fW[4] = {1.f, 1.f, 1.f, 1.f}; |
162 | |
163 | Type fType = Type::kAxisAligned; |
164 | }; |
165 | |
166 | // A simple struct representing the common work unit of a pair of device and local coordinates, as |
167 | // well as the edge flags controlling anti-aliasing for the quadrilateral when drawn. |
168 | struct DrawQuad { |
169 | GrQuad fDevice; |
170 | GrQuad fLocal; |
171 | GrQuadAAFlags fEdgeFlags; |
172 | }; |
173 | |
174 | #endif |
175 | |