summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2015-05-06 14:23:57 +0200
committerAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2015-06-03 12:01:26 +0000
commit8f760808e0fe0fe6dd89d561f118b19ed8085e7a (patch)
tree8cc259b6098d83b91b6ec8ac22745546947860c7
parent754efa57d89c62d1796e01b407e9222e67450f52 (diff)
Fix premul conversion from ARGB32 to A2RGB30 formats.
When a premultiplied alpha changes value because it is rounded to fewer bits the premultiplied colors may need to be recalculated with the new value. Otherwise the color will both be wrong and potentially invalid. Change-Id: I9ec74a22aac73cd7ffab04e180cf2bf35bb4c315 Reviewed-by: Gunnar Sletta <gunnar@sletta.org>
-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);