Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
304SkM44 SkM44::RectToRect(const SkRect& src, const SkRect& dst) {
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 void normalize(int n, double *gauss)
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:3824
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:40
float SkScalar
Definition extension.cpp:12
static bool b
struct MyStruct s
struct MyStruct a[10]
gboolean invert
static float min(float r, float g, float b)
Definition hsl.cpp:48
double y
double x
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
static SKVX_ALWAYS_INLINE Vec Load(const void *ptr)
Definition SkVx.h:109