Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/core/SkM44.cpp
Line
Count
Source (jump to first uncovered line)
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/core/SkRect.h"
11
#include "include/private/base/SkDebug.h"
12
#include "include/private/base/SkFloatingPoint.h"
13
#include "src/base/SkVx.h"
14
#include "src/core/SkMatrixInvert.h"
15
#include "src/core/SkMatrixPriv.h"
16
#include "src/core/SkPathPriv.h"
17
18
26.7k
bool SkM44::operator==(const SkM44& other) const {
19
26.7k
    if (this == &other) {
20
0
        return true;
21
0
    }
22
23
26.7k
    auto a0 = skvx::float4::Load(fMat +  0);
24
26.7k
    auto a1 = skvx::float4::Load(fMat +  4);
25
26.7k
    auto a2 = skvx::float4::Load(fMat +  8);
26
26.7k
    auto a3 = skvx::float4::Load(fMat + 12);
27
28
26.7k
    auto b0 = skvx::float4::Load(other.fMat +  0);
29
26.7k
    auto b1 = skvx::float4::Load(other.fMat +  4);
30
26.7k
    auto b2 = skvx::float4::Load(other.fMat +  8);
31
26.7k
    auto b3 = skvx::float4::Load(other.fMat + 12);
32
33
26.7k
    auto eq = (a0 == b0) & (a1 == b1) & (a2 == b2) & (a3 == b3);
34
26.7k
    return (eq[0] & eq[1] & eq[2] & eq[3]) == ~0;
35
26.7k
}
36
37
0
static void transpose_arrays(SkScalar dst[], const SkScalar src[]) {
38
0
    dst[0]  = src[0]; dst[1]  = src[4]; dst[2]  = src[8];  dst[3]  = src[12];
39
0
    dst[4]  = src[1]; dst[5]  = src[5]; dst[6]  = src[9];  dst[7]  = src[13];
40
0
    dst[8]  = src[2]; dst[9]  = src[6]; dst[10] = src[10]; dst[11] = src[14];
41
0
    dst[12] = src[3]; dst[13] = src[7]; dst[14] = src[11]; dst[15] = src[15];
42
0
}
43
44
0
void SkM44::getRowMajor(SkScalar v[]) const {
45
0
    transpose_arrays(v, fMat);
46
0
}
47
48
1.01M
SkM44& SkM44::setConcat(const SkM44& a, const SkM44& b) {
49
1.01M
    auto c0 = skvx::float4::Load(a.fMat +  0);
50
1.01M
    auto c1 = skvx::float4::Load(a.fMat +  4);
51
1.01M
    auto c2 = skvx::float4::Load(a.fMat +  8);
52
1.01M
    auto c3 = skvx::float4::Load(a.fMat + 12);
53
54
4.05M
    auto compute = [&](skvx::float4 r) {
55
4.05M
        return c0*r[0] + (c1*r[1] + (c2*r[2] + c3*r[3]));
56
4.05M
    };
57
58
1.01M
    auto m0 = compute(skvx::float4::Load(b.fMat +  0));
59
1.01M
    auto m1 = compute(skvx::float4::Load(b.fMat +  4));
60
1.01M
    auto m2 = compute(skvx::float4::Load(b.fMat +  8));
61
1.01M
    auto m3 = compute(skvx::float4::Load(b.fMat + 12));
62
63
1.01M
    m0.store(fMat +  0);
64
1.01M
    m1.store(fMat +  4);
65
1.01M
    m2.store(fMat +  8);
66
1.01M
    m3.store(fMat + 12);
67
1.01M
    return *this;
68
1.01M
}
69
70
0
SkM44& SkM44::preConcat(const SkMatrix& b) {
71
0
    auto c0 = skvx::float4::Load(fMat +  0);
72
0
    auto c1 = skvx::float4::Load(fMat +  4);
73
0
    auto c3 = skvx::float4::Load(fMat + 12);
74
75
0
    auto compute = [&](float r0, float r1, float r3) {
76
0
        return (c0*r0 + (c1*r1 + c3*r3));
77
0
    };
78
79
0
    auto m0 = compute(b[0], b[3], b[6]);
80
0
    auto m1 = compute(b[1], b[4], b[7]);
81
0
    auto m3 = compute(b[2], b[5], b[8]);
82
83
0
    m0.store(fMat +  0);
84
0
    m1.store(fMat +  4);
85
0
    m3.store(fMat + 12);
86
0
    return *this;
87
0
}
88
89
177k
SkM44& SkM44::preTranslate(SkScalar x, SkScalar y, SkScalar z) {
90
177k
    auto c0 = skvx::float4::Load(fMat +  0);
91
177k
    auto c1 = skvx::float4::Load(fMat +  4);
92
177k
    auto c2 = skvx::float4::Load(fMat +  8);
93
177k
    auto c3 = skvx::float4::Load(fMat + 12);
94
95
    // only need to update the last column
96
177k
    (c0*x + (c1*y + (c2*z + c3))).store(fMat + 12);
97
177k
    return *this;
98
177k
}
99
100
118k
SkM44& SkM44::postTranslate(SkScalar x, SkScalar y, SkScalar z) {
101
118k
    skvx::float4 t = { x, y, z, 0 };
102
118k
    (t * fMat[ 3] + skvx::float4::Load(fMat +  0)).store(fMat +  0);
103
118k
    (t * fMat[ 7] + skvx::float4::Load(fMat +  4)).store(fMat +  4);
104
118k
    (t * fMat[11] + skvx::float4::Load(fMat +  8)).store(fMat +  8);
105
118k
    (t * fMat[15] + skvx::float4::Load(fMat + 12)).store(fMat + 12);
106
118k
    return *this;
107
118k
}
108
109
7.11k
SkM44& SkM44::preScale(SkScalar x, SkScalar y) {
110
7.11k
    auto c0 = skvx::float4::Load(fMat +  0);
111
7.11k
    auto c1 = skvx::float4::Load(fMat +  4);
112
113
7.11k
    (c0 * x).store(fMat + 0);
114
7.11k
    (c1 * y).store(fMat + 4);
115
7.11k
    return *this;
116
7.11k
}
117
118
0
SkM44& SkM44::preScale(SkScalar x, SkScalar y, SkScalar z) {
119
0
    auto c0 = skvx::float4::Load(fMat +  0);
120
0
    auto c1 = skvx::float4::Load(fMat +  4);
121
0
    auto c2 = skvx::float4::Load(fMat +  8);
122
123
0
    (c0 * x).store(fMat + 0);
124
0
    (c1 * y).store(fMat + 4);
125
0
    (c2 * z).store(fMat + 8);
126
0
    return *this;
127
0
}
128
129
0
SkV4 SkM44::map(float x, float y, float z, float w) const {
130
0
    auto c0 = skvx::float4::Load(fMat +  0);
131
0
    auto c1 = skvx::float4::Load(fMat +  4);
132
0
    auto c2 = skvx::float4::Load(fMat +  8);
133
0
    auto c3 = skvx::float4::Load(fMat + 12);
134
135
0
    SkV4 v;
136
0
    (c0*x + (c1*y + (c2*z + c3*w))).store(&v.x);
137
0
    return v;
138
0
}
139
140
1.21M
static 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
1.21M
    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
1.21M
    auto c0 = skvx::shuffle<0,1,0,1>(skvx::float2::Load(mat + 0)) * flip;
148
1.21M
    auto c1 = skvx::shuffle<0,1,0,1>(skvx::float2::Load(mat + 4)) * flip;
149
1.21M
    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
1.21M
    auto minMax = c3 + flip * min(min(c0 * src.fLeft  + c1 * src.fTop,
154
1.21M
                                      c0 * src.fRight + c1 * src.fTop),
155
1.21M
                                  min(c0 * src.fLeft  + c1 * src.fBottom,
156
1.21M
                                      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
1.21M
    SkRect r;
160
1.21M
    minMax.store(&r);
161
1.21M
    return r;
162
1.21M
}
163
164
54.2k
static 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
54.2k
    auto c0 = skvx::float4::Load(mat + 0);
168
54.2k
    auto c1 = skvx::float4::Load(mat + 4);
169
54.2k
    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
54.2k
    auto tl = c0 * src.fLeft  + c1 * src.fTop    + c3;
174
54.2k
    auto tr = c0 * src.fRight + c1 * src.fTop    + c3;
175
54.2k
    auto bl = c0 * src.fLeft  + c1 * src.fBottom + c3;
176
54.2k
    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
54.2k
    const skvx::float4 flip{1.f, 1.f, -1.f, -1.f};
181
217k
    auto project = [&flip](const skvx::float4& p0, const skvx::float4& p1, const skvx::float4& p2) {
182
217k
        float w0 = p0[3];
183
217k
        if (w0 >= SkPathPriv::kW0PlaneDistance) {
184
            // Unclipped, just divide by w
185
125k
            return flip * skvx::shuffle<0,1,0,1>(p0) / w0;
186
125k
        } else {
187
182k
            auto clip = [&](const skvx::float4& p) {
188
182k
                float w = p[3];
189
182k
                if (w >= SkPathPriv::kW0PlaneDistance) {
190
63.9k
                    float t = (SkPathPriv::kW0PlaneDistance - w0) / (w - w0);
191
63.9k
                    auto c = (t * skvx::shuffle<0,1>(p) + (1.f - t) * skvx::shuffle<0,1>(p0)) /
192
63.9k
                                  SkPathPriv::kW0PlaneDistance;
193
194
63.9k
                    return flip * skvx::shuffle<0,1,0,1>(c);
195
118k
                } else {
196
118k
                    return skvx::float4(SK_ScalarInfinity);
197
118k
                }
198
182k
            };
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
91.1k
            return min(clip(p1), clip(p2));
203
91.1k
        }
204
217k
    };
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
54.2k
    auto minMax = flip * min(min(project(tl, tr, bl), project(tr, br, tl)),
209
54.2k
                             min(project(br, bl, tr), project(bl, tl, br)));
210
211
54.2k
    SkRect r;
212
54.2k
    minMax.store(&r);
213
54.2k
    return r;
214
54.2k
}
215
216
1.27M
SkRect SkMatrixPriv::MapRect(const SkM44& m, const SkRect& src) {
217
1.27M
    const bool hasPerspective =
218
1.27M
            m.fMat[3] != 0 || m.fMat[7] != 0 || m.fMat[11] != 0 || m.fMat[15] != 1;
219
1.27M
    if (hasPerspective) {
220
54.2k
        return map_rect_perspective(src, m.fMat);
221
1.21M
    } else {
222
1.21M
        return map_rect_affine(src, m.fMat);
223
1.21M
    }
224
1.27M
}
225
226
1.71M
void SkM44::normalizePerspective() {
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
1.71M
    if (fMat[15] != 1 && fMat[15] != 0 && fMat[3] == 0 && fMat[7] == 0 && fMat[11] == 0) {
232
6.74k
        double inv = 1.0 / fMat[15];
233
6.74k
        (skvx::float4::Load(fMat +  0) * inv).store(fMat +  0);
234
6.74k
        (skvx::float4::Load(fMat +  4) * inv).store(fMat +  4);
235
6.74k
        (skvx::float4::Load(fMat +  8) * inv).store(fMat +  8);
236
6.74k
        (skvx::float4::Load(fMat + 12) * inv).store(fMat + 12);
237
6.74k
        fMat[15] = 1.0f;
238
6.74k
    }
239
1.71M
}
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
 */
247
1.96k
bool SkM44::invert(SkM44* inverse) const {
248
1.96k
    SkScalar tmp[16];
249
1.96k
    if (SkInvert4x4Matrix(fMat, tmp) == 0.0f) {
250
11
        return false;
251
11
    }
252
1.95k
    memcpy(inverse->fMat, tmp, sizeof(tmp));
253
1.95k
    return true;
254
1.96k
}
255
256
0
SkM44 SkM44::transpose() const {
257
0
    SkM44 trans(SkM44::kUninitialized_Constructor);
258
0
    transpose_arrays(trans.fMat, fMat);
259
0
    return trans;
260
0
}
261
262
86.0k
SkM44& SkM44::setRotateUnitSinCos(SkV3 axis, SkScalar sinAngle, SkScalar cosAngle) {
263
    // Taken from "Essential Mathematics for Games and Interactive Applications"
264
    //             James M. Van Verth and Lars M. Bishop -- third edition
265
86.0k
    SkScalar x = axis.x;
266
86.0k
    SkScalar y = axis.y;
267
86.0k
    SkScalar z = axis.z;
268
86.0k
    SkScalar c = cosAngle;
269
86.0k
    SkScalar s = sinAngle;
270
86.0k
    SkScalar t = 1 - c;
271
272
86.0k
    *this = { t*x*x + c,   t*x*y - s*z, t*x*z + s*y, 0,
273
86.0k
              t*x*y + s*z, t*y*y + c,   t*y*z - s*x, 0,
274
86.0k
              t*x*z - s*y, t*y*z + s*x, t*z*z + c,   0,
275
86.0k
              0,           0,           0,           1 };
276
86.0k
    return *this;
277
86.0k
}
278
279
86.0k
SkM44& SkM44::setRotate(SkV3 axis, SkScalar radians) {
280
86.0k
    SkScalar len = axis.length();
281
86.0k
    if (len > 0 && SkIsFinite(len)) {
282
86.0k
        this->setRotateUnit(axis * (SK_Scalar1 / len), radians);
283
86.0k
    } else {
284
0
        this->setIdentity();
285
0
    }
286
86.0k
    return *this;
287
86.0k
}
288
289
///////////////////////////////////////////////////////////////////////////////
290
291
0
void SkM44::dump() const {
292
0
    SkDebugf("|%g %g %g %g|\n"
293
0
             "|%g %g %g %g|\n"
294
0
             "|%g %g %g %g|\n"
295
0
             "|%g %g %g %g|\n",
296
0
             fMat[0], fMat[4], fMat[8],  fMat[12],
297
0
             fMat[1], fMat[5], fMat[9],  fMat[13],
298
0
             fMat[2], fMat[6], fMat[10], fMat[14],
299
0
             fMat[3], fMat[7], fMat[11], fMat[15]);
300
0
}
301
302
///////////////////////////////////////////////////////////////////////////////
303
304
0
SkM44 SkM44::RectToRect(const SkRect& src, const SkRect& dst) {
305
0
        if (src.isEmpty()) {
306
0
        return SkM44();
307
0
    } else if (dst.isEmpty()) {
308
0
        return SkM44::Scale(0.f, 0.f, 0.f);
309
0
    }
310
311
0
    float sx = dst.width()  / src.width();
312
0
    float sy = dst.height() / src.height();
313
314
0
    float tx = dst.fLeft - sx * src.fLeft;
315
0
    float ty = dst.fTop  - sy * src.fTop;
316
317
0
    return SkM44{sx,  0.f, 0.f, tx,
318
0
                 0.f, sy,  0.f, ty,
319
0
                 0.f, 0.f, 1.f, 0.f,
320
0
                 0.f, 0.f, 0.f, 1.f};
321
0
}
322
323
5.88k
static SkV3 normalize(SkV3 v) {
324
5.88k
    const auto vlen = v.length();
325
326
5.88k
    return SkScalarNearlyZero(vlen) ? v : v * (1.0f / vlen);
327
5.88k
}
328
329
7.84k
static SkV4 v4(SkV3 v, SkScalar w) { return {v.x, v.y, v.z, w}; }
330
331
1.96k
SkM44 SkM44::LookAt(const SkV3& eye, const SkV3& center, const SkV3& up) {
332
1.96k
    SkV3 f = normalize(center - eye);
333
1.96k
    SkV3 u = normalize(up);
334
1.96k
    SkV3 s = normalize(f.cross(u));
335
336
1.96k
    SkM44 m(SkM44::kUninitialized_Constructor);
337
1.96k
    if (!SkM44::Cols(v4(s, 0), v4(s.cross(f), 0), v4(-f, 0), v4(eye, 1)).invert(&m)) {
338
11
        m.setIdentity();
339
11
    }
340
1.96k
    return m;
341
1.96k
}
342
343
1.96k
SkM44 SkM44::Perspective(float near, float far, float angle) {
344
1.96k
    SkASSERT(far > near);
345
346
1.96k
    float denomInv = sk_ieee_float_divide(1, far - near);
347
1.96k
    float halfAngle = angle * 0.5f;
348
1.96k
    SkASSERT(halfAngle != 0);
349
1.96k
    float cot = sk_ieee_float_divide(1, std::tan(halfAngle));
350
351
1.96k
    SkM44 m;
352
1.96k
    m.setRC(0, 0, cot);
353
1.96k
    m.setRC(1, 1, cot);
354
1.96k
    m.setRC(2, 2, (far + near) * denomInv);
355
1.96k
    m.setRC(2, 3, 2 * far * near * denomInv);
356
1.96k
    m.setRC(3, 2, -1);
357
1.96k
    return m;
358
1.96k
}