From 990d150fe56f879da0b1c15722e9c639ce80ab45 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Thu, 7 Dec 2023 13:46:35 +0100 Subject: [PATCH] Centralize conversion from unpremultiplied to premultiplied image format Makes it possible to add new unpremultiplied formats later. Change-Id: Id998a2674ca9067a0e2a5f85c7baf04bcd9a9912 Reviewed-by: Marc Mutz --- src/gui/image/qimage_conversions.cpp | 16 ++++++-------- src/gui/image/qimage_p.h | 16 ++++++++++++++ tests/auto/gui/image/qimage/tst_qimage.cpp | 25 ++++++++++++++++++++++ 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 2e1eb5e5e40..43d8cdbec9d 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -165,7 +165,7 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied && !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) { // Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format. - fetch = qPixelLayouts[src->format + 1].fetchToARGB32PM; + fetch = qPixelLayouts[qt_toPremultipliedFormat(src->format)].fetchToARGB32PM; if (dest->format == QImage::Format_RGB32) store = storeRGB32FromARGB32; else @@ -383,7 +383,7 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied && !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) { // Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format. - fetch = qPixelLayouts[data->format + 1].fetchToARGB32PM; + fetch = qPixelLayouts[qt_toPremultipliedFormat(data->format)].fetchToARGB32PM; if (data->format == QImage::Format_RGB32) store = storeRGB32FromARGB32; else @@ -485,9 +485,8 @@ bool convert_generic_inplace_over_rgb64(QImageData *data, QImage::Format dst_for if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied && destLayout->hasAlphaChannel && !destLayout->premultiplied) { // Avoid unnecessary premultiply and unpremultiply when converting between two unpremultiplied formats. - // This abuses the fact unpremultiplied formats are always before their premultiplied counterparts. - fetch = qPixelLayouts[data->format + 1].fetchToRGBA64PM; - store = qStoreFromRGBA64PM[dst_format + 1]; + fetch = qPixelLayouts[qt_toPremultipliedFormat(data->format)].fetchToRGBA64PM; + store = qStoreFromRGBA64PM[qt_toPremultipliedFormat(dst_format)]; } auto convertSegment = [=](int yStart, int yEnd) { @@ -580,9 +579,8 @@ bool convert_generic_inplace_over_rgba32f(QImageData *data, QImage::Format dst_f if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied && destLayout->hasAlphaChannel && !destLayout->premultiplied) { // Avoid unnecessary premultiply and unpremultiply when converting between two unpremultiplied formats. - // This abuses the fact unpremultiplied formats are always before their premultiplied counterparts. - fetch = qFetchToRGBA32F[data->format + 1]; - store = qStoreFromRGBA32F[dst_format + 1]; + fetch = qFetchToRGBA32F[qt_toPremultipliedFormat(data->format)]; + store = qStoreFromRGBA32F[qt_toPremultipliedFormat(dst_format)]; } auto convertSegment = [=](int yStart, int yEnd) { @@ -1323,7 +1321,7 @@ static void convert_ARGB32_to_RGBA64(QImageData *dest, const QImageData *src, Qt const uchar *src_data = src->data; uchar *dest_data = dest->data; - const FetchAndConvertPixelsFunc64 fetch = qPixelLayouts[src->format + 1].fetchToRGBA64PM; + const FetchAndConvertPixelsFunc64 fetch = qPixelLayouts[qt_toPremultipliedFormat(src->format)].fetchToRGBA64PM; for (int i = 0; i < src->height; ++i) { fetch(reinterpret_cast(dest_data), src_data, 0, src->width, nullptr, nullptr); diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index 27b32ca2003..d355bccacea 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -21,6 +21,8 @@ #include #include #include +#include + QT_BEGIN_NAMESPACE @@ -315,6 +317,20 @@ inline QImage::Format qt_alphaVersion(QImage::Format format) return QImage::Format_ARGB32_Premultiplied; } +constexpr QImage::Format qt_toUnpremultipliedFormat(QImage::Format format) +{ + // Assumes input is already a premultiplied format with an unpremultiplied counterpart + // This abuses the fact unpremultiplied formats are always before their premultiplied counterparts. + return static_cast(qToUnderlying(format) - 1); +} + +constexpr QImage::Format qt_toPremultipliedFormat(QImage::Format format) +{ + // Assumes input is already an unpremultiplied format + // This abuses the fact unpremultiplied formats are always before their premultiplied counterparts. + return static_cast(qToUnderlying(format) + 1); +} + inline bool qt_highColorPrecision(QImage::Format format, bool opaque = false) { // Formats with higher color precision than ARGB32_Premultiplied. diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index fd155fbf1c2..c9a42af42a6 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -238,6 +238,9 @@ private slots: void fromMonoHBITMAP(); #endif // Q_OS_WIN + void tofromPremultipliedFormat_data(); + void tofromPremultipliedFormat(); + private: const QString m_prefix; }; @@ -4211,5 +4214,27 @@ void tst_QImage::fromMonoHBITMAP() // QTBUG-72343, corruption for mono bitmaps #endif // Q_OS_WIN +void tst_QImage::tofromPremultipliedFormat_data() +{ + QTest::addColumn("unpremul"); + QTest::addColumn("premul"); + + // Test all available formats with both premultiplied and unpremultiplied versions + QTest::newRow("argb32") << QImage::Format_ARGB32 << QImage::Format_ARGB32_Premultiplied; + QTest::newRow("rgba8888") << QImage::Format_RGBA8888 << QImage::Format_RGBA8888_Premultiplied; + QTest::newRow("rgba64") << QImage::Format_RGBA64 << QImage::Format_RGBA64_Premultiplied; + QTest::newRow("rgba16fpx4") << QImage::Format_RGBA16FPx4 << QImage::Format_RGBA16FPx4_Premultiplied; + QTest::newRow("rgba32fpx4") << QImage::Format_RGBA32FPx4 << QImage::Format_RGBA32FPx4_Premultiplied; +} + +void tst_QImage::tofromPremultipliedFormat() +{ + QFETCH(QImage::Format, unpremul); + QFETCH(QImage::Format, premul); + + QCOMPARE(qt_toPremultipliedFormat(unpremul), premul); + QCOMPARE(qt_toUnpremultipliedFormat(premul), unpremul); +} + QTEST_GUILESS_MAIN(tst_QImage) #include "tst_qimage.moc"