diff options
-rw-r--r-- | src/gui/painting/qdrawhelper.cpp | 10 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper_p.h | 17 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper_sse4.cpp | 16 | ||||
-rw-r--r-- | src/gui/painting/qdrawingprimitive_sse2_p.h | 38 | ||||
-rw-r--r-- | tests/auto/gui/image/qimage/tst_qimage.cpp | 27 |
5 files changed, 106 insertions, 2 deletions
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index d34b2b9a44..d74b48a3ca 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -6640,6 +6640,10 @@ void qt_memfill32(quint32 *dest, quint32 color, int count) } #endif +#ifdef QT_COMPILER_SUPPORTS_SSE4_1 +template<QtPixelOrder> const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *); +#endif + void qInitDrawhelperAsm() { CompositionFunction *functionForModeAsm = 0; @@ -6704,7 +6708,7 @@ void qInitDrawhelperAsm() } #endif // SSSE3 -#if QT_COMPILER_SUPPORTS_SSE4_1 +#if defined(QT_COMPILER_SUPPORTS_SSE4_1) if (qCpuHasFeature(SSE4_1)) { #if !defined(__SSE4_1__) extern const uint *QT_FASTCALL convertARGB32ToARGB32PM_sse4(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *); @@ -6718,10 +6722,12 @@ void qInitDrawhelperAsm() qPixelLayouts[QImage::Format_ARGB32].convertFromARGB32PM = convertARGB32FromARGB32PM_sse4; qPixelLayouts[QImage::Format_RGBA8888].convertFromARGB32PM = convertRGBA8888FromARGB32PM_sse4; qPixelLayouts[QImage::Format_RGBX8888].convertFromARGB32PM = convertRGBXFromARGB32PM_sse4; + qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].convertFromARGB32PM = convertA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>; + qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].convertFromARGB32PM = convertA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>; } #endif -#if QT_COMPILER_SUPPORTS_AVX2 && !defined(__AVX2__) +#if defined(QT_COMPILER_SUPPORTS_AVX2) && !defined(__AVX2__) if (qCpuHasFeature(AVX2)) { extern const uint *QT_FASTCALL convertARGB32ToARGB32PM_avx2(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *); extern const uint *QT_FASTCALL convertRGBA8888ToARGB32PM_avx2(uint *buffer, const uint *src, int count, const QPixelLayout *, const QRgb *); diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index 66ef5949d9..73ff21812a 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -847,9 +847,25 @@ template<enum QtPixelOrder> inline uint qConvertRgb32ToRgb30(QRgb); template<enum QtPixelOrder> inline QRgb qConvertA2rgb30ToArgb32(uint c); +// A combined unpremultiply and premultiply with new simplified alpha. +// Needed when alpha loses precision relative to other colors during conversion (ARGB32 -> A2RGB30). +template<unsigned int Shift> +inline QRgb qRepremultiply(QRgb p) +{ + const uint alpha = qAlpha(p); + if (alpha == 255 || alpha == 0) + return p; + p = qUnpremultiply(p); + Q_CONSTEXPR uint mult = 255 / (255 >> Shift); + const uint newAlpha = mult * (alpha >> Shift); + p = (p & ~0xff000000) | (newAlpha<<24); + return qPremultiply(p); +} + template<> inline uint qConvertArgb32ToA2rgb30<PixelOrderBGR>(QRgb c) { + c = qRepremultiply<6>(c); return (c & 0xc0000000) | (((c << 22) & 0x3fc00000) | ((c << 14) & 0x00300000)) | (((c << 4) & 0x000ff000) | ((c >> 4) & 0x00000c00)) @@ -859,6 +875,7 @@ inline uint qConvertArgb32ToA2rgb30<PixelOrderBGR>(QRgb c) template<> inline uint qConvertArgb32ToA2rgb30<PixelOrderRGB>(QRgb c) { + c = qRepremultiply<6>(c); return (c & 0xc0000000) | (((c << 6) & 0x3fc00000) | ((c >> 2) & 0x00300000)) | (((c << 4) & 0x000ff000) | ((c >> 4) & 0x00000c00)) diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp index 43a3958997..7cc498eefc 100644 --- a/src/gui/painting/qdrawhelper_sse4.cpp +++ b/src/gui/painting/qdrawhelper_sse4.cpp @@ -74,6 +74,22 @@ const uint *QT_FASTCALL convertRGBXFromARGB32PM_sse4(uint *buffer, const uint *s return buffer; } +template<QtPixelOrder PixelOrder> +const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4(uint *buffer, const uint *src, int count, + const QPixelLayout *, const QRgb *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = qConvertArgb32ToA2rgb30_sse4<PixelOrder>(src[i]); + return buffer; +} + +template +const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>(uint *buffer, const uint *src, int count, + const QPixelLayout *, const QRgb *); +template +const uint *QT_FASTCALL convertA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>(uint *buffer, const uint *src, int count, + const QPixelLayout *, const QRgb *); + QT_END_NAMESPACE #endif diff --git a/src/gui/painting/qdrawingprimitive_sse2_p.h b/src/gui/painting/qdrawingprimitive_sse2_p.h index 1a7dddf0d5..c74055e440 100644 --- a/src/gui/painting/qdrawingprimitive_sse2_p.h +++ b/src/gui/painting/qdrawingprimitive_sse2_p.h @@ -35,6 +35,7 @@ #define QDRAWINGPRIMITIVE_SSE2_P_H #include <private/qsimd_p.h> +#include "qdrawhelper_p.h" #ifdef __SSE2__ @@ -256,6 +257,43 @@ inline QRgb qUnpremultiply_sse4(QRgb p) vl = _mm_packus_epi16(vl, vl); return _mm_cvtsi128_si32(vl); } + +template<enum QtPixelOrder PixelOrder> +QT_FUNCTION_TARGET(SSE4_1) +inline uint qConvertArgb32ToA2rgb30_sse4(QRgb p) +{ + const uint alpha = qAlpha(p); + if (alpha == 255) + return qConvertRgb32ToRgb30<PixelOrder>(p); + if (alpha == 0) + return 0; + Q_CONSTEXPR uint mult = 255 / (255 >> 6); + const uint invAlpha = qt_inv_premul_factor[alpha]; + const uint newalpha = (alpha >> 6); + const __m128i via = _mm_set1_epi32(invAlpha); + const __m128i vna = _mm_set1_epi32(mult * newalpha); + const __m128i vr1 = _mm_set1_epi32(0x1000); + const __m128i vr2 = _mm_set1_epi32(0x80); + __m128i vl = _mm_cvtepu8_epi32(_mm_cvtsi32_si128(p)); + vl = _mm_mullo_epi32(vl, via); + vl = _mm_add_epi32(vl, vr1); + vl = _mm_srli_epi32(vl, 14); + vl = _mm_mullo_epi32(vl, vna); + vl = _mm_add_epi32(vl, _mm_srli_epi32(vl, 8)); + vl = _mm_add_epi32(vl, vr2); + vl = _mm_srli_epi32(vl, 8); + vl = _mm_packus_epi32(vl, vl); + uint rgb30 = (newalpha << 30); + rgb30 |= ((uint)_mm_extract_epi16(vl, 1)) << 10; + if (PixelOrder == PixelOrderRGB) { + rgb30 |= ((uint)_mm_extract_epi16(vl, 2)) << 20; + rgb30 |= ((uint)_mm_extract_epi16(vl, 0)); + } else { + rgb30 |= ((uint)_mm_extract_epi16(vl, 0)) << 20; + rgb30 |= ((uint)_mm_extract_epi16(vl, 2)); + } + return rgb30; +} #endif QT_END_NAMESPACE diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index da29a57f98..f7c71f05bd 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -190,6 +190,8 @@ private slots: void devicePixelRatio(); void rgb30Unpremul(); + void rgb30Repremul_data(); + void rgb30Repremul(); void metadataPassthrough(); @@ -2946,6 +2948,31 @@ void tst_QImage::rgb30Unpremul() QCOMPARE(bbits[2], (3U << 30) | (201 << 20) | (393 << 10) | 777); } +void tst_QImage::rgb30Repremul_data() +{ + QTest::addColumn<uint>("color"); + for (int i = 255; i > 0; i -= 15) { + QTest::newRow(qPrintable(QStringLiteral("100% red=") + QString::number(i))) << qRgba(i, 0, 0, 0xff); + QTest::newRow(qPrintable(QStringLiteral("75% red=") + QString::number(i))) << qRgba(i, 0, 0, 0xc0); + QTest::newRow(qPrintable(QStringLiteral("50% red=") + QString::number(i))) << qRgba(i, 0, 0, 0x80); + QTest::newRow(qPrintable(QStringLiteral("37.5% red=") + QString::number(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); |