Flutter Engine
The Flutter Engine
SkM44.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 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
10#include "include/core/SkRect.h"
13#include "src/base/SkVx.h"
16#include "src/core/SkPathPriv.h"
17
18bool SkM44::operator==(const SkM44& other) const {
19 if (this == &other) {
20 return true;
21 }
22
23 auto a0 = skvx::float4::Load(fMat + 0);
24 auto a1 = skvx::float4::Load(fMat + 4);
25 auto a2 = skvx::float4::Load(fMat + 8);
26 auto a3 = skvx::float4::Load(fMat + 12);
27
28 auto b0 = skvx::float4::Load(other.fMat + 0);
29 auto b1 = skvx::float4::Load(other.fMat + 4);
30 auto b2 = skvx::float4::Load(other.fMat + 8);
31 auto b3 = skvx::float4::Load(other.fMat + 12);
32
33 auto eq = (a0 == b0) & (a1 == b1) & (a2 == b2) & (a3 == b3);
34 return (eq[0] & eq[1] & eq[2] & eq[3]) == ~0;
35}
36
37static void transpose_arrays(SkScalar dst[], const SkScalar src[]) {
38 dst[0] = src[0]; dst[1] = src[4]; dst[2] = src[8]; dst[3] = src[12];
39 dst[4] = src[1]; dst[5] = src[5]; dst[6] = src[9]; dst[7] = src[13];
40 dst[8] = src[2]; dst[9] = src[6]; dst[10] = src[10]; dst[11] = src[14];
41 dst[12] = src[3]; dst[13] = src[7]; dst[14] = src[11]; dst[15] = src[15];
42}
43
45 transpose_arrays(v, fMat);
46}
47
48SkM44& SkM44::setConcat(const SkM44& a, const SkM44& b) {
49 auto c0 = skvx::float4::Load(a.fMat + 0);
50 auto c1 = skvx::float4::Load(a.fMat + 4);
51 auto c2 = skvx::float4::Load(a.fMat + 8);
52 auto c3 = skvx::float4::Load(a.fMat + 12);
53
54 auto compute = [&](skvx::float4 r) {
55 return c0*r[0] + (c1*r[1] + (c2*r[2] + c3*r[3]));
56 };
57
58 auto m0 = compute(skvx::float4::Load(b.fMat + 0));
59 auto m1 = compute(skvx::float4::Load(b.fMat + 4));
60 auto m2 = compute(skvx::float4::Load(b.fMat + 8));
61 auto m3 = compute(skvx::float4::Load(b.fMat + 12));
62
63 m0.store(fMat + 0);
64 m1.store(fMat + 4);
65 m2.store(fMat + 8);
66 m3.store(fMat + 12);
67 return *this;
68}
69
71 auto c0 = skvx::float4::Load(fMat + 0);
72 auto c1 = skvx::float4::Load(fMat + 4);
73 auto c3 = skvx::float4::Load(fMat + 12);
74
75 auto compute = [&](float r0, float r1, float r3) {
76 return (c0*r0 + (c1*r1 + c3*r3));
77 };
78
79 auto m0 = compute(b[0], b[3], b[6]);
80 auto m1 = compute(b[1], b[4], b[7]);
81 auto m3 = compute(b[2], b[5], b[8]);
82
83 m0.store(fMat + 0);
84 m1.store(fMat + 4);
85 m3.store(fMat + 12);
86 return *this;
87}
88
90 auto c0 = skvx::float4::Load(fMat + 0);
91 auto c1 = skvx::float4::Load(fMat + 4);
92 auto c2 = skvx::float4::Load(fMat + 8);
93 auto c3 = skvx::float4::Load(fMat + 12);
94
95 // only need to update the last column
96 (c0*x + (c1*y + (c2*z + c3))).store(fMat + 12);
97 return *this;
98}
99
101 skvx::float4 t = { x, y, z, 0 };
102 (t * fMat[ 3] + skvx::float4::Load(fMat + 0)).store(fMat + 0);
103 (t * fMat[ 7] + skvx::float4::Load(fMat + 4)).store(fMat + 4);
104 (t * fMat[11] + skvx::float4::Load(fMat + 8)).store(fMat + 8);
105 (t * fMat[15] + skvx::float4::Load(fMat + 12)).store(fMat + 12);
106 return *this;
107}
108
110 auto c0 = skvx::float4::Load(fMat + 0);
111 auto c1 = skvx::float4::Load(fMat + 4);
112
113 (c0 * x).store(fMat + 0);
114 (c1 * y).store(fMat + 4);
115 return *this;
116}
117
119 auto c0 = skvx::float4::Load(fMat + 0);
120 auto c1 = skvx::float4::Load(fMat + 4);
121 auto c2 = skvx::float4::Load(fMat + 8);
122
123 (c0 * x).store(fMat + 0);
124 (c1 * y).store(fMat + 4);
125 (c2 * z).store(fMat + 8);
126 return *this;
127}
128
129SkV4 SkM44::map(float x, float y, float z, float w) const {
130 auto c0 = skvx::float4::Load(fMat + 0);
131 auto c1 = skvx::float4::Load(fMat + 4);
132 auto c2 = skvx::float4::Load(fMat + 8);
133 auto c3 = skvx::float4::Load(fMat + 12);
134
135 SkV4 v;
136 (c0*x + (c1*y + (c2*z + c3*w))).store(&v.x);
137 return v;
138}
139
140static SkRect map_rect_affine(const SkRect& src, const float mat[16]) {
141 // When multiplied against vectors of the form <x,y,x,y>, 'flip' allows a single min()
142 // to compute both the min and "negated" max between the xy coordinates. Once finished, another
143 // multiplication produces the original max.
144 const skvx::float4 flip{1.f, 1.f, -1.f, -1.f};
145
146 // Since z = 0 and it's assumed ther's no perspective, only load the upper 2x2 and (tx,ty) in c3
147 auto c0 = skvx::shuffle<0,1,0,1>(skvx::float2::Load(mat + 0)) * flip;
148 auto c1 = skvx::shuffle<0,1,0,1>(skvx::float2::Load(mat + 4)) * flip;
149 auto c3 = skvx::shuffle<0,1,0,1>(skvx::float2::Load(mat + 12));
150
151 // Compute the min and max of the four transformed corners pre-translation; then translate once
152 // at the end.
153 auto minMax = c3 + flip * min(min(c0 * src.fLeft + c1 * src.fTop,
154 c0 * src.fRight + c1 * src.fTop),
155 min(c0 * src.fLeft + c1 * src.fBottom,
156 c0 * src.fRight + c1 * src.fBottom));
157
158 // minMax holds (min x, min y, max x, max y) so can be copied into an SkRect expecting l,t,r,b
159 SkRect r;
160 minMax.store(&r);
161 return r;
162}
163
164static SkRect map_rect_perspective(const SkRect& src, const float mat[16]) {
165 // Like map_rect_affine, z = 0 so we can skip the 3rd column, but we do need to compute w's
166 // for each corner of the src rect.
167 auto c0 = skvx::float4::Load(mat + 0);
168 auto c1 = skvx::float4::Load(mat + 4);
169 auto c3 = skvx::float4::Load(mat + 12);
170
171 // Unlike map_rect_affine, we do not defer the 4th column since we may need to homogeneous
172 // coordinates to clip against the w=0 plane
173 auto tl = c0 * src.fLeft + c1 * src.fTop + c3;
174 auto tr = c0 * src.fRight + c1 * src.fTop + c3;
175 auto bl = c0 * src.fLeft + c1 * src.fBottom + c3;
176 auto br = c0 * src.fRight + c1 * src.fBottom + c3;
177
178 // After clipping to w>0 and projecting to 2d, 'project' employs the same negation trick to
179 // compute min and max at the same time.
180 const skvx::float4 flip{1.f, 1.f, -1.f, -1.f};
181 auto project = [&flip](const skvx::float4& p0, const skvx::float4& p1, const skvx::float4& p2) {
182 float w0 = p0[3];
184 // Unclipped, just divide by w
185 return flip * skvx::shuffle<0,1,0,1>(p0) / w0;
186 } else {
187 auto clip = [&](const skvx::float4& p) {
188 float w = p[3];
190 float t = (SkPathPriv::kW0PlaneDistance - w0) / (w - w0);
191 auto c = (t * skvx::shuffle<0,1>(p) + (1.f - t) * skvx::shuffle<0,1>(p0)) /
193
194 return flip * skvx::shuffle<0,1,0,1>(c);
195 } else {
197 }
198 };
199 // Clip both edges leaving p0, and return the min/max of the two clipped points
200 // (since clip returns infinity when both p0 and 2nd vertex have w<0, it'll
201 // automatically be ignored).
202 return min(clip(p1), clip(p2));
203 }
204 };
205
206 // Project all 4 corners, and pass in their adjacent vertices for clipping if it has w < 0,
207 // then accumulate the min and max xy's.
208 auto minMax = flip * min(min(project(tl, tr, bl), project(tr, br, tl)),
209 min(project(br, bl, tr), project(bl, tl, br)));
210
211 SkRect r;
212 minMax.store(&r);
213 return r;
214}
215
217 const bool hasPerspective =
218 m.fMat[3] != 0 || m.fMat[7] != 0 || m.fMat[11] != 0 || m.fMat[15] != 1;
219 if (hasPerspective) {
220 return map_rect_perspective(src, m.fMat);
221 } else {
222 return map_rect_affine(src, m.fMat);
223 }
224}
225
227 // If the bottom row of the matrix is [0, 0, 0, not_one], we will treat the matrix as if it
228 // is in perspective, even though it stills behaves like its affine. If we divide everything
229 // by the not_one value, then it will behave the same, but will be treated as affine,
230 // and therefore faster (e.g. clients can forward-difference calculations).
231 if (fMat[15] != 1 && fMat[15] != 0 && fMat[3] == 0 && fMat[7] == 0 && fMat[11] == 0) {
232 double inv = 1.0 / fMat[15];
233 (skvx::float4::Load(fMat + 0) * inv).store(fMat + 0);
234 (skvx::float4::Load(fMat + 4) * inv).store(fMat + 4);
235 (skvx::float4::Load(fMat + 8) * inv).store(fMat + 8);
236 (skvx::float4::Load(fMat + 12) * inv).store(fMat + 12);
237 fMat[15] = 1.0f;
238 }
239}
240
241///////////////////////////////////////////////////////////////////////////////
242
243/** We always perform the calculation in doubles, to avoid prematurely losing
244 precision along the way. This relies on the compiler automatically
245 promoting our SkScalar values to double (if needed).
246 */
247bool SkM44::invert(SkM44* inverse) const {
248 SkScalar tmp[16];
249 if (SkInvert4x4Matrix(fMat, tmp) == 0.0f) {
250 return false;
251 }
252 memcpy(inverse->fMat, tmp, sizeof(tmp));
253 return true;
254}
255
258 transpose_arrays(trans.fMat, fMat);
259 return trans;
260}
261
263 // Taken from "Essential Mathematics for Games and Interactive Applications"
264 // James M. Van Verth and Lars M. Bishop -- third edition
265 SkScalar x = axis.x;
266 SkScalar y = axis.y;
267 SkScalar z = axis.z;
268 SkScalar c = cosAngle;
269 SkScalar s = sinAngle;
270 SkScalar t = 1 - c;
271
272 *this = { t*x*x + c, t*x*y - s*z, t*x*z + s*y, 0,
273 t*x*y + s*z, t*y*y + c, t*y*z - s*x, 0,
274 t*x*z - s*y, t*y*z + s*x, t*z*z + c, 0,
275 0, 0, 0, 1 };
276 return *this;
277}
278
280 SkScalar len = axis.length();
281 if (len > 0 && SkIsFinite(len)) {
282 this->setRotateUnit(axis * (SK_Scalar1 / len), radians);
283 } else {
284 this->setIdentity();
285 }
286 return *this;
287}
288
289///////////////////////////////////////////////////////////////////////////////
290
291void SkM44::dump() const {
292 SkDebugf("|%g %g %g %g|\n"
293 "|%g %g %g %g|\n"
294 "|%g %g %g %g|\n"
295 "|%g %g %g %g|\n",
296 fMat[0], fMat[4], fMat[8], fMat[12],
297 fMat[1], fMat[5], fMat[9], fMat[13],
298 fMat[2], fMat[6], fMat[10], fMat[14],
299 fMat[3], fMat[7], fMat[11], fMat[15]);
300}
301
302///////////////////////////////////////////////////////////////////////////////
303
305 if (src.isEmpty()) {
306 return SkM44();
307 } else if (dst.isEmpty()) {
308 return SkM44::Scale(0.f, 0.f, 0.f);
309 }
310
311 float sx = dst.width() / src.width();
312 float sy = dst.height() / src.height();
313
314 float tx = dst.fLeft - sx * src.fLeft;
315 float ty = dst.fTop - sy * src.fTop;
316
317 return SkM44{sx, 0.f, 0.f, tx,
318 0.f, sy, 0.f, ty,
319 0.f, 0.f, 1.f, 0.f,
320 0.f, 0.f, 0.f, 1.f};
321}
322
323static SkV3 normalize(SkV3 v) {
324 const auto vlen = v.length();
325
326 return SkScalarNearlyZero(vlen) ? v : v * (1.0f / vlen);
327}
328
329static SkV4 v4(SkV3 v, SkScalar w) { return {v.x, v.y, v.z, w}; }
330
331SkM44 SkM44::LookAt(const SkV3& eye, const SkV3& center, const SkV3& up) {
332 SkV3 f = normalize(center - eye);
333 SkV3 u = normalize(up);
334 SkV3 s = normalize(f.cross(u));
335
337 if (!SkM44::Cols(v4(s, 0), v4(s.cross(f), 0), v4(-f, 0), v4(eye, 1)).invert(&m)) {
338 m.setIdentity();
339 }
340 return m;
341}
342
343SkM44 SkM44::Perspective(float near, float far, float angle) {
344 SkASSERT(far > near);
345
346 float denomInv = sk_ieee_float_divide(1, far - near);
347 float halfAngle = angle * 0.5f;
348 SkASSERT(halfAngle != 0);
349 float cot = sk_ieee_float_divide(1, std::tan(halfAngle));
350
351 SkM44 m;
352 m.setRC(0, 0, cot);
353 m.setRC(1, 1, cot);
354 m.setRC(2, 2, (far + near) * denomInv);
355 m.setRC(2, 3, 2 * far * near * denomInv);
356 m.setRC(3, 2, -1);
357 return m;
358}
static SkM44 inv(const SkM44 &m)
Definition: 3d.cpp:26
static bool eq(const SkM44 &a, const SkM44 &b, float tol)
Definition: M44Test.cpp:18
#define SkASSERT(cond)
Definition: SkAssert.h:116
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
static bool SkIsFinite(T x, Pack... values)
static constexpr float sk_ieee_float_divide(float numer, float denom)
static SkRect map_rect_affine(const SkRect &src, const float mat[16])
Definition: SkM44.cpp:140
static void transpose_arrays(SkScalar dst[], const SkScalar src[])
Definition: SkM44.cpp:37
static SkV3 normalize(SkV3 v)
Definition: SkM44.cpp:323
static SkV4 v4(SkV3 v, SkScalar w)
Definition: SkM44.cpp:329
static SkRect map_rect_perspective(const SkRect &src, const float mat[16])
Definition: SkM44.cpp:164
SkScalar SkInvert4x4Matrix(const SkScalar inMatrix[16], SkScalar outMatrix[16])
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition: SkPath.cpp:3892
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
#define SK_Scalar1
Definition: SkScalar.h:18
#define SK_ScalarInfinity
Definition: SkScalar.h:26
static SkScalar center(float pos0, float pos1)
SI void store(P *ptr, const T &val)
Definition: SkM44.h:150
SkV4 map(float x, float y, float z, float w) const
Definition: SkM44.cpp:129
SkM44 & preScale(SkScalar x, SkScalar y)
Definition: SkM44.cpp:109
void getRowMajor(SkScalar v[]) const
Definition: SkM44.cpp:44
static SkM44 LookAt(const SkV3 &eye, const SkV3 &center, const SkV3 &up)
Definition: SkM44.cpp:331
SkM44 & postTranslate(SkScalar x, SkScalar y, SkScalar z=0)
Definition: SkM44.cpp:100
SkM44 & setRotate(SkV3 axis, SkScalar radians)
Definition: SkM44.cpp:279
void normalizePerspective()
Definition: SkM44.cpp:226
void dump() const
Definition: SkM44.cpp:291
SkM44 transpose() const
Definition: SkM44.cpp:256
@ kUninitialized_Constructor
Definition: SkM44.h:167
SkM44 & preConcat(const SkM44 &m)
Definition: SkM44.h:351
static SkM44 RectToRect(const SkRect &src, const SkRect &dst)
Definition: SkM44.cpp:304
SkM44 & setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle)
Definition: SkM44.cpp:262
bool operator==(const SkM44 &other) const
Definition: SkM44.cpp:18
bool invert(SkM44 *inverse) const
Definition: SkM44.cpp:247
SkM44 & setRotateUnit(SkV3 axis, SkScalar radians)
Definition: SkM44.h:332
static SkM44 Perspective(float near, float far, float angle)
Definition: SkM44.cpp:343
static SkM44 Scale(SkScalar x, SkScalar y, SkScalar z=1)
Definition: SkM44.h:232
constexpr SkM44()
Definition: SkM44.h:155
SkM44 & setIdentity()
Definition: SkM44.h:293
static SkM44 Cols(const SkV4 &c0, const SkV4 &c1, const SkV4 &c2, const SkV4 &c3)
Definition: SkM44.h:203
SkM44 & preTranslate(SkScalar x, SkScalar y, SkScalar z=0)
Definition: SkM44.cpp:89
SkM44 & setConcat(const SkM44 &a, const SkM44 &b)
Definition: SkM44.cpp:48
static SkRect MapRect(const SkM44 &m, const SkRect &r)
Definition: SkM44.cpp:216
static constexpr SkScalar kW0PlaneDistance
Definition: SkPathPriv.h:41
float SkScalar
Definition: extension.cpp:12
static bool b
struct MyStruct s
struct MyStruct a[10]
static float min(float r, float g, float b)
Definition: hsl.cpp:48
double y
double x
dst
Definition: cp.py:12
Vec< 4, float > float4
Definition: SkVx.h:1146
SkScalar w
Definition: SkM44.h:56
float y
Definition: SkM44.h:57
float z
Definition: SkM44.h:57
float x
Definition: SkM44.h:57
SkScalar length() const
Definition: SkM44.h:88
Definition: SkM44.h:98
float x
Definition: SkM44.h:99
Definition: SkVx.h:83
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition: SkVx.h:109