summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/painting/qdrawhelper.cpp10
-rw-r--r--src/gui/painting/qdrawhelper_p.h17
-rw-r--r--src/gui/painting/qdrawhelper_sse4.cpp16
-rw-r--r--src/gui/painting/qdrawingprimitive_sse2_p.h38
-rw-r--r--tests/auto/gui/image/qimage/tst_qimage.cpp27
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);