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