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 <gunnar@sletta.org>
@ -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
|
||||
|
@ -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()
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
@ -1143,6 +1149,59 @@ QList<QByteArray> QImageReader::supportedSubTypes() const
|
||||
return QList<QByteArray>();
|
||||
}
|
||||
|
||||
/*!
|
||||
\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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
@ -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<QFile *>(d->device))
|
||||
file->flush();
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
BIN
tests/auto/gui/image/qimage/images/jpeg_exif_QTBUG-45865.jpg
Normal file
After Width: | Height: | Size: 27 KiB |
After Width: | Height: | Size: 910 B |
After Width: | Height: | Size: 910 B |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 995 B |
After Width: | Height: | Size: 912 B |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 987 B |
After Width: | Height: | Size: 991 B |
@ -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);
|
||||
|