/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 | | } |