diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-10-11 16:37:15 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-09-26 18:49:10 +0200 |
commit | b0b4fcd05cd5ae891da868829c1645acf7589acb (patch) | |
tree | 897909b238b2cc0dc9c23d515158291771973461 | |
parent | 45896797c6dea7c3020c0db5ebeb41dc7a2ef4b7 (diff) |
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>
-rw-r--r-- | src/gui/image/qimage.cpp | 60 | ||||
-rw-r--r-- | tests/auto/gui/image/qimage/tst_qimage.cpp | 30 |
2 files changed, 78 insertions, 12 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 793fd665aa..b00b7bc588 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -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); } } diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index db5e474711..98de3dc5df 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -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; |