From 218c48b5c3eeebafef0e4475493e11e466b9fcd9 Mon Sep 17 00:00:00 2001
From: Allan Sandfeld Jensen <allan.jensen@digia.com>
Date: Mon, 3 Feb 2014 12:30:38 +0100
Subject: [PATCH] Save opaque images as opaque TIFF files

We currently save all non-indexed images as RGBA TIFF files. We should
save them without an alpha channel, to save space and maintain the
information that they are opaque.

Task-number: QTBUG-18475
Change-Id: Id656f4078ea0a1b88235fb04add99a4680422354
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Ivan Komissarov <ABBAPOH@gmail.com>
Reviewed-by: Gunnar Sletta <gunnar.sletta@jollamobile.com>
---
 .../imageformats/tiff/qtiffhandler.cpp        | 45 ++++++++++++++++---
 tests/auto/tiff/tst_qtiff.cpp                 |  3 +-
 2 files changed, 40 insertions(+), 8 deletions(-)

diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp
index e2177857..445f0c4b 100644
--- a/src/plugins/imageformats/tiff/qtiffhandler.cpp
+++ b/src/plugins/imageformats/tiff/qtiffhandler.cpp
@@ -267,8 +267,12 @@ bool QTiffHandler::read(QImage *image)
                 // free redTable, greenTable and greenTable done by libtiff
             }
         } else {
-            if (image->size() != QSize(width, height) || image->format() != QImage::Format_ARGB32)
-                *image = QImage(width, height, QImage::Format_ARGB32);
+            QImage::Format format = QImage::Format_ARGB32;
+            if (samplesPerPixel < 4 && image->format() != QImage::Format_ARGB32)
+                format = QImage::Format_RGB32;
+
+            if (image->size() != QSize(width, height) || image->format() != format)
+                *image = QImage(width, height, format);
             if (!image->isNull()) {
                 const int stopOnError = 1;
                 if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32 *>(image->bits()), ORIENTATION_TOPLEFT, stopOnError)) {
@@ -315,7 +319,7 @@ bool QTiffHandler::read(QImage *image)
     // rotate the image if the orientation is defined in the file
     uint16 orientationTag;
     if (TIFFGetField(tiff, TIFFTAG_ORIENTATION, &orientationTag)) {
-        if (image->format() == QImage::Format_ARGB32) {
+        if (image->format() == QImage::Format_ARGB32 || image->format() == QImage::Format_RGB32) {
             // TIFFReadRGBAImageOriented() flip the image but does not rotate them
             switch (orientationTag) {
             case 5:
@@ -552,6 +556,33 @@ bool QTiffHandler::write(const QImage &image)
         }
         TIFFClose(tiff);
 
+    } else if (!image.hasAlphaChannel()) {
+        if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
+            || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
+            || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 3)
+            || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) {
+            TIFFClose(tiff);
+            return false;
+        }
+        // try to do the RGB888 conversion in chunks no greater than 16 MB
+        const int chunks = (width * height * 3 / (1024 * 1024 * 16)) + 1;
+        const int chunkHeight = qMax(height / chunks, 1);
+
+        int y = 0;
+        while (y < height) {
+            const QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_RGB888);
+
+            int chunkStart = y;
+            int chunkEnd = y + chunk.height();
+            while (y < chunkEnd) {
+                if (TIFFWriteScanline(tiff, (void*)chunk.scanLine(y - chunkStart), y) != 1) {
+                    TIFFClose(tiff);
+                    return false;
+                }
+                ++y;
+            }
+        }
+        TIFFClose(tiff);
     } else {
         if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
             || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
@@ -561,17 +592,17 @@ bool QTiffHandler::write(const QImage &image)
             return false;
         }
         // try to do the RGBA8888 conversion in chunks no greater than 16 MB
-        int chunks = (width * height * 4 / (1024 * 1024 * 16)) + 1;
-        int chunkHeight = qMax(height / chunks, 1);
+        const int chunks = (width * height * 4 / (1024 * 1024 * 16)) + 1;
+        const int chunkHeight = qMax(height / chunks, 1);
 
         int y = 0;
         while (y < height) {
-            QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_RGBA8888);
+            const QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_RGBA8888);
 
             int chunkStart = y;
             int chunkEnd = y + chunk.height();
             while (y < chunkEnd) {
-                if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
+                if (TIFFWriteScanline(tiff, (void*)chunk.scanLine(y - chunkStart), y) != 1) {
                     TIFFClose(tiff);
                     return false;
                 }
diff --git a/tests/auto/tiff/tst_qtiff.cpp b/tests/auto/tiff/tst_qtiff.cpp
index 6548d23a..b838a5d8 100644
--- a/tests/auto/tiff/tst_qtiff.cpp
+++ b/tests/auto/tiff/tst_qtiff.cpp
@@ -374,7 +374,8 @@ void tst_qtiff::readWriteNonDestructive_data()
     QTest::addColumn<bool>("grayscale");
     QTest::newRow("tiff mono") << QImage::Format_Mono << QImage::Format_Mono << false;
     QTest::newRow("tiff indexed") << QImage::Format_Indexed8 << QImage::Format_Indexed8 << false;
-    QTest::newRow("tiff rgb32") << QImage::Format_ARGB32 << QImage::Format_ARGB32 << false;
+    QTest::newRow("tiff argb32") << QImage::Format_ARGB32 << QImage::Format_ARGB32 << false;
+    QTest::newRow("tiff rgb32") << QImage::Format_RGB32 << QImage::Format_RGB32 << false;
     QTest::newRow("tiff grayscale") << QImage::Format_Indexed8 << QImage::Format_Indexed8 << true;
 }
 
-- 
GitLab