diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2023-10-24 02:27:52 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2024-04-03 22:15:42 +0100 |
commit | 3fb3d95c335fecf005c938b2a7011286f592325a (patch) | |
tree | 923a10b3d3afb911520e981a74be006f699d279d /tests/auto/gui/image/qimagereader/tst_qimagereader.cpp | |
parent | e68b57e2e0983d66cdf1d48e9216d32fa1e63f68 (diff) |
Add support for CMYK file I/O in JPEG
JPEG part 6 defines CMYK support. This commit adds such support to the
JPEG plugin.
A *very* interesting discovery is the fact that Photoshop inverts the
meaning of the CMYK color channels when saving into JPEG: 0 means "full
ink", and 255 means "no ink". Most other image viewers/editors follow
the same interpretation, I imagine for compatibility.
But others, like Adobe Reader, don't (???) -- a PDF expects a DCT
encoding with 0 meaning "no ink". I am adding a SubType to the image I/O
handler to let the user choose what they want, defaulting to Photoshop
behavior.
Also, turns out that Qt was already loading CMYK files and converting
them to RGB. I don't think we should do automatic, lossy conversions (we
were not taking into account an eventual colorspace...), so I'm changing
that loading to yield a CMYK QImage.
Finally: save the colorspace, even if it's a CMYK image.
QColorSpace doesn't support anything but RGB matrix-based colorspaces.
Yet, it can load an arbitrary ICC profile, and will store it even if
it's unable to use it. We can use this fact to preserve the colorspace
embedded in CMYK images, or let users set an arbitrary ICC profile on
them through Qt APIs, and then saving the result in JPEG.
[ChangeLog][QtGui][JPEG] Added support for loading and saving of JPEG
files in 8-bit CMYK format. When loading a CMYK JPEG file, Qt used to
convert it automatically to a RGB image; now instead it's kept as-is.
This work has been kindly sponsored by the QGIS project
(https://qgis.org/).
Change-Id: Ibdbfa16aa35814f5dba28c2df89577175162b731
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'tests/auto/gui/image/qimagereader/tst_qimagereader.cpp')
-rw-r--r-- | tests/auto/gui/image/qimagereader/tst_qimagereader.cpp | 67 |
1 files changed, 47 insertions, 20 deletions
diff --git a/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp b/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp index 6d875ec0ab..4ea874cec0 100644 --- a/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp +++ b/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp @@ -303,25 +303,52 @@ void tst_QImageReader::jpegRgbCmyk() QImage image1(prefix + QLatin1String("YCbCr_cmyk.jpg")); QImage image2(prefix + QLatin1String("YCbCr_cmyk.png")); - if (image1 != image2) { - // first, do some obvious tests - QCOMPARE(image1.height(), image2.height()); - QCOMPARE(image1.width(), image2.width()); - QCOMPARE(image1.format(), image2.format()); - QCOMPARE(image1.format(), QImage::Format_RGB32); - - // compare all the pixels with a slack of 3. This ignores rounding errors - // in libjpeg/libpng, where some versions sacrifice accuracy for speed. - for (int h = 0; h < image1.height(); ++h) { - const uchar *s1 = image1.constScanLine(h); - const uchar *s2 = image2.constScanLine(h); - for (int w = 0; w < image1.width() * 4; ++w) { - if (*s1 != *s2) { - QVERIFY2(qAbs(*s1 - *s2) <= 3, qPrintable(QString("images differ in line %1, col %2 (image1: %3, image2: %4)").arg(h).arg(w).arg(*s1, 0, 16).arg(*s2, 0, 16))); - } - s1++; - s2++; - } + QVERIFY(!image1.isNull()); + QVERIFY(!image2.isNull()); + + QCOMPARE(image1.height(), image2.height()); + QCOMPARE(image1.width(), image2.width()); + + QCOMPARE(image1.format(), QImage::Format_CMYK32); + QCOMPARE(image2.format(), QImage::Format_RGB32); + + // compare all the pixels with a slack of 3. This ignores rounding errors + // in libjpeg/libpng, where some versions sacrifice accuracy for speed. + const auto fuzzyCompareColors = [](const QColor &c1, const QColor &c2) { + int c1rgba[4]; + int c2rgba[4]; + + c1.getRgb(c1rgba + 0, + c1rgba + 1, + c1rgba + 2, + c1rgba + 3); + + c2.getRgb(c2rgba + 0, + c2rgba + 1, + c2rgba + 2, + c2rgba + 3); + + const auto fuzzyCompare = [](int a, int b) { + return qAbs(a - b) <= 3; + }; + + return fuzzyCompare(c1rgba[0], c2rgba[0]) && + fuzzyCompare(c1rgba[1], c2rgba[1]) && + fuzzyCompare(c1rgba[2], c2rgba[2]) && + fuzzyCompare(c1rgba[3], c2rgba[3]); + }; + + for (int h = 0; h < image1.height(); ++h) { + const uchar *sl1 = image1.constScanLine(h); + const uchar *sl2 = image2.constScanLine(h); + for (int w = 0; w < image1.width(); ++w) { + const uchar *s1 = sl1 + w * 4; + const uchar *s2 = sl2 + w * 4; + + QColor c1 = QColor::fromCmyk(s1[0], s1[1], s1[2], s1[3]); + QColor c2 = QColor::fromRgb(s2[2], s2[1], s2[0]); + QVERIFY2(fuzzyCompareColors(c1, c2), + qPrintable(QString("images differ in line %1, col %2").arg(h).arg(w))); } } } @@ -589,7 +616,7 @@ void tst_QImageReader::imageFormat_data() QTest::newRow("ppm-4") << QString("test.ppm") << QByteArray("ppm") << QImage::Format_RGB32; QTest::newRow("jpeg-1") << QString("beavis.jpg") << QByteArray("jpeg") << QImage::Format_Grayscale8; - QTest::newRow("jpeg-2") << QString("YCbCr_cmyk.jpg") << QByteArray("jpeg") << QImage::Format_RGB32; + QTest::newRow("jpeg-2") << QString("YCbCr_cmyk.jpg") << QByteArray("jpeg") << QImage::Format_CMYK32; QTest::newRow("jpeg-3") << QString("YCbCr_rgb.jpg") << QByteArray("jpeg") << QImage::Format_RGB32; QTest::newRow("gif-1") << QString("earth.gif") << QByteArray("gif") << QImage::Format_Invalid; |