• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "src/gpu/graphite/geom/Transform_graphite.h"
9 
10 #include "src/base/SkVx.h"
11 #include "src/core/SkMatrixPriv.h"
12 #include "src/gpu/graphite/geom/Rect.h"
13 
14 namespace skgpu::graphite {
15 
16 namespace {
17 
map_rect(const SkM44 & m,const Rect & r)18 Rect map_rect(const SkM44& m, const Rect& r) {
19     // TODO: Can Rect's (l,t,-r,-b) structure be used to optimize mapRect?
20     // TODO: Can take this opportunity to implement 100% accurate perspective plane clipping since
21     //       it doesn't have to match raster/ganesh rendering behavior.
22     return SkMatrixPriv::MapRect(m, r.asSkRect());
23 }
24 
map_points(const SkM44 & m,const SkV4 * in,SkV4 * out,int count)25 void map_points(const SkM44& m, const SkV4* in, SkV4* out, int count) {
26     // TODO: These maybe should go into SkM44, since bulk point mapping seems generally useful
27     auto c0 = skvx::float4::Load(SkMatrixPriv::M44ColMajor(m) + 0);
28     auto c1 = skvx::float4::Load(SkMatrixPriv::M44ColMajor(m) + 4);
29     auto c2 = skvx::float4::Load(SkMatrixPriv::M44ColMajor(m) + 8);
30     auto c3 = skvx::float4::Load(SkMatrixPriv::M44ColMajor(m) + 12);
31 
32     for (int i = 0; i < count; ++i) {
33         auto p = (c0 * in[i].x) + (c1 * in[i].y) + (c2 * in[i].z) + (c3 * in[i].w);
34         p.store(out + i);
35     }
36 }
37 
get_matrix_info(const SkM44 & m,SkM44 * inverse,SkV2 * scale)38 Transform::Type get_matrix_info(const SkM44& m, SkM44* inverse, SkV2* scale) {
39     // First compute the inverse.
40     // TODO: Alternatively we could compute type first and have type-specific inverses, but it seems
41     // useful to ensure any non-invalid matrix returns true from SkM44::invert.
42     if (!m.invert(inverse)) {
43         *scale = {1.f, 1.f};
44         return Transform::Type::kInvalid;
45     }
46 
47     static constexpr SkV4 kNoPerspective = {0.f, 0.f, 0.f, 1.f};
48     static constexpr SkV4 kNoZ = {0.f, 0.f, 1.f, 0.f};
49     if (m.row(3) != kNoPerspective || m.col(2) != kNoZ || m.row(2) != kNoZ) {
50         // TODO: Use SkMatrixPriv::DifferentialAreaScale, but we need a representative point then.
51         // Or something like lengths of upper 2x2 divided by w?
52         *scale = {1.f, 1.f};
53         return Transform::Type::kProjection;
54     }
55 
56     //                                              [sx kx 0 tx]
57     // At this point, we know that m is of the form [ky sy 0 ty]
58     //                                              [0  0  1 0 ]
59     //                                              [0  0  0 1 ]
60     // Other than kIdentity, none of the types depend on (tx, ty). The remaining types are
61     // identified by considering the upper 2x2.
62     float sx = m.rc(0, 0);
63     float sy = m.rc(1, 1);
64     float kx = m.rc(0, 1);
65     float ky = m.rc(1, 0);
66     if (kx == 0.f && ky == 0.f) {
67         // 2x2 is a diagonal matrix
68         *scale = {std::abs(sx), std::abs(sy)};
69         if (sx == 1.f && sy == 1.f && m.rc(0, 3) == 0.f && m.rc(1, 3) == 0.f) {
70             return Transform::Type::kIdentity;
71         } else if (sx > 0.f && sy > 0.f) {
72             return Transform::Type::kSimpleRectStaysRect;
73         } else {
74             // We don't need to worry about sx or sy being 0 here because that would imply the
75             // matrix wasn't invertible and that was already tested.
76             SkASSERT(sx != 0.f && sy != 0.f);
77             return Transform::Type::kRectStaysRect;
78         }
79     } else if (sx == 0.f && sy == 0.f) {
80         // 2x2 is an anti-diagonal matrix and represents a 90 or 270 degree rotation plus optional
81         // scale and translate. Similar to before, kx and ky can't be 0 or m wouldn't be invertible.
82         SkASSERT(kx != 0.f && ky != 0.f);
83         *scale = {std::abs(ky), std::abs(kx)};
84         return Transform::Type::kRectStaysRect;
85     } else {
86         *scale = {SkV2{sx, ky}.length(), SkV2{kx, sy}.length()};
87         return Transform::Type::kAffine;
88     }
89 }
90 
91 } // anonymous namespace
92 
Transform(const SkM44 & m)93 Transform::Transform(const SkM44& m) : fM(m) {
94     fType = get_matrix_info(m, &fInvM, &fScale);
95 }
96 
Identity()97 const Transform& Transform::Identity() {
98     static const Transform kIdentity{SkM44()};
99     return kIdentity;
100 }
Invalid()101 const Transform& Transform::Invalid() {
102     static const Transform kInvalid{SkM44(SkM44::kNaN_Constructor)};
103     return kInvalid;
104 }
105 
operator ==(const Transform & t) const106 bool Transform::operator==(const Transform& t) const {
107     // Checking fM should be sufficient as all other values are computed from it.
108     SkASSERT(fM != t.fM || (fInvM == t.fInvM && fType == t.fType && fScale == t.fScale));
109     return fM == t.fM;
110 }
111 
mapRect(const Rect & rect) const112 Rect Transform::mapRect(const Rect& rect) const { return map_rect(fM, rect); }
inverseMapRect(const Rect & rect) const113 Rect Transform::inverseMapRect(const Rect& rect) const { return map_rect(fInvM, rect); }
114 
mapPoints(const Rect & localRect,SkV4 deviceOut[4]) const115 void Transform::mapPoints(const Rect& localRect, SkV4 deviceOut[4]) const {
116     SkV2 localCorners[4] = {{localRect.left(),  localRect.top()},
117                             {localRect.right(), localRect.top()},
118                             {localRect.right(), localRect.bot()},
119                             {localRect.left(),  localRect.bot()}};
120     this->mapPoints(localCorners, deviceOut, 4);
121 }
122 
mapPoints(const SkV2 * localIn,SkV4 * deviceOut,int count) const123 void Transform::mapPoints(const SkV2* localIn, SkV4* deviceOut, int count) const {
124     // TODO: These maybe should go into SkM44, since bulk point mapping seems generally useful
125     auto c0 = skvx::float4::Load(SkMatrixPriv::M44ColMajor(fM) + 0);
126     auto c1 = skvx::float4::Load(SkMatrixPriv::M44ColMajor(fM) + 4);
127     // skip c2 since localIn's z is assumed to be 0
128     auto c3 = skvx::float4::Load(SkMatrixPriv::M44ColMajor(fM) + 12);
129 
130     for (int i = 0; i < count; ++i) {
131         auto p = c0 * localIn[i].x + c1 * localIn[i].y /* + c2*0.f */ + c3 /* *1.f */;
132         p.store(deviceOut + i);
133     }
134 }
135 
mapPoints(const SkV4 * localIn,SkV4 * deviceOut,int count) const136 void Transform::mapPoints(const SkV4* localIn, SkV4* deviceOut, int count) const {
137     return map_points(fM, localIn, deviceOut, count);
138 }
139 
inverseMapPoints(const SkV4 * deviceIn,SkV4 * localOut,int count) const140 void Transform::inverseMapPoints(const SkV4* deviceIn, SkV4* localOut, int count) const {
141     return map_points(fInvM, deviceIn, localOut, count);
142 }
143 
preTranslate(float x,float y) const144 Transform Transform::preTranslate(float x, float y) const {
145     Transform t = *this;
146     t.fM.preTranslate(x, y);
147     t.fInvM.postTranslate(-x, -y);
148 
149     // Under normal conditions, type and scale won't change, but if we've overflown the translation
150     // components, mark the matrix as invalid.
151     if (!t.fM.isFinite() || !t.fInvM.isFinite()) {
152         t.fType = Type::kInvalid;
153     }
154     return t;
155 }
156 
postTranslate(float x,float y) const157 Transform Transform::postTranslate(float x, float y) const {
158     Transform t = *this;
159     t.fM.postTranslate(x, y);
160     t.fInvM.preTranslate(-x, -y);
161     if (!t.fM.isFinite() || !t.fInvM.isFinite()) {
162         t.fType = Type::kInvalid;
163     }
164     return t;
165 }
166 
concat(const Transform & t) const167 Transform Transform::concat(const Transform& t) const {
168     Transform c = {fM * t.fM, t.fInvM * fInvM, std::max(fType, t.fType), {fScale * t.fScale}};
169     if (!c.fM.isFinite() || !c.fInvM.isFinite()) {
170         c.fType = Type::kInvalid;
171     }
172     return c;
173 }
174 
concatInverse(const Transform & t) const175 Transform Transform::concatInverse(const Transform& t) const {
176     Transform c = {fM * t.fInvM, t.fM * fInvM, std::max(fType, t.fType), {fScale * (1.f/t.fScale)}};
177     if (!c.fM.isFinite() || !c.fInvM.isFinite()) {
178         c.fType = Type::kInvalid;
179     }
180     return c;
181 }
182 
concatInverse(const SkM44 & t) const183 Transform Transform::concatInverse(const SkM44& t) const {
184     // saves a multiply compared to inverting just t and then computing fM*t^-1 and t*fInvM, if we
185     // instead start with (t*fInvM) and swap definition of computed fM and fInvM.
186     Transform inverse{t * fInvM};
187     return {inverse.fInvM, inverse.fM, inverse.fType, 1.f / inverse.fScale};
188 }
189 
190 } // namespace skgpu::graphite
191