summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@digia.com>2014-01-23 12:20:21 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-01-31 00:24:48 +0100
commit5f59207bd3a71159d0e21fde7a16bfa60c192057 (patch)
tree041870d5c547c1c94e0441f21bfd53f8264d1383
parent16af55dda11a770bdb53afd46dfc67acbc7d3f0d (diff)
Optimize drawing to and from generic formats
When drawing to and from the less common formats most of the cpu time is spend in conversion. The conversion method is rather slow due to using variable shifts and masks that the compiler does not have a chance to optimize. This patch changes the conversion methods to being templates fed by constexpr methods. This allows the compiler to fully optimize the methods yielding 2x->5x speedups. The reliance on constexpr however means the optimized methods are only used under C++11. Change-Id: I2ec77c4c1c03f12ee463a694a2b59db0f0b52db1 Reviewed-by: Gunnar Sletta <gunnar.sletta@jollamobile.com>
-rw-r--r--src/corelib/global/qcompilerdetection.h2
-rw-r--r--src/gui/painting/qdrawhelper.cpp461
-rw-r--r--tests/benchmarks/gui/image/qimageconversion/tst_qimageconversion.cpp140
3 files changed, 495 insertions, 108 deletions
diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h
index 2c2e1295c5..a2e9d82838 100644
--- a/src/corelib/global/qcompilerdetection.h
+++ b/src/corelib/global/qcompilerdetection.h
@@ -868,8 +868,10 @@
#ifdef Q_COMPILER_CONSTEXPR
# define Q_DECL_CONSTEXPR constexpr
+# define Q_CONSTEXPR constexpr
#else
# define Q_DECL_CONSTEXPR
+# define Q_CONSTEXPR const
#endif
#ifdef Q_COMPILER_EXPLICIT_OVERRIDES
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index 813454fe8b..1851734bbc 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -91,90 +91,254 @@ enum {
// must be multiple of 4 for easier SIMD implementations
static const int buffer_size = 2048;
+#ifdef Q_COMPILER_CONSTEXPR
+
+template<QImage::Format> Q_DECL_CONSTEXPR uint redWidth();
+template<QImage::Format> Q_DECL_CONSTEXPR uint redShift();
+template<QImage::Format> Q_DECL_CONSTEXPR uint greenWidth();
+template<QImage::Format> Q_DECL_CONSTEXPR uint greenShift();
+template<QImage::Format> Q_DECL_CONSTEXPR uint blueWidth();
+template<QImage::Format> Q_DECL_CONSTEXPR uint blueShift();
+template<QImage::Format> Q_DECL_CONSTEXPR uint alphaWidth();
+template<QImage::Format> Q_DECL_CONSTEXPR uint alphaShift();
+
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB16>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB444>() { return 4; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB555>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB666>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_RGB888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint redWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB16>() { return 11; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB444>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB555>() { return 10; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB666>() { return 12; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_RGB888>() { return 16; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB4444_Premultiplied>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8555_Premultiplied>() { return 18; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB8565_Premultiplied>() { return 19; }
+template<> Q_DECL_CONSTEXPR uint redShift<QImage::Format_ARGB6666_Premultiplied>() { return 12; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB16>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB444>() { return 4; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB555>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB666>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_RGB888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB8565_Premultiplied>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint greenWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB16>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB444>() { return 4; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB555>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB666>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_RGB888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB4444_Premultiplied>() { return 4; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8555_Premultiplied>() { return 13; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB8565_Premultiplied>() { return 13; }
+template<> Q_DECL_CONSTEXPR uint greenShift<QImage::Format_ARGB6666_Premultiplied>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB16>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB444>() { return 4; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB555>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB666>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_RGB888>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8555_Premultiplied>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB8565_Premultiplied>() { return 5; }
+template<> Q_DECL_CONSTEXPR uint blueWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB16>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB444>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB555>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB666>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_RGB888>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB4444_Premultiplied>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8555_Premultiplied>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB8565_Premultiplied>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint blueShift<QImage::Format_ARGB6666_Premultiplied>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB4444_Premultiplied>() { return 4; }
+template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8555_Premultiplied>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB8565_Premultiplied>() { return 8; }
+template<> Q_DECL_CONSTEXPR uint alphaWidth<QImage::Format_ARGB6666_Premultiplied>() { return 6; }
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB4444_Premultiplied>() { return 12; }
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8555_Premultiplied>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB8565_Premultiplied>() { return 0; }
+template<> Q_DECL_CONSTEXPR uint alphaShift<QImage::Format_ARGB6666_Premultiplied>() { return 18; }
+
+template<QImage::Format> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel();
+template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB16>() { return QPixelLayout::BPP16; }
+template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB444>() { return QPixelLayout::BPP16; }
+template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB555>() { return QPixelLayout::BPP16; }
+template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB666>() { return QPixelLayout::BPP24; }
+template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB888>() { return QPixelLayout::BPP24; }
+template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB4444_Premultiplied>() { return QPixelLayout::BPP16; }
+template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8555_Premultiplied>() { return QPixelLayout::BPP24; }
+template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB8565_Premultiplied>() { return QPixelLayout::BPP24; }
+template<> Q_DECL_CONSTEXPR QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB6666_Premultiplied>() { return QPixelLayout::BPP24; }
+
+
+template<QImage::Format Format>
+static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
+ Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1);
+ Q_CONSTEXPR uint blueMask = ((1 << blueWidth<Format>()) - 1);
+ Q_CONSTEXPR uchar redLeftShift = 8 - redWidth<Format>();
+ Q_CONSTEXPR uchar greenLeftShift = 8 - greenWidth<Format>();
+ Q_CONSTEXPR uchar blueLeftShift = 8 - blueWidth<Format>();
+ Q_CONSTEXPR uchar redRightShift = 2 * redWidth<Format>() - 8;
+ Q_CONSTEXPR uchar greenRightShift = 2 * greenWidth<Format>() - 8;
+ Q_CONSTEXPR uchar blueRightShift = 2 * blueWidth<Format>() - 8;
-// To convert in place, let 'dest' and 'src' be the same.
-static const uint *QT_FASTCALL convertIndexedToARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *clut)
-{
- for (int i = 0; i < count; ++i)
- buffer[i] = PREMUL(clut[src[i]]);
- return buffer;
-}
+ for (int i = 0; i < count; ++i) {
+ uint red = (src[i] >> redShift<Format>()) & redMask;
+ uint green = (src[i] >> greenShift<Format>()) & greenMask;
+ uint blue = (src[i] >> blueShift<Format>()) & blueMask;
-static const uint *QT_FASTCALL convertPassThrough(uint *, const uint *src, int,
- const QPixelLayout *, const QRgb *)
-{
- return src;
-}
+ red = ((red << redLeftShift) | (red >> redRightShift)) << 16;
+ green = ((green << greenLeftShift) | (green >> greenRightShift)) << 8;
+ blue = (blue << blueLeftShift) | (blue >> blueRightShift);
+ buffer[i] = 0xff000000 | red | green | blue;
+ }
-static const uint *QT_FASTCALL convertRGB16ToRGB32(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
-{
- for (int i = 0; i < count; ++i)
- buffer[i] = qConvertRgb16To32(src[i]);
return buffer;
}
-static const uint *QT_FASTCALL convertARGB32ToARGB32PM(uint *buffer, const uint *src, int count,
+template<QImage::Format Format>
+static const uint *QT_FASTCALL convertARGBPMToARGB32PM(uint *buffer, const uint *src, int count,
const QPixelLayout *, const QRgb *)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = PREMUL(src[i]);
+ Q_CONSTEXPR uint alphaMask = ((1 << alphaWidth<Format>()) - 1);
+ Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
+ Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1);
+ Q_CONSTEXPR uint blueMask = ((1 << blueWidth<Format>()) - 1);
+
+ Q_CONSTEXPR uchar alphaLeftShift = 8 - alphaWidth<Format>();
+ Q_CONSTEXPR uchar redLeftShift = 8 - redWidth<Format>();
+ Q_CONSTEXPR uchar greenLeftShift = 8 - greenWidth<Format>();
+ Q_CONSTEXPR uchar blueLeftShift = 8 - blueWidth<Format>();
+
+ Q_CONSTEXPR uchar alphaRightShift = 2 * alphaWidth<Format>() - 8;
+ Q_CONSTEXPR uchar redRightShift = 2 * redWidth<Format>() - 8;
+ Q_CONSTEXPR uchar greenRightShift = 2 * greenWidth<Format>() - 8;
+ Q_CONSTEXPR uchar blueRightShift = 2 * blueWidth<Format>() - 8;
+
+ for (int i = 0; i < count; ++i) {
+ uint alpha = (src[i] >> alphaShift<Format>()) & alphaMask;
+ uint red = (src[i] >> redShift<Format>()) & redMask;
+ uint green = (src[i] >> greenShift<Format>()) & greenMask;
+ uint blue = (src[i] >> blueShift<Format>()) & blueMask;
+
+ alpha = (alpha << alphaLeftShift) | (alpha >> alphaRightShift);
+ red = qMin(alpha, (red << redLeftShift) | (red >> redRightShift));
+ green = qMin(alpha, (green << greenLeftShift) | (green >> greenRightShift));
+ blue = qMin(alpha, (blue << blueLeftShift) | (blue >> blueRightShift));
+ buffer[i] = (alpha << 24) | (red << 16) | (green << 8) | blue;
+ }
+
return buffer;
}
-static const uint *QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+template<QImage::Format Format>
+static const uint *QT_FASTCALL convertRGBFromARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = RGBA2ARGB(src[i]);
+ Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
+ Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1);
+ Q_CONSTEXPR uint blueMask = ((1 << blueWidth<Format>()) - 1);
+
+ Q_CONSTEXPR uchar redRightShift = 24 - redWidth<Format>();
+ Q_CONSTEXPR uchar greenRightShift = 16 - greenWidth<Format>();
+ Q_CONSTEXPR uchar blueRightShift = 8 - blueWidth<Format>();
+
+ for (int i = 0; i < count; ++i) {
+ const uint color = INV_PREMUL(src[i]);
+ const uint red = ((color >> redRightShift) & redMask) << redShift<Format>();
+ const uint green = ((color >> greenRightShift) & greenMask) << greenShift<Format>();
+ const uint blue = ((color >> blueRightShift) & blueMask) << blueShift<Format>();
+ buffer[i] = red | green | blue;
+ }
return buffer;
}
-static const uint *QT_FASTCALL convertRGBA8888ToARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+template<QImage::Format Format>
+static const uint *QT_FASTCALL convertRGBFromRGB32(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = PREMUL(RGBA2ARGB(src[i]));
+ Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
+ Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1);
+ Q_CONSTEXPR uint blueMask = ((1 << blueWidth<Format>()) - 1);
+
+ Q_CONSTEXPR uchar redRightShift = 24 - redWidth<Format>();
+ Q_CONSTEXPR uchar greenRightShift = 16 - greenWidth<Format>();
+ Q_CONSTEXPR uchar blueRightShift = 8 - blueWidth<Format>();
+
+ for (int i = 0; i < count; ++i) {
+ const uint red = ((src[i] >> redRightShift) & redMask) << redShift<Format>();
+ const uint green = ((src[i] >> greenRightShift) & greenMask) << greenShift<Format>();
+ const uint blue = ((src[i] >> blueRightShift) & blueMask) << blueShift<Format>();
+ buffer[i] = red | green | blue;
+ }
return buffer;
}
-static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int count,
- const QPixelLayout *layout, const QRgb *)
+template<QImage::Format Format>
+static const uint *QT_FASTCALL convertARGBPMFromARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
{
- Q_ASSERT(layout->redWidth >= 4);
- Q_ASSERT(layout->greenWidth >= 4);
- Q_ASSERT(layout->blueWidth >= 4);
- Q_ASSERT(layout->alphaWidth == 0);
-
- const uint redMask = ((1 << layout->redWidth) - 1);
- const uint greenMask = ((1 << layout->greenWidth) - 1);
- const uint blueMask = ((1 << layout->blueWidth) - 1);
+ Q_CONSTEXPR uint alphaMask = ((1 << alphaWidth<Format>()) - 1);
+ Q_CONSTEXPR uint redMask = ((1 << redWidth<Format>()) - 1);
+ Q_CONSTEXPR uint greenMask = ((1 << greenWidth<Format>()) - 1);
+ Q_CONSTEXPR uint blueMask = ((1 << blueWidth<Format>()) - 1);
- const uchar redLeftShift = 8 - layout->redWidth;
- const uchar greenLeftShift = 8 - layout->greenWidth;
- const uchar blueLeftShift = 8 - layout->blueWidth;
-
- const uchar redRightShift = 2 * layout->redWidth - 8;
- const uchar greenRightShift = 2 * layout->greenWidth - 8;
- const uchar blueRightShift = 2 * layout->blueWidth - 8;
+ Q_CONSTEXPR uchar alphaRightShift = 32 - alphaWidth<Format>();
+ Q_CONSTEXPR uchar redRightShift = 24 - redWidth<Format>();
+ Q_CONSTEXPR uchar greenRightShift = 16 - greenWidth<Format>();
+ Q_CONSTEXPR uchar blueRightShift = 8 - blueWidth<Format>();
for (int i = 0; i < count; ++i) {
- uint red = (src[i] >> layout->redShift) & redMask;
- uint green = (src[i] >> layout->greenShift) & greenMask;
- uint blue = (src[i] >> layout->blueShift) & blueMask;
-
- red = ((red << redLeftShift) | (red >> redRightShift)) << 16;
- green = ((green << greenLeftShift) | (green >> greenRightShift)) << 8;
- blue = (blue << blueLeftShift) | (blue >> blueRightShift);
- buffer[i] = 0xff000000 | red | green | blue;
+ const uint alpha = ((src[i] >> alphaRightShift) & alphaMask) << alphaShift<Format>();
+ const uint red = ((src[i] >> redRightShift) & redMask) << redShift<Format>();
+ const uint green = ((src[i] >> greenRightShift) & greenMask) << greenShift<Format>();
+ const uint blue = ((src[i] >> blueRightShift) & blueMask) << blueShift<Format>();
+ buffer[i] = alpha | red | green | blue;
}
-
return buffer;
}
+template<QImage::Format Format> Q_DECL_CONSTEXPR static inline QPixelLayout pixelLayoutRGB()
+{
+ return QPixelLayout{
+ redWidth<Format>(), redShift<Format>(),
+ greenWidth<Format>(), greenShift<Format>(),
+ blueWidth<Format>(), blueShift<Format>(),
+ 0, 0,
+ false, bitsPerPixel<Format>(),
+ convertToRGB32<Format>,
+ convertRGBFromARGB32PM<Format>,
+ convertRGBFromRGB32<Format>
+ };
+}
+
+template<QImage::Format Format> Q_DECL_CONSTEXPR static inline QPixelLayout pixelLayoutARGBPM()
+{
+ return QPixelLayout{
+ redWidth<Format>(), redShift<Format>(),
+ greenWidth<Format>(), greenShift<Format>(),
+ blueWidth<Format>(), blueShift<Format>(),
+ alphaWidth<Format>(), alphaShift<Format>(),
+ true, bitsPerPixel<Format>(),
+ convertARGBPMToARGB32PM<Format>,
+ convertARGBPMFromARGB32PM<Format>,
+ 0
+ };
+}
+
+#else // CONSTEXPR
+
static const uint *QT_FASTCALL convertToARGB32PM(uint *buffer, const uint *src, int count,
const QPixelLayout *layout, const QRgb *)
{
@@ -229,59 +393,36 @@ static const uint *QT_FASTCALL convertToARGB32PM(uint *buffer, const uint *src,
return buffer;
}
-static const uint *QT_FASTCALL convertRGB16FromRGB32(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
-{
- for (int i = 0; i < count; ++i)
- buffer[i] = qConvertRgb32To16(src[i]);
- return buffer;
-}
-
-static const uint *QT_FASTCALL convertRGB16FromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
+static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int count,
+ const QPixelLayout *layout, const QRgb *)
{
- for (int i = 0; i < count; ++i)
- buffer[i] = qConvertRgb32To16(INV_PREMUL(src[i]));
- return buffer;
-}
+ Q_ASSERT(layout->redWidth >= 4);
+ Q_ASSERT(layout->greenWidth >= 4);
+ Q_ASSERT(layout->blueWidth >= 4);
+ Q_ASSERT(layout->alphaWidth == 0);
-static const uint *QT_FASTCALL convertARGB32FromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
-{
- for (int i = 0; i < count; ++i)
- buffer[i] = INV_PREMUL(src[i]);
- return buffer;
-}
+ const uint redMask = ((1 << layout->redWidth) - 1);
+ const uint greenMask = ((1 << layout->greenWidth) - 1);
+ const uint blueMask = ((1 << layout->blueWidth) - 1);
-static const uint *QT_FASTCALL convertRGBA8888PMFromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
-{
- for (int i = 0; i < count; ++i)
- buffer[i] = ARGB2RGBA(src[i]);
- return buffer;
-}
+ const uchar redLeftShift = 8 - layout->redWidth;
+ const uchar greenLeftShift = 8 - layout->greenWidth;
+ const uchar blueLeftShift = 8 - layout->blueWidth;
-static const uint *QT_FASTCALL convertRGBA8888FromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
-{
- for (int i = 0; i < count; ++i)
- buffer[i] = ARGB2RGBA(INV_PREMUL(src[i]));
- return buffer;
-}
+ const uchar redRightShift = 2 * layout->redWidth - 8;
+ const uchar greenRightShift = 2 * layout->greenWidth - 8;
+ const uchar blueRightShift = 2 * layout->blueWidth - 8;
-static const uint *QT_FASTCALL convertRGBXFromRGB32(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
-{
- for (int i = 0; i < count; ++i)
- buffer[i] = ARGB2RGBA(0xff000000 | src[i]);
- return buffer;
-}
+ for (int i = 0; i < count; ++i) {
+ uint red = (src[i] >> layout->redShift) & redMask;
+ uint green = (src[i] >> layout->greenShift) & greenMask;
+ uint blue = (src[i] >> layout->blueShift) & blueMask;
-static const uint *QT_FASTCALL convertRGBXFromARGB32PM(uint *buffer, const uint *src, int count,
- const QPixelLayout *, const QRgb *)
-{
- for (int i = 0; i < count; ++i)
- buffer[i] = ARGB2RGBA(0xff000000 | INV_PREMUL(src[i]));
+ red = (red << redLeftShift) | (red >> redRightShift);
+ green = (green << greenLeftShift) | (green >> greenRightShift);
+ blue = (blue << blueLeftShift) | (blue >> blueRightShift);
+ buffer[i] = 0xff000000 | (red << 16) | (green << 8) | blue;
+ }
return buffer;
}
@@ -344,6 +485,110 @@ static const uint *QT_FASTCALL convertFromRGB32(uint *buffer, const uint *src, i
return buffer;
}
+static const uint *QT_FASTCALL convertRGB16ToRGB32(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = qConvertRgb16To32(src[i]);
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertRGB16FromRGB32(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = qConvertRgb32To16(src[i]);
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertRGB16FromARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = qConvertRgb32To16(INV_PREMUL(src[i]));
+ return buffer;
+}
+#endif
+
+// To convert in place, let 'dest' and 'src' be the same.
+static const uint *QT_FASTCALL convertIndexedToARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *clut)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = PREMUL(clut[src[i]]);
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertPassThrough(uint *, const uint *src, int,
+ const QPixelLayout *, const QRgb *)
+{
+ return src;
+}
+
+static const uint *QT_FASTCALL convertARGB32ToARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = PREMUL(src[i]);
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertRGBA8888PMToARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = RGBA2ARGB(src[i]);
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertRGBA8888ToARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = PREMUL(RGBA2ARGB(src[i]));
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertARGB32FromARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = INV_PREMUL(src[i]);
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertRGBA8888PMFromARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = ARGB2RGBA(src[i]);
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertRGBA8888FromARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = ARGB2RGBA(INV_PREMUL(src[i]));
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertRGBXFromRGB32(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = ARGB2RGBA(0xff000000 | src[i]);
+ return buffer;
+}
+
+static const uint *QT_FASTCALL convertRGBXFromARGB32PM(uint *buffer, const uint *src, int count,
+ const QPixelLayout *, const QRgb *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = ARGB2RGBA(0xff000000 | INV_PREMUL(src[i]));
+ return buffer;
+}
+
template <QPixelLayout::BPP bpp> static
uint QT_FASTCALL fetchPixel(const uchar *src, int index);
@@ -463,6 +708,17 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = {
{ 8, 16, 8, 8, 8, 0, 0, 0, false, QPixelLayout::BPP32, convertPassThrough, convertPassThrough, convertPassThrough }, // Format_RGB32
{ 8, 16, 8, 8, 8, 0, 8, 24, false, QPixelLayout::BPP32, convertARGB32ToARGB32PM, convertARGB32FromARGB32PM, 0 }, // Format_ARGB32
{ 8, 16, 8, 8, 8, 0, 8, 24, true, QPixelLayout::BPP32, convertPassThrough, convertPassThrough, 0 }, // Format_ARGB32_Premultiplied
+#ifdef Q_COMPILER_CONSTEXPR
+ pixelLayoutRGB<QImage::Format_RGB16>(),
+ pixelLayoutARGBPM<QImage::Format_ARGB8565_Premultiplied>(),
+ pixelLayoutRGB<QImage::Format_RGB666>(),
+ pixelLayoutARGBPM<QImage::Format_ARGB6666_Premultiplied>(),
+ pixelLayoutRGB<QImage::Format_RGB555>(),
+ pixelLayoutARGBPM<QImage::Format_ARGB8555_Premultiplied>(),
+ pixelLayoutRGB<QImage::Format_RGB888>(),
+ pixelLayoutRGB<QImage::Format_RGB444>(),
+ pixelLayoutARGBPM<QImage::Format_ARGB4444_Premultiplied>(),
+#else
{ 5, 11, 6, 5, 5, 0, 0, 0, false, QPixelLayout::BPP16, convertRGB16ToRGB32, convertRGB16FromARGB32PM, convertRGB16FromRGB32 }, // Format_RGB16
{ 5, 19, 6, 13, 5, 8, 8, 0, true, QPixelLayout::BPP24, convertToARGB32PM, convertFromARGB32PM, 0 }, // Format_ARGB8565_Premultiplied
{ 6, 12, 6, 6, 6, 0, 0, 0, false, QPixelLayout::BPP24, convertToRGB32, convertFromARGB32PM, convertFromRGB32 }, // Format_RGB666
@@ -472,6 +728,7 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = {
{ 8, 16, 8, 8, 8, 0, 0, 0, false, QPixelLayout::BPP24, convertToRGB32, convertFromARGB32PM, convertFromRGB32 }, // Format_RGB888
{ 4, 8, 4, 4, 4, 0, 0, 0, false, QPixelLayout::BPP16, convertToRGB32, convertFromARGB32PM, convertFromRGB32 }, // Format_RGB444
{ 4, 8, 4, 4, 4, 0, 4, 12, true, QPixelLayout::BPP16, convertToARGB32PM, convertFromARGB32PM, 0 }, // Format_ARGB4444_Premultiplied
+#endif
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
{ 8, 24, 8, 16, 8, 8, 0, 0, false, QPixelLayout::BPP32, convertRGBA8888PMToARGB32PM, convertRGBXFromARGB32PM, convertRGBXFromRGB32 }, // Format_RGBX8888
{ 8, 24, 8, 16, 8, 8, 8, 0, false, QPixelLayout::BPP32, convertRGBA8888ToARGB32PM, convertRGBA8888FromARGB32PM, 0 }, // Format_RGBA8888
diff --git a/tests/benchmarks/gui/image/qimageconversion/tst_qimageconversion.cpp b/tests/benchmarks/gui/image/qimageconversion/tst_qimageconversion.cpp
index fd12c89e80..eda46a1df0 100644
--- a/tests/benchmarks/gui/image/qimageconversion/tst_qimageconversion.cpp
+++ b/tests/benchmarks/gui/image/qimageconversion/tst_qimageconversion.cpp
@@ -42,28 +42,39 @@
#include <qtest.h>
#include <QImage>
+Q_DECLARE_METATYPE(QImage::Format)
class tst_QImageConversion : public QObject
{
Q_OBJECT
private slots:
- void convertRgb888ToRGB32_data();
- void convertRgb888ToRGB32();
+ void convertRgb888ToRgb32_data();
+ void convertRgb888ToRgb32();
+
+ void convertRgb32ToRgb888_data();
+ void convertRgb32ToRgb888();
+
+ void convertGeneric_data();
+ void convertGeneric();
private:
QImage generateImageRgb888(int width, int height);
+ QImage generateImageRgb16(int width, int height);
+ QImage generateImageRgb32(int width, int height);
+ QImage generateImageArgb32(int width, int height);
};
-void tst_QImageConversion::convertRgb888ToRGB32_data()
+void tst_QImageConversion::convertRgb888ToRgb32_data()
{
QTest::addColumn<QImage>("inputImage");
+
// height = 5000 to get interesting timing.
// 3 pixels wide -> smaller than regular vector of 128bits
QTest::newRow("width: 3px; height: 5000px;") << generateImageRgb888(3, 5000);
// 8 pixels wide -> potential for 2 vectors
- QTest::newRow("width: 8px; height: 5000px;") << generateImageRgb888(3, 5000);
+ QTest::newRow("width: 8px; height: 5000px;") << generateImageRgb888(8, 5000);
// 16 pixels, minimum for the SSSE3 implementation
QTest::newRow("width: 16px; height: 5000px;") << generateImageRgb888(16, 5000);
@@ -72,10 +83,10 @@ void tst_QImageConversion::convertRgb888ToRGB32_data()
QTest::newRow("width: 50px; height: 5000px;") << generateImageRgb888(50, 5000);
// 2000 pixels -> typical values for pictures
- QTest::newRow("width: 2000px; height: 5000px;") << generateImageRgb888(2000, 5000);
+ QTest::newRow("width: 2000px; height: 2000px;") << generateImageRgb888(2000, 2000);
}
-void tst_QImageConversion::convertRgb888ToRGB32()
+void tst_QImageConversion::convertRgb888ToRgb32()
{
QFETCH(QImage, inputImage);
@@ -87,6 +98,76 @@ void tst_QImageConversion::convertRgb888ToRGB32()
}
}
+void tst_QImageConversion::convertRgb32ToRgb888_data()
+{
+ QTest::addColumn<QImage>("inputImage");
+ // height = 5000 to get interesting timing.
+
+ // 3 pixels wide -> smaller than regular vector of 128bits
+ QTest::newRow("width: 3px; height: 5000px;") << generateImageRgb32(3, 5000);
+
+ // 8 pixels wide -> potential for 2 vectors
+ QTest::newRow("width: 8px; height: 5000px;") << generateImageRgb32(8, 5000);
+
+ // 16 pixels, minimum for the SSSE3 implementation
+ QTest::newRow("width: 16px; height: 5000px;") << generateImageRgb32(16, 5000);
+
+ // 50 pixels, more realistic use case
+ QTest::newRow("width: 50px; height: 5000px;") << generateImageRgb32(50, 5000);
+
+ // 2000 pixels -> typical values for pictures
+ QTest::newRow("width: 2000px; height: 2000px;") << generateImageRgb32(2000, 2000);
+}
+
+void tst_QImageConversion::convertRgb32ToRgb888()
+{
+ QFETCH(QImage, inputImage);
+
+ QBENCHMARK {
+ volatile QImage output = inputImage.convertToFormat(QImage::Format_RGB888);
+ // we need the volatile and the following to make sure the compiler does not do
+ // anything stupid :)
+ (void)output;
+ }
+}
+
+
+void tst_QImageConversion::convertGeneric_data()
+{
+ QTest::addColumn<QImage>("inputImage");
+ QTest::addColumn<QImage::Format>("outputFormat");
+ QImage rgb16 = generateImageRgb16(1000, 1000);
+ QImage rgb32 = generateImageRgb32(1000, 1000);
+ QImage argb32 = generateImageArgb32(1000, 1000);
+
+ QTest::newRow("rgb16 -> rgb32") << rgb16 << QImage::Format_RGB32;
+ QTest::newRow("rgb16 -> rgb888") << rgb16 << QImage::Format_RGB888;
+ QTest::newRow("rgb16 -> rgb666") << rgb16 << QImage::Format_RGB666;
+ QTest::newRow("rgb16 -> rgb555") << rgb16 << QImage::Format_RGB555;
+
+ QTest::newRow("rgb32 -> rgb16") << rgb32 << QImage::Format_RGB16;
+ QTest::newRow("rgb32 -> rgb888") << rgb32 << QImage::Format_RGB888;
+ QTest::newRow("rgb32 -> rgb666") << rgb32 << QImage::Format_RGB666;
+ QTest::newRow("rgb32 -> rgb555") << rgb32 << QImage::Format_RGB555;
+
+ QTest::newRow("argb32 -> rgba8888") << argb32 << QImage::Format_RGBA8888;
+ QTest::newRow("argb32 -> rgb888") << argb32 << QImage::Format_RGB888;
+ QTest::newRow("argb32 -> rgb666") << argb32 << QImage::Format_RGB666;
+ QTest::newRow("argb32 -> argb8565pm") << argb32 << QImage::Format_ARGB8565_Premultiplied;
+ QTest::newRow("argb32 -> argb4444pm") << argb32 << QImage::Format_ARGB4444_Premultiplied;
+}
+
+void tst_QImageConversion::convertGeneric()
+{
+ QFETCH(QImage, inputImage);
+ QFETCH(QImage::Format, outputFormat);
+
+ QBENCHMARK {
+ QImage output = inputImage.convertToFormat(outputFormat);
+ output.constBits();
+ }
+}
+
/*
Fill a RGB888 image with "random" pixel values.
*/
@@ -103,5 +184,52 @@ QImage tst_QImageConversion::generateImageRgb888(int width, int height)
return image;
}
+/*
+ Fill a RGB16 image with "random" pixel values.
+ */
+QImage tst_QImageConversion::generateImageRgb16(int width, int height)
+{
+ QImage image(width, height, QImage::Format_RGB16);
+ const int byteWidth = width * 2;
+
+ for (int y = 0; y < image.height(); ++y) {
+ uchar *scanline = image.scanLine(y);
+ for (int x = 0; x < byteWidth; ++x)
+ scanline[x] = x ^ y;
+ }
+ return image;
+}
+
+/*
+ Fill a RGB32 image with "random" pixel values.
+ */
+QImage tst_QImageConversion::generateImageRgb32(int width, int height)
+{
+ QImage image(width, height, QImage::Format_RGB32);
+
+ for (int y = 0; y < image.height(); ++y) {
+ QRgb *scanline = (QRgb*)image.scanLine(y);
+ for (int x = 0; x < width; ++x)
+ scanline[x] = qRgb(x, y, x ^ y);
+ }
+ return image;
+}
+
+/*
+ Fill a ARGB32 image with "random" pixel values.
+ */
+QImage tst_QImageConversion::generateImageArgb32(int width, int height)
+{
+ QImage image(width, height, QImage::Format_ARGB32);
+ const int byteWidth = width * 4;
+
+ for (int y = 0; y < image.height(); ++y) {
+ uchar *scanline = image.scanLine(y);
+ for (int x = 0; x < byteWidth; ++x)
+ scanline[x] = x ^ y;
+ }
+ return image;
+}
+
QTEST_MAIN(tst_QImageConversion)
#include "tst_qimageconversion.moc"