File indexing completed on 2025-01-05 03:56:29
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2006-09-15 0007 * Description : Exiv2 library interface. 0008 * Tools for combining rotation operations. 0009 * 0010 * SPDX-FileCopyrightText: 2006-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0011 * SPDX-FileCopyrightText: 2006-2013 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de> 0012 * 0013 * SPDX-License-Identifier: GPL-2.0-or-later 0014 * 0015 * ============================================================ */ 0016 0017 // Local includes 0018 0019 #include "metaengine_rotation.h" 0020 0021 namespace Digikam 0022 { 0023 0024 /** 0025 * If the picture is displayed according to the exif orientation tag, 0026 * the user will request rotating operations relative to what he sees, 0027 * and that is the picture rotated according to the EXIF tag. 0028 * So the operation requested and the given EXIF angle must be combined. 0029 * E.g. if orientation is "6" (rotate 90 clockwiseto show correctly) 0030 * and the user selects 180 clockwise, the operation is 270. 0031 * If the user selected 270, the operation would be None (and clearing the exif tag). 0032 * 0033 * This requires to describe the transformations in a model which 0034 * cares for both composing (180+90=270) and eliminating (180+180=no action), 0035 * as well as the non-commutative nature of the operations (vflip+90 is not 90+vflip) 0036 * 0037 * All 2D transformations can be described by a 2x3 matrix, see QWMetaEngineRotation. 0038 * All transformations needed here - rotate 90, 180, 270, flipV, flipH - 0039 * can be described in a 2x2 matrix with the values 0,1,-1 0040 * (because flipping is expressed by changing the sign only, 0041 * and sine and cosine of 90, 180 and 270 are either 0,1 or -1). 0042 * 0043 * x' = m11 x + m12 y 0044 * y' = m21 x + m22 y 0045 * 0046 * Moreover, all combinations of these rotate/flip operations result in one of the eight 0047 * matrices defined below. 0048 * (I did not proof that mathematically, but empirically) 0049 * 0050 * static const MetaEngineRotation identity; //( 1, 0, 0, 1) 0051 * static const MetaEngineRotation rotate90; //( 0, 1, -1, 0) 0052 * static const MetaEngineRotation rotate180; //(-1, 0, 0, -1) 0053 * static const MetaEngineRotation rotate270; //( 0, -1, 1, 0) 0054 * static const MetaEngineRotation flipHorizontal; //(-1, 0, 0, 1) 0055 * static const MetaEngineRotation flipVertical; //( 1, 0, 0, -1) 0056 * static const MetaEngineRotation rotate90flipHorizontal; //( 0, 1, 1, 0), first rotate, then flip 0057 * static const MetaEngineRotation rotate90flipVertical; //( 0, -1, -1, 0), first rotate, then flip 0058 */ 0059 0060 namespace Matrix 0061 { 0062 0063 static const MetaEngineRotation identity ( 1, 0, 0, 1); 0064 static const MetaEngineRotation rotate90 ( 0, 1, -1, 0); 0065 static const MetaEngineRotation rotate180 (-1, 0, 0, -1); 0066 static const MetaEngineRotation rotate270 ( 0, -1, 1, 0); 0067 static const MetaEngineRotation flipHorizontal (-1, 0, 0, 1); 0068 static const MetaEngineRotation flipVertical ( 1, 0, 0, -1); 0069 static const MetaEngineRotation rotate90flipHorizontal ( 0, 1, 1, 0); 0070 static const MetaEngineRotation rotate90flipVertical ( 0, -1, -1, 0); 0071 0072 MetaEngineRotation matrix(MetaEngineRotation::TransformationAction action) 0073 { 0074 switch (action) 0075 { 0076 case MetaEngineRotation::NoTransformation: 0077 { 0078 return identity; 0079 } 0080 0081 case MetaEngineRotation::FlipHorizontal: 0082 { 0083 return flipHorizontal; 0084 } 0085 0086 case MetaEngineRotation::FlipVertical: 0087 { 0088 return flipVertical; 0089 } 0090 0091 case MetaEngineRotation::Rotate90: 0092 { 0093 return rotate90; 0094 } 0095 0096 case MetaEngineRotation::Rotate180: 0097 { 0098 return rotate180; 0099 } 0100 0101 case MetaEngineRotation::Rotate270: 0102 { 0103 return rotate270; 0104 } 0105 } 0106 0107 return identity; 0108 } 0109 0110 MetaEngineRotation matrix(MetaEngine::ImageOrientation exifOrientation) 0111 { 0112 switch (exifOrientation) 0113 { 0114 case MetaEngine::ORIENTATION_NORMAL: 0115 { 0116 return identity; 0117 } 0118 0119 case MetaEngine::ORIENTATION_HFLIP: 0120 { 0121 return flipHorizontal; 0122 } 0123 0124 case MetaEngine::ORIENTATION_ROT_180: 0125 { 0126 return rotate180; 0127 } 0128 0129 case MetaEngine::ORIENTATION_VFLIP: 0130 { 0131 return flipVertical; 0132 } 0133 0134 case MetaEngine::ORIENTATION_ROT_90_HFLIP: 0135 { 0136 return rotate90flipHorizontal; 0137 } 0138 0139 case MetaEngine::ORIENTATION_ROT_90: 0140 { 0141 return rotate90; 0142 } 0143 0144 case MetaEngine::ORIENTATION_ROT_90_VFLIP: 0145 { 0146 return rotate90flipVertical; 0147 } 0148 0149 case MetaEngine::ORIENTATION_ROT_270: 0150 { 0151 return rotate270; 0152 } 0153 0154 case MetaEngine::ORIENTATION_UNSPECIFIED: 0155 { 0156 return identity; 0157 } 0158 } 0159 0160 return identity; 0161 } 0162 0163 } // namespace Matrix 0164 0165 MetaEngineRotation::MetaEngineRotation() 0166 { 0167 set(1, 0, 0, 1); 0168 } 0169 0170 MetaEngineRotation::MetaEngineRotation(TransformationAction action) 0171 { 0172 *this = Matrix::matrix(action); 0173 } 0174 0175 MetaEngineRotation::MetaEngineRotation(MetaEngine::ImageOrientation exifOrientation) 0176 { 0177 *this = Matrix::matrix(exifOrientation); 0178 } 0179 0180 MetaEngineRotation::MetaEngineRotation(int m11, int m12, int m21, int m22) 0181 { 0182 set(m11, m12, m21, m22); 0183 } 0184 0185 void MetaEngineRotation::set(int m11, int m12, int m21, int m22) 0186 { 0187 m[0][0] = m11; 0188 m[0][1] = m12; 0189 m[1][0] = m21; 0190 m[1][1] = m22; 0191 } 0192 0193 bool MetaEngineRotation::isNoTransform() const 0194 { 0195 return (*this == Matrix::identity); 0196 } 0197 0198 MetaEngineRotation& MetaEngineRotation::operator*=(const MetaEngineRotation& ma) 0199 { 0200 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], 0201 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]); 0202 0203 return *this; 0204 } 0205 0206 bool MetaEngineRotation::operator==(const MetaEngineRotation& ma) const 0207 { 0208 return ( 0209 (m[0][0] == ma.m[0][0]) && 0210 (m[0][1] == ma.m[0][1]) && 0211 (m[1][0] == ma.m[1][0]) && 0212 (m[1][1] == ma.m[1][1]) 0213 ); 0214 } 0215 0216 bool MetaEngineRotation::operator!=(const MetaEngineRotation& ma) const 0217 { 0218 return !(*this==ma); 0219 } 0220 0221 MetaEngineRotation& MetaEngineRotation::operator*=(TransformationAction action) 0222 { 0223 return (*this *= Matrix::matrix(action)); 0224 } 0225 0226 MetaEngineRotation& MetaEngineRotation::operator*=(QList<TransformationAction> actions) 0227 { 0228 Q_FOREACH (const TransformationAction& action, actions) 0229 { 0230 *this *= Matrix::matrix(action); 0231 } 0232 0233 return *this; 0234 } 0235 0236 MetaEngineRotation& MetaEngineRotation::operator*=(MetaEngine::ImageOrientation exifOrientation) 0237 { 0238 return (*this *= Matrix::matrix(exifOrientation)); 0239 } 0240 0241 /** 0242 * Converts the mathematically correct description 0243 * into the primitive operations that can be carried out losslessly. 0244 */ 0245 QList<MetaEngineRotation::TransformationAction> MetaEngineRotation::transformations() const 0246 { 0247 QList<TransformationAction> transforms; 0248 0249 if (*this == Matrix::rotate90) 0250 { 0251 transforms << Rotate90; 0252 } 0253 else if (*this == Matrix::rotate180) 0254 { 0255 transforms << Rotate180; 0256 } 0257 else if (*this == Matrix::rotate270) 0258 { 0259 transforms << Rotate270; 0260 } 0261 else if (*this == Matrix::flipHorizontal) 0262 { 0263 transforms << FlipHorizontal; 0264 } 0265 else if (*this == Matrix::flipVertical) 0266 { 0267 transforms << FlipVertical; 0268 } 0269 else if (*this == Matrix::rotate90flipHorizontal) 0270 { 0271 // first rotate, then flip! 0272 0273 transforms << Rotate90; 0274 transforms << FlipHorizontal; 0275 } 0276 else if (*this == Matrix::rotate90flipVertical) 0277 { 0278 // first rotate, then flip! 0279 0280 transforms << Rotate90; 0281 transforms << FlipVertical; 0282 } 0283 0284 return transforms; 0285 } 0286 0287 MetaEngine::ImageOrientation MetaEngineRotation::exifOrientation() const 0288 { 0289 if (*this == Matrix::identity) 0290 { 0291 return MetaEngine::ORIENTATION_NORMAL; 0292 } 0293 0294 if (*this == Matrix::rotate90) 0295 { 0296 return MetaEngine::ORIENTATION_ROT_90; 0297 } 0298 else if (*this == Matrix::rotate180) 0299 { 0300 return MetaEngine::ORIENTATION_ROT_180; 0301 } 0302 else if (*this == Matrix::rotate270) 0303 { 0304 return MetaEngine::ORIENTATION_ROT_270; 0305 } 0306 else if (*this == Matrix::flipHorizontal) 0307 { 0308 return MetaEngine::ORIENTATION_HFLIP; 0309 } 0310 else if (*this == Matrix::flipVertical) 0311 { 0312 return MetaEngine::ORIENTATION_VFLIP; 0313 } 0314 else if (*this == Matrix::rotate90flipHorizontal) 0315 { 0316 return MetaEngine::ORIENTATION_ROT_90_HFLIP; 0317 } 0318 else if (*this == Matrix::rotate90flipVertical) 0319 { 0320 return MetaEngine::ORIENTATION_ROT_90_VFLIP; 0321 } 0322 0323 return MetaEngine::ORIENTATION_UNSPECIFIED; 0324 } 0325 0326 QTransform MetaEngineRotation::toTransform() const 0327 { 0328 return toTransform(exifOrientation()); 0329 } 0330 0331 QTransform MetaEngineRotation::toTransform(MetaEngine::ImageOrientation orientation) 0332 { 0333 QTransform matrix; 0334 0335 switch (orientation) 0336 { 0337 case MetaEngine::ORIENTATION_NORMAL: 0338 case MetaEngine::ORIENTATION_UNSPECIFIED: 0339 { 0340 break; 0341 } 0342 0343 case MetaEngine::ORIENTATION_HFLIP: 0344 { 0345 matrix.scale(-1, 1); 0346 break; 0347 } 0348 0349 case MetaEngine::ORIENTATION_ROT_180: 0350 { 0351 matrix.rotate(180); 0352 break; 0353 } 0354 0355 case MetaEngine::ORIENTATION_VFLIP: 0356 { 0357 matrix.scale(1, -1); 0358 break; 0359 } 0360 0361 case MetaEngine::ORIENTATION_ROT_90_HFLIP: 0362 { 0363 matrix.scale(-1, 1); 0364 matrix.rotate(90); 0365 break; 0366 } 0367 0368 case MetaEngine::ORIENTATION_ROT_90: 0369 { 0370 matrix.rotate(90); 0371 break; 0372 } 0373 0374 case MetaEngine::ORIENTATION_ROT_90_VFLIP: 0375 { 0376 matrix.scale(1, -1); 0377 matrix.rotate(90); 0378 break; 0379 } 0380 0381 case MetaEngine::ORIENTATION_ROT_270: 0382 { 0383 matrix.rotate(270); 0384 break; 0385 } 0386 } 0387 0388 return matrix; 0389 } 0390 0391 } // namespace Digikam