From ba323b04cd78fb43e9e63b891e973d24b08250af Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 4 May 2015 13:32:32 +0200 Subject: [PATCH] Optionally apply orientation on QImage read Make it possible to read images with EXIF orientation automatically applied. This was originally implemented without opt-out in Qt 5.4, but reverted. Here it is implemented as opt-in for JPEG, and opt-out for TIFF to keep behavioral consistency. The EXIF support for JPEG was written by Rainer Keller. [ChangeLog][QtGui][Image plugins] An option has been added to QImageReader to enable automatic application of EXIF orientation. This behavior was default in Qt 5.4.1, but reverted in Qt 5.4.2. Task-number: QTBUG-37946 Task-number: QTBUG-43563 Task-number: QTBUG-45552 Task-number: QTBUG-45865 Change-Id: Iaafd2519b63ede66ecc1f8aa4c7118081312b8f5 Reviewed-by: Gunnar Sletta --- src/gui/image/qimage.cpp | 14 ++ src/gui/image/qimageiohandler.cpp | 37 +++++ src/gui/image/qimageiohandler.h | 19 ++- src/gui/image/qimagereader.cpp | 63 ++++++++ src/gui/image/qimagereader.h | 5 + src/gui/image/qimagewriter.cpp | 39 ++++- src/gui/image/qimagewriter.h | 3 + src/gui/image/qjpeghandler.cpp | 152 +++++++++++++++++- .../qimage/images/jpeg_exif_QTBUG-45865.jpg | Bin 0 -> 27540 bytes .../images/jpeg_exif_orientation_value_1.jpg | Bin 0 -> 910 bytes .../images/jpeg_exif_orientation_value_2.jpg | Bin 0 -> 910 bytes .../images/jpeg_exif_orientation_value_3.jpg | Bin 0 -> 988 bytes .../images/jpeg_exif_orientation_value_4.jpg | Bin 0 -> 995 bytes .../images/jpeg_exif_orientation_value_5.jpg | Bin 0 -> 912 bytes .../images/jpeg_exif_orientation_value_6.jpg | Bin 0 -> 911 bytes ...jpeg_exif_orientation_value_6_motorola.jpg | Bin 0 -> 911 bytes .../images/jpeg_exif_orientation_value_7.jpg | Bin 0 -> 987 bytes .../images/jpeg_exif_orientation_value_8.jpg | Bin 0 -> 991 bytes tests/auto/gui/image/qimage/tst_qimage.cpp | 60 +++++++ 19 files changed, 388 insertions(+), 4 deletions(-) create mode 100644 tests/auto/gui/image/qimage/images/jpeg_exif_QTBUG-45865.jpg create mode 100644 tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_1.jpg create mode 100644 tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_2.jpg create mode 100644 tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_3.jpg create mode 100644 tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_4.jpg create mode 100644 tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_5.jpg create mode 100644 tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_6.jpg create mode 100644 tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_6_motorola.jpg create mode 100644 tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_7.jpg create mode 100644 tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_8.jpg diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 8a0f570f47..cd0cbf7f49 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -5048,4 +5048,18 @@ QImage::Format QImage::toImageFormat(QPixelFormat format) Q_DECL_NOTHROW return Format_Invalid; } +Q_GUI_EXPORT void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient) +{ + if (orient == QImageIOHandler::TransformationNone) + return; + if (orient == QImageIOHandler::TransformationRotate270) { + src = rotated270(src); + } else { + src = qMove(src).mirrored(orient & QImageIOHandler::TransformationMirror, + orient & QImageIOHandler::TransformationFlip); + if (orient & QImageIOHandler::TransformationRotate90) + src = rotated90(src); + } +} + QT_END_NAMESPACE diff --git a/src/gui/image/qimageiohandler.cpp b/src/gui/image/qimageiohandler.cpp index cc9a6ae2a1..22b4bcf560 100644 --- a/src/gui/image/qimageiohandler.cpp +++ b/src/gui/image/qimageiohandler.cpp @@ -159,6 +159,43 @@ \value ProgressiveScanWrite. A handler which supports this option is expected to write the image as a progressive scan image. + + \value ImageTransformation. A handler which supports this option can read + the transformation metadata of an image. A handler that supports this option + should not apply the transformation itself. + + \value TransformedByDefault. A handler that reports support for this feature + will have image transformation metadata applied by default on read. +*/ + +/*! \enum QImageIOHandler::Transformation + \since 5.5 + + This enum describes the different transformations or orientations + supported by some image formats, usually through EXIF. + + \value TransformationNone No transformation should be applied. + + \value TransformationMirror Mirror the image horizontally. + + \value TransformationFlip Mirror the image vertically. + + \value TransformationRotate180 Rotate the image 180 degrees. + This is the same as mirroring it both horizontally and vertically. + + \value TransformationRotate90 Rotate the image 90 degrees. + + \value TransformationMirrorAndRotate90 Mirror the image horizontally + and then rotate it 90 degrees. + + \value TransformationFlipAndRotate90 Mirror the image vertically + and then rotate it 90 degrees. + + \value TransformationRotate270 Rotate the image 270 degrees. + This is the same as mirroring it both horizontally, vertically and + then rotating it 90 degrees. + + \sa QImageReader::transformation(), QImageReader::setAutoTransform(), QImageWriter::setTransformation() */ /*! diff --git a/src/gui/image/qimageiohandler.h b/src/gui/image/qimageiohandler.h index b48226f619..80cd87c4c3 100644 --- a/src/gui/image/qimageiohandler.h +++ b/src/gui/image/qimageiohandler.h @@ -86,8 +86,25 @@ public: ImageFormat, SupportedSubTypes, OptimizedWrite, - ProgressiveScanWrite + ProgressiveScanWrite, + ImageTransformation +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + , TransformedByDefault +#endif }; + + enum Transformation { + TransformationNone = 0, + TransformationMirror = 1, + TransformationFlip = 2, + TransformationRotate180 = TransformationMirror | TransformationFlip, + TransformationRotate90 = 4, + TransformationMirrorAndRotate90 = TransformationMirror | TransformationRotate90, + TransformationFlipAndRotate90 = TransformationFlip | TransformationRotate90, + TransformationRotate270 = TransformationRotate180 | TransformationRotate90 + }; + Q_DECLARE_FLAGS(Transformations, Transformation) + virtual QVariant option(ImageOption option) const; virtual void setOption(ImageOption option, const QVariant &value); virtual bool supportsOption(ImageOption option) const; diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index c2795cc38d..ba79bf40e5 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -533,6 +533,11 @@ public: int quality; QMap text; void getText(); + enum { + UsePluginDefault, + ApplyTransform, + DoNotApplyTransform + } autoTransform; // error QImageReader::ImageReaderError imageReaderError; @@ -552,6 +557,7 @@ QImageReaderPrivate::QImageReaderPrivate(QImageReader *qq) handler = 0; quality = -1; imageReaderError = QImageReader::UnknownError; + autoTransform = UsePluginDefault; q = qq; } @@ -1143,6 +1149,59 @@ QList QImageReader::supportedSubTypes() const return QList(); } +/*! + \since 5.5 + + Returns the transformation metadata of the image, including image orientation. If the format + does not support transformation metadata \c QImageIOHandler::Transformation_None is returned. + + \sa setAutoTransform(), autoTransform() +*/ +QImageIOHandler::Transformations QImageReader::transformation() const +{ + int option = QImageIOHandler::TransformationNone; + if (d->initHandler() && d->handler->supportsOption(QImageIOHandler::ImageTransformation)) + option = d->handler->option(QImageIOHandler::ImageTransformation).toInt(); + return QImageIOHandler::Transformations(option); +} + +/*! + \since 5.5 + + Sets if images returned by read() should have transformation metadata automatically applied. + + \sa autoTransform(), transform(), read() +*/ +void QImageReader::setAutoTransform(bool enabled) +{ + d->autoTransform = enabled ? QImageReaderPrivate::ApplyTransform + : QImageReaderPrivate::DoNotApplyTransform; +} + +/*! + \since 5.5 + + Returns \c true if the image handler will apply transformation metadata on read(). + + \sa setAutoTransform(), transformation(), read() +*/ +bool QImageReader::autoTransform() const +{ + switch (d->autoTransform) { + case QImageReaderPrivate::ApplyTransform: + return true; + case QImageReaderPrivate::DoNotApplyTransform: + return false; + case QImageReaderPrivate::UsePluginDefault: + if (d->initHandler()) + return d->handler->supportsOption(QImageIOHandler::TransformedByDefault); + // no break + default: + break; + } + return false; +} + /*! Returns \c true if an image can be read for the device (i.e., the image format is supported, and the device seems to contain valid @@ -1185,6 +1244,8 @@ QImage QImageReader::read() return read(&image) ? image : QImage(); } +extern void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient); + /*! \overload @@ -1294,6 +1355,8 @@ bool QImageReader::read(QImage *image) if (!disable2xImageLoading && QFileInfo(fileName()).baseName().endsWith(QLatin1String("@2x"))) { image->setDevicePixelRatio(2.0); } + if (autoTransform()) + qt_imageTransform(*image, transformation()); return true; } diff --git a/src/gui/image/qimagereader.h b/src/gui/image/qimagereader.h index 34191ed657..27a29bed49 100644 --- a/src/gui/image/qimagereader.h +++ b/src/gui/image/qimagereader.h @@ -105,6 +105,11 @@ public: bool supportsAnimation() const; + QImageIOHandler::Transformations transformation() const; + + void setAutoTransform(bool enabled); + bool autoTransform() const; + QByteArray subType() const; QList supportedSubTypes() const; diff --git a/src/gui/image/qimagewriter.cpp b/src/gui/image/qimagewriter.cpp index b418101163..e9de1db4b2 100644 --- a/src/gui/image/qimagewriter.cpp +++ b/src/gui/image/qimagewriter.cpp @@ -91,6 +91,7 @@ #include #include #include +#include #include #include #include @@ -254,6 +255,7 @@ public: QByteArray subType; bool optimizedWrite; bool progressiveScanWrite; + QImageIOHandler::Transformations transformation; // error QImageWriter::ImageWriterError imageWriterError; @@ -277,6 +279,7 @@ QImageWriterPrivate::QImageWriterPrivate(QImageWriter *qq) progressiveScanWrite = false; imageWriterError = QImageWriter::UnknownError; errorString = QImageWriter::tr("Unknown error"); + transformation = QImageIOHandler::TransformationNone; q = qq; } @@ -615,6 +618,33 @@ bool QImageWriter::progressiveScanWrite() const return d->progressiveScanWrite; } +/*! + \since 5.5 + + Sets the image transformations metadata including orientation. + + If transformation metadata is not supported by the image format, + the transform is applied before writing. + + \sa transformation(), write() +*/ +void QImageWriter::setTransformation(QImageIOHandler::Transformations transform) +{ + d->transformation = transform; +} + +/*! + \since 5.5 + + Returns the transformation and orientation the image has been set to written with. + + \sa setTransformation() +*/ +QImageIOHandler::Transformations QImageWriter::transformation() const +{ + return d->transformation; +} + /*! \obsolete @@ -694,6 +724,8 @@ bool QImageWriter::canWrite() const return d->canWriteHelper(); } +extern void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient); + /*! Writes the image \a image to the assigned device or file name. Returns \c true on success; otherwise returns \c false. If the @@ -708,6 +740,7 @@ bool QImageWriter::write(const QImage &image) if (!canWrite()) return false; + QImage img = image; if (d->handler->supportsOption(QImageIOHandler::Quality)) d->handler->setOption(QImageIOHandler::Quality, d->quality); if (d->handler->supportsOption(QImageIOHandler::CompressionRatio)) @@ -722,8 +755,12 @@ bool QImageWriter::write(const QImage &image) d->handler->setOption(QImageIOHandler::OptimizedWrite, d->optimizedWrite); if (d->handler->supportsOption(QImageIOHandler::ProgressiveScanWrite)) d->handler->setOption(QImageIOHandler::ProgressiveScanWrite, d->progressiveScanWrite); + if (d->handler->supportsOption(QImageIOHandler::ImageTransformation)) + d->handler->setOption(QImageIOHandler::ImageTransformation, int(d->transformation)); + else + qt_imageTransform(img, d->transformation); - if (!d->handler->write(image)) + if (!d->handler->write(img)) return false; if (QFile *file = qobject_cast(d->device)) file->flush(); diff --git a/src/gui/image/qimagewriter.h b/src/gui/image/qimagewriter.h index 96d8f51b3a..7f92595c53 100644 --- a/src/gui/image/qimagewriter.h +++ b/src/gui/image/qimagewriter.h @@ -89,6 +89,9 @@ public: void setProgressiveScanWrite(bool progressive); bool progressiveScanWrite() const; + QImageIOHandler::Transformations transformation() const; + void setTransformation(QImageIOHandler::Transformations orientation); + // Obsolete as of 4.1 void setDescription(const QString &description); QString description() const; diff --git a/src/gui/image/qjpeghandler.cpp b/src/gui/image/qjpeghandler.cpp index 2014b7440c..4ff3917fe6 100644 --- a/src/gui/image/qjpeghandler.cpp +++ b/src/gui/image/qjpeghandler.cpp @@ -714,7 +714,7 @@ public: }; QJpegHandlerPrivate(QJpegHandler *qq) - : quality(75), iod_src(0), + : quality(75), transformation(QImageIOHandler::TransformationNone), iod_src(0), rgb888ToRgb32ConverterPtr(qt_convert_rgb888_to_rgb32), state(Ready), optimize(false), progressive(false), q(qq) {} @@ -732,6 +732,7 @@ public: bool read(QImage *image); int quality; + QImageIOHandler::Transformations transformation; QVariant size; QImage::Format format; QSize scaledSize; @@ -754,6 +755,122 @@ public: QJpegHandler *q; }; +static bool readExifHeader(QDataStream &stream) +{ + char prefix[6]; + if (stream.readRawData(prefix, sizeof(prefix)) != sizeof(prefix)) + return false; + static const char exifMagic[6] = {'E', 'x', 'i', 'f', 0, 0}; + return memcmp(prefix, exifMagic, 6) == 0; +} + +/* + * Returns -1 on error + * Returns 0 if no Exif orientation was found + * Returns 1 orientation is horizontal (normal) + * Returns 2 mirror horizontal + * Returns 3 rotate 180 + * Returns 4 mirror vertical + * Returns 5 mirror horizontal and rotate 270 CCW + * Returns 6 rotate 90 CW + * Returns 7 mirror horizontal and rotate 90 CW + * Returns 8 rotate 270 CW + */ +static int getExifOrientation(QByteArray &exifData) +{ + QDataStream stream(&exifData, QIODevice::ReadOnly); + + if (!readExifHeader(stream)) + return -1; + + quint16 val; + quint32 offset; + const qint64 headerStart = stream.device()->pos(); + + // read byte order marker + stream >> val; + if (val == 0x4949) // 'II' == Intel + stream.setByteOrder(QDataStream::LittleEndian); + else if (val == 0x4d4d) // 'MM' == Motorola + stream.setByteOrder(QDataStream::BigEndian); + else + return -1; // unknown byte order + + // read size + stream >> val; + if (val != 0x2a) + return -1; + + stream >> offset; + + // read IFD + while (!stream.atEnd()) { + quint16 numEntries; + + // skip offset bytes to get the next IFD + const qint64 bytesToSkip = offset - (stream.device()->pos() - headerStart); + + if (stream.skipRawData(bytesToSkip) != bytesToSkip) + return -1; + + stream >> numEntries; + + for (; numEntries > 0; --numEntries) { + quint16 tag; + quint16 type; + quint32 components; + quint16 value; + quint16 dummy; + + stream >> tag >> type >> components >> value >> dummy; + if (tag == 0x0112) { // Tag Exif.Image.Orientation + if (components != 1) + return -1; + if (type != 3) // we are expecting it to be an unsigned short + return -1; + if (value < 1 || value > 8) // check for valid range + return -1; + + // It is possible to include the orientation multiple times. + // Right now the first value is returned. + return value; + } + } + + // read offset to next IFD + stream >> offset; + if (offset == 0) // this is the last IFD + break; + } + + // No Exif orientation was found + return 0; +} + +static QImageIOHandler::Transformations exif2Qt(int exifOrientation) +{ + switch (exifOrientation) { + case 1: // normal + return QImageIOHandler::TransformationNone; + case 2: // mirror horizontal + return QImageIOHandler::TransformationMirror; + case 3: // rotate 180 + return QImageIOHandler::TransformationRotate180; + case 4: // mirror vertical + return QImageIOHandler::TransformationFlip; + case 5: // mirror horizontal and rotate 270 CW + return QImageIOHandler::TransformationFlipAndRotate90; + case 6: // rotate 90 CW + return QImageIOHandler::TransformationRotate90; + case 7: // mirror horizontal and rotate 90 CW + return QImageIOHandler::TransformationMirrorAndRotate90; + case 8: // rotate 270 CW + return QImageIOHandler::TransformationRotate270; + } + qWarning("Invalid EXIF orientation"); + return QImageIOHandler::TransformationNone; +} + /*! \internal */ @@ -773,6 +890,7 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) if (!setjmp(err.setjmp_buffer)) { jpeg_save_markers(&info, JPEG_COM, 0xFFFF); + jpeg_save_markers(&info, JPEG_APP0 + 1, 0xFFFF); // Exif uses APP1 marker (void) jpeg_read_header(&info, TRUE); @@ -784,6 +902,8 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) format = QImage::Format_Invalid; read_jpeg_format(format, &info); + QByteArray exifData; + for (jpeg_saved_marker_ptr marker = info.marker_list; marker != NULL; marker = marker->next) { if (marker->marker == JPEG_COM) { QString key, value; @@ -801,9 +921,20 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device) description += key + QLatin1String(": ") + value.simplified(); readTexts.append(key); readTexts.append(value); + } else if (marker->marker == JPEG_APP0 + 1) { + exifData.append((const char*)marker->data, marker->data_length); } } + if (!exifData.isEmpty()) { + // Exif data present + int exifOrientation = getExifOrientation(exifData); + if (exifOrientation == -1) + return false; + if (exifOrientation > 0) + transformation = exif2Qt(exifOrientation); + } + state = ReadHeader; return true; } @@ -905,8 +1036,16 @@ bool QJpegHandler::read(QImage *image) return d->read(image); } +extern void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient); + bool QJpegHandler::write(const QImage &image) { + if (d->transformation != QImageIOHandler::TransformationNone) { + // We don't support writing EXIF headers so apply the transform to the data. + QImage img = image; + qt_imageTransform(img, d->transformation); + return write_jpeg_image(img, device(), d->quality, d->description, d->optimize, d->progressive); + } return write_jpeg_image(image, device(), d->quality, d->description, d->optimize, d->progressive); } @@ -920,7 +1059,8 @@ bool QJpegHandler::supportsOption(ImageOption option) const || option == Size || option == ImageFormat || option == OptimizedWrite - || option == ProgressiveScanWrite; + || option == ProgressiveScanWrite + || option == ImageTransformation; } QVariant QJpegHandler::option(ImageOption option) const @@ -947,6 +1087,9 @@ QVariant QJpegHandler::option(ImageOption option) const return d->optimize; case ProgressiveScanWrite: return d->progressive; + case ImageTransformation: + d->readJpegHeader(device()); + return int(d->transformation); default: break; } @@ -978,6 +1121,11 @@ void QJpegHandler::setOption(ImageOption option, const QVariant &value) case ProgressiveScanWrite: d->progressive = value.toBool(); break; + case ImageTransformation: { + int transformation = value.toInt(); + if (transformation > 0 && transformation < 8) + d->transformation = QImageIOHandler::Transformations(transformation); + } default: break; } diff --git a/tests/auto/gui/image/qimage/images/jpeg_exif_QTBUG-45865.jpg b/tests/auto/gui/image/qimage/images/jpeg_exif_QTBUG-45865.jpg new file mode 100644 index 0000000000000000000000000000000000000000..97deae3e253e9c3463d0cec502a835a00400215b GIT binary patch literal 27540 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

S+;pL=slK_i2Am$eVfTEYH5cyVz|pvryk$ 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 AMF0Q* literal 0 HcmV?d00001 diff --git a/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_2.jpg b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a61d2723d7cf43ee9244f4dcb6b26d848574a480 GIT binary patch literal 910 zcmex=>Y?(AtXBkCs161k%1ZL zPh`N##>Ddf76T76BLkBFvjEVym*4elSJ@emF31klAua&21z7_dGm8LF0|zL)SedF# zo@*8=m&VTrs#O#On$Ez$$_iJ`$i%|Tz{SEB3f?O;B>8U}nWRec5HX`RX%{8Fcm}D@-|a=DWw4GgFtixb9vO zy&D9+-v?PK0JH-oNSIleSlJi_grR}LX3uoocJk)8ewn#!;^x^-oatBB@TSf?)zrl7 zwq{3lG+Wgvv1RM$h+KPmeR-_QEeGED?}TP9+ho}mnX<&1N3Lzfam%xI6P6iztmz1v zGHJ>Q&(oXd{J!bE_kAtUb}>+NAbbmCF*3ko!(NeDxnkYrBt{Ww$9lsm#Z{+bfNlcE zEI5b+82Eukhzp{}C@|$f%mBrW0B0$Sj0ocb1qP?Yea_K6W=6T^>Veusf!a_kW?+P9 z0tK>w+XE(xbM9d$y!aVjFdmu1nGe(->>Kn<)5C;n#;;;xvy%_1)O>qVvO I_5W`I093Zmxc~qF literal 0 HcmV?d00001 diff --git a/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_3.jpg b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..43e56dcef70bbd6cf48fae727ea5f6cb4d63061e GIT binary patch literal 988 zcmex=>Y?(AtWa|2T)uB=t*Xf zM-YI8jf3<5Ee0N-Fp~hY0MNI=ya{gaK05#Dyz!p_s7G7?WDi0E0|N^iI|EP)2Pni? znJ#WoK4ZOk%hH{Hw{D%81J)uZ2(pEV8KI4liG>-ci&arT5Udj@WzYDhx7%IYQ0J@} z&l&$O(=0VrCV!b!H>qy&^T^Wa(f6DK>kir#p|#jj3gK zy;7L%uzKm~!`{gmWj7*cnVIRWTTvbpA8#_7L+2i!-j)>(W+%^1=el~Uy0!2a-|gOy zcTW47oXjhWO!?6%^2VU()=|SV(Gjhhx=8}lw!Y8Ya_0BC(74^pBU5+PKG54?zSW6= z!D=m!BS+g+H${%pwQN>lnqDqp8wKTn&J+d4CURT?<68h0m-dPi7AGB-R@YSu-L(G7 z!tf8QzyK4hQhfD`AE*SJLclpdpg|v$E(C#Ljbaod15)e?EZZ$GZ@s37h}Po(jj2I>+ML`zJ-dJtOEO@}jAme#}38+Ox0L{e=3UC(-NEPw4 c<;{yOihgMQw`8jpNA32x_o^1S*!{l=0P;Kok^lez literal 0 HcmV?d00001 diff --git a/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_4.jpg b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d5d06f740922c4ecd4e49cd2aaa90a8293c4e108 GIT binary patch literal 995 zcmex={xcvIrKfhkD{O+;sc9na`y3^XP=J1+^ zhsyBqEt-{4!gl?_x0zedWloklq-><5dw)OBei?8;Az9A=w4RBTjX^{V8dhxfOn>4w zuWr=sOP-uABzfuS=Qi2t+pJq|uXSF&dj5s%jq5FcM4r-YYkQ+}mH7+1*}9u;wI@w3 zcAeJwETNv*Jn5O`xtygO!4gYm3oO;t&7LrQ%f0V2w=PSoy{k68^u~%=bNhWZb?IKt zWlWvMc6kxg>ju9ojts6*Jx;ROj+eGB)fVmOuLn9*Q~(h~kO*QFfW@Z0qTAz1j~bVG zIf)1RADP!twQtI=eA@!k`hsAf5^y460L7|611N2X2_kzJWELX>#3)ew3LHA;UEO0e zS@+P{2ZnKdKv`D2dO-0baAe^FJq8)i1JnGzZrO4=+3W;JivZY8kohnmUZda0FylhIRF3v literal 0 HcmV?d00001 diff --git a/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_5.jpg b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_5.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1886f3775e047d42bb5ffb2af704f84434af336b GIT binary patch literal 912 zcmex=>Y?(AtXBoCs16Rk%1ZH z4+LOhW?}n(i-89y%p|}p0QBq?f!eYRncJt1cdQ2LkQ88GLe;{;4io|D;Q)mgE7Q8Q zyt`)XR+$|B8K_)Z5M%+e21X_pW&uG(L$C&BMn-$ab-UKDn!RpWciy(%)tgRzyVSLM zRj+iG=CayGz_tXO| zk^@`B#Ka8u3eYEvZ0rJptU`)lyO=aUf)`s*e4lK1C9ersW&(hM; z*Zlc-bCWlF$~PW^vo?XQ=PY9;9#;yt+2n10?%vF2vr0NNg%>(-d4(`-)p22PaW_5= zv{nWZ90*$(m|2)u*%%oFgrFh9X3w;B>G^KeIY)!NO0VYKeEc?k{-F|yc0s8P;rX+< zGp)|Po5&!wZJ%bycU2P!#i%SB{u$vi32%4Yvo<=NyD4(+v`AYmS8E=bO%r{J#ex*Q zJiSupEVVqn>3#KUpaaAN7?3Ond!A7M78&-6O?8vx5+BOv?%nkHI%D{ER(_xqIBvnZ z1RnH*j1UB-8+fpR7(nNPjQ~ZDfcK03osW}J0%reBNVi!M5VC6Ze7u@K;VfV$@3mq= yp*ql(!jjB_8)ts$1NDdspxMK~r~r4WK=ivr$7{z#%~w>Y?(AtXBoCs16Rk%1ZH z4+LOhVP*S&i-89y%p|}p0QBtTXKedox}W7<(R^eN)FL4O)PkyqnT?4BsD}d-VysL@ zU#n}Ln7)6N2~e?|Ajo`1R)l&+CKhIPRyF}eLqV_>pjq~ep&^U`46E!#!!Iz@gvv88 zUtqrQ@#H3Nc9kL?gR?e)uJ``lR4F{!SA1G!^Vip(=4CEF8myy!SjA`Kag~@)XF~;@ z8f@x;wn>3(0|F%9FtaeS3P5aP0@<`~*ZNhn*RAT#-PRtSI(6ri(9n<#b6rg?JTlwY zzUAz?VvifwBqb*b=KVaHxV`^mw$F@X#R|z1hb|i%PjyL4i(ca5x_e3VZV>o>9%!Si z00R@ygGgb>%)-RV#>gNbC=3k|HhZScC-?SU(4DpN7ngL*J@M4^2&tpRKZ>p{vIq!> zT5vvc!`5pnIj_%jeR=(KBip3EvpG*cb6wUG7MX77beKnGvf;VnP%Wn!N7YlDrfi)DJX5To5(TfJO*H%>c!Y0P|iA8HR>O3=T{M_EY?R_qLo228Fg5NE;%kk!%Kq zv_Sq5;TMOML%SxmH|$_2RA-6-Y7r4Y)xf}@0C%c@Xw1nQUHpf8_MBRAbn6F&tM&hH F0sz;@-NOI? literal 0 HcmV?d00001 diff --git a/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_6_motorola.jpg b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_6_motorola.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0aa164b78b986bec1177bbac2ac5a181c5423a3e GIT binary patch literal 911 zcmex=q=O;=GK3-nK$`y_Vi4qD;9}rnW)x@ci+Wc+`GK@992h+82fI|nCFT%3`C z8R$hMz{JAJ_Wu?G4^Ws%fLQ?O$IH*y_QiBR%e|uc$R4OgLI9`*RSz>86AMre2Pni? znU20z*E}(O|0)xpVmU#O`HZXx^^8m`%={Et7y}qq*^7o>V5kX| zXJEd-eBtBCP2TJ(MLY&)Z312I{k^GDc(Skfw94kMuRqPpTz)iINByvh&&J~_F`v$c z3OY5|)B|mk0@(%x2=6d4voNv>Kx|?H*|cug`c<>nt?JI*)*hZZb?21O(2xytT}>`L zGTYa_$}=|x&fuY zaSKkr0!;ltBg6$!0}W_|Ak+*{>ywSyfxM$C)6-T#zP`FzE G|0V$P?A^-% literal 0 HcmV?d00001 diff --git a/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_7.jpg b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_7.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b3dcc466a907a504db72fa83957326bfcc5aac78 GIT binary patch literal 987 zcmex=>Y?(AtXB|7f@V+k%1ZL zPh`Nt#=-gj76T76BLkBFvjEVyD;s9!tyXc4ynVj56{tsC0AvrU7Ip@p77kE|u`*rQ zlI|mGd9UWm*R5M;=KN;>YLOEJTEoDEWECS53o{!#1FNEdAXq0*%AWD#;@-}*X=!OH zo6RJ}RQ{?=+JDLOm*+44=aHq;qwin)TU&a!_K@zjdxA%`H=X*ieBq*1uNGf8xK-q` z+XcJb7x%6E^>*93TiJ_^cZ;d~1llSKvK7UHOw25dtPE`IP)nIWmVWYI_E(KFtxbNx z?<0Y(Rw;d!w(EDBZ1T1`YJAq@5i^R)_rJ90KM_j6b?i2+^Cvs$hk~b_e?G-1i=`)tpd?Tr{Uo zx|O;?Ip_tt&|u52V|C-?FNFmU_!wk74=@3BhzOu*U{HX&S3v5KAy>Lm Z^@`#H;nhnfX))AppL?%rfs5V$n*d6*1>67t literal 0 HcmV?d00001 diff --git a/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_8.jpg b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_8.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8bc390e2b9a7e8704f824eaedf0248a2181b6650 GIT binary patch literal 991 zcmex=w%gi1sIrsvPgi1oq?H+6{w2? z6lknWiBi`e^Vg|MgnsNelE88Rs7Fo^Xb}Sw6Ej>FBNGcV13Q}{tAHR_BhVf8j8Epy zylLQVV(?9Tj;l!UW0k^_PwTC1HhJ4+_k8xX3ttxD-{vthxzB6o(^;#OS{Z61Q@dlm zwq9DbC2!U0eG5a~dKehYA23ugR58y7S}F^+6lgHqu}sV?j6e@EK<#7#+4*yhKHp`t z?VXP$jtLs}qzKKNIq#+9%t>21I<}W|mxDn4{&}g-;_~Zf|NMHr^1H{j+g0u%>rQLG zn!{@v9xB7bw`f*I3ETAx-)3$-mpNJLkg}1I?*08B`yl~^6jng%nONBv1cb$)fyHLe zR36>^=hz{Kc+VL}(kI`jPEkJh^lQoCsBQBL=UctW{v!DzamBJSQK=|%r{hO&J=>V( zb2(i_@% literal 0 HcmV?d00001 diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index 62a9f19f36..da29a57f98 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -181,6 +181,11 @@ private slots: void invertPixelsRGB_data(); void invertPixelsRGB(); + void exifOrientation_data(); + void exifOrientation(); + + void exif_QTBUG45865(); + void cleanupFunctions(); void devicePixelRatio(); @@ -2810,6 +2815,61 @@ void tst_QImage::invertPixelsRGB() QCOMPARE(qBlue(pixel) >> 4, (255 - 96) >> 4); } +void tst_QImage::exifOrientation_data() +{ + QTest::addColumn("fileName"); + QTest::addColumn("orientation"); + QTest::newRow("Orientation 1, Intel format") << m_prefix + "jpeg_exif_orientation_value_1.jpg" << (int)QImageIOHandler::TransformationNone; + QTest::newRow("Orientation 2, Intel format") << m_prefix + "jpeg_exif_orientation_value_2.jpg" << (int)QImageIOHandler::TransformationMirror; + QTest::newRow("Orientation 3, Intel format") << m_prefix + "jpeg_exif_orientation_value_3.jpg" << (int)QImageIOHandler::TransformationRotate180; + QTest::newRow("Orientation 4, Intel format") << m_prefix + "jpeg_exif_orientation_value_4.jpg" << (int)QImageIOHandler::TransformationFlip; + QTest::newRow("Orientation 5, Intel format") << m_prefix + "jpeg_exif_orientation_value_5.jpg" << (int)QImageIOHandler::TransformationFlipAndRotate90; + QTest::newRow("Orientation 6, Intel format") << m_prefix + "jpeg_exif_orientation_value_6.jpg" << (int)QImageIOHandler::TransformationRotate90; + QTest::newRow("Orientation 6, Motorola format") << m_prefix + "jpeg_exif_orientation_value_6_motorola.jpg" << (int)QImageIOHandler::TransformationRotate90; + QTest::newRow("Orientation 7, Intel format") << m_prefix + "jpeg_exif_orientation_value_7.jpg" << (int)QImageIOHandler::TransformationMirrorAndRotate90; + QTest::newRow("Orientation 8, Intel format") << m_prefix + "jpeg_exif_orientation_value_8.jpg" << (int)QImageIOHandler::TransformationRotate270; +} + +QT_BEGIN_NAMESPACE +extern void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient); +QT_END_NAMESPACE +QT_USE_NAMESPACE + +void tst_QImage::exifOrientation() +{ + QFETCH(QString, fileName); + QFETCH(int, orientation); + + QImageReader imageReader(fileName); + imageReader.setAutoTransform(true); + QCOMPARE(imageReader.transformation(), orientation); + QImage img = imageReader.read(); + QRgb px; + QVERIFY(!img.isNull()); + + px = img.pixel(0, 0); + QVERIFY(qRed(px) > 250 && qGreen(px) < 5 && qBlue(px) < 5); + + px = img.pixel(img.width() - 1, 0); + QVERIFY(qRed(px) < 5 && qGreen(px) < 5 && qBlue(px) > 250); + + QImageReader imageReader2(fileName); + QCOMPARE(imageReader2.autoTransform(), false); + QCOMPARE(imageReader2.transformation(), orientation); + QImage img2 = imageReader2.read(); + qt_imageTransform(img2, imageReader2.transformation()); + QCOMPARE(img, img2); +} + +void tst_QImage::exif_QTBUG45865() +{ + QFile file(m_prefix + "jpeg_exif_QTBUG-45865.jpg"); + QVERIFY(file.open(QIODevice::ReadOnly)); + QByteArray byteArray = file.readAll(); + QImage image = QImage::fromData(byteArray); + QCOMPARE(image.size(), QSize(5, 8)); +} + static void cleanupFunction(void* info) { bool *called = static_cast(info);