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>
bb10
Allan Sandfeld Jensen 2014-01-23 12:20:21 +01:00 committed by The Qt Project
parent 16af55dda1
commit 5f59207bd3
3 changed files with 506 additions and 119 deletions

View File

@ -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

View File

@ -91,80 +91,112 @@ 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; }
// 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 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,
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;
}
template<QImage::Format Format>
static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int count,
const QPixelLayout *layout, const QRgb *)
const QPixelLayout *, const QRgb *)
{
Q_ASSERT(layout->redWidth >= 4);
Q_ASSERT(layout->greenWidth >= 4);
Q_ASSERT(layout->blueWidth >= 4);
Q_ASSERT(layout->alphaWidth == 0);
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 uint redMask = ((1 << layout->redWidth) - 1);
const uint greenMask = ((1 << layout->greenWidth) - 1);
const uint blueMask = ((1 << layout->blueWidth) - 1);
Q_CONSTEXPR uchar redLeftShift = 8 - redWidth<Format>();
Q_CONSTEXPR uchar greenLeftShift = 8 - greenWidth<Format>();
Q_CONSTEXPR uchar blueLeftShift = 8 - blueWidth<Format>();
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 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 red = (src[i] >> layout->redShift) & redMask;
uint green = (src[i] >> layout->greenShift) & greenMask;
uint blue = (src[i] >> layout->blueShift) & blueMask;
uint red = (src[i] >> redShift<Format>()) & redMask;
uint green = (src[i] >> greenShift<Format>()) & greenMask;
uint blue = (src[i] >> blueShift<Format>()) & blueMask;
red = ((red << redLeftShift) | (red >> redRightShift)) << 16;
green = ((green << greenLeftShift) | (green >> greenRightShift)) << 8;
@ -175,6 +207,138 @@ static const uint *QT_FASTCALL convertToRGB32(uint *buffer, const uint *src, int
return buffer;
}
template<QImage::Format Format>
static const uint *QT_FASTCALL convertARGBPMToARGB32PM(uint *buffer, const uint *src, int count,
const QPixelLayout *, const QRgb *)
{
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;
}
template<QImage::Format Format>
static const uint *QT_FASTCALL convertRGBFromARGB32PM(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 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;
}
template<QImage::Format Format>
static const uint *QT_FASTCALL convertRGBFromRGB32(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 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;
}
template<QImage::Format Format>
static const uint *QT_FASTCALL convertARGBPMFromARGB32PM(uint *buffer, const uint *src, int count,
const QPixelLayout *, const QRgb *)
{
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 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) {
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 *)
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(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 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;
}
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 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 uchar redLeftShift = 8 - layout->redWidth;
const uchar greenLeftShift = 8 - layout->greenWidth;
const uchar blueLeftShift = 8 - layout->blueWidth;
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 redRightShift = 2 * layout->redWidth - 8;
const uchar greenRightShift = 2 * layout->greenWidth - 8;
const uchar blueRightShift = 2 * layout->blueWidth - 8;
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;
}
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 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]));
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

View File

@ -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"