1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2006-09-15
7  * Description : Exiv2 library interface.
8  *               Tools for combining rotation operations.
9  *
10  * Copyright (C) 2006-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
11  * Copyright (C) 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
12  *
13  * This program is free software; you can redistribute it
14  * and/or modify it under the terms of the GNU General
15  * Public License as published by the Free Software Foundation;
16  * either version 2, or (at your option)
17  * any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * ============================================================ */
25 
26 // Local includes
27 
28 #include "metaengine_rotation.h"
29 
30 namespace Digikam
31 {
32 
33 /**
34  * If the picture is displayed according to the exif orientation tag,
35  * the user will request rotating operations relative to what he sees,
36  * and that is the picture rotated according to the EXIF tag.
37  * So the operation requested and the given EXIF angle must be combined.
38  * E.g. if orientation is "6" (rotate 90 clockwiseto show correctly)
39  * and the user selects 180 clockwise, the operation is 270.
40  * If the user selected 270, the operation would be None (and clearing the exif tag).
41  *
42  * This requires to describe the transformations in a model which
43  * cares for both composing (180+90=270) and eliminating (180+180=no action),
44  * as well as the non-commutative nature of the operations (vflip+90 is not 90+vflip)
45  *
46  * All 2D transformations can be described by a 2x3 matrix, see QWMetaEngineRotation.
47  * All transformations needed here - rotate 90, 180, 270, flipV, flipH -
48  * can be described in a 2x2 matrix with the values 0,1,-1
49  * (because flipping is expressed by changing the sign only,
50  *  and sine and cosine of 90, 180 and 270 are either 0,1 or -1).
51  *
52  *  x' = m11 x + m12 y
53  *  y' = m21 x + m22 y
54  *
55  * Moreover, all combinations of these rotate/flip operations result in one of the eight
56  * matrices defined below.
57  * (I did not proof that mathematically, but empirically)
58  *
59  * static const MetaEngineRotation identity;               //( 1,  0,  0,  1)
60  * static const MetaEngineRotation rotate90;               //( 0,  1, -1,  0)
61  * static const MetaEngineRotation rotate180;              //(-1,  0,  0, -1)
62  * static const MetaEngineRotation rotate270;              //( 0, -1,  1,  0)
63  * static const MetaEngineRotation flipHorizontal;         //(-1,  0,  0,  1)
64  * static const MetaEngineRotation flipVertical;           //( 1,  0,  0, -1)
65  * static const MetaEngineRotation rotate90flipHorizontal; //( 0,  1,  1,  0), first rotate, then flip
66  * static const MetaEngineRotation rotate90flipVertical;   //( 0, -1, -1,  0), first rotate, then flip
67  */
68 
69 namespace Matrix
70 {
71 
72 static const MetaEngineRotation identity               ( 1,  0,  0,  1);
73 static const MetaEngineRotation rotate90               ( 0,  1, -1,  0);
74 static const MetaEngineRotation rotate180              (-1,  0,  0, -1);
75 static const MetaEngineRotation rotate270              ( 0, -1,  1,  0);
76 static const MetaEngineRotation flipHorizontal         (-1,  0,  0,  1);
77 static const MetaEngineRotation flipVertical           ( 1,  0,  0, -1);
78 static const MetaEngineRotation rotate90flipHorizontal ( 0,  1,  1,  0);
79 static const MetaEngineRotation rotate90flipVertical   ( 0, -1, -1,  0);
80 
matrix(MetaEngineRotation::TransformationAction action)81 MetaEngineRotation matrix(MetaEngineRotation::TransformationAction action)
82 {
83     switch (action)
84     {
85         case MetaEngineRotation::NoTransformation:
86         {
87             return identity;
88         }
89 
90         case MetaEngineRotation::FlipHorizontal:
91         {
92             return flipHorizontal;
93         }
94 
95         case MetaEngineRotation::FlipVertical:
96         {
97             return flipVertical;
98         }
99 
100         case MetaEngineRotation::Rotate90:
101         {
102             return rotate90;
103         }
104 
105         case MetaEngineRotation::Rotate180:
106         {
107             return rotate180;
108         }
109 
110         case MetaEngineRotation::Rotate270:
111         {
112             return rotate270;
113         }
114     }
115 
116     return identity;
117 }
118 
matrix(MetaEngine::ImageOrientation exifOrientation)119 MetaEngineRotation matrix(MetaEngine::ImageOrientation exifOrientation)
120 {
121     switch (exifOrientation)
122     {
123         case MetaEngine::ORIENTATION_NORMAL:
124         {
125             return identity;
126         }
127 
128         case MetaEngine::ORIENTATION_HFLIP:
129         {
130             return flipHorizontal;
131         }
132 
133         case MetaEngine::ORIENTATION_ROT_180:
134         {
135             return rotate180;
136         }
137 
138         case MetaEngine::ORIENTATION_VFLIP:
139         {
140             return flipVertical;
141         }
142 
143         case MetaEngine::ORIENTATION_ROT_90_HFLIP:
144         {
145             return rotate90flipHorizontal;
146         }
147 
148         case MetaEngine::ORIENTATION_ROT_90:
149         {
150             return rotate90;
151         }
152 
153         case MetaEngine::ORIENTATION_ROT_90_VFLIP:
154         {
155             return rotate90flipVertical;
156         }
157 
158         case MetaEngine::ORIENTATION_ROT_270:
159         {
160             return rotate270;
161         }
162 
163         case MetaEngine::ORIENTATION_UNSPECIFIED:
164         {
165             return identity;
166         }
167     }
168 
169     return identity;
170 }
171 
172 } // namespace Matrix
173 
MetaEngineRotation()174 MetaEngineRotation::MetaEngineRotation()
175 {
176     set(1, 0, 0, 1);
177 }
178 
MetaEngineRotation(TransformationAction action)179 MetaEngineRotation::MetaEngineRotation(TransformationAction action)
180 {
181     *this = Matrix::matrix(action);
182 }
183 
MetaEngineRotation(MetaEngine::ImageOrientation exifOrientation)184 MetaEngineRotation::MetaEngineRotation(MetaEngine::ImageOrientation exifOrientation)
185 {
186     *this = Matrix::matrix(exifOrientation);
187 }
188 
MetaEngineRotation(int m11,int m12,int m21,int m22)189 MetaEngineRotation::MetaEngineRotation(int m11, int m12, int m21, int m22)
190 {
191     set(m11, m12, m21, m22);
192 }
193 
set(int m11,int m12,int m21,int m22)194 void MetaEngineRotation::set(int m11, int m12, int m21, int m22)
195 {
196     m[0][0] = m11;
197     m[0][1] = m12;
198     m[1][0] = m21;
199     m[1][1] = m22;
200 }
201 
isNoTransform() const202 bool MetaEngineRotation::isNoTransform() const
203 {
204     return (*this == Matrix::identity);
205 }
206 
operator *=(const MetaEngineRotation & ma)207 MetaEngineRotation& MetaEngineRotation::operator*=(const MetaEngineRotation& ma)
208 {
209     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],
210         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]);
211 
212     return *this;
213 }
214 
operator ==(const MetaEngineRotation & ma) const215 bool MetaEngineRotation::operator==(const MetaEngineRotation& ma) const
216 {
217     return (
218             (m[0][0] == ma.m[0][0]) &&
219             (m[0][1] == ma.m[0][1]) &&
220             (m[1][0] == ma.m[1][0]) &&
221             (m[1][1] == ma.m[1][1])
222            );
223 }
224 
operator !=(const MetaEngineRotation & ma) const225 bool MetaEngineRotation::operator!=(const MetaEngineRotation& ma) const
226 {
227     return !(*this==ma);
228 }
229 
operator *=(TransformationAction action)230 MetaEngineRotation& MetaEngineRotation::operator*=(TransformationAction action)
231 {
232     return (*this *= Matrix::matrix(action));
233 }
234 
operator *=(QList<TransformationAction> actions)235 MetaEngineRotation& MetaEngineRotation::operator*=(QList<TransformationAction> actions)
236 {
237     foreach (const TransformationAction& action, actions)
238     {
239         *this *= Matrix::matrix(action);
240     }
241 
242     return *this;
243 }
244 
operator *=(MetaEngine::ImageOrientation exifOrientation)245 MetaEngineRotation& MetaEngineRotation::operator*=(MetaEngine::ImageOrientation exifOrientation)
246 {
247     return (*this *= Matrix::matrix(exifOrientation));
248 }
249 
250 /**
251  * Converts the mathematically correct description
252  * into the primitive operations that can be carried out losslessly.
253  */
transformations() const254 QList<MetaEngineRotation::TransformationAction> MetaEngineRotation::transformations() const
255 {
256     QList<TransformationAction> transforms;
257 
258     if      (*this == Matrix::rotate90)
259     {
260         transforms << Rotate90;
261     }
262     else if (*this == Matrix::rotate180)
263     {
264         transforms << Rotate180;
265     }
266     else if (*this == Matrix::rotate270)
267     {
268         transforms << Rotate270;
269     }
270     else if (*this == Matrix::flipHorizontal)
271     {
272         transforms << FlipHorizontal;
273     }
274     else if (*this == Matrix::flipVertical)
275     {
276         transforms << FlipVertical;
277     }
278     else if (*this == Matrix::rotate90flipHorizontal)
279     {
280         // first rotate, then flip!
281 
282         transforms << Rotate90;
283         transforms << FlipHorizontal;
284     }
285     else if (*this == Matrix::rotate90flipVertical)
286     {
287         // first rotate, then flip!
288 
289         transforms << Rotate90;
290         transforms << FlipVertical;
291     }
292 
293     return transforms;
294 }
295 
exifOrientation() const296 MetaEngine::ImageOrientation MetaEngineRotation::exifOrientation() const
297 {
298     if (*this == Matrix::identity)
299     {
300         return MetaEngine::ORIENTATION_NORMAL;
301     }
302 
303     if      (*this == Matrix::rotate90)
304     {
305         return MetaEngine::ORIENTATION_ROT_90;
306     }
307     else if (*this == Matrix::rotate180)
308     {
309         return MetaEngine::ORIENTATION_ROT_180;
310     }
311     else if (*this == Matrix::rotate270)
312     {
313         return MetaEngine::ORIENTATION_ROT_270;
314     }
315     else if (*this == Matrix::flipHorizontal)
316     {
317         return MetaEngine::ORIENTATION_HFLIP;
318     }
319     else if (*this == Matrix::flipVertical)
320     {
321         return MetaEngine::ORIENTATION_VFLIP;
322     }
323     else if (*this == Matrix::rotate90flipHorizontal)
324     {
325         return MetaEngine::ORIENTATION_ROT_90_HFLIP;
326     }
327     else if (*this == Matrix::rotate90flipVertical)
328     {
329         return MetaEngine::ORIENTATION_ROT_90_VFLIP;
330     }
331 
332     return MetaEngine::ORIENTATION_UNSPECIFIED;
333 }
334 
toMatrix() const335 QMatrix MetaEngineRotation::toMatrix() const
336 {
337     return toMatrix(exifOrientation());
338 }
339 
toMatrix(MetaEngine::ImageOrientation orientation)340 QMatrix MetaEngineRotation::toMatrix(MetaEngine::ImageOrientation orientation)
341 {
342     QMatrix matrix;
343 
344     switch (orientation)
345     {
346         case MetaEngine::ORIENTATION_NORMAL:
347         case MetaEngine::ORIENTATION_UNSPECIFIED:
348         {
349             break;
350         }
351 
352         case MetaEngine::ORIENTATION_HFLIP:
353         {
354             matrix.scale(-1, 1);
355             break;
356         }
357 
358         case MetaEngine::ORIENTATION_ROT_180:
359         {
360             matrix.rotate(180);
361             break;
362         }
363 
364         case MetaEngine::ORIENTATION_VFLIP:
365         {
366             matrix.scale(1, -1);
367             break;
368         }
369 
370         case MetaEngine::ORIENTATION_ROT_90_HFLIP:
371         {
372             matrix.scale(-1, 1);
373             matrix.rotate(90);
374             break;
375         }
376 
377         case MetaEngine::ORIENTATION_ROT_90:
378         {
379             matrix.rotate(90);
380             break;
381         }
382 
383         case MetaEngine::ORIENTATION_ROT_90_VFLIP:
384         {
385             matrix.scale(1, -1);
386             matrix.rotate(90);
387             break;
388         }
389 
390         case MetaEngine::ORIENTATION_ROT_270:
391         {
392             matrix.rotate(270);
393             break;
394         }
395     }
396 
397     return matrix;
398 }
399 
400 } // namespace Digikam
401