From 0226795cf33363a872c777034e0d8934ffaa3819 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Thu, 23 Jan 2014 16:09:20 +0100 Subject: [PATCH] Round evenly in INV_PREMUL Currently INV_PREMUL rounds strictly down. While PREMUL rounds evenly. This patch adds 0x8000 to the intermediate results in INV_PREMUL before right shifting, thereby achieving even rounding. The rounding also makes PREMUL(INV_PREMUL()) into an identify operation, which means we can safely convert ARGB32PM to ARGB32 and back without ever losing color details. A test is added to verify this. Change-Id: I1267e109caddcff0c01d726cb5c1c1e9fa5f7996 Reviewed-by: Gunnar Sletta --- src/gui/painting/qdrawhelper_p.h | 3 ++- tests/auto/gui/image/qimage/tst_qimage.cpp | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index 3c945338a66..0f98b07229e 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -699,7 +699,8 @@ static Q_ALWAYS_INLINE uint INV_PREMUL(uint p) { return 0; // (p*(0x00ff00ff/alpha)) >> 16 == (p*255)/alpha for all p and alpha <= 256. const uint invAlpha = 0x00ff00ffU / alpha; - return qRgba((qRed(p)*invAlpha)>>16, (qGreen(p)*invAlpha)>>16, (qBlue(p)*invAlpha)>>16, alpha); + // We add 0x8000 to get even rounding. The rounding also ensures that PREMUL(INV_PREMUL(p)) == p for all p. + return qRgba((qRed(p)*invAlpha + 0x8000)>>16, (qGreen(p)*invAlpha + 0x8000)>>16, (qBlue(p)*invAlpha + 0x8000)>>16, alpha); } struct quint24 { diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index f7a672ad181..9ddf571dbdc 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -166,6 +166,8 @@ private slots: void deepCopyWhenPaintingActive(); void scaled_QTBUG19157(); + void convertOverUnPreMul(); + void cleanupFunctions(); }; @@ -2422,6 +2424,26 @@ void tst_QImage::scaled_QTBUG19157() 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))); + } + } +} + static void cleanupFunction(void* info) { bool *called = static_cast(info);