summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/image/qimage.cpp14
-rw-r--r--src/gui/image/qimageiohandler.cpp37
-rw-r--r--src/gui/image/qimageiohandler.h19
-rw-r--r--src/gui/image/qimagereader.cpp63
-rw-r--r--src/gui/image/qimagereader.h5
-rw-r--r--src/gui/image/qimagewriter.cpp39
-rw-r--r--src/gui/image/qimagewriter.h3
-rw-r--r--src/gui/image/qjpeghandler.cpp152
-rw-r--r--tests/auto/gui/image/qimage/images/jpeg_exif_QTBUG-45865.jpgbin0 -> 27540 bytes
-rw-r--r--tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_1.jpgbin0 -> 910 bytes
-rw-r--r--tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_2.jpgbin0 -> 910 bytes
-rw-r--r--tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_3.jpgbin0 -> 988 bytes
-rw-r--r--tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_4.jpgbin0 -> 995 bytes
-rw-r--r--tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_5.jpgbin0 -> 912 bytes
-rw-r--r--tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_6.jpgbin0 -> 911 bytes
-rw-r--r--tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_6_motorola.jpgbin0 -> 911 bytes
-rw-r--r--tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_7.jpgbin0 -> 987 bytes
-rw-r--r--tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_8.jpgbin0 -> 991 bytes
-rw-r--r--tests/auto/gui/image/qimage/tst_qimage.cpp60
19 files changed, 388 insertions, 4 deletions
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<QString, QString> 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;
}
@@ -1144,6 +1150,59 @@ QList<QByteArray> QImageReader::supportedSubTypes() const
}
/*!
+ \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
data); otherwise returns \c false.
@@ -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<QByteArray> 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 <qbytearray.h>
#include <qfile.h>
#include <qfileinfo.h>
+#include <qimage.h>
#include <qimageiohandler.h>
#include <qjsonarray.h>
#include <qset.h>
@@ -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;
}
@@ -616,6 +619,33 @@ bool QImageWriter::progressiveScanWrite() const
}
/*!
+ \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
Use setText() instead.
@@ -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<QFile *>(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 0000000000..97deae3e25
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/jpeg_exif_QTBUG-45865.jpg
Binary files differ
diff --git a/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_1.jpg b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_1.jpg
new file mode 100644
index 0000000000..aaa4ac4e10
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_1.jpg
Binary files differ
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 0000000000..a61d2723d7
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_2.jpg
Binary files differ
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 0000000000..43e56dcef7
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_3.jpg
Binary files differ
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 0000000000..d5d06f7409
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_4.jpg
Binary files differ
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 0000000000..1886f3775e
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_5.jpg
Binary files differ
diff --git a/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_6.jpg b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_6.jpg
new file mode 100644
index 0000000000..5cec757354
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_6.jpg
Binary files differ
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 0000000000..0aa164b78b
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_6_motorola.jpg
Binary files differ
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 0000000000..b3dcc466a9
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_7.jpg
Binary files differ
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 0000000000..8bc390e2b9
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/jpeg_exif_orientation_value_8.jpg
Binary files differ
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<QString>("fileName");
+ QTest::addColumn<int>("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<bool*>(info);