xref: /aosp_15_r20/external/skia/src/gpu/graphite/geom/Transform_graphite.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2021 Google LLC
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 skgpu_graphite_geom_Transform_DEFINED
9 #define skgpu_graphite_geom_Transform_DEFINED
10 
11 #include "include/core/SkM44.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/private/base/SkAssert.h"
14 #include "include/private/base/SkFloatingPoint.h"
15 
16 #include <utility>
17 
18 namespace skgpu::graphite {
19 
20 class Rect;
21 
22 // Transform encapsulates an SkM44 matrix, its inverse, and other properties dependent on the
23 // original matrix value that are useful when rendering.
24 class Transform {
25 public:
26     // Type classifies the transform into coarse categories so that certain optimizations or
27     // properties can be queried efficiently
28     enum class Type : unsigned {
29         // Applying the matrix to a vector or point is a no-op, so could be skipped entirely.
30         kIdentity,
31         // The matrix transforms a rect to another rect, without mirrors or rotations, so both
32         // pre-and-post transform coordinates can be exactly represented as rects.
33         kSimpleRectStaysRect,
34         // The matrix transforms a rect to another rect, but may mirror or rotate the corners
35         // relative to each other. This means that the post-transformed rect completely fills
36         // that space.
37         kRectStaysRect,
38         // The matrix transform may have skew or rotation, so a mapped rect does not fill space,
39         // but there is no need to perform perspective division or w-plane clipping. This also
40         // includes orthographic projections.
41         kAffine,
42         // The matrix includes perspective and requires further projection to 2D, so care must be
43         // taken when w is less than or near 0, and homogeneous division and perspective-correct
44         // interpolation are needed when rendering.
45         kPerspective,
46         // The matrix is not invertible or not finite, so should not be used to draw.
47         kInvalid,
48     };
49 
50     explicit Transform(const SkM44& m);
51     Transform(const Transform& t) = default;
52 
Identity()53     static constexpr Transform Identity() {
54         return Transform(SkM44(), SkM44(), Type::kIdentity, 1.f, 1.f);
55     }
Invalid()56     static constexpr Transform Invalid() {
57         return Transform(SkM44(SkM44::kNaN_Constructor), SkM44(SkM44::kNaN_Constructor),
58                          Type::kInvalid, 1.f, 1.f);
59     }
60 
Translate(float x,float y)61     static inline Transform Translate(float x, float y) {
62         if (x == 0.f && y == 0.f) {
63             return Identity();
64         } else if (SkIsFinite(x, y)) {
65             return Transform(SkM44::Translate(x, y), SkM44::Translate(-x, -y),
66                              Type::kSimpleRectStaysRect, 1.f, 1.f);
67         } else {
68             return Invalid();
69         }
70     }
71 
Inverse(const Transform & t)72     static inline Transform Inverse(const Transform& t) {
73         return Transform(t.fInvM, t.fM, t.fType, 1.f / t.fMaxScaleFactor, 1.f / t.fMinScaleFactor);
74     }
75 
76     Transform& operator=(const Transform& t) = default;
77 
78     operator const SkM44&() const { return fM; }
SkMatrix()79     operator SkMatrix() const { return fM.asM33(); }
80 
81     bool operator!=(const Transform& t) const { return !(*this == t); }
82     bool operator==(const Transform& t) const {
83         return this->valid() == t.valid() && (!this->valid() || fM == t.fM);
84     }
85 
matrix()86     const SkM44& matrix() const { return fM; }
inverse()87     const SkM44& inverse() const { return fInvM; }
88 
type()89     Type type() const { return fType; }
valid()90     bool valid() const { return fType != Type::kInvalid; }
91 
92     // Return the {min,max} scale factor at the pre-transformed location 'p'. A unit circle about
93     // 'p' transformed by this Transform will be contained in an ellipse with radii equal to 'min'
94     // and 'max', e.g. moving 1 local unit will move at least 'min' pixels and at most 'max' pixels
95     std::pair<float, float> scaleFactors(const SkV2& p) const;
96 
97     // This is valid for non-projection types and 1.0 for projection matrices.
maxScaleFactor()98     float maxScaleFactor() const {
99         SkASSERT(this->valid());
100         return fMaxScaleFactor;
101     }
102 
103     // Return the minimum distance needed to move in local (pre-transform) space to ensure that the
104     // transformed coordinates are at least 1px away from the original mapped point. This minimum
105     // distance is specific to the given local 'bounds' since the scale factors change with
106     // perspective.
107     //
108     // If the bounds would be clipped by the w=0 plane or otherwise is ill-conditioned, this will
109     // return positive infinity.
110     float localAARadius(const Rect& bounds) const;
111 
112     Rect mapRect(const Rect& rect) const;
113     Rect inverseMapRect(const Rect& rect) const;
114 
115     void mapPoints(const Rect& localRect, SkV4 deviceOut[4]) const;
116     void mapPoints(const SkV2* localIn, SkV4* deviceOut, int count) const;
117 
118     void mapPoints(const SkV4* localIn, SkV4* deviceOut, int count) const;
119     void inverseMapPoints(const SkV4* deviceIn, SkV4* localOut, int count) const;
120 
121     // Returns a transform equal to the pre- or post-translation of this matrix
preTranslate(float x,float y)122     Transform preTranslate(float x, float y) const {
123         return this->concat(SkM44::Translate(x, y));
124     }
postTranslate(float x,float y)125     Transform postTranslate(float x, float y) const {
126         return Translate(x, y).concat(*this);
127     }
128 
129     // Returns a transform equal to (this * t)
concat(const Transform & t)130     Transform concat(const Transform& t) const {
131         SkASSERT(this->valid());
132         return Transform(fM * t.fM);
133     }
concat(const SkM44 & t)134     Transform concat(const SkM44& t) const {
135         SkASSERT(this->valid());
136         return Transform(fM * t);
137     }
138 
139     // Returns a transform equal to (this * t^-1)
concatInverse(const Transform & t)140     Transform concatInverse(const Transform& t) const {
141         SkASSERT(this->valid());
142         return Transform(fM * t.fInvM);
143     }
concatInverse(const SkM44 & t)144     Transform concatInverse(const SkM44& t) const {
145         SkASSERT(this->valid());
146         // Saves a multiply compared to inverting just 't' and calculating both fM*t^-1 and t*fInvM
147         // (t * this^-1)^-1 = this * t^-1
148         return Inverse(Transform(t * fInvM));
149     }
150 
151 private:
152     // Used for static factories that have known properties
Transform(const SkM44 & m,const SkM44 & invM,Type type,float minScale,float maxScale)153     constexpr Transform(const SkM44& m, const SkM44& invM, Type type,
154                         float minScale, float maxScale)
155             : fM(m)
156             , fInvM(invM)
157             , fType(type)
158             , fMinScaleFactor(minScale)
159             , fMaxScaleFactor(maxScale) {}
160 
161     SkM44 fM;
162     SkM44 fInvM; // M^-1
163     Type  fType;
164 
165     // These are cached for non-projection transforms since they are constant; projection matrices
166     // must be computed per point, and these values are ignored.
167     float fMinScaleFactor = 1.f;
168     float fMaxScaleFactor = 1.f;
169 };
170 
171 } // namespace skgpu::graphite
172 
173 #endif // skgpu_graphite_geom_Transform_DEFINED
174