// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_DARWIN #include #endif #if defined(Q_OS_WIN) # include #endif Q_DECLARE_METATYPE(QImage::Format) Q_DECLARE_METATYPE(Qt::GlobalColor) class tst_QImage : public QObject { Q_OBJECT public: tst_QImage(); private slots: void initTestCase(); void swap(); void create(); void createInvalidXPM(); void createFromUChar(); void formatHandlersInput_data(); void formatHandlersInput(); void setAlphaChannel_data(); void setAlphaChannel(); void convertToFormat_data(); void convertToFormat(); void convertToFormatWithColorTable(); void convertToFormatRgb888ToRGB32(); void createAlphaMask_data(); void createAlphaMask(); #ifndef QT_NO_IMAGE_HEURISTIC_MASK void createHeuristicMask(); #endif void dotsPerMeterZero(); void dotsPerMeterAndDpi(); void convertToFormatPreserveDotsPrMeter(); void convertToFormatPreserveText(); void rotate_data(); void rotate(); void rotateBigImage(); void copy(); void load(); void loadFromData(); #if !defined(QT_NO_DATASTREAM) void loadFromDataStream(); #endif void setPixel_data(); void setPixel(); void setPixelWithAlpha_data(); void setPixelWithAlpha(); void setPixelColorWithAlpha_data(); void setPixelColorWithAlpha(); void defaultColorTable_data(); void defaultColorTable(); void setColorCount(); void setColor(); void rasterClipping(); void pointOverloads(); void destructor(); void cacheKey(); void smoothScale(); void smoothScale2_data(); void smoothScale2(); void smoothScale3_data(); void smoothScale3(); void smoothScale4_data(); void smoothScale4(); void smoothScaleBig(); void smoothScaleAlpha(); void smoothScaleFormats_data(); void smoothScaleFormats(); void transformed_data(); void transformed(); void transformed2(); void scaled(); void paintEngine(); void setAlphaChannelWhilePainting(); void smoothScaledSubImage(); void nullSize_data(); void nullSize(); void premultipliedAlphaConsistency(); void compareIndexed(); void fillColor_data(); void fillColor(); void fillColorWithAlpha_data(); void fillColorWithAlpha(); void fillRGB888(); void fillPixel_data(); void fillPixel(); void rgbSwapped_data(); void rgbSwapped(); void mirrored_data(); void mirrored(); void inplaceRgbSwapped_data(); void inplaceRgbSwapped(); void inplaceMirrored_data(); void inplaceMirrored(); void inplaceMirroredOdd_data(); void inplaceMirroredOdd(); void inplaceRgbMirrored(); void genericRgbConversion_data(); void genericRgbConversion(); void inplaceRgbConversion_data(); void inplaceRgbConversion(); void largeGenericRgbConversion_data(); void largeGenericRgbConversion(); void largeInplaceRgbConversion_data(); void largeInplaceRgbConversion(); void deepCopyWhenPaintingActive(); void scaled_QTBUG19157(); void convertOverUnPreMul(); void scaled_QTBUG35972(); void convertToPixelFormat(); void convertToImageFormat_data(); void convertToImageFormat(); void invertPixelsRGB_data(); void invertPixelsRGB(); void invertPixelsIndexed(); void exifOrientation_data(); void exifOrientation(); void exif_QTBUG45865(); void exifInvalidData_data(); void exifInvalidData(); void exifReadComments(); void cleanupFunctions(); void devicePixelRatio(); void deviceIndependentSize(); void rgb30Unpremul(); void rgb30Repremul_data(); void rgb30Repremul(); void metadataPassthrough(); void pixelColor(); void pixel(); void ditherGradient_data(); void ditherGradient(); void reinterpretAsFormat_data(); void reinterpretAsFormat(); void reinterpretAsFormat2(); void complexTransform8bit(); #ifdef Q_OS_DARWIN void toCGImage_data(); void toCGImage(); #endif void hugeQImage(); void convertColorTable(); void wideImage(); void largeFillScale(); void largeRasterScale(); void metadataChangeWithReadOnlyPixels(); #if defined(Q_OS_WIN) void toWinHBITMAP_data(); void toWinHBITMAP(); void fromMonoHBITMAP(); #endif // Q_OS_WIN private: const QString m_prefix; }; static QLatin1String formatToString(QImage::Format format) { switch (format) { case QImage::Format_Invalid: return QLatin1String("Invalid"); case QImage::Format_Mono: return QLatin1String("Mono"); case QImage::Format_MonoLSB: return QLatin1String("MonoLSB"); case QImage::Format_Indexed8: return QLatin1String("Indexed8"); case QImage::Format_RGB32: return QLatin1String("RGB32"); case QImage::Format_ARGB32: return QLatin1String("ARGB32"); case QImage::Format_ARGB32_Premultiplied: return QLatin1String("ARGB32pm"); case QImage::Format_RGB16: return QLatin1String("RGB16"); case QImage::Format_ARGB8565_Premultiplied: return QLatin1String("ARGB8565pm"); case QImage::Format_RGB666: return QLatin1String("RGB666"); case QImage::Format_ARGB6666_Premultiplied: return QLatin1String("ARGB6666pm"); case QImage::Format_RGB555: return QLatin1String("RGB555"); case QImage::Format_ARGB8555_Premultiplied: return QLatin1String("ARGB8555pm"); case QImage::Format_RGB888: return QLatin1String("RGB888"); case QImage::Format_RGB444: return QLatin1String("RGB444"); case QImage::Format_ARGB4444_Premultiplied: return QLatin1String("ARGB4444pm"); case QImage::Format_RGBX8888: return QLatin1String("RGBx88888"); case QImage::Format_RGBA8888: return QLatin1String("RGBA88888"); case QImage::Format_RGBA8888_Premultiplied: return QLatin1String("RGBA88888pm"); case QImage::Format_BGR30: return QLatin1String("BGR30"); case QImage::Format_A2BGR30_Premultiplied: return QLatin1String("A2BGR30pm"); case QImage::Format_RGB30: return QLatin1String("RGB30"); case QImage::Format_A2RGB30_Premultiplied: return QLatin1String("A2RGB30pm"); case QImage::Format_Alpha8: return QLatin1String("Alpha8"); case QImage::Format_Grayscale8: return QLatin1String("Grayscale8"); case QImage::Format_RGBX64: return QLatin1String("RGBx64"); case QImage::Format_RGBA64: return QLatin1String("RGBA64"); case QImage::Format_RGBA64_Premultiplied: return QLatin1String("RGBA64pm"); case QImage::Format_Grayscale16: return QLatin1String("Grayscale16"); case QImage::Format_BGR888: return QLatin1String("BGR888"); 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"); default: break; }; Q_UNREACHABLE(); qWarning("Unhandled image format"); return QLatin1String("unknown"); } tst_QImage::tst_QImage() : m_prefix(QFINDTESTDATA("images/")) { } void tst_QImage::initTestCase() { QVERIFY(!m_prefix.isEmpty()); } void tst_QImage::swap() { QImage i1( 16, 16, QImage::Format_RGB32 ), i2( 32, 32, QImage::Format_RGB32 ); i1.fill( Qt::white ); i2.fill( Qt::black ); const qint64 i1k = i1.cacheKey(); const qint64 i2k = i2.cacheKey(); i1.swap(i2); QCOMPARE(i1.cacheKey(), i2k); QCOMPARE(i1.size(), QSize(32,32)); QCOMPARE(i2.cacheKey(), i1k); QCOMPARE(i2.size(), QSize(16,16)); } // Test if QImage (or any functions called from QImage) throws an // exception when creating an extremely large image. // QImage::create() should return "false" in this case. void tst_QImage::create() { bool cr = true; QT_TRY { QImage image(700000000, 70000000, QImage::Format_Indexed8); image.setColorCount(256); cr = !image.isNull(); } QT_CATCH (...) { } QVERIFY( !cr ); } void tst_QImage::createInvalidXPM() { QTest::ignoreMessage(QtWarningMsg, "QImage::QImage(), XPM is not supported"); const char *xpm[] = {""}; QImage invalidXPM(xpm); QVERIFY(invalidXPM.isNull()); } void tst_QImage::createFromUChar() { uint data[] = { 0xff010101U, 0xff020202U, 0xff030303U, 0xff040404U }; // When the data is const, nothing you do to the image will change the source data. QImage i1((const uchar*)data, 2, 2, 8, QImage::Format_RGB32); QCOMPARE(i1.pixel(0,0), 0xFF010101U); QCOMPARE(i1.pixel(1,0), 0xFF020202U); QCOMPARE(i1.pixel(0,1), 0xFF030303U); QCOMPARE(i1.pixel(1,1), 0xFF040404U); { QImage i(i1); i.setPixel(0,0,5); } QCOMPARE(i1.pixel(0,0), 0xFF010101U); QCOMPARE(*(QRgb*)data, 0xFF010101U); *((QRgb*)i1.bits()) = 0xFF070707U; QCOMPARE(i1.pixel(0,0), 0xFF070707U); QCOMPARE(*(QRgb*)data, 0xFF010101U); // Changing copies should not change the original image or data. { QImage i(i1); i.setPixel(0,0,5); QCOMPARE(*(QRgb*)data, 0xFF010101U); i1.setPixel(0,0,9); QCOMPARE(i1.pixel(0,0), 0xFF000009U); QCOMPARE(i.pixel(0,0), 0xFF000005U); } QCOMPARE(i1.pixel(0,0), 0xFF000009U); // When the data is non-const, nothing you do to copies of the image will change the source data, // but changing the image (here via bits()) will change the source data. QImage i2((uchar*)data, 2, 2, 8, QImage::Format_RGB32); QCOMPARE(i2.pixel(0,0), 0xFF010101U); QCOMPARE(i2.pixel(1,0), 0xFF020202U); QCOMPARE(i2.pixel(0,1), 0xFF030303U); QCOMPARE(i2.pixel(1,1), 0xFF040404U); { QImage i(i2); i.setPixel(0,0,5); } QCOMPARE(i2.pixel(0,0), 0xFF010101U); QCOMPARE(*(QRgb*)data, 0xFF010101U); *((QRgb*)i2.bits()) = 0xFF070707U; QCOMPARE(i2.pixel(0,0), 0xFF070707U); QCOMPARE(*(QRgb*)data, 0xFF070707U); // Changing the data will change the image in either case. QImage i3((uchar*)data, 2, 2, 8, QImage::Format_RGB32); QImage i4((const uchar*)data, 2, 2, 8, QImage::Format_RGB32); *(QRgb*)data = 0xFF060606U; QCOMPARE(i3.pixel(0,0), 0xFF060606U); QCOMPARE(i4.pixel(0,0), 0xFF060606U); } void tst_QImage::formatHandlersInput_data() { QTest::addColumn("testFormat"); QTest::addColumn("testFile"); // add a new line here when a file is added QTest::newRow("ICO") << "ICO" << m_prefix + "image.ico"; QTest::newRow("PNG") << "PNG" << m_prefix + "image.png"; QTest::newRow("GIF") << "GIF" << m_prefix + "image.gif"; QTest::newRow("BMP") << "BMP" << m_prefix + "image.bmp"; QTest::newRow("JPEG") << "JPEG" << m_prefix + "image.jpg"; QTest::newRow("PBM") << "PBM" << m_prefix + "image.pbm"; QTest::newRow("PGM") << "PGM" << m_prefix + "image.pgm"; QTest::newRow("PPM") << "PPM" << m_prefix + "image.ppm"; QTest::newRow("XBM") << "XBM" << m_prefix + "image.xbm"; QTest::newRow("XPM") << "XPM" << m_prefix + "image.xpm"; } void tst_QImage::formatHandlersInput() { QFETCH(QString, testFormat); QFETCH(QString, testFile); QList formats = QImageReader::supportedImageFormats(); // qDebug("Image input formats : %s", formats.join(" | ").latin1()); bool formatSupported = false; for (QList::Iterator it = formats.begin(); it != formats.end(); ++it) { if (*it == testFormat.toLower().toUtf8()) { formatSupported = true; break; } } if (formatSupported) { // qDebug(QImage::imageFormat(testFile)); QCOMPARE(testFormat.toLatin1().toLower(), QImageReader::imageFormat(testFile)); } else { QString msg = "Format not supported : "; QSKIP(QString(msg + testFormat).toLatin1()); } } void tst_QImage::setAlphaChannel_data() { QTest::addColumn("red"); QTest::addColumn("green"); QTest::addColumn("blue"); QTest::addColumn("alpha"); QTest::addColumn("gray"); QTest::newRow("red at 0%, gray") << 255 << 0 << 0 << 0 << true; QTest::newRow("red at 25%, gray") << 255 << 0 << 0 << 63 << true; QTest::newRow("red at 50%, gray") << 255 << 0 << 0 << 127 << true; QTest::newRow("red at 100%, gray") << 255 << 0 << 0 << 191 << true; QTest::newRow("red at 0%, 32bit") << 255 << 0 << 0 << 0 << false; QTest::newRow("red at 25%, 32bit") << 255 << 0 << 0 << 63 << false; QTest::newRow("red at 50%, 32bit") << 255 << 0 << 0 << 127 << false; QTest::newRow("red at 100%, 32bit") << 255 << 0 << 0 << 191 << false; QTest::newRow("green at 0%, gray") << 0 << 255 << 0 << 0 << true; QTest::newRow("green at 25%, gray") << 0 << 255 << 0 << 63 << true; QTest::newRow("green at 50%, gray") << 0 << 255 << 0 << 127 << true; QTest::newRow("green at 100%, gray") << 0 << 255 << 0 << 191 << true; QTest::newRow("green at 0%, 32bit") << 0 << 255 << 0 << 0 << false; QTest::newRow("green at 25%, 32bit") << 0 << 255 << 0 << 63 << false; QTest::newRow("green at 50%, 32bit") << 0 << 255 << 0 << 127 << false; QTest::newRow("green at 100%, 32bit") << 0 << 255 << 0 << 191 << false; QTest::newRow("blue at 0%, gray") << 0 << 0 << 255 << 0 << true; QTest::newRow("blue at 25%, gray") << 0 << 0 << 255 << 63 << true; QTest::newRow("blue at 50%, gray") << 0 << 0 << 255 << 127 << true; QTest::newRow("blue at 100%, gray") << 0 << 0 << 255 << 191 << true; QTest::newRow("blue at 0%, 32bit") << 0 << 0 << 255 << 0 << false; QTest::newRow("blue at 25%, 32bit") << 0 << 0 << 255 << 63 << false; QTest::newRow("blue at 50%, 32bit") << 0 << 0 << 255 << 127 << false; QTest::newRow("blue at 100%, 32bit") << 0 << 0 << 255 << 191 << false; } void tst_QImage::setAlphaChannel() { QFETCH(int, red); QFETCH(int, green); QFETCH(int, blue); QFETCH(int, alpha); QFETCH(bool, gray); int width = 100; int height = 100; QImage image(width, height, QImage::Format_RGB32); image.fill(qRgb(red, green, blue)); // Create the alpha channel QImage alphaChannel; if (gray) { alphaChannel = QImage(width, height, QImage::Format_Indexed8); alphaChannel.setColorCount(256); for (int i=0; i<256; ++i) alphaChannel.setColor(i, qRgb(i, i, i)); alphaChannel.fill(alpha); } else { alphaChannel = QImage(width, height, QImage::Format_ARGB32); alphaChannel.fill(qRgb(alpha, alpha, alpha)); } image.setAlphaChannel(alphaChannel); image = image.convertToFormat(QImage::Format_ARGB32); QCOMPARE(image.format(), QImage::Format_ARGB32); // alpha of 0 becomes black at a=0 due to premultiplication QRgb pixel = alpha == 0 ? 0 : qRgba(red, green, blue, alpha); bool allPixelsOK = true; for (int y=0; y("inFormat"); QTest::addColumn("inPixel"); QTest::addColumn("resFormat"); QTest::addColumn("resPixel"); QTest::newRow("red rgb32 -> argb32") << int(QImage::Format_RGB32) << 0xffff0000 << int(QImage::Format_ARGB32) << 0xffff0000; QTest::newRow("green rgb32 -> argb32") << int(QImage::Format_RGB32) << 0xff00ff00 << int(QImage::Format_ARGB32) << 0xff00ff00; QTest::newRow("blue rgb32 -> argb32") << int(QImage::Format_RGB32) << 0xff0000ff << int(QImage::Format_ARGB32) << 0xff0000ff; QTest::newRow("red rgb32 -> rgb16") << int(QImage::Format_RGB32) << 0xffff0000 << int(QImage::Format_RGB16) << 0xffff0000; QTest::newRow("green rgb32 -> rgb16") << int(QImage::Format_RGB32) << 0xff00ff00 << int(QImage::Format_RGB16) << 0xff00ff00; QTest::newRow("blue rgb32 -> rgb16") << int(QImage::Format_RGB32) << 0xff0000ff << int(QImage::Format_RGB16) << 0xff0000ff; QTest::newRow("funky rgb32 -> rgb16") << int(QImage::Format_RGB32) << 0xfff0c080 << int(QImage::Format_RGB16) << 0xfff7c384; QTest::newRow("red rgb32 -> argb32_pm") << int(QImage::Format_RGB32) << 0xffff0000 << int(QImage::Format_ARGB32_Premultiplied) << 0xffff0000; QTest::newRow("green rgb32 -> argb32_pm") << int(QImage::Format_RGB32) << 0xff00ff00 << int(QImage::Format_ARGB32_Premultiplied) <<0xff00ff00; QTest::newRow("blue rgb32 -> argb32_pm") << int(QImage::Format_RGB32) << 0xff0000ff << int(QImage::Format_ARGB32_Premultiplied) << 0xff0000ff; QTest::newRow("semired argb32 -> pm") << int(QImage::Format_ARGB32) << 0x7fff0000u << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f0000u; QTest::newRow("semigreen argb32 -> pm") << int(QImage::Format_ARGB32) << 0x7f00ff00u << int(QImage::Format_ARGB32_Premultiplied) << 0x7f007f00u; QTest::newRow("semiblue argb32 -> pm") << int(QImage::Format_ARGB32) << 0x7f0000ffu << int(QImage::Format_ARGB32_Premultiplied) << 0x7f00007fu; QTest::newRow("semiwhite argb32 -> pm") << int(QImage::Format_ARGB32) << 0x7fffffffu << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f7f7fu; QTest::newRow("semiblack argb32 -> pm") << int(QImage::Format_ARGB32) << 0x7f000000u << int(QImage::Format_ARGB32_Premultiplied) << 0x7f000000u; QTest::newRow("semired pm -> argb32") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f0000u << int(QImage::Format_ARGB32) << 0x7fff0000u; QTest::newRow("semigreen pm -> argb32") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f007f00u << int(QImage::Format_ARGB32) << 0x7f00ff00u; QTest::newRow("semiblue pm -> argb32") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f00007fu << int(QImage::Format_ARGB32) << 0x7f0000ffu; QTest::newRow("semiwhite pm -> argb32") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f7f7fu << int(QImage::Format_ARGB32) << 0x7fffffffu; QTest::newRow("semiblack pm -> argb32") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f000000u << int(QImage::Format_ARGB32) << 0x7f000000u; QTest::newRow("semired pm -> rgb32") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f0000u << int(QImage::Format_RGB32) << 0xffff0000u; QTest::newRow("semigreen pm -> rgb32") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f007f00u << int(QImage::Format_RGB32) << 0xff00ff00u; QTest::newRow("semiblue pm -> rgb32") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f00007fu << int(QImage::Format_RGB32) << 0xff0000ffu; QTest::newRow("semiwhite pm -> rgb32") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f7f7fu << int(QImage::Format_RGB32) << 0xffffffffu; QTest::newRow("semiblack pm -> rgb32") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f000000u << int(QImage::Format_RGB32) << 0xff000000u; QTest::newRow("semired argb32 -> rgb32") << int(QImage::Format_ARGB32) << 0x7fff0000u << int(QImage::Format_RGB32) << 0xffff0000u; QTest::newRow("semigreen argb32 -> rgb32") << int(QImage::Format_ARGB32) << 0x7f00ff00u << int(QImage::Format_RGB32) << 0xff00ff00u; QTest::newRow("semiblue argb32 -> rgb32") << int(QImage::Format_ARGB32) << 0x7f0000ffu << int(QImage::Format_RGB32) << 0xff0000ffu; QTest::newRow("semiwhite argb -> rgb32") << int(QImage::Format_ARGB32) << 0x7fffffffu << int(QImage::Format_RGB32) << 0xffffffffu; QTest::newRow("semiblack argb -> rgb32") << int(QImage::Format_ARGB32) << 0x7f000000u << int(QImage::Format_RGB32) << 0xff000000u; QTest::newRow("black mono -> rgb32") << int(QImage::Format_Mono) << 0x00000000u << int(QImage::Format_RGB32) << 0xff000000u; QTest::newRow("white mono -> rgb32") << int(QImage::Format_Mono) << 0x00000001u << int(QImage::Format_RGB32) << 0xffffffffu; QTest::newRow("red rgb16 -> argb32") << int(QImage::Format_RGB16) << 0xffff0000 << int(QImage::Format_ARGB32) << 0xffff0000; QTest::newRow("green rgb16 -> argb32") << int(QImage::Format_RGB16) << 0xff00ff00 << int(QImage::Format_ARGB32) << 0xff00ff00; QTest::newRow("blue rgb16 -> argb32") << int(QImage::Format_RGB16) << 0xff0000ff << int(QImage::Format_ARGB32) << 0xff0000ff; QTest::newRow("red rgb16 -> rgb16") << int(QImage::Format_RGB32) << 0xffff0000 << int(QImage::Format_RGB16) << 0xffff0000; QTest::newRow("green rgb16 -> rgb16") << int(QImage::Format_RGB32) << 0xff00ff00 << int(QImage::Format_RGB16) << 0xff00ff00; QTest::newRow("blue rgb16 -> rgb16") << int(QImage::Format_RGB32) << 0xff0000ff << int(QImage::Format_RGB16) << 0xff0000ff; QTest::newRow("semired argb32 -> rgb16") << int(QImage::Format_ARGB32) << 0x7fff0000u << int(QImage::Format_RGB16) << 0xffff0000; QTest::newRow("semigreen argb32 -> rgb16") << int(QImage::Format_ARGB32) << 0x7f00ff00u << int(QImage::Format_RGB16) << 0xff00ff00; QTest::newRow("semiblue argb32 -> rgb16") << int(QImage::Format_ARGB32) << 0x7f0000ffu << int(QImage::Format_RGB16) << 0xff0000ff; QTest::newRow("semired pm -> rgb16") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f0000u << int(QImage::Format_RGB16) << 0xffff0000u; QTest::newRow("semigreen pm -> rgb16") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f007f00u << int(QImage::Format_RGB16) << 0xff00ff00u; QTest::newRow("semiblue pm -> rgb16") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f00007fu << int(QImage::Format_RGB16) << 0xff0000ffu; QTest::newRow("semiwhite pm -> rgb16") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f7f7fu << int(QImage::Format_RGB16) << 0xffffffffu; QTest::newRow("semiblack pm -> rgb16") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f000000u << int(QImage::Format_RGB16) << 0xff000000u; QTest::newRow("mono -> mono lsb") << int(QImage::Format_Mono) << 1u << int(QImage::Format_MonoLSB) << 0xffffffffu; QTest::newRow("mono lsb -> mono") << int(QImage::Format_MonoLSB) << 1u << int(QImage::Format_Mono) << 0xffffffffu; QTest::newRow("red rgb32 -> rgb666") << int(QImage::Format_RGB32) << 0xffff0000 << int(QImage::Format_RGB666) << 0xffff0000; QTest::newRow("green rgb32 -> rgb666") << int(QImage::Format_RGB32) << 0xff00ff00 << int(QImage::Format_RGB666) << 0xff00ff00; QTest::newRow("blue rgb32 -> rgb666") << int(QImage::Format_RGB32) << 0xff0000ff << int(QImage::Format_RGB666) << 0xff0000ff; QTest::newRow("red rgb16 -> rgb666") << int(QImage::Format_RGB16) << 0xffff0000 << int(QImage::Format_RGB666) << 0xffff0000; QTest::newRow("green rgb16 -> rgb666") << int(QImage::Format_RGB16) << 0xff00ff00 << int(QImage::Format_RGB666) << 0xff00ff00; QTest::newRow("blue rgb16 -> rgb666") << int(QImage::Format_RGB16) << 0xff0000ff << int(QImage::Format_RGB666) << 0xff0000ff; QTest::newRow("red rgb32 -> rgb15") << int(QImage::Format_RGB32) << 0xffff0000 << int(QImage::Format_RGB555) << 0xffff0000; QTest::newRow("green rgb32 -> rgb15") << int(QImage::Format_RGB32) << 0xff00ff00 << int(QImage::Format_RGB555) << 0xff00ff00; QTest::newRow("blue rgb32 -> rgb15") << int(QImage::Format_RGB32) << 0xff0000ff << int(QImage::Format_RGB555) << 0xff0000ff; QTest::newRow("funky rgb32 -> rgb15") << int(QImage::Format_RGB32) << 0xfff0c080 << int(QImage::Format_RGB555) << 0xfff7c684; QTest::newRow("red rgb16 -> rgb15") << int(QImage::Format_RGB16) << 0xffff0000 << int(QImage::Format_RGB555) << 0xffff0000; QTest::newRow("green rgb16 -> rgb15") << int(QImage::Format_RGB16) << 0xff00ff00 << int(QImage::Format_RGB555) << 0xff00ff00; QTest::newRow("blue rgb16 -> rgb15") << int(QImage::Format_RGB16) << 0xff0000ff << int(QImage::Format_RGB555) << 0xff0000ff; QTest::newRow("funky rgb16 -> rgb15") << int(QImage::Format_RGB16) << 0xfff0c080 << int(QImage::Format_RGB555) << 0xfff7c684; QTest::newRow("red rgb32 -> argb8565") << int(QImage::Format_RGB32) << 0xffff0000 << int(QImage::Format_ARGB8565_Premultiplied) << 0xffff0000; QTest::newRow("green rgb32 -> argb8565") << int(QImage::Format_RGB32) << 0xff00ff00 << int(QImage::Format_ARGB8565_Premultiplied) << 0xff00ff00; QTest::newRow("blue rgb32 -> argb8565") << int(QImage::Format_RGB32) << 0xff0000ff << int(QImage::Format_ARGB8565_Premultiplied) << 0xff0000ff; QTest::newRow("red rgb16 -> argb8565") << int(QImage::Format_RGB16) << 0xffff0000 << int(QImage::Format_ARGB8565_Premultiplied) << 0xffff0000; QTest::newRow("green rgb16 -> argb8565") << int(QImage::Format_RGB16) << 0xff00ff00 << int(QImage::Format_ARGB8565_Premultiplied) << 0xff00ff00; QTest::newRow("blue rgb16 -> argb8565") << int(QImage::Format_RGB16) << 0xff0000ff << int(QImage::Format_ARGB8565_Premultiplied) << 0xff0000ff; QTest::newRow("red argb8565 -> argb32") << int(QImage::Format_ARGB8565_Premultiplied) << 0xffff0000 << int(QImage::Format_ARGB32) << 0xffff0000; QTest::newRow("green argb8565 -> argb32") << int(QImage::Format_ARGB8565_Premultiplied) << 0xff00ff00 << int(QImage::Format_ARGB32) << 0xff00ff00; QTest::newRow("blue argb8565 -> argb32") << int(QImage::Format_ARGB8565_Premultiplied) << 0xff0000ff << int(QImage::Format_ARGB32) << 0xff0000ff; QTest::newRow("semired argb32 -> argb8565") << int(QImage::Format_ARGB32) << 0x7fff0000u << int(QImage::Format_ARGB8565_Premultiplied) << 0x7f7b0000u; QTest::newRow("semigreen argb32 -> argb8565") << int(QImage::Format_ARGB32) << 0x7f00ff00u << int(QImage::Format_ARGB8565_Premultiplied) << 0x7f007d00u; QTest::newRow("semiblue argb32 -> argb8565") << int(QImage::Format_ARGB32) << 0x7f0000ffu << int(QImage::Format_ARGB8565_Premultiplied) << 0x7f00007bu; QTest::newRow("semired pm -> argb8565") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f0000u << int(QImage::Format_ARGB8565_Premultiplied) << 0x7f7b0000u; QTest::newRow("semigreen pm -> argb8565") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f007f00u << int(QImage::Format_ARGB8565_Premultiplied) << 0x7f007d00u; QTest::newRow("semiblue pm -> argb8565") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f00007fu << int(QImage::Format_ARGB8565_Premultiplied) << 0x7f00007bu; QTest::newRow("semiwhite pm -> argb8565") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f7f7fu << int(QImage::Format_ARGB8565_Premultiplied) << 0x7f7b7d7bu; QTest::newRow("semiblack pm -> argb8565") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f000000u << int(QImage::Format_ARGB8565_Premultiplied) << 0x7f000000u; QTest::newRow("red rgb666 -> argb32") << int(QImage::Format_RGB666) << 0xffff0000 << int(QImage::Format_ARGB32) << 0xffff0000; QTest::newRow("green rgb666 -> argb32") << int(QImage::Format_RGB666) << 0xff00ff00 << int(QImage::Format_ARGB32) << 0xff00ff00; QTest::newRow("blue rgb666 -> argb32") << int(QImage::Format_RGB666) << 0xff0000ff << int(QImage::Format_ARGB32) << 0xff0000ff; QTest::newRow("semired argb32 -> rgb666") << int(QImage::Format_ARGB32) << 0x7fff0000u << int(QImage::Format_RGB666) << 0xffff0000; QTest::newRow("semigreen argb32 -> rgb666") << int(QImage::Format_ARGB32) << 0x7f00ff00u << int(QImage::Format_RGB666) << 0xff00ff00; QTest::newRow("semiblue argb32 -> rgb666") << int(QImage::Format_ARGB32) << 0x7f0000ffu << int(QImage::Format_RGB666) << 0xff0000ff; QTest::newRow("semired pm -> rgb666") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f0000u << int(QImage::Format_RGB666) << 0xffff0000u; QTest::newRow("semigreen pm -> rgb666") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f007f00u << int(QImage::Format_RGB666) << 0xff00ff00u; QTest::newRow("semiblue pm -> rgb666") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f00007fu << int(QImage::Format_RGB666) << 0xff0000ffu; QTest::newRow("semiwhite pm -> rgb666") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f7f7fu << int(QImage::Format_RGB666) << 0xffffffffu; QTest::newRow("semiblack pm -> rgb666") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f000000u << int(QImage::Format_RGB666) << 0xff000000u; QTest::newRow("red rgb15 -> argb32") << int(QImage::Format_RGB555) << 0xffff0000 << int(QImage::Format_ARGB32) << 0xffff0000; QTest::newRow("green rgb15 -> argb32") << int(QImage::Format_RGB555) << 0xff00ff00 << int(QImage::Format_ARGB32) << 0xff00ff00; QTest::newRow("blue rgb15 -> argb32") << int(QImage::Format_RGB555) << 0xff0000ff << int(QImage::Format_ARGB32) << 0xff0000ff; QTest::newRow("semired argb32 -> rgb15") << int(QImage::Format_ARGB32) << 0x7fff0000u << int(QImage::Format_RGB555) << 0xffff0000; QTest::newRow("semigreen argb32 -> rgb15") << int(QImage::Format_ARGB32) << 0x7f00ff00u << int(QImage::Format_RGB555) << 0xff00ff00; QTest::newRow("semiblue argb32 -> rgb15") << int(QImage::Format_ARGB32) << 0x7f0000ffu << int(QImage::Format_RGB555) << 0xff0000ff; QTest::newRow("semired pm -> rgb15") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f0000u << int(QImage::Format_RGB555) << 0xffff0000u; QTest::newRow("semigreen pm -> rgb15") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f007f00u << int(QImage::Format_RGB555) << 0xff00ff00u; QTest::newRow("semiblue pm -> rgb15") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f00007fu << int(QImage::Format_RGB555) << 0xff0000ffu; QTest::newRow("semiwhite pm -> rgb15") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f7f7fu << int(QImage::Format_RGB555) << 0xffffffffu; QTest::newRow("semiblack pm -> rgb15") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f000000u << int(QImage::Format_RGB555) << 0xff000000u; QTest::newRow("red rgb32 -> argb8555") << int(QImage::Format_RGB32) << 0xffff0000 << int(QImage::Format_ARGB8555_Premultiplied) << 0xffff0000; QTest::newRow("green rgb32 -> argb8555") << int(QImage::Format_RGB32) << 0xff00ff00 << int(QImage::Format_ARGB8555_Premultiplied) << 0xff00ff00; QTest::newRow("blue rgb32 -> argb8555") << int(QImage::Format_RGB32) << 0xff0000ff << int(QImage::Format_ARGB8555_Premultiplied) << 0xff0000ff; QTest::newRow("red rgb16 -> argb8555") << int(QImage::Format_RGB16) << 0xffff0000 << int(QImage::Format_ARGB8555_Premultiplied) << 0xffff0000; QTest::newRow("green rgb16 -> argb8555") << int(QImage::Format_RGB16) << 0xff00ff00 << int(QImage::Format_ARGB8555_Premultiplied) << 0xff00ff00; QTest::newRow("blue rgb16 -> argb8555") << int(QImage::Format_RGB16) << 0xff0000ff << int(QImage::Format_ARGB8555_Premultiplied) << 0xff0000ff; QTest::newRow("red argb8555 -> argb32") << int(QImage::Format_ARGB8555_Premultiplied) << 0xffff0000 << int(QImage::Format_ARGB32) << 0xffff0000; QTest::newRow("green argb8555 -> argb32") << int(QImage::Format_ARGB8555_Premultiplied) << 0xff00ff00 << int(QImage::Format_ARGB32) << 0xff00ff00; QTest::newRow("blue argb8555 -> argb32") << int(QImage::Format_ARGB8555_Premultiplied) << 0xff0000ff << int(QImage::Format_ARGB32) << 0xff0000ff; QTest::newRow("semired argb32 -> argb8555") << int(QImage::Format_ARGB32) << 0x7fff0000u << int(QImage::Format_ARGB8555_Premultiplied) << 0x7f7b0000u; QTest::newRow("semigreen argb32 -> argb8555") << int(QImage::Format_ARGB32) << 0x7f00ff00u << int(QImage::Format_ARGB8555_Premultiplied) << 0x7f007b00u; QTest::newRow("semiblue argb32 -> argb8555") << int(QImage::Format_ARGB32) << 0x7f0000ffu << int(QImage::Format_ARGB8555_Premultiplied) << 0x7f00007bu; QTest::newRow("semired pm -> argb8555") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f0000u << int(QImage::Format_ARGB8555_Premultiplied) << 0x7f7b0000u; QTest::newRow("semigreen pm -> argb8555") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f007f00u << int(QImage::Format_ARGB8555_Premultiplied) << 0x7f007b00u; QTest::newRow("semiblue pm -> argb8555") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f00007fu << int(QImage::Format_ARGB8555_Premultiplied) << 0x7f00007bu; QTest::newRow("semiwhite pm -> argb8555") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f7f7fu << int(QImage::Format_ARGB8555_Premultiplied) << 0x7f7b7b7bu; QTest::newRow("semiblack pm -> argb8555") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f000000u << int(QImage::Format_ARGB8555_Premultiplied) << 0x7f000000u; QTest::newRow("red rgb32 -> rgb888") << int(QImage::Format_RGB32) << 0xffff0000 << int(QImage::Format_RGB888) << 0xffff0000; QTest::newRow("green rgb32 -> rgb888") << int(QImage::Format_RGB32) << 0xff00ff00 << int(QImage::Format_RGB888) << 0xff00ff00; QTest::newRow("blue rgb32 -> rgb888") << int(QImage::Format_RGB32) << 0xff0000ff << int(QImage::Format_RGB888) << 0xff0000ff; QTest::newRow("red rgb32 -> bgr888") << int(QImage::Format_RGB32) << 0xffff0000 << int(QImage::Format_BGR888) << 0xffff0000; QTest::newRow("green rgb32 -> bgr888") << int(QImage::Format_RGB32) << 0xff00ff00 << int(QImage::Format_BGR888) << 0xff00ff00; QTest::newRow("blue rgb32 -> bgr888") << int(QImage::Format_RGB32) << 0xff0000ff << int(QImage::Format_BGR888) << 0xff0000ff; QTest::newRow("red rgb16 -> rgb888") << int(QImage::Format_RGB16) << 0xffff0000 << int(QImage::Format_RGB888) << 0xffff0000; QTest::newRow("green rgb16 -> rgb888") << int(QImage::Format_RGB16) << 0xff00ff00 << int(QImage::Format_RGB888) << 0xff00ff00; QTest::newRow("blue rgb16 -> rgb888") << int(QImage::Format_RGB16) << 0xff0000ff << int(QImage::Format_RGB888) << 0xff0000ff; QTest::newRow("red rgb888 -> argb32") << int(QImage::Format_RGB888) << 0xffff0000 << int(QImage::Format_ARGB32) << 0xffff0000; QTest::newRow("green rgb888 -> argb32") << int(QImage::Format_RGB888) << 0xff00ff00 << int(QImage::Format_ARGB32) << 0xff00ff00; QTest::newRow("blue rgb888 -> argb32") << int(QImage::Format_RGB888) << 0xff0000ff << int(QImage::Format_ARGB32) << 0xff0000ff; QTest::newRow("red bgr888 -> argb32") << int(QImage::Format_RGB888) << 0xffff0000 << int(QImage::Format_ARGB32) << 0xffff0000; QTest::newRow("green bgr888 -> argb32") << int(QImage::Format_RGB888) << 0xff00ff00 << int(QImage::Format_ARGB32) << 0xff00ff00; QTest::newRow("blue bgr888 -> argb32") << int(QImage::Format_RGB888) << 0xff0000ff << int(QImage::Format_ARGB32) << 0xff0000ff; QTest::newRow("red rgb888 -> rgbx8888") << int(QImage::Format_RGB888) << 0xffff0000 << int(QImage::Format_RGBX8888) << 0xffff0000; QTest::newRow("green rgb888 -> rgbx8888") << int(QImage::Format_RGB888) << 0xff00ff00 << int(QImage::Format_RGBX8888) << 0xff00ff00; QTest::newRow("blue rgb888 -> rgbx8888") << int(QImage::Format_RGB888) << 0xff0000ff << int(QImage::Format_RGBX8888) << 0xff0000ff; QTest::newRow("semired argb32 -> rgb888") << int(QImage::Format_ARGB32) << 0x7fff0000u << int(QImage::Format_RGB888) << 0xffff0000; QTest::newRow("semigreen argb32 -> rgb888") << int(QImage::Format_ARGB32) << 0x7f00ff00u << int(QImage::Format_RGB888) << 0xff00ff00; QTest::newRow("semiblue argb32 -> rgb888") << int(QImage::Format_ARGB32) << 0x7f0000ffu << int(QImage::Format_RGB888) << 0xff0000ff; QTest::newRow("semired pm -> rgb888") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f0000u << int(QImage::Format_RGB888) << 0xffff0000u; QTest::newRow("semigreen pm -> rgb888") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f007f00u << int(QImage::Format_RGB888) << 0xff00ff00u; QTest::newRow("semiblue pm -> rgb888") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f00007fu << int(QImage::Format_RGB888) << 0xff0000ffu; QTest::newRow("semiwhite pm -> rgb888") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f7f7fu << int(QImage::Format_RGB888) << 0xffffffffu; QTest::newRow("semiblack pm -> rgb888") << int(QImage::Format_ARGB32_Premultiplied) << 0x7f000000u << int(QImage::Format_RGB888) << 0xff000000u; QTest::newRow("red rgba8888 -> argb32") << int(QImage::Format_RGBA8888) << 0xffff0000 << int(QImage::Format_ARGB32) << 0xffff0000; QTest::newRow("green rgba8888 -> argb32") << int(QImage::Format_RGBA8888) << 0xff00ff00 << int(QImage::Format_ARGB32) << 0xff00ff00; QTest::newRow("blue rgba8888 -> argb32") << int(QImage::Format_RGBA8888) << 0xff0000ff << int(QImage::Format_ARGB32) << 0xff0000ff; QTest::newRow("semired rgba8888 -> argb pm") << int(QImage::Format_RGBA8888) << 0x7fff0000u << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f0000u; QTest::newRow("semigreen rgba8888 -> argb pm") << int(QImage::Format_RGBA8888) << 0x7f00ff00u << int(QImage::Format_ARGB32_Premultiplied) << 0x7f007f00u; QTest::newRow("semiblue rgba8888 -> argb pm") << int(QImage::Format_RGBA8888) << 0x7f0000ffu << int(QImage::Format_ARGB32_Premultiplied) << 0x7f00007fu; QTest::newRow("semiwhite rgba8888 -> argb pm") << int(QImage::Format_RGBA8888) << 0x7fffffffu << int(QImage::Format_ARGB32_Premultiplied) << 0x7f7f7f7fu; QTest::newRow("semiblack rgba8888 -> argb pm") << int(QImage::Format_RGBA8888) << 0x7f000000u << int(QImage::Format_ARGB32_Premultiplied) << 0x7f000000u; QTest::newRow("red rgb30 -> argb32") << int(QImage::Format_RGB30) << 0xffff0000 << int(QImage::Format_ARGB32) << 0xffff0000; QTest::newRow("green rgb30 -> argb32") << int(QImage::Format_RGB30) << 0xff00ff00 << int(QImage::Format_ARGB32) << 0xff00ff00; QTest::newRow("blue rgb30 -> argb32") << int(QImage::Format_RGB30) << 0xff0000ff << int(QImage::Format_ARGB32) << 0xff0000ff; QTest::newRow("semigray argb32 -> a2rgb30 pm") << int(QImage::Format_ARGB32) << 0x4c646565u << int(QImage::Format_A2RGB30_Premultiplied) << 0x55212222u; QTest::newRow("white gray8 -> argb pm") << int(QImage::Format_Grayscale8) << 0xfffffeffu << int(QImage::Format_ARGB32_Premultiplied) << 0xfffefefeu; QTest::newRow("gray gray8 -> argb pm") << int(QImage::Format_Grayscale8) << 0xff565557u << int(QImage::Format_ARGB32_Premultiplied) << 0xff555555u; QTest::newRow("black gray8 -> argb pm") << int(QImage::Format_Grayscale8) << 0xff000100u << int(QImage::Format_ARGB32_Premultiplied) << 0xff000000u; } void tst_QImage::convertToFormat() { QFETCH(int, inFormat); QFETCH(uint, inPixel); QFETCH(int, resFormat); QFETCH(uint, resPixel); QImage src(32, 32, QImage::Format(inFormat)); if (inFormat == QImage::Format_Mono) { src.setColor(0, qRgba(0,0,0,0xff)); src.setColor(1, qRgba(255,255,255,0xff)); } for (int y=0; y colors(2); colors[0] = 0xFF000000; colors[1] = 0xFFFFFFFF; for (int format = QImage::Format_RGB32; format < QImage::Format_Alpha8; ++format) { QImage fromImage(10, 10, (QImage::Format)format); QImage bitmap = fromImage.convertToFormat(QImage::Format_Mono, colors); QVERIFY(!bitmap.isNull()); } } void tst_QImage::convertToFormatRgb888ToRGB32() { // 545 so width % 4 != 0. This ensure there is padding at the end of the scanlines const int height = 545; const int width = 545; QImage source(width, height, QImage::Format_RGB888); for (int y = 0; y < height; ++y) { uchar *srcPixels = source.scanLine(y); for (int x = 0; x < width * 3; ++x) srcPixels[x] = x; } QImage rgb32Image = source.convertToFormat(QImage::Format_RGB888); QCOMPARE(rgb32Image.format(), QImage::Format_RGB888); for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) QCOMPARE(rgb32Image.pixel(x, y), source.pixel(x, y)); } } void tst_QImage::createAlphaMask_data() { QTest::addColumn("x"); QTest::addColumn("y"); QTest::addColumn("alpha1"); QTest::addColumn("alpha2"); int alphas[] = { 0, 127, 255 }; for (unsigned a1 = 0; a1 < sizeof(alphas) / sizeof(int); ++a1) { const QByteArray a1B = QByteArray::number(alphas[a1]); for (unsigned a2 = 0; a2 < sizeof(alphas) / sizeof(int); ++a2) { if (a1 == a2) continue; const QByteArray a2B = QByteArray::number(alphas[a2]); for (int x=10; x<18; x+=3) { const QByteArray xB = QByteArray::number(x); for (int y=100; y<108; y+=3) { const QByteArray testName = "x=" + xB + ", y=" + QByteArray::number(y) + ", a1=" + a1B + ", a2=" + a2B; QTest::newRow(testName.constData()) << x << y << alphas[a1] << alphas[a2]; } } } } } void tst_QImage::createAlphaMask() { QFETCH(int, x); QFETCH(int, y); QFETCH(int, alpha1); QFETCH(int, alpha2); QSize size(255, 255); int pixelsInLines = size.width() + size.height() - 1; int pixelsOutofLines = size.width() * size.height() - pixelsInLines; // Generate an white image with two lines, horizontal at y and vertical at x. // Lines have alpha of alpha2, rest has alpha of alpha1 QImage image(size, QImage::Format_ARGB32); for (int cy=0; cy("format"); QTest::addColumn("degrees"); constexpr int degrees[] = {0, 90, 180, 270}; 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(i); QTest::newRow(qPrintable(dB + " " + formatToString(format))) << format << d; } } } void tst_QImage::rotate() { QFETCH(QImage::Format, format); QFETCH(int, degrees); // test if rotate90 is lossless int w = 54; int h = 13; QImage original(w, h, format); original.fill(qRgb(255,255,255)); if (format == QImage::Format_Indexed8) { original.setColorCount(256); for (int i = 0; i < 255; ++i) original.setColor(i, qRgb(0, i, i)); } if (original.colorTable().isEmpty()) { for (int x = 0; x < w; ++x) { original.setPixel(x,0, qRgb(x,0,128)); original.setPixel(x,h - 1, qRgb(0,255 - x,128)); } for (int y = 0; y < h; ++y) { original.setPixel(0, y, qRgb(y,0,255)); original.setPixel(w - 1, y, qRgb(0,255 - y,255)); } } else { const int n = original.colorTable().size(); for (int x = 0; x < w; ++x) { original.setPixel(x, 0, x % n); original.setPixel(x, h - 1, n - (x % n) - 1); } for (int y = 0; y < h; ++y) { original.setPixel(0, y, y % n); original.setPixel(w - 1, y, n - (y % n) - 1); } } // original.save("rotated90_original.png", "png"); // Initialize the matrix manually (do not use rotate) to avoid rounding errors QTransform matRotate90; matRotate90.rotate(degrees); QImage dest = original; // And rotate it 4 times, then the image should be identical to the original for (int i = 0; i < 4 ; ++i) { dest = dest.transformed(matRotate90); } // Make sure they are similar in format before we compare them. dest = dest.convertToFormat(format); // dest.save("rotated90_result.png","png"); QCOMPARE(original, dest); // Test with QTransform::rotate 90 also, since we trust that now matRotate90.rotate(degrees); dest = original; // And rotate it 4 times, then the image should be identical to the original for (int i = 0; i < 4 ; ++i) { dest = dest.transformed(matRotate90); } // Make sure they are similar in format before we compare them. dest = dest.convertToFormat(format); 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); (void)img.copy(QRect(1000,1,1,1)); } } void tst_QImage::load() { const QString filePath = m_prefix + QLatin1String("image.jpg"); QImage dest(filePath); QVERIFY(!dest.isNull()); QVERIFY(!dest.load("image_that_does_not_exist.png")); QVERIFY(dest.isNull()); QVERIFY(dest.load(filePath)); QVERIFY(!dest.isNull()); } void tst_QImage::loadFromData() { const QString filePath = m_prefix + QLatin1String("image.jpg"); QImage original(filePath); QVERIFY(!original.isNull()); QByteArray ba; { QBuffer buf(&ba); QVERIFY(buf.open(QIODevice::WriteOnly)); QVERIFY(original.save(&buf, "BMP")); } QVERIFY(!ba.isEmpty()); const uchar *baPtr = reinterpret_cast(ba.constData()); { QImage dest; QVERIFY(dest.loadFromData(QByteArrayView(ba), "BMP")); 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()); } { 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) void tst_QImage::loadFromDataStream() { const QString filePath = m_prefix + QLatin1String("image.jpg"); QImage original(filePath); QVERIFY(!original.isNull()); QByteArray ba; { QDataStream s(&ba, QIODevice::WriteOnly); s << original; } QVERIFY(!ba.isEmpty()); QImage dest; { QDataStream s(&ba, QIODevice::ReadOnly); s >> dest; } QVERIFY(!dest.isNull()); QCOMPARE(original, dest); { ba.clear(); QDataStream s(&ba, QIODevice::ReadOnly); s >> dest; } QVERIFY(dest.isNull()); } #endif // QT_NO_DATASTREAM void tst_QImage::setPixel_data() { QTest::addColumn("format"); QTest::addColumn("value"); QTest::addColumn("expected"); QTest::newRow("ARGB32 red") << int(QImage::Format_ARGB32) << 0xffff0000 << 0xffff0000; QTest::newRow("ARGB32 green") << int(QImage::Format_ARGB32) << 0xff00ff00 << 0xff00ff00; QTest::newRow("ARGB32 blue") << int(QImage::Format_ARGB32) << 0xff0000ff << 0xff0000ff; QTest::newRow("RGB16 red") << int(QImage::Format_RGB16) << 0xffff0000 << 0xf800u; QTest::newRow("RGB16 green") << int(QImage::Format_RGB16) << 0xff00ff00 << 0x07e0u; QTest::newRow("RGB16 blue") << int(QImage::Format_RGB16) << 0xff0000ff << 0x001fu; QTest::newRow("ARGB8565_Premultiplied red") << int(QImage::Format_ARGB8565_Premultiplied) << 0xffff0000 << 0xf800ffu; QTest::newRow("ARGB8565_Premultiplied green") << int(QImage::Format_ARGB8565_Premultiplied) << 0xff00ff00 << 0x07e0ffu; QTest::newRow("ARGB8565_Premultiplied blue") << int(QImage::Format_ARGB8565_Premultiplied) << 0xff0000ff << 0x001fffu; QTest::newRow("RGB666 red") << int(QImage::Format_RGB666) << 0xffff0000 << 0x03f000u; QTest::newRow("RGB666 green") << int(QImage::Format_RGB666) << 0xff00ff00 << 0x000fc0u; QTest::newRow("RGB666 blue") << int(QImage::Format_RGB666) << 0xff0000ff << 0x00003fu; QTest::newRow("RGB555 red") << int(QImage::Format_RGB555) << 0xffff0000 << 0x7c00u; QTest::newRow("RGB555 green") << int(QImage::Format_RGB555) << 0xff00ff00 << 0x03e0u; QTest::newRow("RGB555 blue") << int(QImage::Format_RGB555) << 0xff0000ff << 0x001fu; QTest::newRow("ARGB8555_Premultiplied red") << int(QImage::Format_ARGB8555_Premultiplied) << 0xffff0000 << 0x7c00ffu; QTest::newRow("ARGB8555_Premultiplied green") << int(QImage::Format_ARGB8555_Premultiplied) << 0xff00ff00 << 0x03e0ffu; QTest::newRow("ARGB8555_Premultiplied blue") << int(QImage::Format_ARGB8555_Premultiplied) << 0xff0000ff << 0x001fffu; QTest::newRow("RGB888 red") << int(QImage::Format_RGB888) << 0xffff0000 << 0xff0000u; QTest::newRow("RGB888 green") << int(QImage::Format_RGB888) << 0xff00ff00 << 0x00ff00u; QTest::newRow("RGB888 blue") << int(QImage::Format_RGB888) << 0xff0000ff << 0x0000ffu; QTest::newRow("BGR888 red") << int(QImage::Format_BGR888) << 0xffff0000 << 0x0000ffu; QTest::newRow("BGR888 green") << int(QImage::Format_BGR888) << 0xff00ff00 << 0x00ff00u; QTest::newRow("BGR888 blue") << int(QImage::Format_BGR888) << 0xff0000ff << 0xff0000u; #if Q_BYTE_ORDER == Q_BIG_ENDIAN QTest::newRow("RGBA8888 red") << int(QImage::Format_RGBA8888) << 0xffff0000u << 0xff0000ffu; QTest::newRow("RGBA8888 green") << int(QImage::Format_RGBA8888) << 0xff00ff00u << 0x00ff00ffu; QTest::newRow("RGBA8888 blue") << int(QImage::Format_RGBA8888) << 0xff0000ffu << 0x0000ffffu; #else QTest::newRow("RGBA8888 red") << int(QImage::Format_RGBA8888) << 0xffff0000u << 0xff0000ffu; QTest::newRow("RGBA8888 green") << int(QImage::Format_RGBA8888) << 0xff00ff00u << 0xff00ff00u; QTest::newRow("RGBA8888 blue") << int(QImage::Format_RGBA8888) << 0xff0000ffu << 0xffff0000u; #endif QTest::newRow("A2BGR30_Premultiplied red") << int(QImage::Format_A2BGR30_Premultiplied) << 0xffff0000u << 0xc00003ffu; QTest::newRow("A2BGR30_Premultiplied green") << int(QImage::Format_A2BGR30_Premultiplied) << 0xff00ff00u << 0xc00ffc00u; QTest::newRow("A2BGR30_Premultiplied blue") << int(QImage::Format_A2BGR30_Premultiplied) << 0xff0000ffu << 0xfff00000u; QTest::newRow("RGB30 red") << int(QImage::Format_RGB30) << 0xffff0000u << 0xfff00000u; QTest::newRow("RGB30 green") << int(QImage::Format_RGB30) << 0xff00ff00u << 0xc00ffc00u; QTest::newRow("RGB30 blue") << int(QImage::Format_RGB30) << 0xff0000ffu << 0xc00003ffu; } void tst_QImage::setPixel() { QFETCH(int, format); QFETCH(uint, value); QFETCH(uint, expected); const int w = 13; const int h = 15; QImage img(w, h, QImage::Format(format)); // fill image for (int y = 0; y < h; ++y) for (int x = 0; x < w; ++x) img.setPixel(x, y, value); // check pixel values switch (format) { case int(QImage::Format_RGB32): case int(QImage::Format_ARGB32): case int(QImage::Format_ARGB32_Premultiplied): case int(QImage::Format_RGBX8888): case int(QImage::Format_RGBA8888): case int(QImage::Format_RGBA8888_Premultiplied): case int(QImage::Format_A2BGR30_Premultiplied): case int(QImage::Format_RGB30): { for (int y = 0; y < h; ++y) { const quint32 *row = (const quint32*)(img.scanLine(y)); for (int x = 0; x < w; ++x) { quint32 result = row[x]; if (result != expected) printf("[x,y]: %d,%d, expected=%08x, result=%08x\n", x, y, expected, result); QCOMPARE(uint(result), expected); } } break; } case int(QImage::Format_RGB555): case int(QImage::Format_RGB16): { for (int y = 0; y < h; ++y) { const quint16 *row = (const quint16*)(img.scanLine(y)); for (int x = 0; x < w; ++x) { quint16 result = row[x]; if (result != expected) printf("[x,y]: %d,%d, expected=%04x, result=%04x\n", x, y, expected, result); QCOMPARE(uint(result), expected); } } break; } case int(QImage::Format_RGB666): case int(QImage::Format_ARGB8565_Premultiplied): case int(QImage::Format_ARGB8555_Premultiplied): case int(QImage::Format_RGB888): case int(QImage::Format_BGR888): { for (int y = 0; y < h; ++y) { const quint24 *row = (const quint24*)(img.scanLine(y)); for (int x = 0; x < w; ++x) { quint32 result = row[x]; if (result != expected) printf("[x,y]: %d,%d, expected=%04x, result=%04x\n", x, y, expected, result); QCOMPARE(result, expected); } } break; } default: qFatal("Test not implemented for format %d", format); } } void tst_QImage::setPixelWithAlpha_data() { QTest::addColumn("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; 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); int dpmx = 123; int dpmy = 234; img.setDotsPerMeterX(dpmx); img.setDotsPerMeterY(dpmy); img.fill(0x12345678); img = img.convertToFormat(QImage::Format_RGB32); QCOMPARE(img.dotsPerMeterX(), dpmx); QCOMPARE(img.dotsPerMeterY(), dpmy); } void tst_QImage::convertToFormatPreserveText() { QImage img(100, 100, QImage::Format_ARGB32_Premultiplied); img.setText("foo", "bar"); img.setText("foo2", "bar2"); img.fill(0x12345678); QStringList listResult; listResult << "foo" << "foo2"; QString result = "foo: bar\n\nfoo2: bar2"; QImage imgResult1 = img.convertToFormat(QImage::Format_RGB32); QCOMPARE(imgResult1.text(), result); QCOMPARE(imgResult1.textKeys(), listResult); QList colorTable(4); for (int i = 0; i < 4; ++i) colorTable[i] = QRgb(42); QImage imgResult2 = img.convertToFormat(QImage::Format_MonoLSB, colorTable); QCOMPARE(imgResult2.text(), result); QCOMPARE(imgResult2.textKeys(), listResult); } void tst_QImage::defaultColorTable_data() { QTest::addColumn("format"); QTest::addColumn("createdDataCount"); QTest::addColumn("externalDataCount"); // For historical reasons, internally created mono images get a default colormap. // Externally created and Indexed8 images do not. QTest::newRow("Mono") << QImage::Format_Mono << 2 << 0; QTest::newRow("MonoLSB") << QImage::Format_MonoLSB << 2 << 0; QTest::newRow("Indexed8") << QImage::Format_Indexed8 << 0 << 0; QTest::newRow("ARGB32_PM") << QImage::Format_A2BGR30_Premultiplied << 0 << 0; } void tst_QImage::defaultColorTable() { QFETCH(QImage::Format, format); QFETCH(int, createdDataCount); QFETCH(int, externalDataCount); QImage img1(1, 1, format); QCOMPARE(img1.colorCount(), createdDataCount); QCOMPARE(img1.colorTable().size(), createdDataCount); quint32 buf; QImage img2(reinterpret_cast(&buf), 1, 1, format); QCOMPARE(img2.colorCount(), externalDataCount); QImage nullImg(0, 0, format); QCOMPARE(nullImg.colorCount(), 0); } void tst_QImage::setColorCount() { QImage img(0, 0, QImage::Format_Indexed8); QTest::ignoreMessage(QtWarningMsg, "QImage::setColorCount: null image"); img.setColorCount(256); QCOMPARE(img.colorCount(), 0); } void tst_QImage::setColor() { QImage img(0, 0, QImage::Format_Indexed8); img.setColor(0, qRgba(18, 219, 108, 128)); QCOMPARE(img.colorCount(), 0); QImage img2(1, 1, QImage::Format_Indexed8); img2.setColor(0, qRgba(18, 219, 108, 128)); QCOMPARE(img2.colorCount(), 1); } /* Just some sanity checking that we don't draw outside the buffer of * the image. Hopefully this will create crashes or at least some * random test fails when broken. */ void tst_QImage::rasterClipping() { QImage image(10, 10, QImage::Format_RGB32); image.fill(0xffffffff); QPainter p(&image); p.drawLine(-1000, 5, 5, 5); p.drawLine(-1000, 5, 1000, 5); p.drawLine(5, 5, 1000, 5); p.drawLine(5, -1000, 5, 5); p.drawLine(5, -1000, 5, 1000); p.drawLine(5, 5, 5, 1000); p.setBrush(Qt::red); p.drawEllipse(3, 3, 4, 4); p.drawEllipse(-100, -100, 210, 210); p.drawEllipse(-1000, 0, 2010, 2010); p.drawEllipse(0, -1000, 2010, 2010); p.drawEllipse(-2010, -1000, 2010, 2010); p.drawEllipse(-1000, -2010, 2010, 2010); QVERIFY(true); } // Tests the new QPoint overloads in QImage in Qt 4.2 void tst_QImage::pointOverloads() { QImage image(100, 100, QImage::Format_RGB32); image.fill(0xff00ff00); // IsValid QVERIFY(image.valid(QPoint(0, 0))); QVERIFY(image.valid(QPoint(99, 0))); QVERIFY(image.valid(QPoint(0, 99))); QVERIFY(image.valid(QPoint(99, 99))); QVERIFY(!image.valid(QPoint(50, -1))); // outside on the top QVERIFY(!image.valid(QPoint(50, 100))); // outside on the bottom QVERIFY(!image.valid(QPoint(-1, 50))); // outside on the left QVERIFY(!image.valid(QPoint(100, 50))); // outside on the right // Test the pixel setter image.setPixel(QPoint(10, 10), 0xff0000ff); QCOMPARE(image.pixel(10, 10), 0xff0000ff); // pixel getter QCOMPARE(image.pixel(QPoint(10, 10)), 0xff0000ff); // pixelIndex() QImage indexed = image.convertToFormat(QImage::Format_Indexed8); QCOMPARE(indexed.pixelIndex(10, 10), indexed.pixelIndex(QPoint(10, 10))); } void tst_QImage::destructor() { QPolygon poly(6); poly.setPoint(0,-1455, 1435); QImage image(100, 100, QImage::Format_RGB32); QPainter ptPix(&image); ptPix.setPen(Qt::black); ptPix.setBrush(Qt::black); ptPix.drawPolygon(poly, Qt::WindingFill); ptPix.end(); } /* XPM */ static const char *monoPixmap[] = { /* width height ncolors chars_per_pixel */ "4 4 2 1", "x c #000000", ". c #ffffff", /* pixels */ "xxxx", "x..x", "x..x", "xxxx" }; #ifndef QT_NO_IMAGE_HEURISTIC_MASK void tst_QImage::createHeuristicMask() { QImage img(monoPixmap); img = img.convertToFormat(QImage::Format_MonoLSB); QImage mask = img.createHeuristicMask(); QImage newMask = mask.convertToFormat(QImage::Format_ARGB32_Premultiplied); // line 2 QVERIFY(newMask.pixel(0,1) != newMask.pixel(1,1)); QCOMPARE(newMask.pixel(1,1), newMask.pixel(2,1)); QVERIFY(newMask.pixel(2,1) != newMask.pixel(3,1)); // line 3 QVERIFY(newMask.pixel(0,2) != newMask.pixel(1,2)); QCOMPARE(newMask.pixel(1,2), newMask.pixel(2,2)); QVERIFY(newMask.pixel(2,2) != newMask.pixel(3,2)); } #endif void tst_QImage::cacheKey() { QImage image1(2, 2, QImage::Format_RGB32); qint64 image1_key = image1.cacheKey(); QImage image2 = image1; QCOMPARE(image2.cacheKey(), image1.cacheKey()); image2.detach(); QVERIFY(image2.cacheKey() != image1.cacheKey()); QCOMPARE(image1.cacheKey(), image1_key); } void tst_QImage::smoothScale() { unsigned int data[2] = { qRgba(0, 0, 0, 0), qRgba(128, 128, 128, 128) }; QImage imgX((unsigned char *)data, 2, 1, QImage::Format_ARGB32_Premultiplied); QImage imgY((unsigned char *)data, 1, 2, QImage::Format_ARGB32_Premultiplied); QImage scaledX = imgX.scaled(QSize(4, 1), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QImage scaledY = imgY.scaled(QSize(1, 4), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); uint *scaled[2] = { (unsigned int *)scaledX.bits(), (unsigned int *)scaledY.bits() }; int expected[4] = { 0, 32, 96, 128 }; for (int image = 0; image < 2; ++image) { for (int index = 0; index < 4; ++index) { for (int component = 0; component < 4; ++component) { int pixel = scaled[image][index]; int val = (pixel >> (component * 8)) & 0xff; QCOMPARE(val, expected[index]); } } } } // test area sampling void tst_QImage::smoothScale2_data() { QTest::addColumn("format"); QTest::addColumn("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, #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) { const QByteArray sizeB = QByteArray::number(sizes[i]); QTest::newRow(QString("%1 %2x%2").arg(formatstr).arg(sizes[i]).toUtf8()) << formats[j] << sizes[i]; } } } void tst_QImage::smoothScale2() { QFETCH(QImage::Format, format); QFETCH(int, size); 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 QImage scaled = img.scaled(QSize(1, 1), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QRgb pixel = scaled.pixel(0, 0); QCOMPARE(qAlpha(pixel), qAlpha(expected)); QCOMPARE(qRed(pixel), qRed(expected)); QCOMPARE(qGreen(pixel), qGreen(expected)); QCOMPARE(qBlue(pixel), qBlue(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 = scaled.pixel(0, y); QCOMPARE(qAlpha(pixel), qAlpha(expected)); QCOMPARE(qRed(pixel), qRed(expected)); QCOMPARE(qGreen(pixel), qGreen(expected)); QCOMPARE(qBlue(pixel), qBlue(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 = scaled.pixel(x, 0); QCOMPARE(qAlpha(pixel), qAlpha(expected)); QCOMPARE(qRed(pixel), qRed(expected)); QCOMPARE(qGreen(pixel), qGreen(expected)); QCOMPARE(qBlue(pixel), qBlue(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 = scaled.pixel(x, y); QCOMPARE(qAlpha(pixel), qAlpha(expected)); QCOMPARE(qRed(pixel), qRed(expected)); QCOMPARE(qGreen(pixel), qGreen(expected)); QCOMPARE(qBlue(pixel), qBlue(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 = scaled.pixel(x, y); QCOMPARE(qAlpha(pixel), qAlpha(expected)); QCOMPARE(qRed(pixel), qRed(expected)); QCOMPARE(qGreen(pixel), qGreen(expected)); QCOMPARE(qBlue(pixel), qBlue(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 = scaled.pixel(x, y); QCOMPARE(qAlpha(pixel), qAlpha(expected)); QCOMPARE(qRed(pixel), qRed(expected)); QCOMPARE(qGreen(pixel), qGreen(expected)); QCOMPARE(qBlue(pixel), qBlue(expected)); } } } static inline int rand8() { return QRandomGenerator::global()->bounded(256); } void tst_QImage::smoothScale3_data() { QTest::addColumn("img"); QTest::addColumn("scale_x"); QTest::addColumn("scale_y"); QImage img(128, 128, QImage::Format_RGB32); for (int y = 0; y < img.height(); ++y) { for (int x = 0; x < img.width(); ++x) { const int red = rand8(); const int green = rand8(); const int blue = rand8(); const int alpha = 255; img.setPixel(x, y, qRgba(red, green, blue, alpha)); } } QTest::newRow("(0.5, 0.5)") << img << qreal(0.5) << qreal(0.5); QTest::newRow("(0.5, 1.0)") << img << qreal(0.5) << qreal(1.0); QTest::newRow("(1.0, 0.5)") << img << qreal(1.0) << qreal(0.5); QTest::newRow("(0.5, 2.0)") << img << qreal(0.5) << qreal(2.0); QTest::newRow("(1.0, 2.0)") << img << qreal(1.0) << qreal(2.0); QTest::newRow("(2.0, 0.5)") << img << qreal(2.0) << qreal(0.5); QTest::newRow("(2.0, 1.0)") << img << qreal(2.0) << qreal(1.0); QTest::newRow("(2.0, 2.0)") << img << qreal(2) << qreal(2); } // compares img.scale against the bilinear filtering used by QPainter void tst_QImage::smoothScale3() { QFETCH(QImage, img); QFETCH(qreal, scale_x); QFETCH(qreal, scale_y); QImage a = img.scaled(img.width() * scale_x, img.height() * scale_y, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QImage b(a.size(), a.format()); b.fill(0x0); QPainter p(&b); p.setRenderHint(QPainter::SmoothPixmapTransform); p.scale(scale_x, scale_y); p.drawImage(0, 0, img); p.end(); int err = 0; for (int y = 0; y < a.height(); ++y) { for (int x = 0; x < a.width(); ++x) { QRgb ca = a.pixel(x, y); QRgb cb = b.pixel(x, y); // tolerate a little bit of rounding errors int tolerance = 3; bool r = true; r &= qAbs(qRed(ca) - qRed(cb)) <= tolerance; r &= qAbs(qGreen(ca) - qGreen(cb)) <= tolerance; r &= qAbs(qBlue(ca) - qBlue(cb)) <= tolerance; if (!r) err++; } } QCOMPARE(err, 0); } // Tests smooth upscale is smooth void tst_QImage::smoothScale4_data() { QTest::addColumn("format"); QTest::newRow("RGB32") << QImage::Format_RGB32; #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() { QFETCH(QImage::Format, format); QImage img(4, 4, format); for (int y = 0; y < 4; ++y) { for (int x = 0; x < 4; ++x) { img.setPixel(x, y, qRgb(x * 255 / 3, y * 255 / 3, 0)); } } QImage scaled = img.scaled(37, 23, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QCOMPARE(scaled.format(), format); for (int y = 0; y < scaled.height(); ++y) { for (int x = 0; x < scaled.width(); ++x) { if (x > 0) QVERIFY(scaled.pixelColor(x, y).redF() >= scaled.pixelColor(x - 1, y).redF()); if (y > 0) QVERIFY(scaled.pixelColor(x, y).greenF() >= scaled.pixelColor(x, y - 1).greenF()); } } } void tst_QImage::smoothScaleBig() { int bigValue = 200000; QImage tall(4, bigValue, QImage::Format_ARGB32); tall.fill(0x0); QImage wide(bigValue, 4, QImage::Format_ARGB32); wide.fill(0x0); QImage tallScaled = tall.scaled(4, tall.height() / 4, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QImage wideScaled = wide.scaled(wide.width() / 4, 4, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QCOMPARE(tallScaled.pixel(0, 0), QRgb(0x0)); QCOMPARE(wideScaled.pixel(0, 0), QRgb(0x0)); } void tst_QImage::smoothScaleAlpha() { QImage src(128, 128, QImage::Format_ARGB32_Premultiplied); src.fill(0x0); QPainter srcPainter(&src); srcPainter.setPen(Qt::NoPen); srcPainter.setBrush(Qt::white); srcPainter.drawEllipse(QRect(QPoint(), src.size())); srcPainter.end(); QImage dst(32, 32, QImage::Format_ARGB32_Premultiplied); dst.fill(0xffffffff); QImage expected = dst; QPainter dstPainter(&dst); dstPainter.drawImage(0, 0, src.scaled(dst.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); dstPainter.end(); QCOMPARE(dst, expected); } void tst_QImage::smoothScaleFormats_data() { QTest::addColumn("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; while (x >= 0 && x < img.width() && y >= 0 && y < img.height()) { i += (img.pixel(x, y) == pixel); x += dx; y += dy; } return i; } const int transformed_image_width = 128; const int transformed_image_height = 128; void tst_QImage::transformed_data() { QTest::addColumn("transform"); { QTransform transform; transform.translate(10.4, 10.4); QTest::newRow("Translate") << transform; } { QTransform transform; transform.scale(1.5, 1.5); QTest::newRow("Scale") << transform; } { QTransform transform; transform.rotate(30); QTest::newRow("Rotate 30") << transform; } { QTransform transform; transform.rotate(90); QTest::newRow("Rotate 90") << transform; } { QTransform transform; transform.rotate(180); QTest::newRow("Rotate 180") << transform; } { QTransform transform; transform.rotate(270); QTest::newRow("Rotate 270") << transform; } { QTransform transform; transform.translate(transformed_image_width/2, transformed_image_height/2); transform.rotate(155, Qt::XAxis); transform.translate(-transformed_image_width/2, -transformed_image_height/2); QTest::newRow("Perspective 1") << transform; } { QTransform transform; transform.rotate(155, Qt::XAxis); transform.translate(-transformed_image_width/2, -transformed_image_height/2); QTest::newRow("Perspective 2") << transform; } } void tst_QImage::transformed() { QFETCH(QTransform, transform); QImage img(transformed_image_width, transformed_image_height, QImage::Format_ARGB32_Premultiplied); QPainter p(&img); p.fillRect(0, 0, img.width(), img.height(), Qt::red); p.drawRect(0, 0, img.width()-1, img.height()-1); p.end(); QImage transformed = img.transformed(transform, Qt::SmoothTransformation); // all borders should have touched pixels QVERIFY(count(transformed, 0, 0, 1, 0, 0x0) < transformed.width()); QVERIFY(count(transformed, 0, 0, 0, 1, 0x0) < transformed.height()); QVERIFY(count(transformed, 0, img.height() - 1, 1, 0, 0x0) < transformed.width()); QVERIFY(count(transformed, img.width() - 1, 0, 0, 1, 0x0) < transformed.height()); QImage transformedPadded(transformed.width() + 2, transformed.height() + 2, img.format()); transformedPadded.fill(0x0); p.begin(&transformedPadded); p.setRenderHint(QPainter::SmoothPixmapTransform); p.setRenderHint(QPainter::Antialiasing); p.setTransform(transformed.trueMatrix(transform, img.width(), img.height()) * QTransform().translate(1, 1)); p.drawImage(0, 0, img); p.end(); // no borders should have touched pixels since we have a one-pixel padding QCOMPARE(count(transformedPadded, 0, 0, 1, 0, 0x0), transformedPadded.width()); QCOMPARE(count(transformedPadded, 0, 0, 0, 1, 0x0), transformedPadded.height()); QCOMPARE(count(transformedPadded, 0, transformedPadded.height() - 1, 1, 0, 0x0), transformedPadded.width()); QCOMPARE(count(transformedPadded, transformedPadded.width() - 1, 0, 0, 1, 0x0), transformedPadded.height()); } void tst_QImage::transformed2() { QImage img(3, 3, QImage::Format_Mono); QPainter p(&img); p.fillRect(0, 0, 3, 3, Qt::white); p.fillRect(0, 0, 3, 3, Qt::Dense4Pattern); p.end(); QTransform transform; transform.scale(3, 3); QImage expected(9, 9, QImage::Format_Mono); p.begin(&expected); p.fillRect(0, 0, 9, 9, Qt::white); p.setBrush(Qt::black); p.setPen(Qt::NoPen); p.drawRect(3, 0, 3, 3); p.drawRect(0, 3, 3, 3); p.drawRect(6, 3, 3, 3); p.drawRect(3, 6, 3, 3); p.end(); { QImage actual = img.transformed(transform); QCOMPARE(actual.format(), expected.format()); QCOMPARE(actual.size(), expected.size()); QCOMPARE(actual, expected); } { transform.rotate(-90); QImage actual = img.transformed(transform); QCOMPARE(actual.convertToFormat(QImage::Format_ARGB32_Premultiplied), expected.convertToFormat(QImage::Format_ARGB32_Premultiplied)); } } void tst_QImage::scaled() { QImage img(102, 3, QImage::Format_Mono); QPainter p(&img); p.fillRect(0, 0, img.width(), img.height(), Qt::white); p.end(); QImage scaled = img.scaled(1994, 10); QImage expected(1994, 10, QImage::Format_Mono); p.begin(&expected); p.fillRect(0, 0, expected.width(), expected.height(), Qt::white); p.end(); QCOMPARE(scaled, expected); } void tst_QImage::paintEngine() { QImage img; QPaintEngine *engine; { QImage temp(100, 100, QImage::Format_RGB32); temp.fill(0xff000000); QPainter p(&temp); p.fillRect(80,80,10,10,Qt::blue); p.end(); img = temp; engine = temp.paintEngine(); } { QPainter p(&img); p.fillRect(80,10,10,10,Qt::yellow); p.end(); } QImage expected(100, 100, QImage::Format_RGB32); expected.fill(0xff000000); QPainter p(&expected); p.fillRect(80,80,10,10,Qt::blue); p.fillRect(80,10,10,10,Qt::yellow); p.end(); QCOMPARE(engine, img.paintEngine()); QCOMPARE(img, expected); { QImage img1(16, 16, QImage::Format_ARGB32); QImage img2 = img1; QVERIFY(img2.paintEngine()); } } void tst_QImage::setAlphaChannelWhilePainting() { QImage image(100, 100, QImage::Format_ARGB32); image.fill(Qt::black); QPainter p(&image); image.setAlphaChannel(image.createMaskFromColor(QColor(Qt::black).rgb(), Qt::MaskInColor)); } // See task 240047 for details void tst_QImage::smoothScaledSubImage() { QImage original(128, 128, QImage::Format_RGB32); QPainter p(&original); p.fillRect(0, 0, 64, 128, Qt::black); p.fillRect(64, 0, 64, 128, Qt::white); p.end(); QImage subimage(((const QImage &) original).bits(), 32, 32, original.bytesPerLine(), QImage::Format_RGB32); // only in the black part of the source... QImage scaled = subimage.scaled(8, 8, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); for (int y=0; y("image"); QTest::newRow("null image") << QImage(); QTest::newRow("zero-size image") << QImage(0, 0, QImage::Format_RGB32); } void tst_QImage::nullSize() { QFETCH(QImage, image); QCOMPARE(image.isNull(), true); QCOMPARE(image.width(), image.size().width()); QCOMPARE(image.height(), image.size().height()); } void tst_QImage::premultipliedAlphaConsistency() { QImage img(256, 1, QImage::Format_ARGB32); for (int x = 0; x < 256; ++x) img.setPixel(x, 0, (x << 24) | 0xffffff); QImage converted = img.convertToFormat(QImage::Format_ARGB8565_Premultiplied); QImage pm32 = converted.convertToFormat(QImage::Format_ARGB32_Premultiplied); for (int i = 0; i < pm32.width(); ++i) { QRgb pixel = pm32.pixel(i, 0); QVERIFY(qRed(pixel) <= qAlpha(pixel)); QVERIFY(qGreen(pixel) <= qAlpha(pixel)); QVERIFY(qBlue(pixel) <= qAlpha(pixel)); } } void tst_QImage::compareIndexed() { QImage img(256, 1, QImage::Format_Indexed8); QList colorTable(256); for (int i = 0; i < 256; ++i) colorTable[i] = qRgb(i, i, i); img.setColorTable(colorTable); for (int i = 0; i < 256; ++i) { img.setPixel(i, 0, i); } QImage imgInverted(256, 1, QImage::Format_Indexed8); QList invertedColorTable(256); for (int i = 0; i < 256; ++i) invertedColorTable[255-i] = qRgb(i, i, i); imgInverted.setColorTable(invertedColorTable); for (int i = 0; i < 256; ++i) { imgInverted.setPixel(i, 0, (255-i)); } QCOMPARE(img, imgInverted); } void tst_QImage::fillColor_data() { QTest::addColumn("format"); QTest::addColumn("color"); QTest::addColumn("pixelValue"); QTest::newRow("Mono, color0") << QImage::Format_Mono << Qt::color0 << 0u; QTest::newRow("Mono, color1") << QImage::Format_Mono << Qt::color1 << 1u; QTest::newRow("MonoLSB, color0") << QImage::Format_MonoLSB << Qt::color0 << 0u; QTest::newRow("MonoLSB, color1") << QImage::Format_MonoLSB << Qt::color1 << 1u; const char *names[] = { "Indexed8", "RGB32", "ARGB32", "ARGB32pm", "RGB16", "ARGB8565pm", "RGB666", "ARGB6666pm", "RGB555", "ARGB8555pm", "RGB888", "RGB444", "ARGB4444pm", "RGBx8888", "RGBA8888pm", "BGR30", "A2RGB30pm", 0 }; QImage::Format formats[] = { QImage::Format_Indexed8, QImage::Format_RGB32, QImage::Format_ARGB32, QImage::Format_ARGB32_Premultiplied, QImage::Format_RGB16, QImage::Format_ARGB8565_Premultiplied, QImage::Format_RGB666, QImage::Format_ARGB6666_Premultiplied, QImage::Format_RGB555, QImage::Format_ARGB8555_Premultiplied, QImage::Format_RGB888, QImage::Format_RGB444, QImage::Format_ARGB4444_Premultiplied, QImage::Format_RGBX8888, 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) { QByteArray name; name.append(names[i]).append(", "); QTest::newRow(QByteArray(name).append("black").constData()) << formats[i] << Qt::black << 0xff000000; QTest::newRow(QByteArray(name).append("white").constData()) << formats[i] << Qt::white << 0xffffffff; QTest::newRow(QByteArray(name).append("red").constData()) << formats[i] << Qt::red << 0xffff0000; QTest::newRow(QByteArray(name).append("green").constData()) << formats[i] << Qt::green << 0xff00ff00; QTest::newRow(QByteArray(name).append("blue").constData()) << formats[i] << Qt::blue << 0xff0000ff; } QTest::newRow("RGB16, transparent") << QImage::Format_RGB16 << Qt::transparent << 0xff000000; QTest::newRow("RGB32, transparent") << QImage::Format_RGB32 << Qt::transparent << 0xff000000; QTest::newRow("ARGB32, transparent") << QImage::Format_ARGB32 << Qt::transparent << 0x00000000u; QTest::newRow("ARGB32pm, transparent") << QImage::Format_ARGB32_Premultiplied << Qt::transparent << 0x00000000u; QTest::newRow("RGBA8888pm, transparent") << QImage::Format_RGBA8888_Premultiplied << Qt::transparent << 0x00000000u; QTest::newRow("A2RGB30pm, transparent") << QImage::Format_A2RGB30_Premultiplied << Qt::transparent << 0x00000000u; } void tst_QImage::fillColor() { QFETCH(QImage::Format, format); QFETCH(Qt::GlobalColor, color); QFETCH(uint, pixelValue); QImage image(1, 1, format); if (image.depth() == 8) { QList table; table << 0xff000000; table << 0xffffffff; table << 0xffff0000; table << 0xff00ff00; table << 0xff0000ff; image.setColorTable(table); } image.fill(color); if (image.depth() == 1) { QCOMPARE(image.pixelIndex(0, 0), (int) pixelValue); } else { QCOMPARE(image.pixel(0, 0), pixelValue); } image.fill(QColor(color)); if (image.depth() == 1) { QCOMPARE(image.pixelIndex(0, 0), (int) pixelValue); } else { QCOMPARE(image.pixel(0, 0), pixelValue); } } void tst_QImage::fillColorWithAlpha_data() { setPixelWithAlpha_data(); } 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() { QImage expected(1, 1, QImage::Format_RGB888); QImage actual(1, 1, QImage::Format_RGB888); for (int c = Qt::black; c < Qt::transparent; ++c) { QColor color = QColor(Qt::GlobalColor(c)); expected.fill(color); actual.fill(color.rgba()); QCOMPARE(actual.pixel(0, 0), expected.pixel(0, 0)); } } void tst_QImage::fillPixel_data() { QTest::addColumn("format"); QTest::addColumn("color"); QTest::addColumn("pixelValue"); 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; QTest::newRow("RGB32, red") << QImage::Format_RGB32 << 0xffff0000u << 0xffff0000u; 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 << 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-transparent") << QImage::Format_Alpha8 << 0x80u << 0x80000000u; } void tst_QImage::fillPixel() { QFETCH(QImage::Format, format); QFETCH(uint, color); QFETCH(uint, pixelValue); QImage image(1, 1, format); image.fill(color); QCOMPARE(image.pixel(0, 0), pixelValue); if (image.depth() == 8) QCOMPARE(*(const uchar *)image.constBits(), color); } void tst_QImage::rgbSwapped_data() { QTest::addColumn("format"); for (int i = QImage::Format_Indexed8; i < QImage::NImageFormats; ++i) { if (i == QImage::Format_Alpha8 || i == QImage::Format_Grayscale8 || i == QImage::Format_Grayscale16) { continue; } QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i); } } void tst_QImage::rgbSwapped() { QFETCH(QImage::Format, format); QImage image(100, 1, format); image.fill(0); QList testColor(image.width()); for (int i = 0; i < image.width(); ++i) testColor[i] = QColor(i, 10 + i, 20 + i * 2, 30 + i); if (format != QImage::Format_Indexed8) { QPainter p(&image); p.setCompositionMode(QPainter::CompositionMode_Source); for (int i = 0; i < image.width(); ++i) p.fillRect(QRect(i, 0, 1, 1), testColor[i].rgb()); } else { image.setColorCount(image.width()); for (int i = 0; i < image.width(); ++i) { image.setColor(0, testColor[i].rgba()); image.setPixel(i, 0, i); } } QImage imageSwapped = image.rgbSwapped(); for (int i = 0; i < image.width(); ++i) { QColor referenceColor = QColor(image.pixel(i, 0)); QColor swappedColor = QColor(imageSwapped.pixel(i, 0)); QCOMPARE(swappedColor.alpha(), referenceColor.alpha()); QCOMPARE(swappedColor.red(), referenceColor.blue()); QCOMPARE(swappedColor.green(), referenceColor.green()); QCOMPARE(swappedColor.blue(), referenceColor.red()); } imageSwapped.rgbSwap(); QCOMPARE(image, imageSwapped); QCOMPARE(memcmp(image.constBits(), imageSwapped.constBits(), image.sizeInBytes()), 0); } void tst_QImage::mirrored_data() { QTest::addColumn("format"); QTest::addColumn("swap_vertical"); QTest::addColumn("swap_horizontal"); QTest::addColumn("width"); QTest::addColumn("height"); QTest::newRow("Format_RGB32, vertical") << QImage::Format_RGB32 << true << false << 16 << 16; QTest::newRow("Format_ARGB32, vertical") << QImage::Format_ARGB32 << true << false << 16 << 16; QTest::newRow("Format_ARGB32_Premultiplied, vertical") << QImage::Format_ARGB32_Premultiplied << true << false << 16 << 16; QTest::newRow("Format_RGB16, vertical") << QImage::Format_RGB16 << true << false << 16 << 16; QTest::newRow("Format_ARGB8565_Premultiplied, vertical") << QImage::Format_ARGB8565_Premultiplied << true << false << 16 << 16; QTest::newRow("Format_ARGB6666_Premultiplied, vertical") << QImage::Format_ARGB6666_Premultiplied << true << false << 16 << 16; QTest::newRow("Format_ARGB4444_Premultiplied, vertical") << QImage::Format_ARGB4444_Premultiplied << true << false << 16 << 16; QTest::newRow("Format_RGB666, vertical") << QImage::Format_RGB666 << true << false << 16 << 16; QTest::newRow("Format_RGB555, vertical") << QImage::Format_RGB555 << true << false << 16 << 16; QTest::newRow("Format_ARGB8555_Premultiplied, vertical") << QImage::Format_ARGB8555_Premultiplied << true << false << 16 << 16; QTest::newRow("Format_RGB888, vertical") << QImage::Format_RGB888 << true << false << 16 << 16; QTest::newRow("Format_BGR888, vertical") << QImage::Format_BGR888 << true << false << 16 << 16; QTest::newRow("Format_RGB444, vertical") << QImage::Format_RGB444 << true << false << 16 << 16; QTest::newRow("Format_RGBX8888, vertical") << QImage::Format_RGBX8888 << true << false << 16 << 16; QTest::newRow("Format_RGBA8888_Premultiplied, vertical") << QImage::Format_RGBA8888_Premultiplied << true << false << 16 << 16; QTest::newRow("Format_A2BGR30_Premultiplied, vertical") << QImage::Format_A2BGR30_Premultiplied << true << false << 16 << 16; QTest::newRow("Format_RGB30, vertical") << QImage::Format_RGB30 << true << false << 16 << 16; QTest::newRow("Format_Indexed8, vertical") << QImage::Format_Indexed8 << true << false << 16 << 16; QTest::newRow("Format_Mono, vertical") << QImage::Format_Mono << true << false << 16 << 16; QTest::newRow("Format_MonoLSB, vertical") << QImage::Format_MonoLSB << true << false << 16 << 16; QTest::newRow("Format_ARGB32_Premultiplied, horizontal") << QImage::Format_ARGB32_Premultiplied << false << true << 16 << 16; QTest::newRow("Format_RGB888, horizontal") << QImage::Format_RGB888 << false << true << 16 << 16; QTest::newRow("Format_RGB16, horizontal") << QImage::Format_RGB16 << false << true << 16 << 16; QTest::newRow("Format_Indexed8, horizontal") << QImage::Format_Indexed8 << false << true << 16 << 16; QTest::newRow("Format_Mono, horizontal") << QImage::Format_Mono << false << true << 16 << 16; QTest::newRow("Format_MonoLSB, horizontal") << QImage::Format_MonoLSB << false << true << 16 << 16; QTest::newRow("Format_ARGB32_Premultiplied, horizontal+vertical") << QImage::Format_ARGB32_Premultiplied << true << true << 16 << 16; QTest::newRow("Format_RGB888, horizontal+vertical") << QImage::Format_RGB888 << true << true << 16 << 16; QTest::newRow("Format_RGB16, horizontal+vertical") << QImage::Format_RGB16 << true << true << 16 << 16; QTest::newRow("Format_Indexed8, horizontal+vertical") << QImage::Format_Indexed8 << true << true << 16 << 16; 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, 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, 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, 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; } void tst_QImage::mirrored() { QFETCH(QImage::Format, format); QFETCH(bool, swap_vertical); QFETCH(bool, swap_horizontal); QFETCH(int, width); QFETCH(int, height); QImage image(width, height, format); switch (format) { case QImage::Format_Mono: case QImage::Format_MonoLSB: for (int i = 0; i < image.height(); ++i) { ushort* scanLine = (ushort*)image.scanLine(i); *scanLine = (i % 2) ? 0x5555U : 0xCCCCU; } break; case QImage::Format_Indexed8: for (int i = 0; i < image.height(); ++i) { for (int j = 0; j < image.width(); ++j) { image.setColor(i*16+j, qRgb(j*16, i*16, 0)); image.setPixel(j, i, i*16+j); } } break; default: 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)); break; } QImage imageMirrored = image.mirrored(swap_horizontal, swap_vertical); for (int i = 0; i < image.height(); ++i) { int mirroredI = swap_vertical ? (image.height() - i - 1) : i; for (int j = 0; j < image.width(); ++j) { QRgb referenceColor = image.pixel(j, i); int mirroredJ = swap_horizontal ? (image.width() - j - 1) : j; QRgb mirroredColor = imageMirrored.pixel(mirroredJ, mirroredI); QCOMPARE(mirroredColor, referenceColor); } } imageMirrored.mirror(swap_horizontal, swap_vertical); QCOMPARE(image, imageMirrored); if (format != QImage::Format_Mono && format != QImage::Format_MonoLSB) 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), imageMirrored.pixel(j,i)); } } void tst_QImage::inplaceRgbSwapped_data() { rgbSwapped_data(); } void tst_QImage::inplaceRgbSwapped() { QFETCH(QImage::Format, format); QImage image(64, 1, format); image.fill(0); QList testColor(image.width()); for (int i = 0; i < image.width(); ++i) 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) { image.setColor(i, testColor[i]); image.setPixel(i, 0, i); } } else { for (int i = 0; i < image.width(); ++i) image.setPixel(i, 0, testColor[i]); } const uchar* orginalPtr = image.constScanLine(0); QImage imageSwapped = std::move(image).rgbSwapped(); for (int i = 0; i < imageSwapped.width(); ++i) { QRgb referenceColor = testColor[i]; QRgb swappedColor = imageSwapped.pixel(i, 0); QCOMPARE(qRed(swappedColor) & 0xf0, qBlue(referenceColor) & 0xf0); QCOMPARE(qGreen(swappedColor) & 0xf0, qGreen(referenceColor) & 0xf0); QCOMPARE(qBlue(swappedColor) & 0xf0, qRed(referenceColor) & 0xf0); } QCOMPARE(imageSwapped.constScanLine(0), orginalPtr); for (int rw = 0; rw <= 1; rw++) { // Test attempted inplace conversion of images created on existing buffer uchar *volatileData = 0; QImage orig = imageSwapped; QImage dataSwapped; { QVERIFY(!orig.isNull()); volatileData = new uchar[orig.sizeInBytes()]; memcpy(volatileData, orig.constBits(), orig.sizeInBytes()); QImage dataImage; if (rw) dataImage = QImage(volatileData, orig.width(), orig.height(), orig.format()); else dataImage = QImage((const uchar *)volatileData, orig.width(), orig.height(), orig.format()); if (orig.colorCount()) dataImage.setColorTable(orig.colorTable()); dataSwapped = std::move(dataImage).rgbSwapped(); QVERIFY(!dataSwapped.isNull()); delete[] volatileData; } QVERIFY2(dataSwapped.constBits() != volatileData, rw ? "non-const" : "const"); QCOMPARE(dataSwapped, orig.rgbSwapped()); } } void tst_QImage::inplaceMirrored_data() { QTest::addColumn("format"); QTest::addColumn("swap_vertical"); QTest::addColumn("swap_horizontal"); for (int i = QImage::Format_Mono; i < QImage::NImageFormats; ++i) { if (i == QImage::Format_Alpha8 || i == QImage::Format_Grayscale8 || i == QImage::Format_Grayscale16) { continue; } if (i == QImage::Format_RGB444 || i == QImage::Format_ARGB4444_Premultiplied) continue; const auto fmt = formatToString(QImage::Format(i)); QTest::addRow("%s, vertical", fmt.data()) << QImage::Format(i) << true << false; QTest::addRow("%s, horizontal", fmt.data()) << QImage::Format(i) << false << true; QTest::addRow("%s, horizontal+vertical", fmt.data()) << QImage::Format(i) << true << true; } } void tst_QImage::inplaceMirrored() { #if defined(Q_COMPILER_REF_QUALIFIERS) QFETCH(QImage::Format, format); QFETCH(bool, swap_vertical); QFETCH(bool, swap_horizontal); QImage image(16, 16, format); switch (format) { case QImage::Format_Mono: case QImage::Format_MonoLSB: for (int i = 0; i < image.height(); ++i) { ushort* scanLine = (ushort*)image.scanLine(i); *scanLine = (i % 2) ? 0x0fffU : 0xf000U; } break; case QImage::Format_Indexed8: for (int i = 0; i < image.height(); ++i) { for (int j = 0; j < image.width(); ++j) { image.setColor(i*16+j, qRgb(j*16, i*16, 0)); image.setPixel(j, i, i*16+j); } } break; default: 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)); } const uchar* originalPtr = image.constScanLine(0); QImage imageMirrored = std::move(image).mirrored(swap_horizontal, swap_vertical); if (format != QImage::Format_Mono && format != QImage::Format_MonoLSB) { for (int i = 0; i < imageMirrored.height(); ++i) { int mirroredI = swap_vertical ? (imageMirrored.height() - i - 1) : i; for (int j = 0; j < imageMirrored.width(); ++j) { int mirroredJ = swap_horizontal ? (imageMirrored.width() - j - 1) : j; QRgb mirroredColor = imageMirrored.pixel(mirroredJ, mirroredI); QCOMPARE(qRed(mirroredColor) & 0xF8, j * 16); QCOMPARE(qGreen(mirroredColor) & 0xF8, i * 16); } } } else { for (int i = 0; i < imageMirrored.height(); ++i) { ushort* scanLine = (ushort*)imageMirrored.scanLine(i); ushort expect; if (swap_vertical && swap_horizontal) expect = (i % 2) ? 0x000fU : 0xfff0U; else if (swap_vertical) expect = (i % 2) ? 0xf000U : 0x0fffU; else expect = (i % 2) ? 0xfff0U : 0x000fU; QCOMPARE(*scanLine, expect); } } QCOMPARE(imageMirrored.constScanLine(0), originalPtr); for (int rw = 0; rw <= 1; rw++) { // Test attempted inplace conversion of images created on existing buffer uchar *volatileData = 0; QImage orig = imageMirrored; QImage dataSwapped; { QVERIFY(!orig.isNull()); volatileData = new uchar[orig.sizeInBytes()]; memcpy(volatileData, orig.constBits(), orig.sizeInBytes()); QImage dataImage; if (rw) dataImage = QImage(volatileData, orig.width(), orig.height(), orig.format()); else dataImage = QImage((const uchar *)volatileData, orig.width(), orig.height(), orig.format()); if (orig.colorCount()) dataImage.setColorTable(orig.colorTable()); dataSwapped = std::move(dataImage).mirrored(swap_horizontal, swap_vertical); QVERIFY(!dataSwapped.isNull()); delete[] volatileData; } QVERIFY2(dataSwapped.constBits() != volatileData, rw ? "non-const" : "const"); QCOMPARE(dataSwapped, orig.mirrored(swap_horizontal, swap_vertical)); } #endif } void tst_QImage::inplaceMirroredOdd_data() { QTest::addColumn("format"); QTest::addColumn("swap_vertical"); QTest::addColumn("swap_horizontal"); QTest::newRow("Format_ARGB32, vertical") << QImage::Format_ARGB32 << true << false; QTest::newRow("Format_RGB888, vertical") << QImage::Format_RGB888 << true << false; QTest::newRow("Format_RGB16, vertical") << QImage::Format_RGB16 << true << false; QTest::newRow("Format_ARGB32, horizontal") << QImage::Format_ARGB32 << false << true; QTest::newRow("Format_RGB888, horizontal") << QImage::Format_RGB888 << false << true; QTest::newRow("Format_RGB16, horizontal") << QImage::Format_RGB16 << false << true; QTest::newRow("Format_ARGB32, horizontal+vertical") << QImage::Format_ARGB32 << true << true; QTest::newRow("Format_RGB888, horizontal+vertical") << QImage::Format_RGB888 << true << true; QTest::newRow("Format_RGB16, horizontal+vertical") << QImage::Format_RGB16 << true << true; } void tst_QImage::inplaceMirroredOdd() { #if defined(Q_COMPILER_REF_QUALIFIERS) QFETCH(QImage::Format, format); QFETCH(bool, swap_vertical); QFETCH(bool, swap_horizontal); QImage image(15, 15, 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)); const uchar* originalPtr = image.constScanLine(0); QImage imageMirrored = std::move(image).mirrored(swap_horizontal, swap_vertical); for (int i = 0; i < imageMirrored.height(); ++i) { int mirroredI = swap_vertical ? (imageMirrored.height() - i - 1) : i; for (int j = 0; j < imageMirrored.width(); ++j) { int mirroredJ = swap_horizontal ? (imageMirrored.width() - j - 1) : j; QRgb mirroredColor = imageMirrored.pixel(mirroredJ, mirroredI); QCOMPARE(qRed(mirroredColor) & 0xF8, j * 16); QCOMPARE(qGreen(mirroredColor) & 0xF8, i * 16); } } QCOMPARE(imageMirrored.constScanLine(0), originalPtr); #endif } void tst_QImage::inplaceRgbMirrored() { #if defined(Q_COMPILER_REF_QUALIFIERS) QImage image1(32, 32, QImage::Format_ARGB32); QImage image2(32, 32, QImage::Format_ARGB32); image1.fill(0); image2.fill(0); const uchar* originalPtr1 = image1.constScanLine(0); const uchar* originalPtr2 = image2.constScanLine(0); QCOMPARE(std::move(image1).rgbSwapped().mirrored().constScanLine(0), originalPtr1); QCOMPARE(std::move(image2).mirrored().rgbSwapped().constScanLine(0), originalPtr2); #endif } void tst_QImage::genericRgbConversion_data() { QTest::addColumn("format"); QTest::addColumn("dest_format"); for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) { if (i == QImage::Format_Alpha8) continue; const QLatin1String formatI = formatToString(QImage::Format(i)); for (int j = QImage::Format_RGB32; j < QImage::NImageFormats; ++j) { if (j == QImage::Format_Alpha8) continue; if (i == j) continue; QTest::addRow("%s -> %s", formatI.data(), formatToString(QImage::Format(j)).data()) << QImage::Format(i) << QImage::Format(j); } } } void tst_QImage::genericRgbConversion() { // Test that all RGB conversions work and maintain at least 4bit of color accuracy. QFETCH(QImage::Format, format); QFETCH(QImage::Format, dest_format); bool srcGrayscale = format == QImage::Format_Grayscale8 || format == QImage::Format_Grayscale16; bool dstGrayscale = dest_format == QImage::Format_Grayscale8 || dest_format == QImage::Format_Grayscale16; QImage image(16, 16, format); 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) 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); } } } void tst_QImage::inplaceRgbConversion_data() { QTest::addColumn("format"); QTest::addColumn("dest_format"); for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) { if (i == QImage::Format_Alpha8 || i == QImage::Format_Grayscale8 || i == QImage::Format_Grayscale16) { continue; } for (int j = QImage::Format_RGB32; j < QImage::NImageFormats; ++j) { if (j == QImage::Format_Alpha8 || j == QImage::Format_Grayscale8 || j == QImage::Format_Grayscale16) { continue; } if (i == j) continue; if (qt_depthForFormat(QImage::Format(i)) >= qt_depthForFormat(QImage::Format(j))) QTest::addRow("%s -> %s", formatToString(QImage::Format(i)).data(), formatToString(QImage::Format(j)).data()) << QImage::Format(i) << QImage::Format(j); } } } 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); 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)); const uchar* originalPtr = image.constScanLine(0); QImage imageConverted = std::move(image).convertToFormat(dest_format); 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); QCOMPARE(convertedColor & 0xFFF0F0F0, qRgb(j * 16, i * 16, 0)); } } if (qt_depthForFormat(format) == qt_depthForFormat(dest_format)) QCOMPARE(imageConverted.constScanLine(0), originalPtr); if (qt_depthForFormat(format) <= 32) { // Test attempted inplace conversion of images created on existing buffer static const quint32 readOnlyData[] = { 0xff0102ffU, 0xff0506ffU, 0xff0910ffU, 0xff1314ffU }; quint32 readWriteData[] = { 0xff0102ffU, 0xff0506ffU, 0xff0910ffU, 0xff1314ffU }; QImage roInplaceConverted; QImage rwInplaceConverted; { QImage roImage((const uchar *)readOnlyData, 2, 2, format); roInplaceConverted = std::move(roImage).convertToFormat(dest_format); QImage rwImage((uchar *)readWriteData, 2, 2, format); rwInplaceConverted = std::move(rwImage).convertToFormat(dest_format); } QImage roImage2((const uchar *)readOnlyData, 2, 2, format); QImage normalConverted = roImage2.convertToFormat(dest_format); QVERIFY(roInplaceConverted.constBits() != (const uchar *)readOnlyData); QCOMPARE(normalConverted, roInplaceConverted); QVERIFY(rwInplaceConverted.constBits() != (const uchar *)readWriteData); QCOMPARE(normalConverted, rwInplaceConverted); } } void tst_QImage::largeGenericRgbConversion_data() { QTest::addColumn("format"); QTest::addColumn("dest_format"); QImage::Format formats[] = { QImage::Format_RGB32, QImage::Format_ARGB32, QImage::Format_ARGB32_Premultiplied, QImage::Format_RGB16, QImage::Format_RGB888, QImage::Format_RGBA8888, QImage::Format_BGR30, QImage::Format_A2RGB30_Premultiplied, QImage::Format_RGBA64_Premultiplied, }; for (QImage::Format src_format : formats) { for (QImage::Format dst_format : formats) { if (src_format == dst_format) continue; QTest::addRow("%s -> %s", formatToString(src_format).data(), formatToString(dst_format).data()) << src_format << dst_format; } } } void tst_QImage::largeGenericRgbConversion() { // Also test a larger conversion for a few formats (here the tested precision is also higher) QFETCH(QImage::Format, format); QFETCH(QImage::Format, dest_format); // Must have more than 64k pixels to trigger threaded codepath: QImage image(512, 216, format); for (int i = 0; i < image.height(); ++i) for (int j = 0; j < image.width(); ++j) image.setPixel(j, i, qRgb(j % 256, i, 0)); const bool precision_8bit = (format != QImage::Format_RGB16) && (dest_format != QImage::Format_RGB16); QImage imageConverted = image.convertToFormat(dest_format); QCOMPARE(imageConverted.format(), dest_format); for (int i = 0; i < imageConverted.height(); ++i) { for (int j = 0; j < imageConverted.width(); ++j) { if (precision_8bit) { QCOMPARE(imageConverted.pixel(j, i), image.pixel(j, i)); } else { QRgb convertedColor = imageConverted.pixel(j,i); QCOMPARE(qRed(convertedColor) & 0xF8, (j % 256) & 0xF8); QCOMPARE(qGreen(convertedColor) & 0xFC, i & 0xFC); } } } } void tst_QImage::largeInplaceRgbConversion_data() { QTest::addColumn("format"); QTest::addColumn("dest_format"); QImage::Format formats[] = { QImage::Format_RGB32, QImage::Format_ARGB32, QImage::Format_ARGB32_Premultiplied, QImage::Format_RGB16, QImage::Format_RGB888, QImage::Format_RGBA8888, QImage::Format_BGR30, QImage::Format_A2RGB30_Premultiplied, QImage::Format_RGBA64_Premultiplied, }; for (QImage::Format src_format : formats) { for (QImage::Format dst_format : formats) { if (src_format == dst_format) continue; if (qt_depthForFormat(src_format) < qt_depthForFormat(dst_format)) continue; QTest::addRow("%s -> %s", formatToString(src_format).data(), formatToString(dst_format).data()) << src_format << dst_format; } } } void tst_QImage::largeInplaceRgbConversion() { // Also test a larger conversion for a few formats QFETCH(QImage::Format, format); QFETCH(QImage::Format, dest_format); // Must have more than 64k pixels to trigger threaded codepath: QImage image(512, 216, format); for (int i = 0; i < image.height(); ++i) for (int j = 0; j < image.width(); ++j) image.setPixel(j, i, qRgb(j % 256, i, 0)); const bool precision_8bit = (format != QImage::Format_RGB16) && (dest_format != QImage::Format_RGB16); image.convertTo(dest_format); QCOMPARE(image.format(), dest_format); for (int i = 0; i < image.height(); ++i) { for (int j = 0; j < image.width(); ++j) { if (precision_8bit) { QCOMPARE(image.pixel(j,i), qRgb(j % 256, i, 0)); } else { QRgb convertedColor = image.pixel(j,i); QCOMPARE(qRed(convertedColor) & 0xF8, (j % 256) & 0xF8); QCOMPARE(qGreen(convertedColor) & 0xFC, i & 0xFC); } } } } void tst_QImage::deepCopyWhenPaintingActive() { QImage image(64, 64, QImage::Format_ARGB32_Premultiplied); image.fill(0); QPainter painter(&image); QImage copy = image; painter.setBrush(Qt::black); painter.drawEllipse(image.rect()); QVERIFY(copy != image); } void tst_QImage::scaled_QTBUG19157() { QImage foo(5000, 1, QImage::Format_RGB32); foo = foo.scaled(1024, 1024, Qt::KeepAspectRatio); QVERIFY(!foo.isNull()); } void tst_QImage::convertOverUnPreMul() { QImage image(256, 256, QImage::Format_ARGB32_Premultiplied); for (int j = 0; j < 256; j++) { for (int i = 0; i <= j; i++) { image.setPixel(i, j, qRgba(i, i, i, j)); } } QImage image2 = image.convertToFormat(QImage::Format_ARGB32).convertToFormat(QImage::Format_ARGB32_Premultiplied); for (int j = 0; j < 256; j++) { for (int i = 0; i <= j; i++) { QCOMPARE(qAlpha(image2.pixel(i, j)), qAlpha(image.pixel(i, j))); QCOMPARE(qGray(image2.pixel(i, j)), qGray(image.pixel(i, j))); } } } void tst_QImage::scaled_QTBUG35972() { QImage src(532,519,QImage::Format_ARGB32_Premultiplied); src.fill(QColor(Qt::white)); QImage dest(1000,1000,QImage::Format_ARGB32_Premultiplied); dest.fill(QColor(Qt::white)); QPainter painter1(&dest); const QTransform trf(1.25, 0, 0, 1.25, /*dx */ 15.900000000000034, /* dy */ 72.749999999999986); painter1.setTransform(trf); painter1.drawImage(QRectF(-2.6, -2.6, 425.6, 415.20000000000005), src, QRectF(0,0,532,519)); const quint32 *pixels = reinterpret_cast(dest.constBits()); int size = dest.width()*dest.height(); for (int i = 0; i < size; ++i) QCOMPARE(pixels[i], 0xffffffff); } void tst_QImage::convertToPixelFormat() { QPixelFormat rgb565 = qPixelFormatRgba(5,6,5,0,QPixelFormat::IgnoresAlpha, QPixelFormat::AtBeginning, QPixelFormat::NotPremultiplied, QPixelFormat::UnsignedShort); QPixelFormat rgb565ImageFormat = QImage::toPixelFormat(QImage::Format_RGB16); QCOMPARE(rgb565, rgb565ImageFormat); } void tst_QImage::convertToImageFormat_data() { QTest::addColumn("image_format"); QTest::newRow("Convert Format_Invalid") << QImage::Format_Invalid; QTest::newRow("Convert Format_Mono") << QImage::Format_Mono; //This ends up being a QImage::Format_Mono since we cant specify LSB in QPixelFormat //QTest::newRow("Convert Format_MonoLSB") << QImage::Format_MonoLSB; QTest::newRow("Convert Format_Indexed8") << QImage::Format_Indexed8; QTest::newRow("Convert Format_RGB32") << QImage::Format_RGB32; QTest::newRow("Convert Format_ARGB32") << QImage::Format_ARGB32; QTest::newRow("Convert Format_ARGB32_Premultiplied") << QImage::Format_ARGB32_Premultiplied; QTest::newRow("Convert Format_RGB16") << QImage::Format_RGB16; QTest::newRow("Convert Format_ARGB8565_Premultiplied") << QImage::Format_ARGB8565_Premultiplied; QTest::newRow("Convert Format_RGB666") << QImage::Format_RGB666; QTest::newRow("Convert Format_ARGB6666_Premultiplied") << QImage::Format_ARGB6666_Premultiplied; QTest::newRow("Convert Format_RGB555") << QImage::Format_RGB555; QTest::newRow("Convert Format_ARGB8555_Premultiplied") << QImage::Format_ARGB8555_Premultiplied; QTest::newRow("Convert Format_RGB888") << QImage::Format_RGB888; QTest::newRow("Convert Format_RGB444") << QImage::Format_RGB444; QTest::newRow("Convert Format_ARGB4444_Premultiplied") << QImage::Format_ARGB4444_Premultiplied; QTest::newRow("Convert Format_RGBX8888") << QImage::Format_RGBX8888; QTest::newRow("Convert Format_RGBA8888") << QImage::Format_RGBA8888; QTest::newRow("Convert Format_RGBA8888_Premultiplied") << QImage::Format_RGBA8888_Premultiplied; } void tst_QImage::convertToImageFormat() { QFETCH(QImage::Format, image_format); QPixelFormat pixel_format = QImage::toPixelFormat(image_format); QImage::Format format = QImage::toImageFormat(pixel_format); QCOMPARE(format, image_format); } void tst_QImage::invertPixelsRGB_data() { QTest::addColumn("image_format"); for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) { if (i == QImage::Format_Alpha8 || i == QImage::Format_Grayscale8 || i == QImage::Format_Grayscale16) { continue; } QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i); } } void tst_QImage::invertPixelsRGB() { QFETCH(QImage::Format, image_format); QImage image(1, 1, image_format); image.fill(QColor::fromRgb(32, 64, 96)); image.invertPixels(); QCOMPARE(image.format(), image_format); uint pixel = image.pixel(0, 0); QCOMPARE(qRed(pixel) >> 4, (255 - 32) >> 4); QCOMPARE(qGreen(pixel) >> 4, (255 - 64) >> 4); QCOMPARE(qBlue(pixel) >> 4, (255 - 96) >> 4); } void tst_QImage::invertPixelsIndexed() { { QImage image(1, 1, QImage::Format_Mono); image.fill(Qt::color1); image.invertPixels(); QCOMPARE(image.pixelIndex(0, 0), 0); } { QImage image(1, 1, QImage::Format_MonoLSB); image.fill(Qt::color0); image.invertPixels(); QCOMPARE(image.pixelIndex(0, 0), 1); } { QImage image(1, 1, QImage::Format_Indexed8); image.setColorTable({0xff000000, 0xffffffff}); image.fill(Qt::black); image.invertPixels(); QCOMPARE(image.pixelIndex(0, 0), 255); } { QImage image(1, 1, QImage::Format_Indexed8); image.setColorTable({0xff000000, 0xffffffff, 0x80000000, 0x80ffffff, 0x00000000}); image.fill(Qt::white); image.invertPixels(); QCOMPARE(image.pixelIndex(0, 0), 254); } } void tst_QImage::exifOrientation_data() { QTest::addColumn("fileName"); QTest::addColumn("orientation"); QTest::addColumn("dpmx"); QTest::addColumn("dpmy"); QTest::newRow("Orientation 1, Intel format") << m_prefix + "jpeg_exif_orientation_value_1.jpg" << (int)QImageIOHandler::TransformationNone << 39 << 39; QTest::newRow("Orientation 2, Intel format") << m_prefix + "jpeg_exif_orientation_value_2.jpg" << (int)QImageIOHandler::TransformationMirror << 39 << 39; QTest::newRow("Orientation 3, Intel format") << m_prefix + "jpeg_exif_orientation_value_3.jpg" << (int)QImageIOHandler::TransformationRotate180 << 39 << 39; QTest::newRow("Orientation 4, Intel format") << m_prefix + "jpeg_exif_orientation_value_4.jpg" << (int)QImageIOHandler::TransformationFlip << 39 << 39; QTest::newRow("Orientation 5, Intel format") << m_prefix + "jpeg_exif_orientation_value_5.jpg" << (int)QImageIOHandler::TransformationFlipAndRotate90 << 39 << 39; QTest::newRow("Orientation 6, Intel format") << m_prefix + "jpeg_exif_orientation_value_6.jpg" << (int)QImageIOHandler::TransformationRotate90 << 39 << 39; QTest::newRow("Orientation 6, Motorola format") << m_prefix + "jpeg_exif_orientation_value_6_motorola.jpg" << (int)QImageIOHandler::TransformationRotate90 << 39 << 39; QTest::newRow("Orientation 7, Intel format") << m_prefix + "jpeg_exif_orientation_value_7.jpg" << (int)QImageIOHandler::TransformationMirrorAndRotate90 << 39 << 39; QTest::newRow("Orientation 8, Intel format") << m_prefix + "jpeg_exif_orientation_value_8.jpg" << (int)QImageIOHandler::TransformationRotate270 << 39 << 39; } QT_BEGIN_NAMESPACE extern void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient); QT_END_NAMESPACE QT_USE_NAMESPACE void tst_QImage::exifOrientation() { QFETCH(QString, fileName); QFETCH(int, orientation); QFETCH(int, dpmx); QFETCH(int, dpmy); QImageReader imageReader(fileName); imageReader.setAutoTransform(true); QCOMPARE(imageReader.transformation(), orientation); QImage img = imageReader.read(); QCOMPARE(img.dotsPerMeterX(), dpmx); QCOMPARE(img.dotsPerMeterY(), dpmy); QRgb px; QVERIFY(!img.isNull()); px = img.pixel(0, 0); QVERIFY(qRed(px) > 250 && qGreen(px) < 5 && qBlue(px) < 5); px = img.pixel(img.width() - 1, 0); QVERIFY(qRed(px) < 5 && qGreen(px) < 5 && qBlue(px) > 250); QImageReader imageReader2(fileName); QCOMPARE(imageReader2.autoTransform(), false); QCOMPARE(imageReader2.transformation(), orientation); QImage img2 = imageReader2.read(); qt_imageTransform(img2, imageReader2.transformation()); QCOMPARE(img, img2); } void tst_QImage::exif_QTBUG45865() { QFile file(m_prefix + "jpeg_exif_QTBUG-45865.jpg"); QVERIFY(file.open(QIODevice::ReadOnly)); QByteArray byteArray = file.readAll(); QImage image = QImage::fromData(byteArray); QCOMPARE(image.size(), QSize(5, 8)); } void tst_QImage::exifInvalidData_data() { QTest::addColumn("$never used"); QTest::newRow("QTBUG-46870"); QTest::newRow("back_pointers"); QTest::newRow("past_end"); QTest::newRow("too_many_ifds"); QTest::newRow("too_many_tags"); } void tst_QImage::exifInvalidData() { QImage image; QVERIFY(image.load(m_prefix + "jpeg_exif_invalid_data_" + QTest::currentDataTag() + ".jpg")); QVERIFY(!image.isNull()); } 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()); QCOMPARE(image.textKeys().size(), 1); QCOMPARE(image.textKeys().first(), "Description"); // check if exif comment is read as utf-8 QCOMPARE(image.text("Description"), QString::fromUtf8("some unicode chars: ÖÄÜ€@")); QByteArray ba; { QBuffer buf(&ba); QVERIFY(buf.open(QIODevice::WriteOnly)); QVERIFY(image.save(&buf, "JPG")); } QVERIFY(!ba.isEmpty()); image = QImage(); QCOMPARE(image.textKeys().size(), 0); { QBuffer buf(&ba); QVERIFY(buf.open(QIODevice::ReadOnly)); QVERIFY(image.load(&buf, "JPG")); } // compare written (and reread) description text QCOMPARE(image.text("Description"), QString::fromUtf8("some unicode chars: ÖÄÜ€@")); } static void cleanupFunction(void* info) { bool *called = static_cast(info); *called = true; } void tst_QImage::cleanupFunctions() { QImage bufferImage(64, 64, QImage::Format_ARGB32); bufferImage.fill(0); bool called; { called = false; { QImage image(bufferImage.bits(), bufferImage.width(), bufferImage.height(), bufferImage.format(), cleanupFunction, &called); } QVERIFY(called); } { called = false; QImage *copy = nullptr; { QImage image(bufferImage.bits(), bufferImage.width(), bufferImage.height(), bufferImage.format(), cleanupFunction, &called); copy = new QImage(image); } QVERIFY(!called); 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 void tst_QImage::devicePixelRatio() { // create image QImage a(64, 64, QImage::Format_ARGB32); a.fill(Qt::white); QCOMPARE(a.devicePixelRatio(), qreal(1.0)); QCOMPARE(a.isDetached(), true); // copy image QImage b = a; QCOMPARE(b.devicePixelRatio(), qreal(1.0)); QCOMPARE(a.isDetached(), false); QCOMPARE(b.isDetached(), false); // set devicePixelRatio to the current value: does not detach a.setDevicePixelRatio(qreal(1.0)); QCOMPARE(a.isDetached(), false); QCOMPARE(b.isDetached(), false); // set devicePixelRatio to a new value: may detach (currently // does, but we may want to avoid the data copy the future) a.setDevicePixelRatio(qreal(2.0)); QCOMPARE(a.devicePixelRatio(), qreal(2.0)); 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); ((uint*)a.bits())[0] = (3U << 30) | (128 << 20) | (256 << 10) | 512; ((uint*)a.bits())[1] = (2U << 30) | (131 << 20) | (259 << 10) | 515; ((uint*)a.bits())[2] = (1U << 30) | ( 67 << 20) | (131 << 10) | 259; QImage b = a.convertToFormat(QImage::Format_RGB30); const uint* bbits = (const uint*)b.bits(); QCOMPARE(bbits[0], (3U << 30) | (128 << 20) | (256 << 10) | 512); QCOMPARE(bbits[1], (3U << 30) | (196 << 20) | (388 << 10) | 772); QCOMPARE(bbits[2], (3U << 30) | (201 << 20) | (393 << 10) | 777); } void tst_QImage::rgb30Repremul_data() { QTest::addColumn("color"); for (int i = 255; i > 0; i -= 15) { QTest::addRow("100%% red=%d", i) << qRgba(i, 0, 0, 0xff); QTest::addRow("75%% red=%d", i) << qRgba(i, 0, 0, 0xc0); QTest::addRow("50%% red=%d", i) << qRgba(i, 0, 0, 0x80); QTest::addRow("37.5%% red=%d", i) << qRgba(i, 0, 0, 0x60); } } void tst_QImage::rgb30Repremul() { QFETCH(uint, color); QImage a(1, 1, QImage::Format_ARGB32); a.setPixel(0, 0, color); QImage b = a.convertToFormat(QImage::Format_A2BGR30_Premultiplied); b = b.convertToFormat(QImage::Format_ARGB32); uint expectedColor = qUnpremultiply(qPremultiply(color)); uint newColor = b.pixel(0, 0); QVERIFY(qAbs(qRed(newColor) - qRed(expectedColor)) <= 1); } void tst_QImage::metadataPassthrough() { QImage a(64, 64, QImage::Format_ARGB32); a.fill(Qt::white); a.setText(QStringLiteral("Test"), QStringLiteral("Text")); a.setDotsPerMeterX(100); a.setDotsPerMeterY(80); a.setDevicePixelRatio(2.0); QImage scaled = a.scaled(QSize(32, 32), Qt::IgnoreAspectRatio, Qt::SmoothTransformation); QCOMPARE(scaled.text(QStringLiteral("Test")), a.text(QStringLiteral("Test"))); QCOMPARE(scaled.dotsPerMeterX(), a.dotsPerMeterX()); QCOMPARE(scaled.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(scaled.devicePixelRatio(), a.devicePixelRatio()); scaled = a.scaled(QSize(128, 128), Qt::IgnoreAspectRatio, Qt::FastTransformation); QCOMPARE(scaled.text(QStringLiteral("Test")), a.text(QStringLiteral("Test"))); QCOMPARE(scaled.dotsPerMeterX(), a.dotsPerMeterX()); QCOMPARE(scaled.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(scaled.devicePixelRatio(), a.devicePixelRatio()); QImage mirrored = a.mirrored(); QCOMPARE(mirrored.text(QStringLiteral("Test")), a.text(QStringLiteral("Test"))); QCOMPARE(mirrored.dotsPerMeterX(), a.dotsPerMeterX()); QCOMPARE(mirrored.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(mirrored.devicePixelRatio(), a.devicePixelRatio()); QTransform t; t.rotate(90); QImage rotated = a.transformed(t); QCOMPARE(rotated.text(QStringLiteral("Test")), a.text(QStringLiteral("Test"))); QCOMPARE(rotated.dotsPerMeterX(), a.dotsPerMeterX()); QCOMPARE(rotated.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(rotated.devicePixelRatio(), a.devicePixelRatio()); QImage swapped = a.rgbSwapped(); QCOMPARE(swapped.text(QStringLiteral("Test")), a.text(QStringLiteral("Test"))); QCOMPARE(swapped.dotsPerMeterX(), a.dotsPerMeterX()); QCOMPARE(swapped.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(swapped.devicePixelRatio(), a.devicePixelRatio()); QImage converted = a.convertToFormat(QImage::Format_RGB32); QCOMPARE(converted.text(QStringLiteral("Test")), a.text(QStringLiteral("Test"))); QCOMPARE(converted.dotsPerMeterX(), a.dotsPerMeterX()); QCOMPARE(converted.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(converted.devicePixelRatio(), a.devicePixelRatio()); QList 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()); QCOMPARE(copied.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(copied.devicePixelRatio(), a.devicePixelRatio()); QImage alphaMask = a.createAlphaMask(); QCOMPARE(alphaMask.dotsPerMeterX(), a.dotsPerMeterX()); QCOMPARE(alphaMask.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(alphaMask.devicePixelRatio(), a.devicePixelRatio()); QImage heuristicMask = a.createHeuristicMask(); QCOMPARE(heuristicMask.dotsPerMeterX(), a.dotsPerMeterX()); QCOMPARE(heuristicMask.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(heuristicMask.devicePixelRatio(), a.devicePixelRatio()); QImage maskFromColor = a.createMaskFromColor(qRgb(0, 0, 0)); QCOMPARE(maskFromColor.dotsPerMeterX(), a.dotsPerMeterX()); QCOMPARE(maskFromColor.dotsPerMeterY(), a.dotsPerMeterY()); QCOMPARE(maskFromColor.devicePixelRatio(), a.devicePixelRatio()); } void tst_QImage::pixelColor() { QImage argb32(1, 1, QImage::Format_ARGB32); QImage argb32pm(1, 1, QImage::Format_ARGB32_Premultiplied); QColor c(Qt::red); c.setAlpha(128); argb32.setPixelColor(QPoint(0, 0), c); argb32pm.setPixelColor(QPoint(0, 0), c); QCOMPARE(argb32.pixelColor(QPoint(0, 0)), c); QCOMPARE(argb32pm.pixelColor(QPoint(0, 0)), c); QImage t = argb32.convertToFormat(QImage::Format_ARGB32_Premultiplied); QCOMPARE(t.pixel(0,0), argb32pm.pixel(0,0)); // Try specifying an invalid position. QTest::ignoreMessage(QtWarningMsg, "QImage::setPixelColor: coordinate (-1,-1) out of range"); argb32.setPixelColor(-1, -1, QColor(Qt::red)); // 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() { { QImage mono(1, 1, QImage::Format_Mono); QImage monolsb(1, 1, QImage::Format_MonoLSB); QImage indexed(1, 1, QImage::Format_Indexed8); mono.fill(0); monolsb.fill(0); indexed.fill(0); QCOMPARE(QColor(mono.pixel(0, 0)), QColor(Qt::black)); QCOMPARE(QColor(monolsb.pixel(0, 0)), QColor(Qt::black)); indexed.pixel(0, 0); // Don't crash } { uchar a = 0; QImage mono(&a, 1, 1, QImage::Format_Mono); QImage monolsb(&a, 1, 1, QImage::Format_MonoLSB); QImage indexed(&a, 1, 1, QImage::Format_Indexed8); mono.pixel(0, 0); // Don't crash monolsb.pixel(0, 0); // Don't crash indexed.pixel(0, 0); // Don't crash } } void tst_QImage::ditherGradient_data() { QTest::addColumn("image"); QTest::addColumn("format"); QTest::addColumn("flags"); QTest::addColumn("minimumExpectedGradient"); QImage rgb32(256, 16, QImage::Format_RGB32); QLinearGradient gradient(QRectF(rgb32.rect()).topLeft(), QRectF(rgb32.rect()).topRight()); gradient.setColorAt(0.0, QColor(0, 0, 0)); gradient.setColorAt(1.0, QColor(255, 255, 255)); QPainter p; p.begin(&rgb32); p.fillRect(rgb32.rect(), gradient); p.end(); QTest::newRow("rgb32 -> rgb444 (no dither)") << rgb32 << QImage::Format_RGB444 << 0 << 16; QTest::newRow("rgb32 -> rgb444 (dithering)") << rgb32 << QImage::Format_RGB444 << int(Qt::PreferDither | Qt::OrderedDither) << 33; QTest::newRow("rgb32 -> argb4444pm (dithering)") << rgb32 << QImage::Format_ARGB4444_Premultiplied << int(Qt::PreferDither | Qt::OrderedDither) << 33; QTest::newRow("rgb32 -> rgb16 (no dither)") << rgb32 << QImage::Format_RGB16 << 0 << 32; QTest::newRow("rgb32 -> rgb16 (dithering)") << rgb32 << QImage::Format_RGB16 << int(Qt::PreferDither | Qt::OrderedDither) << 65; QTest::newRow("rgb32 -> rgb666 (no dither)") << rgb32 << QImage::Format_RGB666 << 0 << 64; QTest::newRow("rgb32 -> rgb666 (dithering)") << rgb32 << QImage::Format_RGB666 << int(Qt::PreferDither | Qt::OrderedDither) << 129; // Test we get the same results for opaque input in the ARGBPM implementation. rgb32 = std::move(rgb32).convertToFormat(QImage::Format_ARGB32_Premultiplied); QTest::newRow("argb32pm -> argb4444pm (no dither)") << rgb32 << QImage::Format_ARGB4444_Premultiplied << 0 << 16; QTest::newRow("argb32pm -> rgb444 (dithering)") << rgb32 << QImage::Format_RGB444 << int(Qt::PreferDither | Qt::OrderedDither) << 33; QTest::newRow("argb32pm -> argb4444pm (dithering)") << rgb32 << QImage::Format_ARGB4444_Premultiplied << int(Qt::PreferDither | Qt::OrderedDither) << 33; QTest::newRow("argb32pm -> argb8565pm (no dither)") << rgb32 << QImage::Format_ARGB8565_Premultiplied << 0 << 32; QTest::newRow("argb32pm -> argb8565pm (dithering)") << rgb32 << QImage::Format_ARGB8565_Premultiplied << int(Qt::PreferDither | Qt::OrderedDither) << 65; QTest::newRow("argb32pm -> argb6666pm (no dither)") << rgb32 << QImage::Format_ARGB6666_Premultiplied << 0 << 64; QTest::newRow("argb32pm -> argb6666pm (dithering)") << rgb32 << QImage::Format_ARGB6666_Premultiplied << int(Qt::PreferDither | Qt::OrderedDither) << 129; #if QT_CONFIG(raster_64bit) QImage rgb30(1024, 16, QImage::Format_RGB30); QLinearGradient gradient30(QRectF(rgb30.rect()).topLeft(), QRectF(rgb30.rect()).topRight()); gradient30.setColorAt(0.0, QColor(0, 0, 0)); gradient30.setColorAt(1.0, QColor(255, 255, 255)); p.begin(&rgb30); p.fillRect(rgb30.rect(), gradient30); p.end(); QTest::newRow("rgb30 -> rgb32 (no dither)") << rgb30 << QImage::Format_RGB32 << 0 << 256; QTest::newRow("rgb30 -> rgb32 (dithering)") << rgb30 << QImage::Format_RGB32 << int(Qt::PreferDither | Qt::OrderedDither) << 513; QTest::newRow("rgb30 -> rgb888 (no dither)") << rgb30 << QImage::Format_RGB888 << 0 << 256; QTest::newRow("rgb30 -> rgb888 (dithering)") << rgb30 << QImage::Format_RGB888 << int(Qt::PreferDither | Qt::OrderedDither) << 513; #endif } void tst_QImage::ditherGradient() { QFETCH(QImage, image); QFETCH(QImage::Format, format); QFETCH(int, flags); QFETCH(int, minimumExpectedGradient); QImage converted = image.convertToFormat(format, (Qt::ImageConversionFlags)flags); int observedGradientSteps = 0; int lastTotal = -1; for (int i = 0; i < converted.width(); ++i) { int total = 0; for (int j = 0; j < converted.height(); ++j) { uint c = converted.pixel(i, j); QCOMPARE(qAlpha(c), 255); total += qRed(c); } if (total > lastTotal) { observedGradientSteps++; lastTotal = total; } } QVERIFY(observedGradientSteps >= minimumExpectedGradient); } void tst_QImage::reinterpretAsFormat_data() { QTest::addColumn("in_format"); QTest::addColumn("out_format"); QTest::addColumn("in_color"); QTest::addColumn("out_color"); #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN QTest::newRow("rgb32 -> rgbx8888") << QImage::Format_RGB32 << QImage::Format_RGBX8888 << QColor(Qt::red) << QColor(Qt::blue); QTest::newRow("rgba8888 -> argb32") << QImage::Format_RGBA8888 << QImage::Format_ARGB32 << QColor(Qt::red) << QColor(Qt::blue); QTest::newRow("argb32pm -> rgba8888pm") << QImage::Format_RGBA8888_Premultiplied << QImage::Format_ARGB32_Premultiplied << QColor(Qt::green) << QColor(Qt::green); #endif 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 (red) -> rgb32") << QImage::Format_ARGB32_Premultiplied << QImage::Format_RGB32 << QColor(255, 0, 0, 127) << QColor(127, 0, 0); } void tst_QImage::reinterpretAsFormat() { QFETCH(QImage::Format, in_format); QFETCH(QImage::Format, out_format); QFETCH(QColor, in_color); QFETCH(QColor, out_color); QImage image(1, 1, in_format); image.setPixelColor(0, 0, in_color); QVERIFY(image.reinterpretAsFormat(out_format)); QCOMPARE(image.pixelColor(0, 0), out_color); } void tst_QImage::reinterpretAsFormat2() { const uint imageData[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; { QImage image(reinterpret_cast(imageData), 4, 2, QImage::Format_RGB32); QCOMPARE(image.pixelColor(0, 0), QColor(Qt::black)); QVERIFY(image.isDetached()); QVERIFY(image.reinterpretAsFormat(QImage::Format_ARGB32_Premultiplied)); QCOMPARE(image.constBits(), reinterpret_cast(imageData)); QCOMPARE(image.pixelColor(0, 0), QColor(Qt::transparent)); QVERIFY(!image.reinterpretAsFormat(QImage::Format_Grayscale8)); } { QImage image(reinterpret_cast(imageData), 8, 4, QImage::Format_Indexed8); image.setColor(0, qRgb(255, 255, 255)); QCOMPARE(image.pixelColor(0, 0), QColor(Qt::white)); QVERIFY(image.reinterpretAsFormat(QImage::Format_Grayscale8)); QCOMPARE(image.pixelColor(0, 0), QColor(Qt::black)); QVERIFY(image.reinterpretAsFormat(QImage::Format_Alpha8)); QCOMPARE(image.pixelColor(0, 0), QColor(Qt::transparent)); QVERIFY(!image.reinterpretAsFormat(QImage::Format_RGB16)); } } void tst_QImage::complexTransform8bit() { QImage img1(100, 100, QImage::Format_RGB32); img1.fill(Qt::green); img1 = img1.convertToFormat(QImage::Format_Indexed8); QImage img2 = img1.transformed(QTransform().rotate(45), Qt::SmoothTransformation); // Currently the format is always QImage::Format_ARGB32_Premultiplied, but it // doesn't have to be, and if it becomes indexed this test is no longer be valid. QVERIFY(img2.format() > QImage::Format_Indexed8); QCOMPARE(img2.colorCount(), 0); } #ifdef Q_OS_DARWIN void tst_QImage::toCGImage_data() { QTest::addColumn("format"); QTest::addColumn("supported"); // Populate test data with supported status for all QImage formats. QSet supported = { QImage::Format_ARGB32, QImage::Format_RGB32, QImage::Format_RGBA8888_Premultiplied, QImage::Format_RGBA8888, QImage::Format_RGBX8888, QImage::Format_ARGB32_Premultiplied }; for (int i = QImage::Format_Invalid; i < QImage::Format_Grayscale8; ++i) { QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i) << supported.contains(QImage::Format(i)); } } // Verify that toCGImage() returns a valid CGImageRef for supported image formats. void tst_QImage::toCGImage() { QFETCH(QImage::Format, format); QFETCH(bool, supported); QImage qimage(64, 64, format); qimage.fill(Qt::red); CGImageRef cgimage = qimage.toCGImage(); QCOMPARE(cgimage != nullptr, supported); CGImageRelease(cgimage); } #endif void tst_QImage::hugeQImage() { #if Q_PROCESSOR_WORDSIZE < 8 QSKIP("Test only makes sense on 64-bit machines"); #else std::unique_ptr 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); QCOMPARE(image.width(), 25000); QCOMPARE(image.sizeInBytes(), qsizetype(25000)*25000*4); QCOMPARE(image.bytesPerLine(), 25000 * 4); QCOMPARE(image.constScanLine(24990), image.constBits() + qsizetype(25000)*24990*4); image.setPixel(20000, 24990, 0xffaabbcc); QCOMPARE(image.pixel(20000, 24990), 0xffaabbcc); QCOMPARE((reinterpret_cast(image.constScanLine(24990)))[20000], 0xffaabbcc); QImage canvas(100, 100, QImage::Format_RGB32); QPainter painter(&canvas); painter.drawImage(0,0, image, 19950, 24900, 100, 100); painter.end(); QCOMPARE(reinterpret_cast(canvas.constScanLine(90))[50], 0xffaabbcc); #endif } void tst_QImage::convertColorTable() { QImage image(10, 10, QImage::Format_Indexed8); image.setColor(0, 0x80ffffff); image.fill(0); QImage argb32 = image.convertToFormat(QImage::Format_ARGB32); QCOMPARE(argb32.pixel(0,0), 0x80ffffff); QImage argb32pm = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); QCOMPARE(argb32pm.pixel(0,0), 0x80808080); QImage rgb32 = image.convertToFormat(QImage::Format_RGB32); QCOMPARE(rgb32.pixel(0,0), 0xffffffff); } void tst_QImage::wideImage() { // QTBUG-73731 and QTBUG-73732 QImage i(538994187, 2, QImage::Format_ARGB32); QImage i2(32, 32, QImage::Format_ARGB32); i2.fill(Qt::white); // Test that it doesn't crash: QPainter painter(&i); // With the composition mode is SourceOver out it's an invalid write // With the composition mode is Source it's an invalid read painter.drawImage(0, 0, i2); painter.setCompositionMode(QPainter::CompositionMode_Source); painter.drawImage(0, 0, i2); // 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::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); } #if defined(Q_OS_WIN) static inline QColor COLORREFToQColor(COLORREF cr) { return QColor(GetRValue(cr), GetGValue(cr), GetBValue(cr)); } void tst_QImage::toWinHBITMAP_data() { QTest::addColumn("format"); QTest::addColumn("color"); QTest::addColumn("bottomRightColor"); const QColor red(Qt::red); const QColor green(Qt::green); const QColor blue(Qt::blue); const QColor gray(Qt::gray); const QColor gray555(0x5a, 0x5a, 0x5a); // Note: Interpolation 8<->5 bit occurs. const QColor white(Qt::white); const QColor black(Qt::black); QTest::newRow("argb32p-red") << QImage::Format_ARGB32_Premultiplied << red << gray; QTest::newRow("argb32p-green") << QImage::Format_ARGB32_Premultiplied << green << gray; QTest::newRow("argb32p-blue") << QImage::Format_ARGB32_Premultiplied << blue << gray; QTest::newRow("rgb888-red") << QImage::Format_RGB888 << red << gray; QTest::newRow("rgb888-green") << QImage::Format_RGB888 << green << gray; QTest::newRow("rgb888-blue") << QImage::Format_RGB888 << blue << gray; QTest::newRow("indexed8-red") << QImage::Format_Indexed8 << red << gray; QTest::newRow("indexed8-green") << QImage::Format_Indexed8 << green << gray; QTest::newRow("indexed8-blue") << QImage::Format_Indexed8 << blue << gray; QTest::newRow("rgb555-red") << QImage::Format_RGB555 << red << gray555; QTest::newRow("rgb555-green") << QImage::Format_RGB555 << green << gray555; QTest::newRow("rgb555-blue") << QImage::Format_RGB555 << blue << gray555; QTest::newRow("mono") << QImage::Format_Mono << white << black; } // Test image filled with color, black pixel at botttom right corner. static inline QImage createTestImage(QImage::Format format, int width, int height, const QColor &fillColor, const QColor &bottomRightColor) { QImage image(QSize(width, height), format); image.fill(fillColor); QPainter painter(&image); QPen pen = painter.pen(); pen.setColor(bottomRightColor); painter.setPen(pen); painter.drawPoint(width -1, height - 1); return image; } void tst_QImage::toWinHBITMAP() { static const int width = 73; static const int height = 57; QFETCH(QImage::Format, format); QFETCH(QColor, color); QFETCH(QColor, bottomRightColor); // Cannot paint on indexed/mono images. const QImage image = format == QImage::Format_Indexed8 || format == QImage::Format_Mono ? createTestImage(QImage::Format_RGB32, width, height, color, bottomRightColor).convertToFormat(format) : createTestImage(format, width, height, color, bottomRightColor); const HBITMAP bitmap = image.toHBITMAP(); QVERIFY(bitmap != 0); // Verify size BITMAP bitmapInfo; memset(&bitmapInfo, 0, sizeof(BITMAP)); const int res = GetObject(bitmap, sizeof(BITMAP), &bitmapInfo); QVERIFY(res); QCOMPARE(width, int(bitmapInfo.bmWidth)); QCOMPARE(height, int(bitmapInfo.bmHeight)); const HDC displayDc = GetDC(0); const HDC bitmapDc = CreateCompatibleDC(displayDc); const HBITMAP nullBitmap = static_cast(SelectObject(bitmapDc, bitmap)); QCOMPARE(COLORREFToQColor(GetPixel(bitmapDc, 0, 0)), color); QCOMPARE(COLORREFToQColor(GetPixel(bitmapDc, width - 1, 3)), color); QCOMPARE(COLORREFToQColor(GetPixel(bitmapDc, 3, height - 1)), color); QCOMPARE(COLORREFToQColor(GetPixel(bitmapDc, width - 1, height - 1)), bottomRightColor); const QImage convertedBack = QImage::fromHBITMAP(bitmap); QCOMPARE(convertedBack.convertToFormat(QImage::Format_ARGB32_Premultiplied), image.convertToFormat(QImage::Format_ARGB32_Premultiplied)); // Clean up SelectObject(bitmapDc, nullBitmap); DeleteObject(bitmap); DeleteDC(bitmapDc); ReleaseDC(0, displayDc); } void tst_QImage::fromMonoHBITMAP() // QTBUG-72343, corruption for mono bitmaps { enum : int { width = 32, height = 32, size = width * height / 8 }; // 32x32 mono bitmap char bitmapData[size]; memset(bitmapData, 0, size); const HBITMAP hbitmap = CreateBitmap(width, height, /* planes */ 1, /* bitcount */ 1, bitmapData); const QImage image = QImage::fromHBITMAP(hbitmap); QCOMPARE(image.size(), QSize(width, height)); QCOMPARE(image.scanLine(0)[0], 0u); DeleteObject(hbitmap); } #endif // Q_OS_WIN QTEST_GUILESS_MAIN(tst_QImage) #include "tst_qimage.moc"