diff options
author | Allan Sandfeld Jensen <allan.jensen@digia.com> | 2014-01-23 12:20:21 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-01-31 00:24:48 +0100 |
commit | 5f59207bd3a71159d0e21fde7a16bfa60c192057 (patch) | |
tree | 041870d5c547c1c94e0441f21bfd53f8264d1383 | |
parent | 16af55dda11a770bdb53afd46dfc67acbc7d3f0d (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.h | 2 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper.cpp | 461 | ||||
-rw-r--r-- | tests/benchmarks/gui/image/qimageconversion/tst_qimageconversion.cpp | 140 |
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" |