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