Preserve formats in QImage::scaled()

Do not always use the smoothScaled routine, the normal routines are
also optimized, and do not convert to alpha formats when not necessary.

Task-number: QTBUG-49719
Change-Id: I6ee9b620cc259472c419e7363357f41ce29b594a
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
bb10
Allan Sandfeld Jensen 2018-10-11 16:37:15 +02:00
parent 45896797c6
commit b0b4fcd05c
2 changed files with 78 additions and 12 deletions

View File

@ -4419,8 +4419,15 @@ int QImage::bitPlaneCount() const
}
/*!
\internal
Returns a smoothly scaled copy of the image. The returned image has a size
of width \a w by height \a h pixels.
The function operates internally on \c Format_RGB32, \c Format_ARGB32_Premultiplied,
\c Format_RGBX8888, \c Format_RGBA8888_Premultiplied, \c Format_RGBX64,
or \c Format_RGBA64_Premultiplied and will convert to those formats
if necessary. To avoid unnecessary conversion the result is returned in the format
internally used, and not in the original format.
*/
QImage QImage::smoothScaled(int w, int h) const {
QImage src = *this;
@ -4547,8 +4554,8 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode
Q_TRACE_SCOPE(QImage_transformed, matrix, mode);
// source image data
int ws = width();
int hs = height();
const int ws = width();
const int hs = height();
// target image data
int wd;
@ -4558,6 +4565,7 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode
QTransform mat = trueMatrix(matrix, ws, hs);
bool complex_xform = false;
bool scale_xform = false;
bool nonpaintable_scale_xform = false;
if (mat.type() <= QTransform::TxScale) {
if (mat.type() == QTransform::TxNone) // identity matrix
return *this;
@ -4572,6 +4580,10 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode
wd = int(qAbs(mat.m11()) * ws + 0.9999);
}
scale_xform = true;
// The paint-based scaling is only bilinear, and has problems
// with scaling smoothly more than 2x down.
if (hd * 2 < hs || wd * 2 < ws)
nonpaintable_scale_xform = true;
} else {
if (mat.type() <= QTransform::TxRotate && mat.m11() == 0 && mat.m22() == 0) {
if (mat.m12() == 1. && mat.m21() == -1.)
@ -4591,16 +4603,40 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode
if (wd == 0 || hd == 0)
return QImage();
// Make use of the optimized algorithm when we're scaling
if (scale_xform && mode == Qt::SmoothTransformation) {
if (mat.m11() < 0.0F && mat.m22() < 0.0F) { // horizontal/vertical flip
return smoothScaled(wd, hd).mirrored(true, true);
} else if (mat.m11() < 0.0F) { // horizontal flip
return smoothScaled(wd, hd).mirrored(true, false);
} else if (mat.m22() < 0.0F) { // vertical flip
return smoothScaled(wd, hd).mirrored(false, true);
} else { // no flipping
return smoothScaled(wd, hd);
switch (format()) {
case QImage::Format_RGB32:
case QImage::Format_ARGB32_Premultiplied:
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
case QImage::Format_RGBX8888:
#endif
case QImage::Format_RGBA8888_Premultiplied:
#if QT_CONFIG(raster_64bit)
case QImage::Format_RGBX64:
case QImage::Format_RGBA64_Premultiplied:
#endif
// Use smoothScaled for scaling when we can do so without conversion.
if (mat.m11() > 0.0F && mat.m22() > 0.0F)
return smoothScaled(wd, hd);
break;
default:
break;
}
// Otherwise only use it when the scaling factor demands it, or the image is large enough to scale multi-threaded
if (nonpaintable_scale_xform
#if QT_CONFIG(thread) && !defined(Q_OS_WASM)
|| (ws * hs) >= (1<<20)
#endif
) {
if (mat.m11() < 0.0F && mat.m22() < 0.0F) { // horizontal/vertical flip
return smoothScaled(wd, hd).mirrored(true, true).convertToFormat(format());
} else if (mat.m11() < 0.0F) { // horizontal flip
return smoothScaled(wd, hd).mirrored(true, false).convertToFormat(format());
} else if (mat.m22() < 0.0F) { // vertical flip
return smoothScaled(wd, hd).mirrored(false, true).convertToFormat(format());
} else { // no flipping
return smoothScaled(wd, hd).convertToFormat(format());
}
}
}
@ -4612,7 +4648,7 @@ QImage QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode
QImage::Format target_format = d->format;
if (complex_xform || mode == Qt::SmoothTransformation) {
if (d->format < QImage::Format_RGB32 || !hasAlphaChannel()) {
if (d->format < QImage::Format_RGB32 || (!hasAlphaChannel() && complex_xform)) {
target_format = qt_alphaVersion(d->format);
}
}

View File

@ -123,6 +123,8 @@ private slots:
void smoothScaleBig();
void smoothScaleAlpha();
void smoothScaleFormats_data();
void smoothScaleFormats();
void transformed_data();
void transformed();
@ -1922,6 +1924,34 @@ void tst_QImage::smoothScaleAlpha()
QCOMPARE(dst, expected);
}
void tst_QImage::smoothScaleFormats_data()
{
QTest::addColumn<QImage::Format>("format");
for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) {
QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i);
}
}
void tst_QImage::smoothScaleFormats()
{
QFETCH(QImage::Format, format);
QImage src(32, 32, format);
src.fill(0x0);
// Upscale using painter scaling
QImage scaled = src.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
QCOMPARE(scaled.format(), src.format());
// > 2x down-scaling using QImage::smoothScaled()
scaled = src.scaled(8, 8, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
QCOMPARE(scaled.format(), src.format());
QTransform transform;
transform.rotate(45);
QImage rotated = src.transformed(transform);
QVERIFY(rotated.hasAlphaChannel());
}
static int count(const QImage &img, int x, int y, int dx, int dy, QRgb pixel)
{
int i = 0;