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 <allan.jensen@qt.io>
This commit is contained in:
parent
1e0bc86dc0
commit
19e3ec4e2f
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user