summaryrefslogtreecommitdiffstats
path: root/tests/auto/gui/image/qimage/tst_qimage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/gui/image/qimage/tst_qimage.cpp')
-rw-r--r--tests/auto/gui/image/qimage/tst_qimage.cpp690
1 files changed, 600 insertions, 90 deletions
diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp
index 7fcbabd532..1d0cdfcc4e 100644
--- a/tests/auto/gui/image/qimage/tst_qimage.cpp
+++ b/tests/auto/gui/image/qimage/tst_qimage.cpp
@@ -1,37 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-#include <QtTest/QtTest>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+
+#include <QTest>
+#include <QBuffer>
+#include <QMatrix4x4>
#include <qimage.h>
#include <qimagereader.h>
#include <qlist.h>
+#include <qset.h>
#include <qtransform.h>
#include <qrandom.h>
#include <stdio.h>
@@ -90,6 +68,7 @@ private slots:
void rotate_data();
void rotate();
+ void rotateBigImage();
void copy();
@@ -101,6 +80,10 @@ private slots:
void setPixel_data();
void setPixel();
+ void setPixelWithAlpha_data();
+ void setPixelWithAlpha();
+ void setPixelColorWithAlpha_data();
+ void setPixelColorWithAlpha();
void defaultColorTable_data();
void defaultColorTable();
@@ -123,6 +106,10 @@ private slots:
void smoothScaleBig();
void smoothScaleAlpha();
+ void smoothScaleFormats_data();
+ void smoothScaleFormats();
+ void smoothScaleNoConversion_data();
+ void smoothScaleNoConversion();
void transformed_data();
void transformed();
@@ -145,6 +132,7 @@ private slots:
void fillColor_data();
void fillColor();
+ void fillColorWithAlpha_data();
void fillColorWithAlpha();
void fillRGB888();
@@ -181,6 +169,11 @@ private slots:
void largeInplaceRgbConversion_data();
void largeInplaceRgbConversion();
+ void colorSpaceRgbConversion_data();
+ void colorSpaceRgbConversion();
+ void colorSpaceCmykConversion_data();
+ void colorSpaceCmykConversion();
+
void deepCopyWhenPaintingActive();
void scaled_QTBUG19157();
@@ -208,6 +201,7 @@ private slots:
void cleanupFunctions();
void devicePixelRatio();
+ void deviceIndependentSize();
void rgb30Unpremul();
void rgb30Repremul_data();
void rgb30Repremul();
@@ -238,12 +232,21 @@ private slots:
void wideImage();
+ void largeFillScale();
+ void largeRasterScale();
+
+ void metadataChangeWithReadOnlyPixels();
+ void scaleIndexed();
+
#if defined(Q_OS_WIN)
void toWinHBITMAP_data();
void toWinHBITMAP();
void fromMonoHBITMAP();
#endif // Q_OS_WIN
+ void tofromPremultipliedFormat_data();
+ void tofromPremultipliedFormat();
+
private:
const QString m_prefix;
};
@@ -311,7 +314,21 @@ static QLatin1String formatToString(QImage::Format format)
return QLatin1String("Grayscale16");
case QImage::Format_BGR888:
return QLatin1String("BGR888");
- default:
+ case QImage::Format_RGBX16FPx4:
+ return QLatin1String("RGBx16FPx4");
+ case QImage::Format_RGBA16FPx4:
+ return QLatin1String("RGBA16FPx4");
+ case QImage::Format_RGBA16FPx4_Premultiplied:
+ return QLatin1String("RGBA16FPx4pm");
+ case QImage::Format_RGBX32FPx4:
+ return QLatin1String("RGBx32FPx4");
+ case QImage::Format_RGBA32FPx4:
+ return QLatin1String("RGBA32FPx4");
+ case QImage::Format_RGBA32FPx4_Premultiplied:
+ return QLatin1String("RGBA32FPx4pm");
+ case QImage::Format_CMYK8888:
+ return QLatin1String("CMYK8888");
+ case QImage::NImageFormats:
break;
};
Q_UNREACHABLE();
@@ -1129,10 +1146,9 @@ void tst_QImage::rotate_data()
QTest::addColumn<QImage::Format>("format");
QTest::addColumn<int>("degrees");
- QList<int> degrees;
- degrees << 0 << 90 << 180 << 270;
+ constexpr int degrees[] = {0, 90, 180, 270};
- foreach (int d, degrees) {
+ for (int d : degrees) {
const QString dB = QString::number(d);
for (int i = QImage::Format_Indexed8; i < QImage::NImageFormats; i++) {
QImage::Format format = static_cast<QImage::Format>(i);
@@ -1210,12 +1226,29 @@ void tst_QImage::rotate()
QCOMPARE(original, dest);
}
+void tst_QImage::rotateBigImage()
+{
+ // QTBUG-105088
+ QImage big_image(3840, 2160, QImage::Format_ARGB32_Premultiplied);
+ QTransform t;
+ t.translate(big_image.width() / 2.0, big_image.height() / 2.0);
+ t.rotate(-89, Qt::YAxis, big_image.width());
+ t.translate(-big_image.width() / 2.0, -big_image.height() / 2.0);
+ QVERIFY(!big_image.transformed(t).isNull());
+
+ QMatrix4x4 m;
+ m.translate(big_image.width() / 2.0, big_image.height() / 2.0);
+ m.projectedRotate(89, 0, 1, 0, big_image.width());
+ m.translate(-big_image.width() / 2.0, -big_image.height() / 2.0);
+ QVERIFY(!big_image.transformed(m.toTransform()).isNull());
+}
+
void tst_QImage::copy()
{
// Task 99250
{
QImage img(16,16,QImage::Format_ARGB32);
- img.copy(QRect(1000,1,1,1));
+ (void)img.copy(QRect(1000,1,1,1));
}
}
@@ -1245,15 +1278,36 @@ void tst_QImage::loadFromData()
QVERIFY(original.save(&buf, "BMP"));
}
QVERIFY(!ba.isEmpty());
+ const uchar *baPtr = reinterpret_cast<const uchar *>(ba.constData());
- QImage dest;
- QVERIFY(dest.loadFromData(ba, "BMP"));
- QVERIFY(!dest.isNull());
+ {
+ QImage dest;
+ QVERIFY(dest.loadFromData(QByteArrayView(ba), "BMP"));
+ QCOMPARE(original, dest);
- QCOMPARE(original, dest);
+ QVERIFY(!dest.loadFromData(QByteArrayView()));
+ QVERIFY(dest.isNull());
+ }
+ {
+ QImage dest;
+ QVERIFY(dest.loadFromData(ba, "BMP"));
+ QCOMPARE(original, dest);
- QVERIFY(!dest.loadFromData(QByteArray()));
- QVERIFY(dest.isNull());
+ QVERIFY(!dest.loadFromData(QByteArray()));
+ QVERIFY(dest.isNull());
+ }
+ {
+ QImage dest;
+ QVERIFY(dest.loadFromData(baPtr, int(ba.size()), "BMP"));
+ QCOMPARE(original, dest);
+
+ QVERIFY(!dest.loadFromData(nullptr, 0));
+ QVERIFY(dest.isNull());
+ }
+
+ QCOMPARE(original, QImage::fromData(QByteArrayView(ba), "BMP"));
+ QCOMPARE(original, QImage::fromData(ba, "BMP"));
+ QCOMPARE(original, QImage::fromData(baPtr, int(ba.size()), "BMP"));
}
#if !defined(QT_NO_DATASTREAM)
@@ -1449,6 +1503,64 @@ void tst_QImage::setPixel()
}
}
+void tst_QImage::setPixelWithAlpha_data()
+{
+ QTest::addColumn<QImage::Format>("format");
+
+ for (int c = QImage::Format_RGB32; c < QImage::NImageFormats; ++c) {
+ if (c == QImage::Format_Grayscale8)
+ continue;
+ if (c == QImage::Format_Grayscale16)
+ continue;
+ if (c == QImage::Format_Alpha8)
+ continue;
+ if (c == QImage::Format_CMYK8888)
+ continue;
+ QTest::newRow(qPrintable(formatToString(QImage::Format(c)))) << QImage::Format(c);
+ }
+}
+
+void tst_QImage::setPixelWithAlpha()
+{
+ QFETCH(QImage::Format, format);
+ QImage image(1, 1, format);
+ QRgb referenceColor = qRgba(0, 170, 85, 170);
+ image.setPixel(0, 0, referenceColor);
+
+ if (!image.hasAlphaChannel())
+ referenceColor = 0xff000000 | referenceColor;
+
+ QRgb color = image.pixel(0, 0);
+ QCOMPARE(qRed(color) & 0xf0, qRed(referenceColor) & 0xf0);
+ QCOMPARE(qGreen(color) & 0xf0, qGreen(referenceColor) & 0xf0);
+ QCOMPARE(qBlue(color) & 0xf0, qBlue(referenceColor) & 0xf0);
+ QCOMPARE(qAlpha(color) & 0xf0, qAlpha(referenceColor) & 0xf0);
+}
+
+void tst_QImage::setPixelColorWithAlpha_data()
+{
+ setPixelWithAlpha_data();
+}
+
+void tst_QImage::setPixelColorWithAlpha()
+{
+ QFETCH(QImage::Format, format);
+ QImage image(1, 1, format);
+ image.setPixelColor(0, 0, QColor(170, 85, 255, 170));
+ QRgb referenceColor = qRgba(170, 85, 255, 170);
+
+ if (!image.hasAlphaChannel())
+ referenceColor = 0xff000000 | referenceColor;
+ else if (image.pixelFormat().premultiplied() == QPixelFormat::Premultiplied)
+ referenceColor = qPremultiply(referenceColor);
+
+ QRgb color = image.pixel(0, 0);
+ QCOMPARE(qRed(color) & 0xf0, qRed(referenceColor) & 0xf0);
+ QCOMPARE(qGreen(color) & 0xf0, qGreen(referenceColor) & 0xf0);
+ QCOMPARE(qBlue(color) & 0xf0, qBlue(referenceColor) & 0xf0);
+ QCOMPARE(qAlpha(color) & 0xf0, qAlpha(referenceColor) & 0xf0);
+}
+
void tst_QImage::convertToFormatPreserveDotsPrMeter()
{
QImage img(100, 100, QImage::Format_ARGB32_Premultiplied);
@@ -1697,7 +1809,17 @@ void tst_QImage::smoothScale2_data()
QTest::addColumn<int>("size");
int sizes[] = { 2, 3, 4, 6, 7, 8, 10, 16, 20, 32, 40, 64, 100, 101, 128, 0 };
- QImage::Format formats[] = { QImage::Format_RGB32, QImage::Format_ARGB32_Premultiplied, QImage::Format_RGBX64, QImage::Format_RGBA64_Premultiplied, QImage::Format_Invalid };
+ QImage::Format formats[] = { QImage::Format_RGB32,
+ QImage::Format_ARGB32_Premultiplied,
+#if QT_CONFIG(raster_64bit)
+ QImage::Format_RGBX64,
+ QImage::Format_RGBA64_Premultiplied,
+#endif
+#if QT_CONFIG(raster_fp)
+ QImage::Format_RGBX32FPx4,
+ QImage::Format_RGBA32FPx4_Premultiplied,
+#endif
+ QImage::Format_Invalid };
for (int j = 0; formats[j] != QImage::Format_Invalid; ++j) {
QString formatstr = formatToString(formats[j]);
for (int i = 0; sizes[i] != 0; ++i) {
@@ -1712,11 +1834,9 @@ void tst_QImage::smoothScale2()
QFETCH(QImage::Format, format);
QFETCH(int, size);
- bool opaque = (format == QImage::Format_RGB32 || format == QImage::Format_RGBX64);
-
- QRgb expected = opaque ? qRgb(63, 127, 255) : qRgba(31, 63, 127, 127);
-
QImage img(size, size, format);
+ bool opaque = !img.hasAlphaChannel();
+ QRgb expected = opaque ? qRgb(63, 127, 255) : qRgba(31, 63, 127, 127);
img.fill(expected);
// scale x down, y down
@@ -1861,6 +1981,9 @@ void tst_QImage::smoothScale4_data()
#if QT_CONFIG(raster_64bit)
QTest::newRow("RGBx64") << QImage::Format_RGBX64;
#endif
+#if QT_CONFIG(raster_fp)
+ QTest::newRow("RGBx32FP") << QImage::Format_RGBX32FPx4;
+#endif
}
void tst_QImage::smoothScale4()
@@ -1922,6 +2045,52 @@ 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());
+}
+
+void tst_QImage::smoothScaleNoConversion_data()
+{
+ QTest::addColumn<QImage::Format>("format");
+ QTest::addRow("Mono") << QImage::Format_Mono;
+ QTest::addRow("MonoLSB") << QImage::Format_MonoLSB;
+ QTest::addRow("Indexed8") << QImage::Format_Indexed8;
+}
+
+void tst_QImage::smoothScaleNoConversion()
+{
+ QFETCH(QImage::Format, format);
+ QImage img(128, 128, format);
+ img.fill(1);
+ img.setColorTable(QList<QRgb>() << qRgba(255,0,0,255) << qRgba(0,0,0,0));
+ img = img.scaled(QSize(48, 48), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ QVERIFY(img.hasAlphaChannel());
+}
+
static int count(const QImage &img, int x, int y, int dx, int dy, QRgb pixel)
{
int i = 0;
@@ -2260,6 +2429,8 @@ void tst_QImage::fillColor_data()
QImage::Format_RGBA8888_Premultiplied,
QImage::Format_BGR30,
QImage::Format_A2RGB30_Premultiplied,
+ QImage::Format_RGBX16FPx4,
+ QImage::Format_RGBA32FPx4_Premultiplied,
};
for (int i=0; names[i] != 0; ++i) {
@@ -2314,15 +2485,28 @@ void tst_QImage::fillColor()
}
}
-void tst_QImage::fillColorWithAlpha()
+void tst_QImage::fillColorWithAlpha_data()
{
- QImage argb32(1, 1, QImage::Format_ARGB32);
- argb32.fill(QColor(255, 0, 0, 127));
- QCOMPARE(argb32.pixel(0, 0), qRgba(255, 0, 0, 127));
+ setPixelWithAlpha_data();
+}
- QImage argb32pm(1, 1, QImage::Format_ARGB32_Premultiplied);
- argb32pm.fill(QColor(255, 0, 0, 127));
- QCOMPARE(argb32pm.pixel(0, 0), 0x7f7f0000u);
+void tst_QImage::fillColorWithAlpha()
+{
+ QFETCH(QImage::Format, format);
+ QImage image(1, 1, format);
+ image.fill(QColor(255, 170, 85, 170));
+ QRgb referenceColor = qRgba(255, 170, 85, 170);
+
+ if (!image.hasAlphaChannel())
+ referenceColor = 0xff000000 | referenceColor;
+ else if (image.pixelFormat().premultiplied() == QPixelFormat::Premultiplied)
+ referenceColor = qPremultiply(referenceColor);
+
+ QRgb color = image.pixel(0, 0);
+ QCOMPARE(qRed(color) & 0xf0, qRed(referenceColor) & 0xf0);
+ QCOMPARE(qGreen(color) & 0xf0, qGreen(referenceColor) & 0xf0);
+ QCOMPARE(qBlue(color) & 0xf0, qBlue(referenceColor) & 0xf0);
+ QCOMPARE(qAlpha(color) & 0xf0, qAlpha(referenceColor) & 0xf0);
}
void tst_QImage::fillRGB888()
@@ -2348,10 +2532,13 @@ void tst_QImage::fillPixel_data()
QTest::newRow("RGB16, transparent") << QImage::Format_RGB16 << 0x0u << 0xff000000u;
QTest::newRow("RGB32, transparent") << QImage::Format_RGB32 << 0x0u << 0xff000000u;
+ QTest::newRow("RGB444, transparent") << QImage::Format_RGB444 << 0x0u << 0xff000000u;
+ QTest::newRow("RGB666, transparent") << QImage::Format_RGB666 << 0x0u << 0xff000000u;
QTest::newRow("RGBx8888, transparent") << QImage::Format_RGBX8888 << 0x0u << 0xff000000u;
QTest::newRow("ARGB32, transparent") << QImage::Format_ARGB32 << 0x0u << 0x00000000u;
QTest::newRow("ARGB32pm, transparent") << QImage::Format_ARGB32_Premultiplied << 0x0u << 0x00000000u;
QTest::newRow("RGBA8888pm, transparent") << QImage::Format_RGBA8888_Premultiplied << 0x0u << 0x00000000u;
+ QTest::newRow("Grayscale8, transparent") << QImage::Format_Grayscale8 << 0x0u << 0xff000000u;
QTest::newRow("Alpha8, transparent") << QImage::Format_Alpha8 << 0x0u << 0x00000000u;
QTest::newRow("RGB16, red") << QImage::Format_RGB16 << (uint)qConvertRgb32To16(0xffff0000) << 0xffff0000u;
@@ -2359,13 +2546,14 @@ void tst_QImage::fillPixel_data()
QTest::newRow("ARGB32, red") << QImage::Format_ARGB32 << 0xffff0000u << 0xffff0000u;
QTest::newRow("RGBA8888, red") << QImage::Format_RGBA8888 << 0xff0000ffu << 0xffff0000u;
- QTest::newRow("Grayscale8, grey") << QImage::Format_Grayscale8 << 0xff808080u << 0xff808080u;
+ QTest::newRow("Grayscale8, grey") << QImage::Format_Grayscale8 << 0x80u << 0xff808080u;
QTest::newRow("RGB32, semi-red") << QImage::Format_RGB32 << 0x80ff0000u << 0xffff0000u;
QTest::newRow("ARGB32, semi-red") << QImage::Format_ARGB32 << 0x80ff0000u << 0x80ff0000u;
QTest::newRow("ARGB32pm, semi-red") << QImage::Format_ARGB32 << 0x80800000u << 0x80800000u;
QTest::newRow("RGBA8888pm, semi-red") << QImage::Format_RGBA8888_Premultiplied << 0x80000080u << 0x80800000u;
- QTest::newRow("Alpha8, semi-red") << QImage::Format_Alpha8 << 0x80000080u << 0x80000000u;
+
+ QTest::newRow("Alpha8, semi-transparent") << QImage::Format_Alpha8 << 0x80u << 0x80000000u;
}
void tst_QImage::fillPixel()
@@ -2378,6 +2566,8 @@ void tst_QImage::fillPixel()
image.fill(color);
QCOMPARE(image.pixel(0, 0), pixelValue);
+ if (image.depth() == 8)
+ QCOMPARE(*(const uchar *)image.constBits(), color);
}
void tst_QImage::rgbSwapped_data()
@@ -2387,7 +2577,8 @@ void tst_QImage::rgbSwapped_data()
for (int i = QImage::Format_Indexed8; i < QImage::NImageFormats; ++i) {
if (i == QImage::Format_Alpha8
|| i == QImage::Format_Grayscale8
- || i == QImage::Format_Grayscale16) {
+ || i == QImage::Format_Grayscale16
+ || i == QImage::Format_CMYK8888) {
continue;
}
QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i);
@@ -2431,11 +2622,11 @@ void tst_QImage::rgbSwapped()
QCOMPARE(swappedColor.blue(), referenceColor.red());
}
- QImage imageSwappedTwice = imageSwapped.rgbSwapped();
+ imageSwapped.rgbSwap();
- QCOMPARE(image, imageSwappedTwice);
+ QCOMPARE(image, imageSwapped);
- QCOMPARE(memcmp(image.constBits(), imageSwappedTwice.constBits(), image.sizeInBytes()), 0);
+ QCOMPARE(memcmp(image.constBits(), imageSwapped.constBits(), image.sizeInBytes()), 0);
}
void tst_QImage::mirrored_data()
@@ -2481,20 +2672,20 @@ void tst_QImage::mirrored_data()
QTest::newRow("Format_Mono, horizontal+vertical") << QImage::Format_Mono << true << true << 16 << 16;
QTest::newRow("Format_MonoLSB, horizontal+vertical") << QImage::Format_MonoLSB << true << true << 16 << 16;
- QTest::newRow("Format_RGB32, vertical") << QImage::Format_RGB32 << true << false << 8 << 16;
- QTest::newRow("Format_ARGB32, vertical") << QImage::Format_ARGB32 << true << false << 16 << 8;
+ QTest::newRow("Format_RGB32, vertical, narrow") << QImage::Format_RGB32 << true << false << 8 << 16;
+ QTest::newRow("Format_ARGB32, vertical, short") << QImage::Format_ARGB32 << true << false << 16 << 8;
QTest::newRow("Format_Mono, vertical, non-aligned") << QImage::Format_Mono << true << false << 19 << 25;
QTest::newRow("Format_MonoLSB, vertical, non-aligned") << QImage::Format_MonoLSB << true << false << 19 << 25;
// Non-aligned horizontal 1-bit needs special handling so test this.
QTest::newRow("Format_Mono, horizontal, non-aligned") << QImage::Format_Mono << false << true << 13 << 17;
- QTest::newRow("Format_Mono, horizontal, non-aligned") << QImage::Format_Mono << false << true << 19 << 25;
- QTest::newRow("Format_Mono, horizontal+vertical, non-aligned") << QImage::Format_Mono << true << true << 25 << 47;
+ QTest::newRow("Format_Mono, horizontal, non-aligned, big") << QImage::Format_Mono << false << true << 19 << 25;
+ QTest::newRow("Format_Mono, horizontal+vertical, non-aligned, big") << QImage::Format_Mono << true << true << 25 << 47;
QTest::newRow("Format_Mono, horizontal+vertical, non-aligned") << QImage::Format_Mono << true << true << 21 << 16;
QTest::newRow("Format_MonoLSB, horizontal, non-aligned") << QImage::Format_MonoLSB << false << true << 13 << 17;
- QTest::newRow("Format_MonoLSB, horizontal, non-aligned") << QImage::Format_MonoLSB << false << true << 19 << 25;
- QTest::newRow("Format_MonoLSB, horizontal+vertical, non-aligned") << QImage::Format_MonoLSB << true << true << 25 << 47;
+ QTest::newRow("Format_MonoLSB, horizontal, non-aligned, big") << QImage::Format_MonoLSB << false << true << 19 << 25;
+ QTest::newRow("Format_MonoLSB, horizontal+vertical, non-aligned, big") << QImage::Format_MonoLSB << true << true << 25 << 47;
QTest::newRow("Format_MonoLSB, horizontal+vertical, non-aligned") << QImage::Format_MonoLSB << true << true << 21 << 16;
}
@@ -2543,16 +2734,16 @@ void tst_QImage::mirrored()
}
}
- QImage imageMirroredTwice = imageMirrored.mirrored(swap_horizontal, swap_vertical);
+ imageMirrored.mirror(swap_horizontal, swap_vertical);
- QCOMPARE(image, imageMirroredTwice);
+ QCOMPARE(image, imageMirrored);
if (format != QImage::Format_Mono && format != QImage::Format_MonoLSB)
- QCOMPARE(memcmp(image.constBits(), imageMirroredTwice.constBits(), image.sizeInBytes()), 0);
+ QCOMPARE(memcmp(image.constBits(), imageMirrored.constBits(), image.sizeInBytes()), 0);
else {
for (int i = 0; i < image.height(); ++i)
for (int j = 0; j < image.width(); ++j)
- QCOMPARE(image.pixel(j,i), imageMirroredTwice.pixel(j,i));
+ QCOMPARE(image.pixel(j,i), imageMirrored.pixel(j,i));
}
}
@@ -2563,7 +2754,6 @@ void tst_QImage::inplaceRgbSwapped_data()
void tst_QImage::inplaceRgbSwapped()
{
-#if defined(Q_COMPILER_REF_QUALIFIERS)
QFETCH(QImage::Format, format);
QImage image(64, 1, format);
@@ -2571,7 +2761,7 @@ void tst_QImage::inplaceRgbSwapped()
QList<QRgb> testColor(image.width());
for (int i = 0; i < image.width(); ++i)
- testColor[i] = qRgb(i * 2, i * 3, 255 - i * 4);
+ testColor[i] = qRgb(i * 2, i * 3, std::min(255 - i * 4, 0));
if (format == QImage::Format_Indexed8) {
for (int i = 0; i < image.width(); ++i) {
@@ -2624,7 +2814,6 @@ void tst_QImage::inplaceRgbSwapped()
QCOMPARE(dataSwapped, orig.rgbSwapped());
}
-#endif
}
@@ -2836,21 +3025,29 @@ void tst_QImage::genericRgbConversion()
QImage image(16, 16, format);
- for (int i = 0; i < image.height(); ++i)
- for (int j = 0; j < image.width(); ++j)
- image.setPixel(j, i, qRgb(j*16, i*16, 0));
+ for (int i = 0; i < image.height(); ++i) {
+ for (int j = 0; j < image.width(); ++j) {
+ if (srcGrayscale || dstGrayscale)
+ image.setPixel(j, i, qRgb((i + j) * 8, (i + j) * 8, (i + j) * 8));
+ else
+ image.setPixel(j, i, qRgb(j * 16, i * 16, (i + j) * 8));
+ }
+ }
QImage imageConverted = image.convertToFormat(dest_format);
+ uint mask = std::min(image.depth(), imageConverted.depth()) < 32 ? 0xFFF0F0F0 : 0xFFFFFFFF;
+ if (srcGrayscale || dstGrayscale)
+ mask = std::max(image.depth(), imageConverted.depth()) < 32 ? 0xFFF0F0F0 : 0xFFFFFFFF;
+ if (srcGrayscale && dstGrayscale)
+ mask = 0xFFFFFFFF;
QCOMPARE(imageConverted.format(), dest_format);
for (int i = 0; i < imageConverted.height(); ++i) {
for (int j = 0; j < imageConverted.width(); ++j) {
QRgb convertedColor = imageConverted.pixel(j,i);
- if (srcGrayscale || dstGrayscale) {
- QVERIFY(qAbs(qGray(convertedColor) - qGray(qRgb(j*16, i*16, 0))) < 15);
- } else {
- QCOMPARE(qRed(convertedColor) & 0xF0, j * 16);
- QCOMPARE(qGreen(convertedColor) & 0xF0, i * 16);
- }
+ if (srcGrayscale || dstGrayscale)
+ QCOMPARE(convertedColor & mask, qRgb((i + j) * 8, (i + j) * 8, (i + j) * 8) & mask);
+ else
+ QCOMPARE(convertedColor & mask, qRgb(j * 16, i * 16, (i + j) * 8) & mask);
}
}
}
@@ -2863,13 +3060,15 @@ void tst_QImage::inplaceRgbConversion_data()
for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) {
if (i == QImage::Format_Alpha8
|| i == QImage::Format_Grayscale8
- || i == QImage::Format_Grayscale16) {
+ || i == QImage::Format_Grayscale16
+ || i == QImage::Format_CMYK8888) {
continue;
}
for (int j = QImage::Format_RGB32; j < QImage::NImageFormats; ++j) {
if (j == QImage::Format_Alpha8
|| j == QImage::Format_Grayscale8
- || j == QImage::Format_Grayscale16) {
+ || j == QImage::Format_Grayscale16
+ || j == QImage::Format_CMYK8888) {
continue;
}
if (i == j)
@@ -2883,6 +3082,7 @@ void tst_QImage::inplaceRgbConversion_data()
void tst_QImage::inplaceRgbConversion()
{
+ // Test that conversions between RGB formats of the same bitwidth can be done inplace.
QFETCH(QImage::Format, format);
QFETCH(QImage::Format, dest_format);
@@ -2899,8 +3099,7 @@ void tst_QImage::inplaceRgbConversion()
for (int i = 0; i < imageConverted.height(); ++i) {
for (int j = 0; j < imageConverted.width(); ++j) {
QRgb convertedColor = imageConverted.pixel(j,i);
- QCOMPARE(qRed(convertedColor) & 0xF0, j * 16);
- QCOMPARE(qGreen(convertedColor) & 0xF0, i * 16);
+ QCOMPARE(convertedColor & 0xFFF0F0F0, qRgb(j * 16, i * 16, 0));
}
}
if (qt_depthForFormat(format) == qt_depthForFormat(dest_format))
@@ -3050,6 +3249,144 @@ void tst_QImage::largeInplaceRgbConversion()
}
}
+void tst_QImage::colorSpaceRgbConversion_data()
+{
+ QTest::addColumn<QImage::Format>("fromFormat");
+ QTest::addColumn<QImage::Format>("toFormat");
+
+ // The various possible code paths for color space conversions compatible with RGB color spaces:
+ QImage::Format formats[] = {
+ QImage::Format_RGB32,
+ QImage::Format_ARGB32,
+ QImage::Format_ARGB32_Premultiplied,
+ QImage::Format_RGBX64,
+ QImage::Format_RGBA64,
+ QImage::Format_RGBA64_Premultiplied,
+ QImage::Format_RGBX32FPx4,
+ QImage::Format_RGBA32FPx4,
+ QImage::Format_RGBA32FPx4_Premultiplied,
+ QImage::Format_Grayscale8,
+ QImage::Format_Grayscale16,
+ };
+
+ for (auto fromFormat : formats) {
+ const QLatin1String formatI = formatToString(fromFormat);
+ for (auto toFormat : formats) {
+ QTest::addRow("%s -> %s", formatI.data(), formatToString(toFormat).data())
+ << fromFormat << toFormat;
+ }
+ }
+}
+
+void tst_QImage::colorSpaceRgbConversion()
+{
+ // Test that all color space conversions work
+ QFETCH(QImage::Format, fromFormat);
+ QFETCH(QImage::Format, toFormat);
+
+ bool srcGrayscale = fromFormat == QImage::Format_Grayscale8 || fromFormat == QImage::Format_Grayscale16;
+ bool dstGrayscale = toFormat == QImage::Format_Grayscale8 || toFormat == QImage::Format_Grayscale16;
+
+ QImage image(16, 16, fromFormat);
+ image.setColorSpace(QColorSpace::SRgb);
+
+ for (int i = 0; i < image.height(); ++i) {
+ for (int j = 0; j < image.width(); ++j) {
+ if (srcGrayscale || dstGrayscale)
+ image.setPixel(j, i, qRgb((i + j) * 8, (i + j) * 8, (i + j) * 8));
+ else
+ image.setPixel(j, i, qRgb(j * 16, i * 16, (i + j) * 8));
+ }
+ }
+
+ QImage imageConverted = image.convertedToColorSpace(QColorSpace::DisplayP3, toFormat);
+ QCOMPARE(imageConverted.format(), toFormat);
+ QCOMPARE(imageConverted.size(), image.size());
+ if (dstGrayscale) {
+ int gray = 0;
+ for (int x = 0; x < image.width(); ++x) {
+ int newGray = qGray(imageConverted.pixel(x, 6));
+ QCOMPARE_GE(newGray, gray);
+ gray = newGray;
+ }
+ } else {
+ int red = 0;
+ int blue = 0;
+ for (int x = 0; x < image.width(); ++x) {
+ int newRed = qRed(imageConverted.pixel(x, 5));
+ int newBlue = qBlue(imageConverted.pixel(x, 7));
+ QCOMPARE_GE(newBlue, blue);
+ QCOMPARE_GE(newRed, red);
+ blue = newBlue;
+ red = newRed;
+ }
+ }
+}
+
+
+void tst_QImage::colorSpaceCmykConversion_data()
+{
+ QTest::addColumn<QImage::Format>("toFormat");
+
+ QImage::Format formats[] = {
+ QImage::Format_RGB32,
+ QImage::Format_ARGB32,
+ QImage::Format_ARGB32_Premultiplied,
+ QImage::Format_RGBX64,
+ QImage::Format_RGBA64,
+ QImage::Format_RGBA64_Premultiplied,
+ QImage::Format_RGBX32FPx4,
+ QImage::Format_RGBA32FPx4,
+ QImage::Format_RGBA32FPx4_Premultiplied,
+ QImage::Format_Grayscale8,
+ QImage::Format_Grayscale16,
+ };
+
+ for (auto toFormat : formats)
+ QTest::addRow("CMYK8888 -> %s", formatToString(toFormat).data()) << toFormat;
+}
+
+void tst_QImage::colorSpaceCmykConversion()
+{
+ QFETCH(QImage::Format, toFormat);
+
+ bool dstGrayscale = toFormat == QImage::Format_Grayscale8 || toFormat == QImage::Format_Grayscale16;
+
+ QImage image(16, 16, QImage::Format_CMYK8888);
+ QFile iccProfile(m_prefix +"CGATS001Compat-v2-micro.icc");
+ iccProfile.open(QIODevice::ReadOnly);
+ image.setColorSpace(QColorSpace::fromIccProfile(iccProfile.readAll()));
+ QVERIFY(image.colorSpace().isValid());
+
+ for (int i = 0; i < image.height(); ++i) {
+ for (int j = 0; j < image.width(); ++j) {
+ if (dstGrayscale)
+ image.setPixel(j, i, qRgb((i + j) * 8, (i + j) * 8, (i + j) * 8));
+ else
+ image.setPixel(j, i, qRgb(j * 16, i * 16, (i + j) * 8));
+ }
+ }
+
+ QImage imageConverted = image.convertedToColorSpace(QColorSpace::SRgb, toFormat);
+ QCOMPARE(imageConverted.format(), toFormat);
+ QCOMPARE(imageConverted.size(), image.size());
+ if (dstGrayscale) {
+ int gray = 0;
+ for (int x = 0; x < image.width(); ++x) {
+ int newGray = qGray(imageConverted.pixel(x, 6));
+ QCOMPARE_GE(newGray, gray);
+ gray = newGray;
+ }
+ } else {
+ int red = 0;
+ for (int x = 0; x < image.width(); ++x) {
+ int newRed = qRed(imageConverted.pixel(x, 5));
+ QCOMPARE_GE(newRed, red);
+ red = newRed;
+ }
+ }
+}
+
void tst_QImage::deepCopyWhenPaintingActive()
{
QImage image(64, 64, QImage::Format_ARGB32_Premultiplied);
@@ -3158,7 +3495,8 @@ void tst_QImage::invertPixelsRGB_data()
for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) {
if (i == QImage::Format_Alpha8
|| i == QImage::Format_Grayscale8
- || i == QImage::Format_Grayscale16) {
+ || i == QImage::Format_Grayscale16
+ || i == QImage::Format_CMYK8888) {
continue;
}
QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i);
@@ -3290,6 +3628,9 @@ void tst_QImage::exifInvalidData()
void tst_QImage::exifReadComments()
{
+#ifdef QT_NO_IMAGEIO_TEXT_LOADING
+ QSKIP("Reading text from image file is configured off");
+#endif
QImage image;
QVERIFY(image.load(m_prefix + "jpeg_exif_utf8_comment.jpg"));
QVERIFY(!image.isNull());
@@ -3339,7 +3680,7 @@ void tst_QImage::cleanupFunctions()
{
called = false;
- QImage *copy = 0;
+ QImage *copy = nullptr;
{
QImage image(bufferImage.bits(), bufferImage.width(), bufferImage.height(), bufferImage.format(), cleanupFunction, &called);
copy = new QImage(image);
@@ -3348,7 +3689,38 @@ void tst_QImage::cleanupFunctions()
delete copy;
QVERIFY(called);
}
-
+ {
+ called = false;
+ QImage container;
+ {
+ QImage image(bufferImage.bits(), bufferImage.width(), bufferImage.height(), bufferImage.format(), cleanupFunction, &called);
+ container = std::move(image);
+ // Test methods don't crash after move:
+ Q_UNUSED(image.isNull());
+ Q_UNUSED(image.width());
+ Q_UNUSED(image.bytesPerLine());
+ Q_UNUSED(image.sizeInBytes());
+ Q_UNUSED(image.constBits());
+ }
+ // 'image' was moved and should outlive its scope
+ QVERIFY(!called);
+ container = QImage();
+ QVERIFY(called);
+ }
+ {
+ called = false;
+ QImage outer(bufferImage.bits(), bufferImage.width(), bufferImage.height(), bufferImage.format(), cleanupFunction, &called);
+ bool called2 = false;
+ {
+ uchar internalData[256];
+ QImage internal(internalData, 16, 16, QImage::Format_Grayscale8, cleanupFunction, &called2);
+ internal = std::move(outer);
+ }
+ // 'internal' was _not_ moved and should not outlive its original scope
+ QVERIFY(called2);
+ // 'outer' was moved into the inner scope and should now be dead.
+ QVERIFY(called);
+ }
}
// test image devicePixelRatio setting and detaching
@@ -3378,6 +3750,15 @@ void tst_QImage::devicePixelRatio()
QCOMPARE(b.devicePixelRatio(), qreal(1.0));
}
+void tst_QImage::deviceIndependentSize() {
+ QImage a(64, 64, QImage::Format_ARGB32);
+ a.fill(Qt::white);
+ a.setDevicePixelRatio(1.0);
+ QCOMPARE(a.deviceIndependentSize(), QSizeF(64, 64));
+ a.setDevicePixelRatio(2.0);
+ QCOMPARE(a.deviceIndependentSize(), QSizeF(32, 32));
+}
+
void tst_QImage::rgb30Unpremul()
{
QImage a(3, 1, QImage::Format_A2RGB30_Premultiplied);
@@ -3464,6 +3845,13 @@ void tst_QImage::metadataPassthrough()
QCOMPARE(converted.dotsPerMeterY(), a.dotsPerMeterY());
QCOMPARE(converted.devicePixelRatio(), a.devicePixelRatio());
+ QList<QRgb> clut({ 0xFFFF0000, 0xFF00FF00, 0xFF0000FF });
+ QImage convertedWithClut = a.convertToFormat(QImage::Format_Indexed8, clut);
+ QCOMPARE(convertedWithClut.text(QStringLiteral("Test")), a.text(QStringLiteral("Test")));
+ QCOMPARE(convertedWithClut.dotsPerMeterX(), a.dotsPerMeterX());
+ QCOMPARE(convertedWithClut.dotsPerMeterY(), a.dotsPerMeterY());
+ QCOMPARE(convertedWithClut.devicePixelRatio(), a.devicePixelRatio());
+
QImage copied = a.copy(0, 0, a.width() / 2, a.height() / 2);
QCOMPARE(copied.text(QStringLiteral("Test")), a.text(QStringLiteral("Test")));
QCOMPARE(copied.dotsPerMeterX(), a.dotsPerMeterX());
@@ -3508,6 +3896,14 @@ void tst_QImage::pixelColor()
// Try setting an invalid color.
QTest::ignoreMessage(QtWarningMsg, "QImage::setPixelColor: color is invalid");
argb32.setPixelColor(0, 0, QColor());
+
+ // Test correct premultiplied handling of RGBA64 as well
+ QImage rgba64(1, 1, QImage::Format_RGBA64);
+ QImage rgba64pm(1, 1, QImage::Format_RGBA64_Premultiplied);
+ rgba64.setPixelColor(QPoint(0, 0), c);
+ rgba64pm.setPixelColor(QPoint(0, 0), c);
+ QCOMPARE(rgba64.pixelColor(QPoint(0, 0)), c);
+ QCOMPARE(rgba64pm.pixelColor(QPoint(0, 0)), c);
}
void tst_QImage::pixel()
@@ -3628,7 +4024,7 @@ void tst_QImage::reinterpretAsFormat_data()
QTest::newRow("rgb32 -> argb32") << QImage::Format_RGB32 << QImage::Format_ARGB32 << QColor(Qt::cyan) << QColor(Qt::cyan);
QTest::newRow("argb32pm -> rgb32") << QImage::Format_ARGB32_Premultiplied << QImage::Format_RGB32 << QColor(Qt::transparent) << QColor(Qt::black);
QTest::newRow("argb32 -> rgb32") << QImage::Format_ARGB32 << QImage::Format_RGB32 << QColor(255, 0, 0, 127) << QColor(255, 0, 0);
- QTest::newRow("argb32pm -> rgb32") << QImage::Format_ARGB32_Premultiplied << QImage::Format_RGB32 << QColor(255, 0, 0, 127) << QColor(127, 0, 0);
+ QTest::newRow("argb32pm (red) -> rgb32") << QImage::Format_ARGB32_Premultiplied << QImage::Format_RGB32 << QColor(255, 0, 0, 127) << QColor(127, 0, 0);
}
void tst_QImage::reinterpretAsFormat()
@@ -3723,7 +4119,10 @@ void tst_QImage::hugeQImage()
#if Q_PROCESSOR_WORDSIZE < 8
QSKIP("Test only makes sense on 64-bit machines");
#else
- QImage image(25000, 25000, QImage::Format_RGB32);
+ std::unique_ptr<char[]> enough(new (std::nothrow) char[qsizetype(25000)*25000*4]);
+ if (!enough)
+ QSKIP("Could not allocate enough memory");
+ QImage image((uchar*)enough.get(), 25000, 25000, QImage::Format_RGB32);
QVERIFY(!image.isNull());
QCOMPARE(image.height(), 25000);
@@ -3776,6 +4175,95 @@ void tst_QImage::wideImage()
// Qt6: Test that it actually works on 64bit architectures.
}
+void tst_QImage::largeFillScale()
+{
+#if Q_PROCESSOR_WORDSIZE < 8
+ QSKIP("Test fails on 32-bit builds");
+#endif
+ // Test from QTBUG-84428
+ QImage input(QSize(std::numeric_limits<qint16>::max() + 10, 1), QImage::Format_ARGB32_Premultiplied);
+ input.fill(Qt::white);
+
+ const int scaleFactor = 2;
+ QImage scaled = input.scaled(input.width(), input.height() * scaleFactor);
+
+ for (int x = 0, w = input.width(); x < w; ++x) {
+ const auto inputPixel = input.pixel(x, 0);
+ auto scaledPixel = scaled.pixel(x, 0);
+ QCOMPARE(scaledPixel, inputPixel);
+ scaledPixel = scaled.pixel(x, 1);
+ QCOMPARE(scaledPixel, inputPixel);
+ }
+}
+
+void tst_QImage::largeRasterScale()
+{
+#if Q_PROCESSOR_WORDSIZE < 8
+ QSKIP("Test fails on 32-bit builds");
+#endif
+ // Now test that qgrayraster still works at these ranges
+ QImage image(QSize(40000, 200), QImage::Format_RGB32);
+ image.fill(Qt::white);
+
+ QPainter painter(&image);
+ painter.setRenderHint(QPainter::Antialiasing, true);
+ painter.setBrush(Qt::black);
+ painter.drawEllipse(QPoint(33000, 100), 6990, 99);
+ painter.end();
+ QCOMPARE(image.pixelColor(27000, 10), Qt::white);
+ QCOMPARE(image.pixelColor(33000, 10), Qt::black);
+ QCOMPARE(image.pixelColor(39000, 10), Qt::white);
+ QCOMPARE(image.pixelColor(27000, 100), Qt::black);
+ QCOMPARE(image.pixelColor(33000, 100), Qt::black);
+ QCOMPARE(image.pixelColor(39000, 100), Qt::black);
+ QCOMPARE(image.pixelColor(27000, 190), Qt::white);
+ QCOMPARE(image.pixelColor(33000, 190), Qt::black);
+ QCOMPARE(image.pixelColor(39000, 190), Qt::white);
+
+ // Now check grayscale antialiasing takes place in the higher coords
+ bool grayObserved = false;
+ for (int x = 33000; x < 39000; ++x) {
+ QRgb pixel = image.pixel(x, 20);
+ if (pixel == 0xff000000)
+ continue; // still black
+ if (pixel == 0xffffffff) {
+ QVERIFY(grayObserved);
+ break;
+ }
+ grayObserved = true;
+ }
+
+// image.save("largeRasterScale.png", "PNG");
+}
+
+void tst_QImage::metadataChangeWithReadOnlyPixels()
+{
+ const QRgb data[3] = { qRgb(255, 0, 0), qRgb(0, 255, 0), qRgb(0, 0, 255) };
+ QImage image((const uchar *)data, 3, 1, QImage::Format_RGB32);
+
+ QCOMPARE(image.constBits(), (const uchar *)data);
+ image.setDotsPerMeterX(100);
+ QCOMPARE(image.constBits(), (const uchar *)data);
+
+ QImage image2 = image;
+ QCOMPARE(image2.constBits(), (const uchar *)data);
+ image2.setDotsPerMeterX(200);
+ // Pixels and metadata has the same sharing mechanism, so a change of a shared
+ // image metadata forces pixel detach (remove this sub-test if that ever changes).
+ QVERIFY(image2.constBits() != (const uchar *)data);
+ QCOMPARE(image.constBits(), (const uchar *)data);
+}
+
+void tst_QImage::scaleIndexed()
+{
+ QImage image(10, 10, QImage::Format_Indexed8);
+ image.setColor(0, qRgb(0,0,0));
+ image.setColor(1, qRgb(1,1,1));
+ image.fill(1);
+ image.setDevicePixelRatio(2);
+ QImage image2 = image.scaled(20, 20, Qt::KeepAspectRatio, Qt::SmoothTransformation); // do not crash
+}
+
#if defined(Q_OS_WIN)
static inline QColor COLORREFToQColor(COLORREF cr)
@@ -3888,5 +4376,27 @@ void tst_QImage::fromMonoHBITMAP() // QTBUG-72343, corruption for mono bitmaps
#endif // Q_OS_WIN
+void tst_QImage::tofromPremultipliedFormat_data()
+{
+ QTest::addColumn<QImage::Format>("unpremul");
+ QTest::addColumn<QImage::Format>("premul");
+
+ // Test all available formats with both premultiplied and unpremultiplied versions
+ QTest::newRow("argb32") << QImage::Format_ARGB32 << QImage::Format_ARGB32_Premultiplied;
+ QTest::newRow("rgba8888") << QImage::Format_RGBA8888 << QImage::Format_RGBA8888_Premultiplied;
+ QTest::newRow("rgba64") << QImage::Format_RGBA64 << QImage::Format_RGBA64_Premultiplied;
+ QTest::newRow("rgba16fpx4") << QImage::Format_RGBA16FPx4 << QImage::Format_RGBA16FPx4_Premultiplied;
+ QTest::newRow("rgba32fpx4") << QImage::Format_RGBA32FPx4 << QImage::Format_RGBA32FPx4_Premultiplied;
+}
+
+void tst_QImage::tofromPremultipliedFormat()
+{
+ QFETCH(QImage::Format, unpremul);
+ QFETCH(QImage::Format, premul);
+
+ QCOMPARE(qt_toPremultipliedFormat(unpremul), premul);
+ QCOMPARE(qt_toUnpremultipliedFormat(premul), unpremul);
+}
+
QTEST_GUILESS_MAIN(tst_QImage)
#include "tst_qimage.moc"