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
16enum 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 */
22class GrQuad {
23public:
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 }
136private:
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.
168struct DrawQuad {
169 GrQuad fDevice;
170 GrQuad fLocal;
171 GrQuadAAFlags fEdgeFlags;
172};
173
174#endif
175