diff options
author | Giuseppe D'Angelo <[email protected]> | 2024-05-24 11:06:59 +0200 |
---|---|---|
committer | Giuseppe D'Angelo <[email protected]> | 2024-05-28 10:06:08 +0200 |
commit | 19e3ec4e2fe1c6d27570ee68480846e9dbf87db7 (patch) | |
tree | c17e37fc0a6b98ff31232cb3dd6701983b9f5ef1 | |
parent | 1e0bc86dc01a00b115aeb1c0647d61ac8cef3c60 (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.cpp | 8 | ||||
-rw-r--r-- | src/gui/painting/qimagescale.cpp | 2 | ||||
-rw-r--r-- | tests/auto/gui/image/qimage/tst_qimage.cpp | 73 |
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; |