From 9f69817344eb75fbc4613892c1f54ee69cae2e66 Mon Sep 17 00:00:00 2001
From: Allan Sandfeld Jensen <allan.jensen@qt.io>
Date: Tue, 11 Dec 2018 10:48:15 +0100
Subject: [PATCH] Add Grayscale16 support to TIFF

Change-Id: I927d9ab0af78baf90d8fd8d44088218dff0e7082
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
---
 .../imageformats/tiff/qtiffhandler.cpp        |  10 +++++++--
 tests/auto/tiff/tst_qtiff.cpp                 |  20 +++++++++++++++++-
 tests/shared/images/tiff.qrc                  |   1 +
 tests/shared/images/tiff/gray16.tiff          | Bin 0 -> 8164 bytes
 4 files changed, 28 insertions(+), 3 deletions(-)
 create mode 100644 tests/shared/images/tiff/gray16.tiff

diff --git a/src/plugins/imageformats/tiff/qtiffhandler.cpp b/src/plugins/imageformats/tiff/qtiffhandler.cpp
index edb54438..837f9ff5 100644
--- a/src/plugins/imageformats/tiff/qtiffhandler.cpp
+++ b/src/plugins/imageformats/tiff/qtiffhandler.cpp
@@ -272,6 +272,8 @@ bool QTiffHandlerPrivate::readHeaders(QIODevice *device)
         format = QImage::Format_Mono;
     else if (photometric == PHOTOMETRIC_MINISBLACK && bitPerSample == 8 && samplesPerPixel == 1)
         format = QImage::Format_Grayscale8;
+    else if (photometric == PHOTOMETRIC_MINISBLACK && bitPerSample == 16 && samplesPerPixel == 1)
+        format = QImage::Format_Grayscale16;
     else if ((grayscale || photometric == PHOTOMETRIC_PALETTE) && bitPerSample == 8 && samplesPerPixel == 1)
         format = QImage::Format_Indexed8;
     else if (samplesPerPixel < 4)
@@ -402,9 +404,11 @@ bool QTiffHandler::read(QImage *image)
         }
     }
     bool format8bit = (format == QImage::Format_Mono || format == QImage::Format_Indexed8 || format == QImage::Format_Grayscale8);
+    bool format16bit = (format == QImage::Format_Grayscale16);
     bool format64bit = (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64 || format == QImage::Format_RGBA64_Premultiplied);
 
-    if (format8bit || format64bit) {
+    // Formats we read directly, instead of over RGBA32:
+    if (format8bit || format16bit || format64bit) {
         int bytesPerPixel = image->depth() / 8;
         if (format == QImage::Format_RGBX64)
             bytesPerPixel = 6;
@@ -513,6 +517,7 @@ static QVector<QRgb> effectiveColorTable(const QImage &image)
             colors[i] = qRgba(0, 0, 0, i);
         break;
     case QImage::Format_Grayscale8:
+    case QImage::Format_Grayscale16:
         colors.resize(256);
         for (int i = 0; i < 256; ++i)
             colors[i] = qRgb(i, i, i);
@@ -622,6 +627,7 @@ bool QTiffHandler::write(const QImage &image)
         TIFFClose(tiff);
     } else if (format == QImage::Format_Indexed8
                || format == QImage::Format_Grayscale8
+               || format == QImage::Format_Grayscale16
                || format == QImage::Format_Alpha8) {
         QVector<QRgb> colorTable = effectiveColorTable(image);
         bool isGrayscale = checkGrayscale(colorTable);
@@ -631,7 +637,7 @@ bool QTiffHandler::write(const QImage &image)
                 photometric = PHOTOMETRIC_MINISWHITE;
             if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric)
                     || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
-                    || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)
+                    || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, image.depth())
                     || !TIFFSetField(tiff, TIFFTAG_ROWSPERSTRIP, defaultStripSize(tiff))) {
                 TIFFClose(tiff);
                 return false;
diff --git a/tests/auto/tiff/tst_qtiff.cpp b/tests/auto/tiff/tst_qtiff.cpp
index 9c815d57..b2c55468 100644
--- a/tests/auto/tiff/tst_qtiff.cpp
+++ b/tests/auto/tiff/tst_qtiff.cpp
@@ -85,6 +85,7 @@ private slots:
     void tiled();
 
     void readRgba64();
+    void readGray16();
 
 private:
     QString prefix;
@@ -168,6 +169,7 @@ void tst_qtiff::readImage_data()
     QTest::newRow("tiled_oddsize_grayscale") << QString("tiled_oddsize_grayscale.tiff") << QSize(59, 71);
     QTest::newRow("tiled_oddsize_mono") << QString("tiled_oddsize_mono.tiff") << QSize(59, 71);
     QTest::newRow("16bpc") << QString("16bpc.tiff") << QSize(64, 46);
+    QTest::newRow("gray16") << QString("gray16.tiff") << QSize(64, 46);
 }
 
 void tst_qtiff::readImage()
@@ -178,9 +180,13 @@ void tst_qtiff::readImage()
     QString path = prefix + fileName;
     QImageReader reader(path);
     QVERIFY(reader.canRead());
+    QImage::Format headerFormat = reader.imageFormat();
+    QSize headerSize = reader.size();
     QImage image = reader.read();
     QVERIFY(!image.isNull());
     QCOMPARE(image.size(), size);
+    QCOMPARE(image.size(), headerSize);
+    QCOMPARE(image.format(), headerFormat);
 }
 
 void tst_qtiff::readCorruptImage_data()
@@ -386,7 +392,8 @@ void tst_qtiff::readWriteNonDestructive_data()
     QTest::newRow("tiff indexed") << QImage::Format_Indexed8 << QImage::Format_Indexed8 << QImageIOHandler::TransformationMirror;
     QTest::newRow("tiff argb32pm") << QImage::Format_ARGB32_Premultiplied << QImage::Format_ARGB32_Premultiplied << QImageIOHandler::TransformationRotate90;
     QTest::newRow("tiff rgb32") << QImage::Format_RGB32 << QImage::Format_RGB32 << QImageIOHandler::TransformationRotate270;
-    QTest::newRow("tiff grayscale") << QImage::Format_Grayscale8 << QImage::Format_Grayscale8 << QImageIOHandler::TransformationFlip;
+    QTest::newRow("tiff grayscale8") << QImage::Format_Grayscale8 << QImage::Format_Grayscale8 << QImageIOHandler::TransformationFlip;
+    QTest::newRow("tiff grayscale16") << QImage::Format_Grayscale16 << QImage::Format_Grayscale16 << QImageIOHandler::TransformationMirror;
     QTest::newRow("tiff rgb64") << QImage::Format_RGBX64 << QImage::Format_RGBX64 << QImageIOHandler::TransformationNone;
     QTest::newRow("tiff rgba64") << QImage::Format_RGBA64 << QImage::Format_RGBA64 << QImageIOHandler::TransformationRotate90;
     QTest::newRow("tiff rgba64pm") << QImage::Format_RGBA64_Premultiplied << QImage::Format_RGBA64_Premultiplied << QImageIOHandler::TransformationNone;
@@ -609,5 +616,16 @@ void tst_qtiff::readRgba64()
     QCOMPARE(image.format(), QImage::Format_RGBX64);
 }
 
+void tst_qtiff::readGray16()
+{
+    QString path = prefix + QString("gray16.tiff");
+    QImageReader reader(path);
+    QVERIFY(reader.canRead());
+    QCOMPARE(reader.imageFormat(), QImage::Format_Grayscale16);
+    QImage image = reader.read();
+    QVERIFY(!image.isNull());
+    QCOMPARE(image.format(), QImage::Format_Grayscale16);
+}
+
 QTEST_MAIN(tst_qtiff)
 #include "tst_qtiff.moc"
diff --git a/tests/shared/images/tiff.qrc b/tests/shared/images/tiff.qrc
index 91bbf93b..e1ce9daf 100644
--- a/tests/shared/images/tiff.qrc
+++ b/tests/shared/images/tiff.qrc
@@ -51,5 +51,6 @@
         <file>tiff/tiled_oddsize_mono.tiff</file>
         <file>tiff/oddsize_mono.tiff</file>
         <file>tiff/tiled_rgb.tiff</file>
+        <file>tiff/gray16.tiff</file>
     </qresource>
 </RCC>
diff --git a/tests/shared/images/tiff/gray16.tiff b/tests/shared/images/tiff/gray16.tiff
new file mode 100644
index 0000000000000000000000000000000000000000..aeeb0458d54972dd1a686052efddf68d8328866b
GIT binary patch
literal 8164
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

literal 0
HcmV?d00001

-- 
GitLab