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