summaryrefslogtreecommitdiffstats
path: root/src/gui/image/qjpeghandler.cpp
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@theqtcompany.com>2014-11-24 13:37:06 +0100
committerFrederik Gladhorn <frederik.gladhorn@theqtcompany.com>2014-11-24 13:39:13 +0100
commit34aba4724f196e34ed02cf50073f41968f119bb6 (patch)
tree0ebdfcabda989ab76ee6de53c6461553c7a767a5 /src/gui/image/qjpeghandler.cpp
parentb86b2a742afae118bf974c82ba966ddb0cae4afb (diff)
parentb1cf07f495e10c93e53651ac03e46ebdaea0a97e (diff)
Merge remote-tracking branch 'origin/5.4' into dev
Conflicts: src/corelib/io/qiodevice.cpp src/plugins/bearer/linux_common/qofonoservice_linux.cpp src/plugins/bearer/linux_common/qofonoservice_linux_p.h src/plugins/platforms/android/qandroidplatformtheme.cpp src/tools/bootstrap/bootstrap.pro src/widgets/styles/qmacstyle_mac.mm Change-Id: Ia02aab6c4598ce74e9c30bb4666d5e2ef000f99b
Diffstat (limited to 'src/gui/image/qjpeghandler.cpp')
-rw-r--r--src/gui/image/qjpeghandler.cpp155
1 files changed, 152 insertions, 3 deletions
diff --git a/src/gui/image/qjpeghandler.cpp b/src/gui/image/qjpeghandler.cpp
index fbcc0608e2..ae30de634a 100644
--- a/src/gui/image/qjpeghandler.cpp
+++ b/src/gui/image/qjpeghandler.cpp
@@ -729,7 +729,7 @@ public:
};
QJpegHandlerPrivate(QJpegHandler *qq)
- : quality(75), iod_src(0), state(Ready), q(qq)
+ : quality(75), exifOrientation(1), iod_src(0), state(Ready), q(qq)
{}
~QJpegHandlerPrivate()
@@ -744,8 +744,10 @@ public:
bool readJpegHeader(QIODevice*);
bool read(QImage *image);
+ void applyExifOrientation(QImage *image);
int quality;
+ int exifOrientation;
QVariant size;
QImage::Format format;
QSize scaledSize;
@@ -763,6 +765,97 @@ public:
QJpegHandler *q;
};
+static bool readExifHeader(QDataStream &stream)
+{
+ char prefix[6];
+ if (stream.readRawData(prefix, sizeof(prefix)) != sizeof(prefix))
+ return false;
+ if (prefix[0] != 'E' || prefix[1] != 'x' || prefix[2] != 'i' || prefix[3] != 'f' || prefix[4] != 0 || prefix[5] != 0)
+ return false;
+ return true;
+}
+
+/*
+ * 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;
+
+ // 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;
+ // we have already used 8 bytes of TIFF header
+ offset -= 8;
+
+ // read IFD
+ while (!stream.atEnd()) {
+ quint16 numEntries;
+
+ // skip offset bytes to get the next IFD
+ if (stream.skipRawData(offset) != (qint32)offset)
+ return -1;
+
+ stream >> numEntries;
+
+ for (;numEntries > 0; --numEntries) {
+ quint16 tag;
+ quint16 type;
+ quint32 components;
+ quint32 value;
+
+ stream >> tag >> type >> components >> value;
+
+ 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;
+}
/*!
\internal
*/
@@ -782,6 +875,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);
@@ -793,6 +887,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;
@@ -810,9 +906,18 @@ 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.size()) {
+ // Exif data present
+ int orientation = getExifOrientation(exifData);
+ if (orientation > 0)
+ exifOrientation = orientation;
+ }
+
state = ReadHeader;
return true;
}
@@ -826,6 +931,48 @@ bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device)
return true;
}
+void QJpegHandlerPrivate::applyExifOrientation(QImage *image)
+{
+ // This is not an optimized implementation, but easiest to maintain
+ QTransform transform;
+
+ switch (exifOrientation) {
+ case 1: // normal
+ break;
+ case 2: // mirror horizontal
+ *image = image->mirrored(true, false);
+ break;
+ case 3: // rotate 180
+ transform.rotate(180);
+ *image = image->transformed(transform);
+ break;
+ case 4: // mirror vertical
+ *image = image->mirrored(false, true);
+ break;
+ case 5: // mirror horizontal and rotate 270 CCW
+ *image = image->mirrored(true, false);
+ transform.rotate(270);
+ *image = image->transformed(transform);
+ break;
+ case 6: // rotate 90 CW
+ transform.rotate(90);
+ *image = image->transformed(transform);
+ break;
+ case 7: // mirror horizontal and rotate 90 CW
+ *image = image->mirrored(true, false);
+ transform.rotate(90);
+ *image = image->transformed(transform);
+ break;
+ case 8: // rotate 270 CW
+ transform.rotate(-90);
+ *image = image->transformed(transform);
+ break;
+ default:
+ qWarning("This should never happen");
+ }
+ exifOrientation = 1;
+}
+
bool QJpegHandlerPrivate::read(QImage *image)
{
if(state == Ready)
@@ -837,6 +984,7 @@ bool QJpegHandlerPrivate::read(QImage *image)
if (success) {
for (int i = 0; i < readTexts.size()-1; i+=2)
image->setText(readTexts.at(i), readTexts.at(i+1));
+ applyExifOrientation(image);
state = Ready;
return true;
@@ -856,12 +1004,13 @@ extern "C" void qt_convert_rgb888_to_rgb32_mips_dspr2_asm(quint32 *dst, const uc
QJpegHandler::QJpegHandler()
: d(new QJpegHandlerPrivate(this))
{
-#if defined(__ARM_NEON__)
+#if defined(__ARM_NEON__) && !defined(Q_PROCESSOR_ARM_64)
// from qimage_neon.cpp
if (qCpuHasFeature(NEON))
rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_neon;
-#endif // __ARM_NEON__
+#endif
+
#if defined(QT_COMPILER_SUPPORTS_SSSE3)
// from qimage_ssse3.cpp