Coverage Report

Created: 2026-03-12 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libkexiv2/src/rotationmatrix.cpp
Line
Count
Source
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
12
namespace 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
52
namespace Matrix
53
{
54
55
static const RotationMatrix identity               ( 1,  0,  0,  1);
56
static const RotationMatrix rotate90               ( 0,  1, -1,  0);
57
static const RotationMatrix rotate180              (-1,  0,  0, -1);
58
static const RotationMatrix rotate270              ( 0, -1,  1,  0);
59
static const RotationMatrix flipHorizontal         (-1,  0,  0,  1);
60
static const RotationMatrix flipVertical           ( 1,  0,  0, -1);
61
static const RotationMatrix rotate90flipHorizontal ( 0,  1,  1,  0);
62
static const RotationMatrix rotate90flipVertical   ( 0, -1, -1,  0);
63
64
RotationMatrix matrix(RotationMatrix::TransformationAction action)
65
0
{
66
0
    switch (action)
67
0
    {
68
0
        case RotationMatrix::NoTransformation:
69
0
            return identity;
70
0
        case RotationMatrix::FlipHorizontal:
71
0
            return flipHorizontal;
72
0
        case RotationMatrix::FlipVertical:
73
0
            return flipVertical;
74
0
        case RotationMatrix::Rotate90:
75
0
            return rotate90;
76
0
        case RotationMatrix::Rotate180:
77
0
            return rotate180;
78
0
        case RotationMatrix::Rotate270:
79
0
            return rotate270;
80
0
    }
81
82
0
    return identity;
83
0
}
84
85
RotationMatrix matrix(KExiv2::ImageOrientation exifOrientation)
86
0
{
87
0
    switch (exifOrientation)
88
0
    {
89
0
        case KExiv2::ORIENTATION_NORMAL:
90
0
            return identity;
91
0
        case KExiv2::ORIENTATION_HFLIP:
92
0
            return flipHorizontal;
93
0
        case KExiv2::ORIENTATION_ROT_180:
94
0
            return rotate180;
95
0
        case KExiv2::ORIENTATION_VFLIP:
96
0
            return flipVertical;
97
0
        case KExiv2::ORIENTATION_ROT_90_HFLIP:
98
0
            return rotate90flipHorizontal;
99
0
        case KExiv2::ORIENTATION_ROT_90:
100
0
            return rotate90;
101
0
        case KExiv2::ORIENTATION_ROT_90_VFLIP:
102
0
            return rotate90flipVertical;
103
0
        case KExiv2::ORIENTATION_ROT_270:
104
0
            return rotate270;
105
0
        case KExiv2::ORIENTATION_UNSPECIFIED:
106
0
            return identity;
107
0
    }
108
109
0
    return identity;
110
0
}
111
112
} // namespace Matrix
113
114
RotationMatrix::RotationMatrix()
115
0
{
116
0
    set( 1, 0, 0, 1 );
117
0
}
118
119
RotationMatrix::RotationMatrix(TransformationAction action)
120
0
{
121
0
    *this = Matrix::matrix(action);
122
0
}
123
124
RotationMatrix::RotationMatrix(KExiv2::ImageOrientation exifOrientation)
125
0
{
126
0
    *this = Matrix::matrix(exifOrientation);
127
0
}
128
129
RotationMatrix::RotationMatrix(int m11, int m12, int m21, int m22)
130
0
{
131
0
    set(m11, m12, m21, m22);
132
0
}
133
134
void RotationMatrix::set(int m11, int m12, int m21, int m22)
135
0
{
136
0
    m[0][0]=m11;
137
0
    m[0][1]=m12;
138
0
    m[1][0]=m21;
139
0
    m[1][1]=m22;
140
0
}
141
142
bool RotationMatrix::isNoTransform() const
143
0
{
144
0
    return (*this == Matrix::identity);
145
0
}
146
147
RotationMatrix& RotationMatrix::operator*=(const RotationMatrix& ma)
148
0
{
149
0
    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
0
         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
0
    return *this;
153
0
}
154
155
bool RotationMatrix::operator==(const RotationMatrix& ma) const
156
0
{
157
0
    return m[0][0]==ma.m[0][0] &&
158
0
           m[0][1]==ma.m[0][1] &&
159
0
           m[1][0]==ma.m[1][0] &&
160
0
           m[1][1]==ma.m[1][1];
161
0
}
162
163
bool RotationMatrix::operator!=(const RotationMatrix& ma) const
164
0
{
165
0
    return !(*this==ma);
166
0
}
167
168
RotationMatrix& RotationMatrix::operator*=(TransformationAction action)
169
0
{
170
0
    return (*this *= Matrix::matrix(action));
171
0
}
172
173
RotationMatrix& RotationMatrix::operator*=(QList<TransformationAction> actions)
174
0
{
175
0
    for (const TransformationAction& action : std::as_const(actions))
176
0
    {
177
0
        *this *= Matrix::matrix(action);
178
0
    }
179
180
0
    return *this;
181
0
}
182
183
RotationMatrix& RotationMatrix::operator*=(KExiv2::ImageOrientation exifOrientation)
184
0
{
185
0
    return (*this *= Matrix::matrix(exifOrientation));
186
0
}
187
188
/** Converts the mathematically correct description
189
    into the primitive operations that can be carried out losslessly.
190
*/
191
QList<RotationMatrix::TransformationAction> RotationMatrix::transformations() const
192
0
{
193
0
    QList<TransformationAction> transforms;
194
195
0
    if (*this == Matrix::rotate90)
196
0
    {
197
0
        transforms << Rotate90;
198
0
    }
199
0
    else if (*this == Matrix::rotate180)
200
0
    {
201
0
        transforms << Rotate180;
202
0
    }
203
0
    else if (*this == Matrix::rotate270)
204
0
    {
205
0
        transforms << Rotate270;
206
0
    }
207
0
    else if (*this == Matrix::flipHorizontal)
208
0
    {
209
0
        transforms << FlipHorizontal;
210
0
    }
211
0
    else if (*this == Matrix::flipVertical)
212
0
    {
213
0
        transforms << FlipVertical;
214
0
    }
215
0
    else if (*this == Matrix::rotate90flipHorizontal)
216
0
    {
217
        //first rotate, then flip!
218
0
        transforms << Rotate90;
219
0
        transforms << FlipHorizontal;
220
0
    }
221
0
    else if (*this == Matrix::rotate90flipVertical)
222
0
    {
223
        //first rotate, then flip!
224
0
        transforms << Rotate90;
225
0
        transforms << FlipVertical;
226
0
    }
227
228
0
    return transforms;
229
0
}
230
231
KExiv2::ImageOrientation RotationMatrix::exifOrientation() const
232
0
{
233
0
    if (*this == Matrix::identity)
234
0
    {
235
0
        return KExiv2::ORIENTATION_NORMAL;
236
0
    }
237
238
0
    if (*this == Matrix::rotate90)
239
0
    {
240
0
        return KExiv2::ORIENTATION_ROT_90;
241
0
    }
242
0
    else if (*this == Matrix::rotate180)
243
0
    {
244
0
        return KExiv2::ORIENTATION_ROT_180;
245
0
    }
246
0
    else if (*this == Matrix::rotate270)
247
0
    {
248
0
        return KExiv2::ORIENTATION_ROT_270;
249
0
    }
250
0
    else if (*this == Matrix::flipHorizontal)
251
0
    {
252
0
        return KExiv2::ORIENTATION_HFLIP;
253
0
    }
254
0
    else if (*this == Matrix::flipVertical)
255
0
    {
256
0
        return KExiv2::ORIENTATION_VFLIP;
257
0
    }
258
0
    else if (*this == Matrix::rotate90flipHorizontal)
259
0
    {
260
0
        return KExiv2::ORIENTATION_ROT_90_HFLIP;
261
0
    }
262
0
    else if (*this == Matrix::rotate90flipVertical)
263
0
    {
264
0
        return KExiv2::ORIENTATION_ROT_90_VFLIP;
265
0
    }
266
267
0
    return KExiv2::ORIENTATION_UNSPECIFIED;
268
0
}
269
270
QTransform RotationMatrix::toTransform() const
271
0
{
272
0
    return toTransform(exifOrientation());
273
0
}
274
275
QTransform RotationMatrix::toTransform(KExiv2::ImageOrientation orientation)
276
34
{
277
34
    QTransform matrix;
278
279
34
    switch (orientation)
280
34
    {
281
13
        case KExiv2::ORIENTATION_NORMAL:
282
23
        case KExiv2::ORIENTATION_UNSPECIFIED:
283
23
            break;
284
285
0
        case KExiv2::ORIENTATION_HFLIP:
286
0
            matrix.scale(-1, 1);
287
0
            break;
288
289
0
        case KExiv2::ORIENTATION_ROT_180:
290
0
            matrix.rotate(180);
291
0
            break;
292
293
4
        case KExiv2::ORIENTATION_VFLIP:
294
4
            matrix.scale(1, -1);
295
4
            break;
296
297
0
        case KExiv2::ORIENTATION_ROT_90_HFLIP:
298
0
            matrix.scale(-1, 1);
299
0
            matrix.rotate(90);
300
0
            break;
301
302
7
        case KExiv2::ORIENTATION_ROT_90:
303
7
            matrix.rotate(90);
304
7
            break;
305
306
0
        case KExiv2::ORIENTATION_ROT_90_VFLIP:
307
0
            matrix.scale(1, -1);
308
0
            matrix.rotate(90);
309
0
            break;
310
311
0
        case KExiv2::ORIENTATION_ROT_270:
312
0
            matrix.rotate(270);
313
0
            break;
314
34
    }
315
316
34
    return matrix;
317
34
}
318
319
}  // namespace KExiv2Iface