KExiv2

rotationmatrix.cpp
1/*
2 SPDX-FileCopyrightText: 2006-2015 Gilles Caulier <caulier dot gilles at gmail dot com>
3 SPDX-FileCopyrightText: 2004-2012 Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
4
5 SPDX-License-Identifier: GPL-2.0-or-later
6*/
7
8// Local includes
9
10#include "rotationmatrix.h"
11
12namespace KExiv2Iface
13{
14
15/**
16 If the picture is displayed according to the exif orientation tag,
17 the user will request rotating operations relative to what he sees,
18 and that is the picture rotated according to the EXIF tag.
19 So the operation requested and the given EXIF angle must be combined.
20 E.g. if orientation is "6" (rotate 90 clockwiseto show correctly)
21 and the user selects 180 clockwise, the operation is 270.
22 If the user selected 270, the operation would be None (and clearing the exif tag).
23
24 This requires to describe the transformations in a model which
25 cares for both composing (180+90=270) and eliminating (180+180=no action),
26 as well as the non-commutative nature of the operations (vflip+90 is not 90+vflip)
27
28 All 2D transformations can be described by a 2x3 matrix, see QWRotationMatrix.
29 All transformations needed here - rotate 90, 180, 270, flipV, flipH -
30 can be described in a 2x2 matrix with the values 0,1,-1
31 (because flipping is expressed by changing the sign only,
32 and sine and cosine of 90, 180 and 270 are either 0,1 or -1).
33
34 x' = m11 x + m12 y
35 y' = m21 x + m22 y
36
37 Moreover, all combinations of these rotate/flip operations result in one of the eight
38 matrices defined below.
39 (I did not proof that mathematically, but empirically)
40
41 static const RotationMatrix identity; //( 1, 0, 0, 1)
42 static const RotationMatrix rotate90; //( 0, 1, -1, 0)
43 static const RotationMatrix rotate180; //(-1, 0, 0, -1)
44 static const RotationMatrix rotate270; //( 0, -1, 1, 0)
45 static const RotationMatrix flipHorizontal; //(-1, 0, 0, 1)
46 static const RotationMatrix flipVertical; //( 1, 0, 0, -1)
47 static const RotationMatrix rotate90flipHorizontal; //( 0, 1, 1, 0), first rotate, then flip
48 static const RotationMatrix rotate90flipVertical; //( 0, -1, -1, 0), first rotate, then flip
49
50*/
51
52namespace Matrix
53{
54
55static const RotationMatrix identity ( 1, 0, 0, 1);
56static const RotationMatrix rotate90 ( 0, 1, -1, 0);
57static const RotationMatrix rotate180 (-1, 0, 0, -1);
58static const RotationMatrix rotate270 ( 0, -1, 1, 0);
59static const RotationMatrix flipHorizontal (-1, 0, 0, 1);
60static const RotationMatrix flipVertical ( 1, 0, 0, -1);
61static const RotationMatrix rotate90flipHorizontal ( 0, 1, 1, 0);
62static const RotationMatrix rotate90flipVertical ( 0, -1, -1, 0);
63
65{
66 switch (action)
67 {
68 case RotationMatrix::NoTransformation:
69 return identity;
71 return flipHorizontal;
73 return flipVertical;
75 return rotate90;
77 return rotate180;
79 return rotate270;
80 }
81
82 return identity;
83}
84
85RotationMatrix matrix(KExiv2::ImageOrientation exifOrientation)
86{
87 switch (exifOrientation)
88 {
89 case KExiv2::ORIENTATION_NORMAL:
90 return identity;
91 case KExiv2::ORIENTATION_HFLIP:
92 return flipHorizontal;
93 case KExiv2::ORIENTATION_ROT_180:
94 return rotate180;
95 case KExiv2::ORIENTATION_VFLIP:
96 return flipVertical;
97 case KExiv2::ORIENTATION_ROT_90_HFLIP:
98 return rotate90flipHorizontal;
99 case KExiv2::ORIENTATION_ROT_90:
100 return rotate90;
101 case KExiv2::ORIENTATION_ROT_90_VFLIP:
102 return rotate90flipVertical;
103 case KExiv2::ORIENTATION_ROT_270:
104 return rotate270;
105 case KExiv2::ORIENTATION_UNSPECIFIED:
106 return identity;
107 }
108
109 return identity;
110}
111
112} // namespace Matrix
113
115{
116 set( 1, 0, 0, 1 );
117}
118
120{
121 *this = Matrix::matrix(action);
122}
123
125{
126 *this = Matrix::matrix(exifOrientation);
127}
128
129RotationMatrix::RotationMatrix(int m11, int m12, int m21, int m22)
130{
131 set(m11, m12, m21, m22);
132}
133
134void RotationMatrix::set(int m11, int m12, int m21, int m22)
135{
136 m[0][0]=m11;
137 m[0][1]=m12;
138 m[1][0]=m21;
139 m[1][1]=m22;
140}
141
143{
144 return (*this == Matrix::identity);
145}
146
147RotationMatrix& RotationMatrix::operator*=(const RotationMatrix& ma)
148{
149 set( ma.m[0][0]*m[0][0] + ma.m[0][1]*m[1][0], ma.m[0][0]*m[0][1] + ma.m[0][1]*m[1][1],
150 ma.m[1][0]*m[0][0] + ma.m[1][1]*m[1][0], ma.m[1][0]*m[0][1] + ma.m[1][1]*m[1][1] );
151
152 return *this;
153}
154
155bool RotationMatrix::operator==(const RotationMatrix& ma) const
156{
157 return m[0][0]==ma.m[0][0] &&
158 m[0][1]==ma.m[0][1] &&
159 m[1][0]==ma.m[1][0] &&
160 m[1][1]==ma.m[1][1];
161}
162
163bool RotationMatrix::operator!=(const RotationMatrix& ma) const
164{
165 return !(*this==ma);
166}
167
168RotationMatrix& RotationMatrix::operator*=(TransformationAction action)
169{
170 return (*this *= Matrix::matrix(action));
171}
172
173RotationMatrix& RotationMatrix::operator*=(QList<TransformationAction> actions)
174{
175 for (const TransformationAction& action : qAsConst(actions))
176 {
177 *this *= Matrix::matrix(action);
178 }
179
180 return *this;
181}
182
183RotationMatrix& RotationMatrix::operator*=(KExiv2::ImageOrientation exifOrientation)
184{
185 return (*this *= Matrix::matrix(exifOrientation));
186}
187
188/** Converts the mathematically correct description
189 into the primitive operations that can be carried out losslessly.
190*/
192{
194
195 if (*this == Matrix::rotate90)
196 {
197 transforms << Rotate90;
198 }
199 else if (*this == Matrix::rotate180)
200 {
201 transforms << Rotate180;
202 }
203 else if (*this == Matrix::rotate270)
204 {
205 transforms << Rotate270;
206 }
207 else if (*this == Matrix::flipHorizontal)
208 {
209 transforms << FlipHorizontal;
210 }
211 else if (*this == Matrix::flipVertical)
212 {
213 transforms << FlipVertical;
214 }
215 else if (*this == Matrix::rotate90flipHorizontal)
216 {
217 //first rotate, then flip!
218 transforms << Rotate90;
219 transforms << FlipHorizontal;
220 }
221 else if (*this == Matrix::rotate90flipVertical)
222 {
223 //first rotate, then flip!
224 transforms << Rotate90;
225 transforms << FlipVertical;
226 }
227
228 return transforms;
229}
230
232{
233 if (*this == Matrix::identity)
234 {
235 return KExiv2::ORIENTATION_NORMAL;
236 }
237
238 if (*this == Matrix::rotate90)
239 {
240 return KExiv2::ORIENTATION_ROT_90;
241 }
242 else if (*this == Matrix::rotate180)
243 {
244 return KExiv2::ORIENTATION_ROT_180;
245 }
246 else if (*this == Matrix::rotate270)
247 {
248 return KExiv2::ORIENTATION_ROT_270;
249 }
250 else if (*this == Matrix::flipHorizontal)
251 {
252 return KExiv2::ORIENTATION_HFLIP;
253 }
254 else if (*this == Matrix::flipVertical)
255 {
256 return KExiv2::ORIENTATION_VFLIP;
257 }
258 else if (*this == Matrix::rotate90flipHorizontal)
259 {
260 return KExiv2::ORIENTATION_ROT_90_HFLIP;
261 }
262 else if (*this == Matrix::rotate90flipVertical)
263 {
264 return KExiv2::ORIENTATION_ROT_90_VFLIP;
265 }
266
267 return KExiv2::ORIENTATION_UNSPECIFIED;
268}
269
274
276{
277 QTransform matrix;
278
279 switch (orientation)
280 {
281 case KExiv2::ORIENTATION_NORMAL:
282 case KExiv2::ORIENTATION_UNSPECIFIED:
283 break;
284
285 case KExiv2::ORIENTATION_HFLIP:
286 matrix.scale(-1, 1);
287 break;
288
289 case KExiv2::ORIENTATION_ROT_180:
290 matrix.rotate(180);
291 break;
292
293 case KExiv2::ORIENTATION_VFLIP:
294 matrix.scale(1, -1);
295 break;
296
297 case KExiv2::ORIENTATION_ROT_90_HFLIP:
298 matrix.scale(-1, 1);
299 matrix.rotate(90);
300 break;
301
302 case KExiv2::ORIENTATION_ROT_90:
303 matrix.rotate(90);
304 break;
305
306 case KExiv2::ORIENTATION_ROT_90_VFLIP:
307 matrix.scale(1, -1);
308 matrix.rotate(90);
309 break;
310
311 case KExiv2::ORIENTATION_ROT_270:
312 matrix.rotate(270);
313 break;
314 }
315
316 return matrix;
317}
318
319#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
320#if KEXIV2_BUILD_DEPRECATED_SINCE(5, 1)
321QMatrix RotationMatrix::toMatrix() const
322{
323 return toMatrix(exifOrientation());
324}
325#endif
326#endif
327
328#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
329#if KEXIV2_BUILD_DEPRECATED_SINCE(5, 1)
330QMatrix RotationMatrix::toMatrix(KExiv2::ImageOrientation orientation)
331{
332 return toTransform(orientation).toAffine();
333}
334#endif
335#endif
336
337} // namespace KExiv2Iface
ImageOrientation
The image orientation values given by Exif metadata.
Definition kexiv2.h:86
bool isNoTransform() const
Returns true of this matrix describes no transformation (is the identity matrix)
TransformationAction
This describes single transform primitives.
@ Rotate180
90-degree clockwise rotation
@ FlipHorizontal
no transformation
@ Rotate270
180-degree rotation
@ FlipVertical
horizontal flip
QList< TransformationAction > transformations() const
Returns the actions described by this matrix.
RotationMatrix()
Constructs the identity matrix (the matrix describing no transformation)
KExiv2::ImageOrientation exifOrientation() const
Returns the Exif orienation flag describing this matrix.
QTransform toTransform() const
Returns a QTransform representing this matrix.
KExiv2Iface - Exiv2 library interface.
Definition kexiv2.cpp:17
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:16:02 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.