Coverage Report

Created: 2025-09-05 06:52

/src/serenity/Userland/Libraries/LibGfx/AffineTransform.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <AK/Math.h>
8
#include <AK/Optional.h>
9
#include <LibGfx/AffineTransform.h>
10
#include <LibGfx/Quad.h>
11
#include <LibGfx/Rect.h>
12
13
namespace Gfx {
14
15
float AffineTransform::x_scale() const
16
257k
{
17
257k
    return AK::hypot(m_values[0], m_values[1]);
18
257k
}
19
20
float AffineTransform::y_scale() const
21
257k
{
22
257k
    return AK::hypot(m_values[2], m_values[3]);
23
257k
}
24
25
FloatPoint AffineTransform::scale() const
26
257k
{
27
257k
    return { x_scale(), y_scale() };
28
257k
}
29
30
float AffineTransform::x_translation() const
31
540k
{
32
540k
    return e();
33
540k
}
34
35
float AffineTransform::y_translation() const
36
540k
{
37
540k
    return f();
38
540k
}
39
40
FloatPoint AffineTransform::translation() const
41
540k
{
42
540k
    return { x_translation(), y_translation() };
43
540k
}
44
45
AffineTransform& AffineTransform::scale(float sx, float sy)
46
257k
{
47
257k
    m_values[0] *= sx;
48
257k
    m_values[1] *= sx;
49
257k
    m_values[2] *= sy;
50
257k
    m_values[3] *= sy;
51
257k
    return *this;
52
257k
}
53
54
AffineTransform& AffineTransform::scale(FloatPoint s)
55
0
{
56
0
    return scale(s.x(), s.y());
57
0
}
58
59
AffineTransform& AffineTransform::set_scale(float sx, float sy)
60
0
{
61
0
    m_values[0] = sx;
62
0
    m_values[1] = 0;
63
0
    m_values[2] = 0;
64
0
    m_values[3] = sy;
65
0
    return *this;
66
0
}
67
68
AffineTransform& AffineTransform::set_scale(FloatPoint s)
69
0
{
70
0
    return set_scale(s.x(), s.y());
71
0
}
72
73
AffineTransform& AffineTransform::skew_radians(float x_radians, float y_radians)
74
0
{
75
0
    AffineTransform skew_transform(1, AK::tan(y_radians), AK::tan(x_radians), 1, 0, 0);
76
0
    multiply(skew_transform);
77
0
    return *this;
78
0
}
79
80
AffineTransform& AffineTransform::translate(float tx, float ty)
81
1.46k
{
82
1.46k
    if (is_identity_or_translation()) {
83
1.46k
        m_values[4] += tx;
84
1.46k
        m_values[5] += ty;
85
1.46k
        return *this;
86
1.46k
    }
87
0
    m_values[4] += tx * m_values[0] + ty * m_values[2];
88
0
    m_values[5] += tx * m_values[1] + ty * m_values[3];
89
0
    return *this;
90
1.46k
}
91
92
AffineTransform& AffineTransform::translate(FloatPoint t)
93
1.46k
{
94
1.46k
    return translate(t.x(), t.y());
95
1.46k
}
96
97
AffineTransform& AffineTransform::set_translation(float tx, float ty)
98
0
{
99
0
    m_values[4] = tx;
100
0
    m_values[5] = ty;
101
0
    return *this;
102
0
}
103
104
AffineTransform& AffineTransform::set_translation(FloatPoint t)
105
0
{
106
0
    return set_translation(t.x(), t.y());
107
0
}
108
109
AffineTransform& AffineTransform::multiply(AffineTransform const& other)
110
259k
{
111
259k
    if (other.is_identity())
112
259k
        return *this;
113
0
    AffineTransform result;
114
0
    result.m_values[0] = other.a() * a() + other.b() * c();
115
0
    result.m_values[1] = other.a() * b() + other.b() * d();
116
0
    result.m_values[2] = other.c() * a() + other.d() * c();
117
0
    result.m_values[3] = other.c() * b() + other.d() * d();
118
0
    result.m_values[4] = other.e() * a() + other.f() * c() + e();
119
0
    result.m_values[5] = other.e() * b() + other.f() * d() + f();
120
0
    *this = result;
121
0
    return *this;
122
259k
}
123
124
AffineTransform& AffineTransform::rotate_radians(float radians)
125
0
{
126
0
    float sin_angle;
127
0
    float cos_angle;
128
0
    AK::sincos(radians, sin_angle, cos_angle);
129
0
    AffineTransform rotation(cos_angle, sin_angle, -sin_angle, cos_angle, 0, 0);
130
0
    multiply(rotation);
131
0
    return *this;
132
0
}
133
134
float AffineTransform::determinant() const
135
257k
{
136
257k
    return a() * d() - b() * c();
137
257k
}
138
139
Optional<AffineTransform> AffineTransform::inverse() const
140
257k
{
141
257k
    auto det = determinant();
142
257k
    if (det == 0)
143
0
        return {};
144
257k
    return AffineTransform {
145
257k
        d() / det,
146
257k
        -b() / det,
147
257k
        -c() / det,
148
257k
        a() / det,
149
257k
        (c() * f() - d() * e()) / det,
150
257k
        (b() * e() - a() * f()) / det,
151
257k
    };
152
257k
}
153
154
void AffineTransform::map(float unmapped_x, float unmapped_y, float& mapped_x, float& mapped_y) const
155
77.8M
{
156
77.8M
    mapped_x = a() * unmapped_x + c() * unmapped_y + e();
157
77.8M
    mapped_y = b() * unmapped_x + d() * unmapped_y + f();
158
77.8M
}
159
160
template<>
161
IntPoint AffineTransform::map(IntPoint point) const
162
0
{
163
0
    float mapped_x;
164
0
    float mapped_y;
165
0
    map(static_cast<float>(point.x()), static_cast<float>(point.y()), mapped_x, mapped_y);
166
0
    return { round_to<int>(mapped_x), round_to<int>(mapped_y) };
167
0
}
168
169
template<>
170
FloatPoint AffineTransform::map(FloatPoint point) const
171
77.8M
{
172
77.8M
    float mapped_x;
173
77.8M
    float mapped_y;
174
77.8M
    map(point.x(), point.y(), mapped_x, mapped_y);
175
77.8M
    return { mapped_x, mapped_y };
176
77.8M
}
177
178
template<>
179
IntSize AffineTransform::map(IntSize size) const
180
0
{
181
0
    return {
182
0
        round_to<int>(static_cast<float>(size.width()) * x_scale()),
183
0
        round_to<int>(static_cast<float>(size.height()) * y_scale()),
184
0
    };
185
0
}
186
187
template<>
188
FloatSize AffineTransform::map(FloatSize size) const
189
0
{
190
0
    return { size.width() * x_scale(), size.height() * y_scale() };
191
0
}
192
193
template<typename T>
194
static T smallest_of(T p1, T p2, T p3, T p4)
195
0
{
196
0
    return min(min(p1, p2), min(p3, p4));
197
0
}
198
199
template<typename T>
200
static T largest_of(T p1, T p2, T p3, T p4)
201
0
{
202
0
    return max(max(p1, p2), max(p3, p4));
203
0
}
204
205
template<>
206
FloatRect AffineTransform::map(FloatRect const& rect) const
207
730
{
208
730
    if (is_identity()) {
209
730
        return rect;
210
730
    }
211
0
    if (is_identity_or_translation()) {
212
0
        return rect.translated(e(), f());
213
0
    }
214
0
    FloatPoint p1 = map(rect.top_left());
215
0
    FloatPoint p2 = map(rect.top_right());
216
0
    FloatPoint p3 = map(rect.bottom_right());
217
0
    FloatPoint p4 = map(rect.bottom_left());
218
0
    float left = smallest_of(p1.x(), p2.x(), p3.x(), p4.x());
219
0
    float top = smallest_of(p1.y(), p2.y(), p3.y(), p4.y());
220
0
    float right = largest_of(p1.x(), p2.x(), p3.x(), p4.x());
221
0
    float bottom = largest_of(p1.y(), p2.y(), p3.y(), p4.y());
222
0
    return { left, top, right - left, bottom - top };
223
0
}
224
225
template<>
226
IntRect AffineTransform::map(IntRect const& rect) const
227
0
{
228
0
    return enclosing_int_rect(map(FloatRect(rect)));
229
0
}
230
231
Quad<float> AffineTransform::map_to_quad(Rect<float> const& rect) const
232
0
{
233
0
    return {
234
0
        map(rect.top_left()),
235
0
        map(rect.top_right()),
236
0
        map(rect.bottom_right()),
237
0
        map(rect.bottom_left()),
238
0
    };
239
0
}
240
241
float AffineTransform::rotation() const
242
0
{
243
0
    auto rotation = AK::atan2(b(), a());
244
0
    while (rotation < -AK::Pi<float>)
245
0
        rotation += 2.0f * AK::Pi<float>;
246
0
    while (rotation > AK::Pi<float>)
247
0
        rotation -= 2.0f * AK::Pi<float>;
248
0
    return rotation;
249
0
}
250
251
}