summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGiuseppe D'Angelo <[email protected]>2024-05-24 11:06:59 +0200
committerGiuseppe D'Angelo <[email protected]>2024-05-28 10:06:08 +0200
commit19e3ec4e2fe1c6d27570ee68480846e9dbf87db7 (patch)
treec17e37fc0a6b98ff31232cb3dd6701983b9f5ef1
parent1e0bc86dc01a00b115aeb1c0647d61ac8cef3c60 (diff)
QImage: allow for scaling CMYK images
The codepaths for image scaling are a bit convoluted, so some "surgery" is needed. QImage::scaled() delegates to transformed(), building a suitable scaling matrix. transformed() checks if the matrix is a scaling matrix, and then has several dispatches. If smooth scaling was requested: * if the image format is supported by smoothScaled() without needing a conversion, delegate to that; * otherwise, if the transform is "non paintable" or the source image is big enough, then again call smoothScale. "non paintable" here means that we're scaling more than 2x down, and QPainter wouldn't do a good job. Otherwise, images in color formats (>= RGB32) are converted by applying the needed transformation on a QPainter and draw the source image with that transformation. Otherwise, if the matrix is invertible (a scaling matrix with non-zero scaling always is, it's a diagonal matrix), then dispatch to qt_xForm_helper. -- Amend this reasoning to support CMYK images: * Make smoothScaled support CMYK without conversions. To do so, make qSmoothScaleImage scale CMYK as if it was a ARGB image. * Make transformed() call smoothScaled() for CMYK images * In transformed(), consider CMYK as nonpaintable, because we can't paint over a CMYK image. * In the non-smooth codepath, also check that we don't try to paint over CMYK, and always go through qt_xForm_helper instead. Note that we still don't support any other transformation for CMYK. Add a test, adapting the exiting one for RGB. Change-Id: Ic72d78923a17fb3963aa22c57265904c716792b0 Reviewed-by: Allan Sandfeld Jensen <[email protected]>
-rw-r--r--src/gui/image/qimage.cpp8
-rw-r--r--src/gui/painting/qimagescale.cpp2
-rw-r--r--tests/auto/gui/image/qimage/tst_qimage.cpp73
3 files changed, 81 insertions, 2 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index a24d7ccde75..8998db301d2 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -4693,6 +4693,8 @@ QImage QImage::smoothScaled(int w, int h) const
src.convertTo(QImage::Format_RGBA32FPx4_Premultiplied);
break;
#endif
+ case QImage::Format_CMYK8888:
+ break;
default:
if (src.hasAlphaChannel())
src.convertTo(QImage::Format_ARGB32_Premultiplied);
@@ -4835,6 +4837,9 @@ QImage Q_TRACE_INSTRUMENT(qtgui) QImage::transformed(const QTransform &matrix, Q
// with scaling smoothly more than 2x down.
if (hd * 2 < hs || wd * 2 < ws)
nonpaintable_scale_xform = true;
+ // We cannot paint on a CMYK image, so don't try to do so
+ if (format() == QImage::Format_CMYK8888)
+ nonpaintable_scale_xform = true;
} else {
if (mat.type() <= QTransform::TxRotate && mat.m11() == 0 && mat.m22() == 0) {
if (mat.m12() == 1. && mat.m21() == -1.)
@@ -4866,6 +4871,7 @@ QImage Q_TRACE_INSTRUMENT(qtgui) QImage::transformed(const QTransform &matrix, Q
case QImage::Format_RGBX64:
case QImage::Format_RGBA64_Premultiplied:
#endif
+ case QImage::Format_CMYK8888:
// Use smoothScaled for scaling when we can do so without conversion.
if (mat.m11() > 0.0F && mat.m22() > 0.0F)
return smoothScaled(wd, hd);
@@ -4936,7 +4942,7 @@ QImage Q_TRACE_INSTRUMENT(qtgui) QImage::transformed(const QTransform &matrix, Q
} else
memset(dImage.bits(), 0x00, dImage.d->nbytes);
- if (target_format >= QImage::Format_RGB32) {
+ if (target_format >= QImage::Format_RGB32 && target_format != QImage::Format_CMYK8888) {
// Prevent QPainter from applying devicePixelRatio corrections
QImage sImage = (devicePixelRatio() != 1) ? QImage(constBits(), width(), height(), format()) : *this;
if (sImage.d != d
diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp
index a636635fd5d..eb5f12389fd 100644
--- a/src/gui/painting/qimagescale.cpp
+++ b/src/gui/painting/qimagescale.cpp
@@ -1208,7 +1208,7 @@ QImage qSmoothScaleImage(const QImage &src, int dw, int dh)
dw, dh, dw, src.bytesPerLine() / 8);
else
#endif
- if (src.hasAlphaChannel())
+ if (src.hasAlphaChannel() || src.format() == QImage::Format_CMYK8888)
qt_qimageScaleAARGBA(scaleinfo, (unsigned int *)buffer.scanLine(0),
dw, dh, dw, src.bytesPerLine() / 4);
else
diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp
index 3e3d0a49bc0..9d3311c13c9 100644
--- a/tests/auto/gui/image/qimage/tst_qimage.cpp
+++ b/tests/auto/gui/image/qimage/tst_qimage.cpp
@@ -15,6 +15,7 @@
#include <stdio.h>
#include <qpainter.h>
+#include <private/qcmyk_p.h>
#include <private/qimage_p.h>
#include <private/qdrawhelper_p.h>
@@ -110,6 +111,8 @@ private slots:
void smoothScaleFormats();
void smoothScaleNoConversion_data();
void smoothScaleNoConversion();
+ void smoothScale_CMYK_data();
+ void smoothScale_CMYK();
void transformed_data();
void transformed();
@@ -2091,6 +2094,76 @@ void tst_QImage::smoothScaleNoConversion()
QVERIFY(img.hasAlphaChannel());
}
+void tst_QImage::smoothScale_CMYK_data()
+{
+ QTest::addColumn<int>("size");
+
+ const int sizes[] = { 2, 3, 4, 6, 7, 8, 10, 16, 20, 32, 40, 64, 100, 101, 128 };
+ for (int size : sizes)
+ QTest::addRow("%d x %d", size, size) << size;
+}
+
+void tst_QImage::smoothScale_CMYK()
+{
+ QFETCH(int, size);
+ QImage img(size, size, QImage::Format_CMYK8888);
+ QCmyk32 expected(31, 63, 127, 127);
+ img.fill(expected.toUint());
+
+ auto getCmykPixel = [](const QImage &image, int x, int y) {
+ Q_ASSERT(image.format() == QImage::Format_CMYK8888);
+ const uint *line = reinterpret_cast<const uint *>(image.scanLine(y));
+ const uint pixel = line[x];
+ return QCmyk32::fromCmyk32(pixel);
+ };
+
+ // scale x down, y down
+ QImage scaled = img.scaled(QSize(1, 1), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ QCmyk32 pixel = getCmykPixel(scaled, 0, 0);
+ QCOMPARE(pixel, expected);
+
+ // scale x down, y up
+ scaled = img.scaled(QSize(1, size * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ for (int y = 0; y < scaled.height(); ++y) {
+ pixel = getCmykPixel(scaled, 0, y);
+ QCOMPARE(pixel, expected);
+ }
+
+ // scale x up, y down
+ scaled = img.scaled(QSize(size * 2, 1), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ for (int x = 0; x < scaled.width(); ++x) {
+ pixel = getCmykPixel(scaled, x, 0);
+ QCOMPARE(pixel, expected);
+ }
+
+ // scale x up
+ scaled = img.scaled(QSize(size, size * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ for (int y = 0; y < scaled.height(); ++y) {
+ for (int x = 0; x < scaled.width(); ++x) {
+ pixel = getCmykPixel(scaled, x, y);
+ QCOMPARE(pixel, expected);
+ }
+ }
+
+ // scale y up
+ scaled = img.scaled(QSize(size * 2, size), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ for (int y = 0; y < scaled.height(); ++y) {
+ for (int x = 0; x < scaled.width(); ++x) {
+ pixel = getCmykPixel(scaled, x, y);
+ QCOMPARE(pixel, expected);
+ }
+ }
+
+ // scale x up, y up
+ scaled = img.scaled(QSize(size * 2, size * 2), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ for (int y = 0; y < scaled.height(); ++y) {
+ for (int x = 0; x < scaled.width(); ++x) {
+ pixel = getCmykPixel(scaled, x, y);
+ QCOMPARE(pixel, expected);
+ }
+ }
+}
+
static int count(const QImage &img, int x, int y, int dx, int dy, QRgb pixel)
{
int i = 0;