diff --git a/lib/imageoperation.cpp b/lib/imageoperation.cpp index a6cf5b5..3f28e4b 100644 --- a/lib/imageoperation.cpp +++ b/lib/imageoperation.cpp @@ -29,9 +29,9 @@ #include #include -#include +#include #include -#include ) +#include /*! \class ImageOperation \brief The ImageOperation class is a helper class to manipulate images. @@ -71,7 +71,6 @@ QString ImageOperation::uniqueFilePath(const QString &sourceFilePath, const QStr QFileInfo fileInfo(sourceFilePath); - // Construct target temp file path first: QDir dir(path); QStringList prevFiles = dir.entryList(QStringList() << fileInfo.baseName() + QLatin1String("*"), QDir::Files); @@ -185,16 +184,35 @@ QString ImageOperation::scaleImage(const QString &sourceFile, qreal scaleFactor, // Using just basic QImage scale here. We can easily replace this implementation later, if we notice // performance bottlenecks here. - QImage tmpImg(sourceFile); - if (tmpImg.isNull()) { - qWarning() << Q_FUNC_INFO << "Null source image!"; + QImageReader ir(sourceFile); + if (!ir.canRead()) { + qWarning() << Q_FUNC_INFO << "Couldn't read source image!"; return QString(); } - QImage scaled = tmpImg.scaled(tmpImg.size() * scaleFactor, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QSize imageSize(ir.size()); + imageSize = imageSize.scaled(imageSize * scaleFactor, Qt::KeepAspectRatio); + ir.setScaledSize(imageSize); + QImage image = ir.read(); + + int angle; + bool mirrored; + imageOrientation(sourceFile, &angle, &mirrored); + + if (mirrored) { + image = image.mirrored(true, false); + } + + if (angle != 0) { + QTransform transform; + transform.rotate(angle); + image = image.transformed(transform); + } - if (!scaled.save(tmpFile)) { - qWarning() << Q_FUNC_INFO << "Failed to save scaled image to temp file!"; + if (!image.save(tmpFile)) { + qWarning() << Q_FUNC_INFO + << "Failed to save scaled image to temp file!" + << tmpFile; return QString(); } @@ -234,9 +252,9 @@ QString ImageOperation::scaleImageToSize(const QString &sourceFile, quint64 targ tmpFile = uniqueFilePath(sourceFile); } - QImage tmpImage(sourceFile); - if (tmpImage.isNull()) { - qWarning() << Q_FUNC_INFO << "NULL original image!"; + QImageReader ir(sourceFile); + if (!ir.canRead()) { + qWarning() << Q_FUNC_INFO << "Can't read the original image!"; return QString(); } @@ -258,17 +276,75 @@ QString ImageOperation::scaleImageToSize(const QString &sourceFile, quint64 targ // w * w = (s' * r) / a => // w = sqrt( (s' * r) / a ) - qint32 w = tmpImage.width(); // Width - qint32 h = tmpImage.height(); // Height + qint32 w = ir.size().width(); // Width + qint32 h = ir.size().height(); // Height qreal r = w / (h * 1.0); // Aspect ratio qreal a = originalSize / (w * h * 1.0); // The magic number, which combines depth and compression qint32 newWidth = qSqrt((targetSize * r) / a); - QImage scaled = tmpImage.scaledToWidth(newWidth, Qt::SmoothTransformation); + qint32 newHeight = newWidth / r; - if (!scaled.save(tmpFile)) { - qWarning() << Q_FUNC_INFO << "Failed to save scaled image to temp file!"; + QSize imageSize(ir.size()); + imageSize = imageSize.scaled(newWidth, newHeight, Qt::KeepAspectRatio); + ir.setScaledSize(imageSize); + QImage image = ir.read(); + + if (image.isNull()) { + qWarning() << Q_FUNC_INFO + << "NULL image"; return QString(); } + // Make sure orientation is right. + int angle; + bool mirrored; + imageOrientation(sourceFile, &angle, &mirrored); + + if (mirrored) { + image = image.mirrored(true, false); + } + + if (angle != 0) { + QTransform transform; + transform.rotate(angle); + image = image.transformed(transform); + } + + if (!image.save(tmpFile)) { + qWarning() << Q_FUNC_INFO + << "Failed to save scaled image to temp file!" + << tmpFile; + return QString(); + } + return tmpFile; } + +void ImageOperation::imageOrientation(const QString &sourceFile, int *angle, bool *mirror) +{ + if(!QuillMetadata::canRead(sourceFile)) { + qWarning() << Q_FUNC_INFO << "Can't read metadata"; + *angle = 0; + *mirror = false; + return; + } + QuillMetadata md(sourceFile); + if (!md.hasExif()) { + qWarning() << "Metadata invalid"; + *angle = 0; + *mirror = false; + return; + } + + int exifOrientation = md.entry(QuillMetadata::Tag_Orientation).toInt(); + switch (exifOrientation) { + case 1: *angle = 0 ; *mirror = false; break; + case 2: *angle = 0 ; *mirror = true ; break; + case 3: *angle = 180; *mirror = false; break; + case 4: *angle = 180; *mirror = true ; break; + case 5: *angle = 90 ; *mirror = true ; break; + case 6: *angle = 90 ; *mirror = false; break; + case 7: *angle = 270; *mirror = true ; break; + case 8: *angle = 270; *mirror = false; break; + default: break; + } +} diff --git a/lib/imageoperation.h b/lib/imageoperation.h index 5900643..b65680c 100644 --- a/lib/imageoperation.h +++ b/lib/imageoperation.h @@ -37,6 +37,7 @@ class ImageOperation static QString removeImageMetadata(const QString &sourceFile); static QString scaleImage(const QString &sourceFile, qreal scaleFactor, const QString &targetFile=QString()); static QString scaleImageToSize(const QString &sourceFile, quint64 targetSize, const QString &targetFile=QString()); + static void imageOrientation(const QString &sourceFile, int *angle, bool *mirror); }; #endif // IMAGEOPERATION_H diff --git a/tests/images/testimage-0-mirrored.jpg b/tests/images/testimage-0-mirrored.jpg new file mode 100644 index 0000000..7dcdf57 Binary files /dev/null and b/tests/images/testimage-0-mirrored.jpg differ diff --git a/tests/images/testimage-0.jpg b/tests/images/testimage-0.jpg new file mode 100644 index 0000000..27d5680 Binary files /dev/null and b/tests/images/testimage-0.jpg differ diff --git a/tests/images/testimage-180-mirrored.jpg b/tests/images/testimage-180-mirrored.jpg new file mode 100644 index 0000000..ed489cc Binary files /dev/null and b/tests/images/testimage-180-mirrored.jpg differ diff --git a/tests/images/testimage-180.jpg b/tests/images/testimage-180.jpg new file mode 100644 index 0000000..6384414 Binary files /dev/null and b/tests/images/testimage-180.jpg differ diff --git a/tests/images/testimage-270-mirrored.jpg b/tests/images/testimage-270-mirrored.jpg new file mode 100644 index 0000000..a952a3f Binary files /dev/null and b/tests/images/testimage-270-mirrored.jpg differ diff --git a/tests/images/testimage-270.jpg b/tests/images/testimage-270.jpg new file mode 100644 index 0000000..1a64902 Binary files /dev/null and b/tests/images/testimage-270.jpg differ diff --git a/tests/images/testimage-90-mirrored.jpg b/tests/images/testimage-90-mirrored.jpg new file mode 100644 index 0000000..ccaae47 Binary files /dev/null and b/tests/images/testimage-90-mirrored.jpg differ diff --git a/tests/images/testimage-90.jpg b/tests/images/testimage-90.jpg new file mode 100644 index 0000000..2c48d30 Binary files /dev/null and b/tests/images/testimage-90.jpg differ diff --git a/tests/images/testimage-90.jpg.jpg b/tests/images/testimage-90.jpg.jpg new file mode 100644 index 0000000..6c955f6 Binary files /dev/null and b/tests/images/testimage-90.jpg.jpg differ diff --git a/tests/tests.pro b/tests/tests.pro index 7bbdc91..d310690 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -49,7 +49,7 @@ tests_install.path = $$PATH tests_install.files = $$tests_xml.target tests_install.CONFIG += no_check_exist -resources.files = images/testimage.jpg +resources.files = images/*.jpg resources.path = $$PATH/images target.path = $$PATH diff --git a/tests/ut_imageoperation.cpp b/tests/ut_imageoperation.cpp index 2bbbe57..91b8875 100644 --- a/tests/ut_imageoperation.cpp +++ b/tests/ut_imageoperation.cpp @@ -114,17 +114,29 @@ void ut_imageoperation::testScale() QString result = ImageOperation::scaleImage(filePath, 0.5, target); QCOMPARE(result, target); + int angle; + bool mirror; + ImageOperation::imageOrientation(filePath, &angle, &mirror); + QCOMPARE(angle, 90); + QCOMPARE(mirror, false); + QImage resImage(result); + QTransform x; + x.rotate(angle); + resImage = resImage.transformed(x); + QCOMPARE(resImage.size(), img.size()*0.5); QFile::remove(result); result = ImageOperation::scaleImage(filePath, 0.1, target); resImage.load(result); + resImage = resImage.transformed(x); QCOMPARE(resImage.size(), img.size()*0.1); QFile::remove(result); result = ImageOperation::scaleImage(filePath, 0.9, target); resImage.load(result); + resImage = resImage.transformed(x); QVERIFY(qAbs(resImage.width() - img.width()*0.9) < 2); QVERIFY(qAbs(resImage.height() - img.height()*0.9) < 2); QFile::remove(result); @@ -139,7 +151,20 @@ void ut_imageoperation::testScaleToSize() QString filePath("images/testimage.jpg"); QString target = ImageOperation::uniqueFilePath(filePath); + QVERIFY(!target.isEmpty()); + QFileInfo f("images/testimage.jpg"); + + int angle; + bool mirror; + ImageOperation::imageOrientation(filePath, &angle, &mirror); + QCOMPARE(angle, 90); + QCOMPARE(mirror, false); + + QTransform x; + x.rotate(angle); + img = img.transformed(x); + int targetSize = f.size() * 0.5; // Invalid sourceFile -> fail @@ -216,6 +241,43 @@ void ut_imageoperation::testDropMetadata() QFile::remove(path); } +void ut_imageoperation::testOrientation() +{ + int angle; + bool mirrored; + ImageOperation::imageOrientation("images/testimage-0.jpg", &angle, &mirrored); + QCOMPARE(angle, 0); + QCOMPARE(mirrored, false); + + ImageOperation::imageOrientation("images/testimage-0-mirrored.jpg", &angle, &mirrored); + QCOMPARE(angle, 0); + QCOMPARE(mirrored, true); + + ImageOperation::imageOrientation("images/testimage-90.jpg", &angle, &mirrored); + QCOMPARE(angle, 90); + QCOMPARE(mirrored, false); + + ImageOperation::imageOrientation("images/testimage-90-mirrored.jpg", &angle, &mirrored); + QCOMPARE(angle, 90); + QCOMPARE(mirrored, true); + + ImageOperation::imageOrientation("images/testimage-180.jpg", &angle, &mirrored); + QCOMPARE(angle, 180); + QCOMPARE(mirrored, false); + + ImageOperation::imageOrientation("images/testimage-180-mirrored.jpg", &angle, &mirrored); + QCOMPARE(angle, 180); + QCOMPARE(mirrored, true); + + ImageOperation::imageOrientation("images/testimage-270.jpg", &angle, &mirrored); + QCOMPARE(angle, 270); + QCOMPARE(mirrored, false); + + ImageOperation::imageOrientation("images/testimage-270-mirrored.jpg", &angle, &mirrored); + QCOMPARE(angle, 270); + QCOMPARE(mirrored, true); +} + /* QTEST_MAIN(ut_imageoperation) diff --git a/tests/ut_imageoperation.h b/tests/ut_imageoperation.h index 01de8a1..09f7a08 100644 --- a/tests/ut_imageoperation.h +++ b/tests/ut_imageoperation.h @@ -41,6 +41,7 @@ private slots: void testScaleToSize(); void testDropMetadata(); void testUniqueFilePath(); + void testOrientation(); }; #endif // UT_IMAGEOPERATION_H