Coverage Report

Created: 2025-11-16 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibGfx/Matrix.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2020, the SerenityOS developers.
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#pragma once
8
9
#include <AK/Types.h>
10
#include <initializer_list>
11
12
namespace Gfx {
13
14
template<size_t N, typename T>
15
class Matrix {
16
    template<size_t U, typename V>
17
    friend class Matrix;
18
19
public:
20
    static constexpr size_t Size = N;
21
22
    constexpr Matrix() = default;
23
    constexpr Matrix(std::initializer_list<T> elements)
24
0
    {
25
0
        VERIFY(elements.size() == N * N);
26
0
        size_t i = 0;
27
0
        for (auto& element : elements) {
28
0
            m_elements[i / N][i % N] = element;
29
0
            ++i;
30
0
        }
31
0
    }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::Matrix(std::initializer_list<float>)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix(std::initializer_list<float>)
Unexecuted instantiation: Gfx::Matrix<4ul, double>::Matrix(std::initializer_list<double>)
32
33
    template<typename... Args>
34
    constexpr Matrix(Args... args)
35
0
        : Matrix({ (T)args... })
36
0
    {
37
0
    }
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, int, int, int, int, int, int, int, int, int, float, int>(int, int, int, int, int, int, int, int, int, int, int, int, int, int, float, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, float, int, float, float, float, int, float, int, int, int, int, int, int, int, int>(float, float, int, float, float, float, int, float, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float>(float, float, float, float, float, float, float, float, float, float, float, float, float, float, float, float)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, float, int, int, int, int, int, int, int, int, int, int, int, int>(int, int, int, float, int, int, int, int, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, float, int, int, int, float, int, int, int, int, int, int, int, int>(int, int, int, float, int, int, int, float, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, float, int, int, int, float, int, int, int, float, int, int, int, int>(int, int, int, float, int, int, int, float, int, int, int, float, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, int, int, float, int, int, int, int, int, int, int, int>(int, int, int, int, int, int, int, float, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, int, int, int, int, int, int, float, int, int, int, int>(int, int, int, int, int, int, int, int, int, int, int, float, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, int, int, int, int, float, int, int, int, int, int, int, int, int, int, int>(float, int, int, int, int, float, int, int, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, int, int, int, int, float, int, int, int, int, float, int, int, int, int, int>(float, int, int, int, int, float, int, int, int, int, float, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int>(float, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, float, int, int, int, int, int, int, int, int, int, int>(int, int, int, int, int, float, int, int, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, int, int, int, int, int, int, float, int, int, int, int, int>(int, int, int, int, int, int, int, int, int, int, float, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<float, float, float, int, float, float, float, int, float, float, float, int, int, int, int, int>(float, float, float, int, float, float, float, int, float, float, float, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, float, int, int, int, int, int, int, int, int, int, int, int, int, int, int>(int, float, int, int, int, int, int, int, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, float, int, int, float, int, int, int, int, int, int, int, int, int, int, int>(int, float, int, int, float, int, int, int, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix<int, int, int, int, float, int, int, int, int, int, int, int, int, int, int, int>(int, int, int, int, float, int, int, int, int, int, int, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, double>::Matrix<int, int, int, double, int, int, int, double, int, int, int, double, int, int, int, int>(int, int, int, double, int, int, int, double, int, int, int, double, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, double>::Matrix<double, int, int, int, int, double, int, int, int, int, double, int, int, int, int, int>(double, int, int, int, int, double, int, int, int, int, double, int, int, int, int, int)
Unexecuted instantiation: Gfx::Matrix<4ul, double>::Matrix<double, double, double, int, double, double, double, int, double, double, double, int, int, int, int, int>(double, double, double, int, double, double, double, int, double, double, double, int, int, int, int, int)
38
39
    constexpr Matrix(Matrix const& other)
40
0
    {
41
0
        *this = other;
42
0
    }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::Matrix(Gfx::Matrix<3ul, float> const&)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::Matrix(Gfx::Matrix<4ul, float> const&)
Unexecuted instantiation: Gfx::Matrix<4ul, double>::Matrix(Gfx::Matrix<4ul, double> const&)
43
44
    constexpr Matrix& operator=(Matrix const& other)
45
0
    {
46
#ifndef __clang__
47
        if (is_constant_evaluated()) {
48
            for (size_t i = 0; i < N; i++) {
49
                for (size_t j = 0; j < N; j++) {
50
                    m_elements[i][j] = other.elements()[i][j];
51
                }
52
            }
53
            return *this;
54
        }
55
#endif
56
0
        __builtin_memcpy(m_elements, other.elements(), sizeof(T) * N * N);
57
0
        return *this;
58
0
    }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::operator=(Gfx::Matrix<3ul, float> const&)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::operator=(Gfx::Matrix<4ul, float> const&)
Unexecuted instantiation: Gfx::Matrix<4ul, double>::operator=(Gfx::Matrix<4ul, double> const&)
59
60
0
    constexpr auto elements() const { return m_elements; }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::elements() const
Unexecuted instantiation: Gfx::Matrix<4ul, float>::elements() const
Unexecuted instantiation: Gfx::Matrix<4ul, double>::elements() const
61
0
    constexpr auto elements() { return m_elements; }
Unexecuted instantiation: Gfx::Matrix<2ul, float>::elements()
Unexecuted instantiation: Gfx::Matrix<1ul, float>::elements()
Unexecuted instantiation: Gfx::Matrix<3ul, float>::elements()
Unexecuted instantiation: Gfx::Matrix<4ul, float>::elements()
Unexecuted instantiation: Gfx::Matrix<4ul, double>::elements()
Unexecuted instantiation: Gfx::Matrix<3ul, double>::elements()
Unexecuted instantiation: Gfx::Matrix<2ul, double>::elements()
Unexecuted instantiation: Gfx::Matrix<1ul, double>::elements()
62
63
    // FIXME: Change to multi-arg operator[] once we upgrade to C++23
64
    constexpr auto const& operator()(size_t row, size_t col) const { return m_elements[row][col]; }
65
0
    constexpr auto& operator()(size_t row, size_t col) { return m_elements[row][col]; }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::operator()(unsigned long, unsigned long)
Unexecuted instantiation: Gfx::Matrix<4ul, float>::operator()(unsigned long, unsigned long)
66
67
    [[nodiscard]] constexpr Matrix operator*(Matrix const& other) const
68
0
    {
69
0
        Matrix product;
70
0
        for (size_t i = 0; i < N; ++i) {
71
0
            for (size_t j = 0; j < N; ++j) {
72
0
                auto& element = product.m_elements[i][j];
73
74
0
                if constexpr (N == 4) {
75
0
                    element = m_elements[i][0] * other.m_elements[0][j]
76
0
                        + m_elements[i][1] * other.m_elements[1][j]
77
0
                        + m_elements[i][2] * other.m_elements[2][j]
78
0
                        + m_elements[i][3] * other.m_elements[3][j];
79
0
                } else if constexpr (N == 3) {
80
0
                    element = m_elements[i][0] * other.m_elements[0][j]
81
0
                        + m_elements[i][1] * other.m_elements[1][j]
82
0
                        + m_elements[i][2] * other.m_elements[2][j];
83
                } else if constexpr (N == 2) {
84
                    element = m_elements[i][0] * other.m_elements[0][j]
85
                        + m_elements[i][1] * other.m_elements[1][j];
86
                } else if constexpr (N == 1) {
87
                    element = m_elements[i][0] * other.m_elements[0][j];
88
                } else {
89
                    T value {};
90
                    for (size_t k = 0; k < N; ++k)
91
                        value += m_elements[i][k] * other.m_elements[k][j];
92
93
                    element = value;
94
                }
95
0
            }
96
0
        }
97
98
0
        return product;
99
0
    }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::operator*(Gfx::Matrix<3ul, float> const&) const
Unexecuted instantiation: Gfx::Matrix<4ul, float>::operator*(Gfx::Matrix<4ul, float> const&) const
Unexecuted instantiation: Gfx::Matrix<4ul, double>::operator*(Gfx::Matrix<4ul, double> const&) const
100
101
    [[nodiscard]] constexpr Matrix operator+(Matrix const& other) const
102
0
    {
103
0
        Matrix sum;
104
0
        for (size_t i = 0; i < N; ++i) {
105
0
            for (size_t j = 0; j < N; ++j)
106
0
                sum.m_elements[i][j] = m_elements[i][j] + other.m_elements[i][j];
107
0
        }
108
0
        return sum;
109
0
    }
110
111
    [[nodiscard]] constexpr Matrix operator/(T divisor) const
112
0
    {
113
0
        Matrix division;
114
0
        for (size_t i = 0; i < N; ++i) {
115
0
            for (size_t j = 0; j < N; ++j)
116
0
                division.m_elements[i][j] = m_elements[i][j] / divisor;
117
0
        }
118
0
        return division;
119
0
    }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::operator/(float) const
Unexecuted instantiation: Gfx::Matrix<4ul, float>::operator/(float) const
Unexecuted instantiation: Gfx::Matrix<4ul, double>::operator/(double) const
120
121
    [[nodiscard]] friend constexpr Matrix operator*(Matrix const& matrix, T scalar)
122
0
    {
123
0
        Matrix scaled;
124
0
        for (size_t i = 0; i < N; ++i) {
125
0
            for (size_t j = 0; j < N; ++j)
126
0
                scaled.m_elements[i][j] = matrix.m_elements[i][j] * scalar;
127
0
        }
128
0
        return scaled;
129
0
    }
130
131
    [[nodiscard]] friend constexpr Matrix operator*(T scalar, Matrix const& matrix)
132
0
    {
133
0
        return matrix * scalar;
134
0
    }
135
136
    [[nodiscard]] constexpr Matrix adjugate() const
137
0
    {
138
        if constexpr (N == 1)
139
            return Matrix(1);
140
141
0
        Matrix adjugate;
142
0
        for (size_t i = 0; i < N; ++i) {
143
0
            for (size_t j = 0; j < N; ++j) {
144
0
                int sign = (i + j) % 2 == 0 ? 1 : -1;
145
0
                adjugate.m_elements[j][i] = sign * first_minor(i, j);
146
0
            }
147
0
        }
148
0
        return adjugate;
149
0
    }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::adjugate() const
Unexecuted instantiation: Gfx::Matrix<4ul, float>::adjugate() const
Unexecuted instantiation: Gfx::Matrix<4ul, double>::adjugate() const
150
151
    [[nodiscard]] constexpr T determinant() const
152
0
    {
153
0
        if constexpr (N == 1) {
154
0
            return m_elements[0][0];
155
0
        } else {
156
0
            T result = {};
157
0
            int sign = 1;
158
0
            for (size_t j = 0; j < N; ++j) {
159
0
                result += sign * m_elements[0][j] * first_minor(0, j);
160
0
                sign *= -1;
161
0
            }
162
0
            return result;
163
0
        }
164
0
    }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::determinant() const
Unexecuted instantiation: Gfx::Matrix<2ul, float>::determinant() const
Unexecuted instantiation: Gfx::Matrix<1ul, float>::determinant() const
Unexecuted instantiation: Gfx::Matrix<4ul, float>::determinant() const
Unexecuted instantiation: Gfx::Matrix<4ul, double>::determinant() const
Unexecuted instantiation: Gfx::Matrix<3ul, double>::determinant() const
Unexecuted instantiation: Gfx::Matrix<2ul, double>::determinant() const
Unexecuted instantiation: Gfx::Matrix<1ul, double>::determinant() const
165
166
    [[nodiscard]] constexpr T first_minor(size_t skip_row, size_t skip_column) const
167
0
    {
168
0
        static_assert(N > 1);
169
0
        VERIFY(skip_row < N);
170
0
        VERIFY(skip_column < N);
171
172
0
        Matrix<N - 1, T> first_minor;
173
0
        constexpr auto new_size = N - 1;
174
0
        size_t k = 0;
175
176
0
        for (size_t i = 0; i < N; ++i) {
177
0
            for (size_t j = 0; j < N; ++j) {
178
0
                if (i == skip_row || j == skip_column)
179
0
                    continue;
180
181
0
                first_minor.elements()[k / new_size][k % new_size] = m_elements[i][j];
182
0
                ++k;
183
0
            }
184
0
        }
185
186
0
        return first_minor.determinant();
187
0
    }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::first_minor(unsigned long, unsigned long) const
Unexecuted instantiation: Gfx::Matrix<2ul, float>::first_minor(unsigned long, unsigned long) const
Unexecuted instantiation: Gfx::Matrix<4ul, float>::first_minor(unsigned long, unsigned long) const
Unexecuted instantiation: Gfx::Matrix<4ul, double>::first_minor(unsigned long, unsigned long) const
Unexecuted instantiation: Gfx::Matrix<3ul, double>::first_minor(unsigned long, unsigned long) const
Unexecuted instantiation: Gfx::Matrix<2ul, double>::first_minor(unsigned long, unsigned long) const
188
189
    [[nodiscard]] constexpr static Matrix identity()
190
0
    {
191
0
        Matrix result;
192
0
        for (size_t i = 0; i < N; ++i) {
193
0
            for (size_t j = 0; j < N; ++j) {
194
0
                if (i == j)
195
0
                    result.m_elements[i][j] = 1;
196
0
                else
197
0
                    result.m_elements[i][j] = 0;
198
0
            }
199
0
        }
200
0
        return result;
201
0
    }
Unexecuted instantiation: Gfx::Matrix<4ul, float>::identity()
Unexecuted instantiation: Gfx::Matrix<4ul, double>::identity()
202
203
    [[nodiscard]] constexpr Matrix inverse() const
204
0
    {
205
0
        return adjugate() / determinant();
206
0
    }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::inverse() const
Unexecuted instantiation: Gfx::Matrix<4ul, float>::inverse() const
Unexecuted instantiation: Gfx::Matrix<4ul, double>::inverse() const
207
208
    [[nodiscard]] constexpr Matrix transpose() const
209
0
    {
210
0
        Matrix result;
211
0
        for (size_t i = 0; i < N; ++i) {
212
0
            for (size_t j = 0; j < N; ++j)
213
0
                result.m_elements[i][j] = m_elements[j][i];
214
0
        }
215
0
        return result;
216
0
    }
Unexecuted instantiation: Gfx::Matrix<4ul, float>::transpose() const
Unexecuted instantiation: Gfx::Matrix<3ul, float>::transpose() const
217
218
    [[nodiscard]] constexpr T element_sum() const
219
0
    {
220
0
        T s = 0;
221
0
        for (size_t i = 0; i < N; ++i) {
222
0
            for (size_t j = 0; j < N; ++j)
223
0
                s += m_elements[i][j];
224
0
        }
225
0
        return s;
226
0
    }
227
228
    [[nodiscard]] constexpr Matrix hadamard_product(Matrix const& matrix)
229
0
    {
230
0
        Matrix product;
231
0
        for (size_t i = 0; i < N; ++i) {
232
0
            for (size_t j = 0; j < N; ++j)
233
0
                product.m_elements[i][j] = m_elements[i][j] * matrix.m_elements[i][j];
234
0
        }
235
0
        return product;
236
0
    }
237
238
    template<size_t U>
239
    [[nodiscard]] constexpr Matrix<U, T> submatrix_from_topleft() const
240
    requires(U > 0 && U < N)
241
    {
242
        Matrix<U, T> result;
243
        for (size_t i = 0; i < U; ++i) {
244
            for (size_t j = 0; j < U; ++j)
245
                result.m_elements[i][j] = m_elements[i][j];
246
        }
247
        return result;
248
    }
249
250
    constexpr bool is_invertible() const
251
0
    {
252
0
        return determinant() != static_cast<T>(0.0);
253
0
    }
Unexecuted instantiation: Gfx::Matrix<3ul, float>::is_invertible() const
Unexecuted instantiation: Gfx::Matrix<4ul, float>::is_invertible() const
Unexecuted instantiation: Gfx::Matrix<4ul, double>::is_invertible() const
254
255
private:
256
    T m_elements[N][N];
257
};
258
259
}
260
261
namespace AK {
262
263
template<size_t N, typename T>
264
struct Formatter<Gfx::Matrix<N, T>> : Formatter<FormatString> {
265
    ErrorOr<void> format(FormatBuilder& builder, Gfx::Matrix<N, T> const& value)
266
    {
267
        TRY(Formatter<FormatString>::format(builder, "[ "sv));
268
        for (u32 i = 0; i < N; ++i) {
269
            TRY(Formatter<FormatString>::format(builder, "[ "sv));
270
271
            for (u32 j = 0; j < N; ++j) {
272
                TRY(Formatter<FormatString>::format(builder, "{}"sv, value(i, j)));
273
                if (j != N - 1)
274
                    TRY(Formatter<FormatString>::format(builder, ", "sv, value(i, j)));
275
            }
276
            TRY(Formatter<FormatString>::format(builder, " ]"sv));
277
            if (i != N - 1)
278
                TRY(Formatter<FormatString>::format(builder, ",\n"sv));
279
        }
280
281
        return Formatter<FormatString>::format(builder, " ]\n"sv);
282
    }
283
};
284
285
}
286
287
using Gfx::Matrix;