diff options
Diffstat (limited to 'src/gui/painting/qdrawhelper.cpp')
-rw-r--r-- | src/gui/painting/qdrawhelper.cpp | 8078 |
1 files changed, 8078 insertions, 0 deletions
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp new file mode 100644 index 0000000000..5e75e4a884 --- /dev/null +++ b/src/gui/painting/qdrawhelper.cpp @@ -0,0 +1,8078 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/qdrawhelper_p.h> +#include <private/qpaintengine_raster_p.h> +#include <private/qpainter_p.h> +#include <private/qdrawhelper_x86_p.h> +#ifdef QT_HAVE_ARM_SIMD +#include <private/qdrawhelper_arm_simd_p.h> +#endif +#include <private/qdrawhelper_neon_p.h> +#include <private/qmath_p.h> +#include <qmath.h> + +QT_BEGIN_NAMESPACE + + +#define MASK(src, a) src = BYTE_MUL(src, a) + +#if defined(Q_OS_IRIX) && defined(Q_CC_GNU) && __GNUC__ == 3 && __GNUC__ < 4 && QT_POINTER_SIZE == 8 +#define Q_IRIX_GCC3_3_WORKAROUND +// +// work around http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14484 +// +static uint gccBug(uint value) __attribute__((noinline)); +static uint gccBug(uint value) +{ + return value; +} +#endif + +/* + constants and structures +*/ + +enum { + fixed_scale = 1 << 16, + half_point = 1 << 15 +}; +static const int buffer_size = 2048; + +struct LinearGradientValues +{ + qreal dx; + qreal dy; + qreal l; + qreal off; +}; + +struct RadialGradientValues +{ + qreal dx; + qreal dy; + qreal a; +}; + +struct Operator; +typedef uint* (QT_FASTCALL *DestFetchProc)(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length); +typedef void (QT_FASTCALL *DestStoreProc)(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length); +typedef const uint* (QT_FASTCALL *SourceFetchProc)(uint *buffer, const Operator *o, const QSpanData *data, int y, int x, int length); + + +struct Operator +{ + QPainter::CompositionMode mode; + DestFetchProc dest_fetch; + DestStoreProc dest_store; + SourceFetchProc src_fetch; + CompositionFunctionSolid funcSolid; + CompositionFunction func; + union { + LinearGradientValues linear; + RadialGradientValues radial; +// TextureValues texture; + }; +}; + +/* + Destination fetch. This is simple as we don't have to do bounds checks or + transformations +*/ + +static uint * QT_FASTCALL destFetchMono(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) +{ + uchar *data = (uchar *)rasterBuffer->scanLine(y); + uint *start = buffer; + const uint *end = buffer + length; + while (buffer < end) { + *buffer = data[x>>3] & (0x80 >> (x & 7)) ? rasterBuffer->destColor1 : rasterBuffer->destColor0; + ++buffer; + ++x; + } + return start; +} + +static uint * QT_FASTCALL destFetchMonoLsb(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) +{ + uchar *data = (uchar *)rasterBuffer->scanLine(y); + uint *start = buffer; + const uint *end = buffer + length; + while (buffer < end) { + *buffer = data[x>>3] & (0x1 << (x & 7)) ? rasterBuffer->destColor1 : rasterBuffer->destColor0; + ++buffer; + ++x; + } + return start; +} + +static uint * QT_FASTCALL destFetchARGB32(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) +{ + const uint *data = (const uint *)rasterBuffer->scanLine(y) + x; + for (int i = 0; i < length; ++i) + buffer[i] = PREMUL(data[i]); + return buffer; +} + +static uint * QT_FASTCALL destFetchARGB32P(uint *, QRasterBuffer *rasterBuffer, int x, int y, int) +{ + return (uint *)rasterBuffer->scanLine(y) + x; +} + +static uint * QT_FASTCALL destFetchRGB16(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) +{ + const ushort *data = (const ushort *)rasterBuffer->scanLine(y) + x; + for (int i = 0; i < length; ++i) + buffer[i] = qConvertRgb16To32(data[i]); + return buffer; +} + +template <class DST> +Q_STATIC_TEMPLATE_FUNCTION uint * QT_FASTCALL destFetch(uint *buffer, QRasterBuffer *rasterBuffer, + int x, int y, int length) +{ + const DST *src = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x; + quint32 *dest = reinterpret_cast<quint32*>(buffer); + while (length--) + *dest++ = *src++; + return buffer; +} + +# define SPANFUNC_POINTER_DESTFETCH(Arg) destFetch<Arg> + +static DestFetchProc destFetchProc[QImage::NImageFormats] = +{ + 0, // Format_Invalid + destFetchMono, // Format_Mono, + destFetchMonoLsb, // Format_MonoLSB + 0, // Format_Indexed8 + destFetchARGB32P, // Format_RGB32 + destFetchARGB32, // Format_ARGB32, + destFetchARGB32P, // Format_ARGB32_Premultiplied + destFetchRGB16, // Format_RGB16 + SPANFUNC_POINTER_DESTFETCH(qargb8565), // Format_ARGB8565_Premultiplied + SPANFUNC_POINTER_DESTFETCH(qrgb666), // Format_RGB666 + SPANFUNC_POINTER_DESTFETCH(qargb6666), // Format_ARGB6666_Premultiplied + SPANFUNC_POINTER_DESTFETCH(qrgb555), // Format_RGB555 + SPANFUNC_POINTER_DESTFETCH(qargb8555), // Format_ARGB8555_Premultiplied + SPANFUNC_POINTER_DESTFETCH(qrgb888), // Format_RGB888 + SPANFUNC_POINTER_DESTFETCH(qrgb444), // Format_RGB444 + SPANFUNC_POINTER_DESTFETCH(qargb4444) // Format_ARGB4444_Premultiplied +}; + +/* + Returns the color in the mono destination color table + that is the "nearest" to /color/. +*/ +static inline QRgb findNearestColor(QRgb color, QRasterBuffer *rbuf) +{ + QRgb color_0 = PREMUL(rbuf->destColor0); + QRgb color_1 = PREMUL(rbuf->destColor1); + color = PREMUL(color); + + int r = qRed(color); + int g = qGreen(color); + int b = qBlue(color); + int rx, gx, bx; + int dist_0, dist_1; + + rx = r - qRed(color_0); + gx = g - qGreen(color_0); + bx = b - qBlue(color_0); + dist_0 = rx*rx + gx*gx + bx*bx; + + rx = r - qRed(color_1); + gx = g - qGreen(color_1); + bx = b - qBlue(color_1); + dist_1 = rx*rx + gx*gx + bx*bx; + + if (dist_0 < dist_1) + return color_0; + return color_1; +} + +/* + Destination store. +*/ + +static void QT_FASTCALL destStoreMono(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length) +{ + uchar *data = (uchar *)rasterBuffer->scanLine(y); + if (rasterBuffer->monoDestinationWithClut) { + for (int i = 0; i < length; ++i) { + if (buffer[i] == rasterBuffer->destColor0) { + data[x >> 3] &= ~(0x80 >> (x & 7)); + } else if (buffer[i] == rasterBuffer->destColor1) { + data[x >> 3] |= 0x80 >> (x & 7); + } else if (findNearestColor(buffer[i], rasterBuffer) == rasterBuffer->destColor0) { + data[x >> 3] &= ~(0x80 >> (x & 7)); + } else { + data[x >> 3] |= 0x80 >> (x & 7); + } + ++x; + } + } else { + for (int i = 0; i < length; ++i) { + if (qGray(buffer[i]) < int(qt_bayer_matrix[y & 15][x & 15])) + data[x >> 3] |= 0x80 >> (x & 7); + else + data[x >> 3] &= ~(0x80 >> (x & 7)); + ++x; + } + } +} + +static void QT_FASTCALL destStoreMonoLsb(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length) +{ + uchar *data = (uchar *)rasterBuffer->scanLine(y); + if (rasterBuffer->monoDestinationWithClut) { + for (int i = 0; i < length; ++i) { + if (buffer[i] == rasterBuffer->destColor0) { + data[x >> 3] &= ~(1 << (x & 7)); + } else if (buffer[i] == rasterBuffer->destColor1) { + data[x >> 3] |= 1 << (x & 7); + } else if (findNearestColor(buffer[i], rasterBuffer) == rasterBuffer->destColor0) { + data[x >> 3] &= ~(1 << (x & 7)); + } else { + data[x >> 3] |= 1 << (x & 7); + } + ++x; + } + } else { + for (int i = 0; i < length; ++i) { + if (qGray(buffer[i]) < int(qt_bayer_matrix[y & 15][x & 15])) + data[x >> 3] |= 1 << (x & 7); + else + data[x >> 3] &= ~(1 << (x & 7)); + ++x; + } + } +} + +static void QT_FASTCALL destStoreARGB32(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length) +{ + uint *data = (uint *)rasterBuffer->scanLine(y) + x; + for (int i = 0; i < length; ++i) { + int p = buffer[i]; + int alpha = qAlpha(p); + if (alpha == 255) + data[i] = p; + else if (alpha == 0) + data[i] = 0; + else { + int inv_alpha = 0xff0000/qAlpha(buffer[i]); + data[i] = (p & 0xff000000) + | ((qRed(p)*inv_alpha) & 0xff0000) + | (((qGreen(p)*inv_alpha) >> 8) & 0xff00) + | ((qBlue(p)*inv_alpha) >> 16); + } + } +} + +static void QT_FASTCALL destStoreRGB16(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length) +{ + quint16 *data = (quint16*)rasterBuffer->scanLine(y) + x; + qt_memconvert<quint16, quint32>(data, buffer, length); +} + +template <class DST> +Q_STATIC_TEMPLATE_FUNCTION void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, + int x, int y, + const uint *buffer, int length) +{ + DST *dest = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x; + const quint32p *src = reinterpret_cast<const quint32p*>(buffer); + while (length--) + *dest++ = DST(*src++); +} + +# define SPANFUNC_POINTER_DESTSTORE(DEST) destStore<DEST> + +static DestStoreProc destStoreProc[QImage::NImageFormats] = +{ + 0, // Format_Invalid + destStoreMono, // Format_Mono, + destStoreMonoLsb, // Format_MonoLSB + 0, // Format_Indexed8 + 0, // Format_RGB32 + destStoreARGB32, // Format_ARGB32, + 0, // Format_ARGB32_Premultiplied + destStoreRGB16, // Format_RGB16 + SPANFUNC_POINTER_DESTSTORE(qargb8565), // Format_ARGB8565_Premultiplied + SPANFUNC_POINTER_DESTSTORE(qrgb666), // Format_RGB666 + SPANFUNC_POINTER_DESTSTORE(qargb6666), // Format_ARGB6666_Premultiplied + SPANFUNC_POINTER_DESTSTORE(qrgb555), // Format_RGB555 + SPANFUNC_POINTER_DESTSTORE(qargb8555), // Format_ARGB8555_Premultiplied + SPANFUNC_POINTER_DESTSTORE(qrgb888), // Format_RGB888 + SPANFUNC_POINTER_DESTSTORE(qrgb444), // Format_RGB444 + SPANFUNC_POINTER_DESTSTORE(qargb4444) // Format_ARGB4444_Premultiplied +}; + +/* + Source fetches + + This is a bit more complicated, as we need several fetch routines for every surface type + + We need 5 fetch methods per surface type: + untransformed + transformed (tiled and not tiled) + transformed bilinear (tiled and not tiled) + + We don't need bounds checks for untransformed, but we need them for the other ones. + + The generic implementation does pixel by pixel fetches +*/ + +template <QImage::Format format> +Q_STATIC_TEMPLATE_FUNCTION uint QT_FASTCALL qt_fetchPixel(const uchar *scanLine, int x, const QVector<QRgb> *rgb); + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_Mono>(const uchar *scanLine, + int x, const QVector<QRgb> *rgb) +{ + bool pixel = scanLine[x>>3] & (0x80 >> (x & 7)); + if (rgb) return PREMUL(rgb->at(pixel ? 1 : 0)); + return pixel ? 0xff000000 : 0xffffffff; +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_MonoLSB>(const uchar *scanLine, + int x, const QVector<QRgb> *rgb) +{ + bool pixel = scanLine[x>>3] & (0x1 << (x & 7)); + if (rgb) return PREMUL(rgb->at(pixel ? 1 : 0)); + return pixel ? 0xff000000 : 0xffffffff; +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_Indexed8>(const uchar *scanLine, + int x, const QVector<QRgb> *rgb) +{ + return PREMUL(rgb->at(scanLine[x])); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB32>(const uchar *scanLine, + int x, const QVector<QRgb> *) +{ + return PREMUL(((const uint *)scanLine)[x]); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB32_Premultiplied>(const uchar *scanLine, + int x, const QVector<QRgb> *) +{ + return ((const uint *)scanLine)[x]; +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB16>(const uchar *scanLine, + int x, const QVector<QRgb> *) +{ + return qConvertRgb16To32(((const ushort *)scanLine)[x]); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB8565_Premultiplied>(const uchar *scanLine, + int x, + const QVector<QRgb> *) +{ + const qargb8565 color = reinterpret_cast<const qargb8565*>(scanLine)[x]; + return qt_colorConvert<quint32, qargb8565>(color, 0); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB666>(const uchar *scanLine, + int x, + const QVector<QRgb> *) +{ + const qrgb666 color = reinterpret_cast<const qrgb666*>(scanLine)[x]; + return qt_colorConvert<quint32, qrgb666>(color, 0); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB6666_Premultiplied>(const uchar *scanLine, + int x, + const QVector<QRgb> *) +{ + const qargb6666 color = reinterpret_cast<const qargb6666*>(scanLine)[x]; + return qt_colorConvert<quint32, qargb6666>(color, 0); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB555>(const uchar *scanLine, + int x, + const QVector<QRgb> *) +{ + const qrgb555 color = reinterpret_cast<const qrgb555*>(scanLine)[x]; + return qt_colorConvert<quint32, qrgb555>(color, 0); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB8555_Premultiplied>(const uchar *scanLine, + int x, + const QVector<QRgb> *) +{ + const qargb8555 color = reinterpret_cast<const qargb8555*>(scanLine)[x]; + return qt_colorConvert<quint32, qargb8555>(color, 0); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB888>(const uchar *scanLine, + int x, + const QVector<QRgb> *) +{ + const qrgb888 color = reinterpret_cast<const qrgb888*>(scanLine)[x]; + return qt_colorConvert<quint32, qrgb888>(color, 0); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_RGB444>(const uchar *scanLine, + int x, + const QVector<QRgb> *) +{ + const qrgb444 color = reinterpret_cast<const qrgb444*>(scanLine)[x]; + return qt_colorConvert<quint32, qrgb444>(color, 0); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_ARGB4444_Premultiplied>(const uchar *scanLine, + int x, + const QVector<QRgb> *) +{ + const qargb4444 color = reinterpret_cast<const qargb4444*>(scanLine)[x]; + return qt_colorConvert<quint32, qargb4444>(color, 0); +} + +template<> +Q_STATIC_TEMPLATE_SPECIALIZATION +uint QT_FASTCALL qt_fetchPixel<QImage::Format_Invalid>(const uchar *, + int , + const QVector<QRgb> *) +{ + return 0; +} + +typedef uint (QT_FASTCALL *FetchPixelProc)(const uchar *scanLine, int x, const QVector<QRgb> *); + +#define SPANFUNC_POINTER_FETCHPIXEL(Arg) qt_fetchPixel<QImage::Arg> + + +static const FetchPixelProc fetchPixelProc[QImage::NImageFormats] = +{ + 0, + SPANFUNC_POINTER_FETCHPIXEL(Format_Mono), + SPANFUNC_POINTER_FETCHPIXEL(Format_MonoLSB), + SPANFUNC_POINTER_FETCHPIXEL(Format_Indexed8), + SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB32_Premultiplied), + SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB32), + SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB32_Premultiplied), + SPANFUNC_POINTER_FETCHPIXEL(Format_RGB16), + SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB8565_Premultiplied), + SPANFUNC_POINTER_FETCHPIXEL(Format_RGB666), + SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB6666_Premultiplied), + SPANFUNC_POINTER_FETCHPIXEL(Format_RGB555), + SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB8555_Premultiplied), + SPANFUNC_POINTER_FETCHPIXEL(Format_RGB888), + SPANFUNC_POINTER_FETCHPIXEL(Format_RGB444), + SPANFUNC_POINTER_FETCHPIXEL(Format_ARGB4444_Premultiplied) +}; + +enum TextureBlendType { + BlendUntransformed, + BlendTiled, + BlendTransformed, + BlendTransformedTiled, + BlendTransformedBilinear, + BlendTransformedBilinearTiled, + NBlendTypes +}; + +template <QImage::Format format> +Q_STATIC_TEMPLATE_FUNCTION const uint * QT_FASTCALL qt_fetchUntransformed(uint *buffer, const Operator *, const QSpanData *data, + int y, int x, int length) +{ + const uchar *scanLine = data->texture.scanLine(y); + for (int i = 0; i < length; ++i) + buffer[i] = qt_fetchPixel<format>(scanLine, x + i, data->texture.colorTable); + return buffer; +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION const uint * QT_FASTCALL +qt_fetchUntransformed<QImage::Format_ARGB32_Premultiplied>(uint *, const Operator *, + const QSpanData *data, + int y, int x, int) +{ + const uchar *scanLine = data->texture.scanLine(y); + return ((const uint *)scanLine) + x; +} + +template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */ +Q_STATIC_TEMPLATE_FUNCTION +const uint * QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, const QSpanData *data, + int y, int x, int length) +{ + FetchPixelProc fetch = fetchPixelProc[data->texture.format]; + + int image_width = data->texture.width; + int image_height = data->texture.height; + + const qreal cx = x + qreal(0.5); + const qreal cy = y + qreal(0.5); + + const uint *end = buffer + length; + uint *b = buffer; + if (data->fast_matrix) { + // The increment pr x in the scanline + int fdx = (int)(data->m11 * fixed_scale); + int fdy = (int)(data->m12 * fixed_scale); + + int fx = int((data->m21 * cy + + data->m11 * cx + data->dx) * fixed_scale); + int fy = int((data->m22 * cy + + data->m12 * cx + data->dy) * fixed_scale); + + while (b < end) { + int px = fx >> 16; + int py = fy >> 16; + + if (blendType == BlendTransformedTiled) { + px %= image_width; + py %= image_height; + if (px < 0) px += image_width; + if (py < 0) py += image_height; + + const uchar *scanLine = data->texture.scanLine(py); + *b = fetch(scanLine, px, data->texture.colorTable); + } else { + if ((px < 0) || (px >= image_width) + || (py < 0) || (py >= image_height)) { + *b = uint(0); + } else { + const uchar *scanLine = data->texture.scanLine(py); + *b = fetch(scanLine, px, data->texture.colorTable); + } + } + fx += fdx; + fy += fdy; + ++b; + } + } else { + const qreal fdx = data->m11; + const qreal fdy = data->m12; + const qreal fdw = data->m13; + + qreal fx = data->m21 * cy + data->m11 * cx + data->dx; + qreal fy = data->m22 * cy + data->m12 * cx + data->dy; + qreal fw = data->m23 * cy + data->m13 * cx + data->m33; + + while (b < end) { + const qreal iw = fw == 0 ? 1 : 1 / fw; + const qreal tx = fx * iw; + const qreal ty = fy * iw; + int px = int(tx) - (tx < 0); + int py = int(ty) - (ty < 0); + + if (blendType == BlendTransformedTiled) { + px %= image_width; + py %= image_height; + if (px < 0) px += image_width; + if (py < 0) py += image_height; + + const uchar *scanLine = data->texture.scanLine(py); + *b = fetch(scanLine, px, data->texture.colorTable); + } else { + if ((px < 0) || (px >= image_width) + || (py < 0) || (py >= image_height)) { + *b = uint(0); + } else { + const uchar *scanLine = data->texture.scanLine(py); + *b = fetch(scanLine, px, data->texture.colorTable); + } + } + fx += fdx; + fy += fdy; + fw += fdw; + //force increment to avoid /0 + if (!fw) { + fw += fdw; + } + ++b; + } + } + + return buffer; +} + +/** \internal + interpolate 4 argb pixels with the distx and disty factor. + distx and disty bust be between 0 and 16 + */ +static inline uint interpolate_4_pixels_16(uint tl, uint tr, uint bl, uint br, int distx, int disty) +{ + uint distxy = distx * disty; + //idistx * disty = (16-distx) * disty = 16*disty - distxy + //idistx * idisty = (16-distx) * (16-disty) = 16*16 - 16*distx -16*dity + distxy + uint tlrb = (tl & 0x00ff00ff) * (16*16 - 16*distx - 16*disty + distxy); + uint tlag = ((tl & 0xff00ff00) >> 8) * (16*16 - 16*distx - 16*disty + distxy); + uint trrb = ((tr & 0x00ff00ff) * (distx*16 - distxy)); + uint trag = (((tr & 0xff00ff00) >> 8) * (distx*16 - distxy)); + uint blrb = ((bl & 0x00ff00ff) * (disty*16 - distxy)); + uint blag = (((bl & 0xff00ff00) >> 8) * (disty*16 - distxy)); + uint brrb = ((br & 0x00ff00ff) * (distxy)); + uint brag = (((br & 0xff00ff00) >> 8) * (distxy)); + return (((tlrb + trrb + blrb + brrb) >> 8) & 0x00ff00ff) | ((tlag + trag + blag + brag) & 0xff00ff00); +} + +#if defined(QT_ALWAYS_HAVE_SSE2) +#define interpolate_4_pixels_16_sse2(tl, tr, bl, br, distx, disty, colorMask, v_256, b) \ +{ \ + const __m128i dxdy = _mm_mullo_epi16 (distx, disty); \ + const __m128i distx_ = _mm_slli_epi16(distx, 4); \ + const __m128i disty_ = _mm_slli_epi16(disty, 4); \ + const __m128i idxidy = _mm_add_epi16(dxdy, _mm_sub_epi16(v_256, _mm_add_epi16(distx_, disty_))); \ + const __m128i dxidy = _mm_sub_epi16(distx_, dxdy); \ + const __m128i idxdy = _mm_sub_epi16(disty_, dxdy); \ + \ + __m128i tlAG = _mm_srli_epi16(tl, 8); \ + __m128i tlRB = _mm_and_si128(tl, colorMask); \ + __m128i trAG = _mm_srli_epi16(tr, 8); \ + __m128i trRB = _mm_and_si128(tr, colorMask); \ + __m128i blAG = _mm_srli_epi16(bl, 8); \ + __m128i blRB = _mm_and_si128(bl, colorMask); \ + __m128i brAG = _mm_srli_epi16(br, 8); \ + __m128i brRB = _mm_and_si128(br, colorMask); \ + \ + tlAG = _mm_mullo_epi16(tlAG, idxidy); \ + tlRB = _mm_mullo_epi16(tlRB, idxidy); \ + trAG = _mm_mullo_epi16(trAG, dxidy); \ + trRB = _mm_mullo_epi16(trRB, dxidy); \ + blAG = _mm_mullo_epi16(blAG, idxdy); \ + blRB = _mm_mullo_epi16(blRB, idxdy); \ + brAG = _mm_mullo_epi16(brAG, dxdy); \ + brRB = _mm_mullo_epi16(brRB, dxdy); \ + \ + /* Add the values, and shift to only keep 8 significant bits per colors */ \ + __m128i rAG =_mm_add_epi16(_mm_add_epi16(tlAG, trAG), _mm_add_epi16(blAG, brAG)); \ + __m128i rRB =_mm_add_epi16(_mm_add_epi16(tlRB, trRB), _mm_add_epi16(blRB, brRB)); \ + rAG = _mm_andnot_si128(colorMask, rAG); \ + rRB = _mm_srli_epi16(rRB, 8); \ + _mm_storeu_si128((__m128i*)(b), _mm_or_si128(rAG, rRB)); \ +} +#endif + +#if defined(QT_ALWAYS_HAVE_NEON) +#define interpolate_4_pixels_16_neon(tl, tr, bl, br, distx, disty, disty_, colorMask, invColorMask, v_256, b) \ +{ \ + const int16x8_t dxdy = vmulq_s16(distx, disty); \ + const int16x8_t distx_ = vshlq_n_s16(distx, 4); \ + const int16x8_t idxidy = vaddq_s16(dxdy, vsubq_s16(v_256, vaddq_s16(distx_, disty_))); \ + const int16x8_t dxidy = vsubq_s16(distx_, dxdy); \ + const int16x8_t idxdy = vsubq_s16(disty_, dxdy); \ + \ + int16x8_t tlAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(tl), 8)); \ + int16x8_t tlRB = vandq_s16(tl, colorMask); \ + int16x8_t trAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(tr), 8)); \ + int16x8_t trRB = vandq_s16(tr, colorMask); \ + int16x8_t blAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(bl), 8)); \ + int16x8_t blRB = vandq_s16(bl, colorMask); \ + int16x8_t brAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(br), 8)); \ + int16x8_t brRB = vandq_s16(br, colorMask); \ + \ + int16x8_t rAG = vmulq_s16(tlAG, idxidy); \ + int16x8_t rRB = vmulq_s16(tlRB, idxidy); \ + rAG = vmlaq_s16(rAG, trAG, dxidy); \ + rRB = vmlaq_s16(rRB, trRB, dxidy); \ + rAG = vmlaq_s16(rAG, blAG, idxdy); \ + rRB = vmlaq_s16(rRB, blRB, idxdy); \ + rAG = vmlaq_s16(rAG, brAG, dxdy); \ + rRB = vmlaq_s16(rRB, brRB, dxdy); \ + \ + rAG = vandq_s16(invColorMask, rAG); \ + rRB = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rRB), 8)); \ + vst1q_s16((int16_t*)(b), vorrq_s16(rAG, rRB)); \ +} +#endif + +template<TextureBlendType blendType> +Q_STATIC_TEMPLATE_FUNCTION inline void fetchTransformedBilinear_pixelBounds(int max, int l1, int l2, int &v1, int &v2) +{ + if (blendType == BlendTransformedBilinearTiled) { + v1 %= max; + if (v1 < 0) v1 += max; + v2 = v1 + 1; + v2 %= max; + } else { + if (v1 < l1) { + v2 = v1 = l1; + } else if (v1 >= l2) { + v2 = v1 = l2; + } else { + v2 = v1 + 1; + } + } + + Q_ASSERT(v1 >= 0 && v1 < max); + Q_ASSERT(v2 >= 0 && v2 < max); +} + +template<TextureBlendType blendType, QImage::Format format> /* blendType = BlendTransformedBilinear or BlendTransformedBilinearTiled */ +Q_STATIC_TEMPLATE_FUNCTION +const uint * QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Operator *, const QSpanData *data, + int y, int x, int length) +{ +#ifdef Q_CC_RVCT // needed to avoid compiler crash in RVCT 2.2 + FetchPixelProc fetch; + if (format != QImage::Format_Invalid) + fetch = qt_fetchPixel<format>; + else + fetch = fetchPixelProc[data->texture.format]; +#else + FetchPixelProc fetch = (format != QImage::Format_Invalid) ? FetchPixelProc(qt_fetchPixel<format>) : fetchPixelProc[data->texture.format]; +#endif + + int image_width = data->texture.width; + int image_height = data->texture.height; + + int image_x1 = data->texture.x1; + int image_y1 = data->texture.y1; + int image_x2 = data->texture.x2 - 1; + int image_y2 = data->texture.y2 - 1; + + const qreal cx = x + qreal(0.5); + const qreal cy = y + qreal(0.5); + + uint *end = buffer + length; + uint *b = buffer; + if (data->fast_matrix) { + // The increment pr x in the scanline + int fdx = (int)(data->m11 * fixed_scale); + int fdy = (int)(data->m12 * fixed_scale); + + int fx = int((data->m21 * cy + + data->m11 * cx + data->dx) * fixed_scale); + int fy = int((data->m22 * cy + + data->m12 * cx + data->dy) * fixed_scale); + + fx -= half_point; + fy -= half_point; + + if (fdy == 0) { //simple scale, no rotation + int y1 = (fy >> 16); + int y2; + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); + + if (fdx <= fixed_scale && fdx > 0) { // scale up on X + int disty = (fy & 0x0000ffff) >> 8; + int idisty = 256 - disty; + int x = fx >> 16; + + // The idea is first to do the interpolation between the row s1 and the row s2 + // into an intermediate buffer, then we interpolate between two pixel of this buffer. + + // intermediate_buffer[0] is a buffer of red-blue component of the pixel, in the form 0x00RR00BB + // intermediate_buffer[1] is the alpha-green component of the pixel, in the form 0x00AA00GG + quint32 intermediate_buffer[2][buffer_size + 2]; + // count is the size used in the intermediate_buffer. + int count = qCeil(length * data->m11) + 2; //+1 for the last pixel to interpolate with, and +1 for rounding errors. + Q_ASSERT(count <= buffer_size + 2); //length is supposed to be <= buffer_size and data->m11 < 1 in this case + int f = 0; + int lim = count; + if (blendType == BlendTransformedBilinearTiled) { + x %= image_width; + if (x < 0) x += image_width; + } else { + lim = qMin(count, image_x2-x+1); + if (x < image_x1) { + Q_ASSERT(x <= image_x2); + uint t = fetch(s1, image_x1, data->texture.colorTable); + uint b = fetch(s2, image_x1, data->texture.colorTable); + quint32 rb = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; + quint32 ag = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; + do { + intermediate_buffer[0][f] = rb; + intermediate_buffer[1][f] = ag; + f++; + x++; + } while (x < image_x1 && f < lim); + } + } + + if (blendType != BlendTransformedBilinearTiled && + (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)) { +#if defined(QT_ALWAYS_HAVE_SSE2) + const __m128i disty_ = _mm_set1_epi16(disty); + const __m128i idisty_ = _mm_set1_epi16(idisty); + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + + lim -= 3; + for (; f < lim; x += 4, f += 4) { + // Load 4 pixels from s1, and split the alpha-green and red-blue component + __m128i top = _mm_loadu_si128((__m128i*)((const uint *)(s1)+x)); + __m128i topAG = _mm_srli_epi16(top, 8); + __m128i topRB = _mm_and_si128(top, colorMask); + // Multiplies each colour component by idisty + topAG = _mm_mullo_epi16 (topAG, idisty_); + topRB = _mm_mullo_epi16 (topRB, idisty_); + + // Same for the s2 vector + __m128i bottom = _mm_loadu_si128((__m128i*)((const uint *)(s2)+x)); + __m128i bottomAG = _mm_srli_epi16(bottom, 8); + __m128i bottomRB = _mm_and_si128(bottom, colorMask); + bottomAG = _mm_mullo_epi16 (bottomAG, disty_); + bottomRB = _mm_mullo_epi16 (bottomRB, disty_); + + // Add the values, and shift to only keep 8 significant bits per colors + __m128i rAG =_mm_add_epi16(topAG, bottomAG); + rAG = _mm_srli_epi16(rAG, 8); + _mm_storeu_si128((__m128i*)(&intermediate_buffer[1][f]), rAG); + __m128i rRB =_mm_add_epi16(topRB, bottomRB); + rRB = _mm_srli_epi16(rRB, 8); + _mm_storeu_si128((__m128i*)(&intermediate_buffer[0][f]), rRB); + } +#elif defined(QT_ALWAYS_HAVE_NEON) + const int16x8_t disty_ = vdupq_n_s16(disty); + const int16x8_t idisty_ = vdupq_n_s16(idisty); + const int16x8_t colorMask = vdupq_n_s16(0x00ff); + + lim -= 3; + for (; f < lim; x += 4, f += 4) { + // Load 4 pixels from s1, and split the alpha-green and red-blue component + int16x8_t top = vld1q_s16((int16_t*)((const uint *)(s1)+x)); + int16x8_t topAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(top), 8)); + int16x8_t topRB = vandq_s16(top, colorMask); + // Multiplies each colour component by idisty + topAG = vmulq_s16(topAG, idisty_); + topRB = vmulq_s16(topRB, idisty_); + + // Same for the s2 vector + int16x8_t bottom = vld1q_s16((int16_t*)((const uint *)(s2)+x)); + int16x8_t bottomAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(bottom), 8)); + int16x8_t bottomRB = vandq_s16(bottom, colorMask); + bottomAG = vmulq_s16(bottomAG, disty_); + bottomRB = vmulq_s16(bottomRB, disty_); + + // Add the values, and shift to only keep 8 significant bits per colors + int16x8_t rAG = vaddq_s16(topAG, bottomAG); + rAG = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rAG), 8)); + vst1q_s16((int16_t*)(&intermediate_buffer[1][f]), rAG); + int16x8_t rRB = vaddq_s16(topRB, bottomRB); + rRB = vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(rRB), 8)); + vst1q_s16((int16_t*)(&intermediate_buffer[0][f]), rRB); + } +#endif + } + for (; f < count; f++) { // Same as above but without sse2 + if (blendType == BlendTransformedBilinearTiled) { + if (x >= image_width) x -= image_width; + } else { + x = qMin(x, image_x2); + } + + uint t = fetch(s1, x, data->texture.colorTable); + uint b = fetch(s2, x, data->texture.colorTable); + + intermediate_buffer[0][f] = (((t & 0xff00ff) * idisty + (b & 0xff00ff) * disty) >> 8) & 0xff00ff; + intermediate_buffer[1][f] = ((((t>>8) & 0xff00ff) * idisty + ((b>>8) & 0xff00ff) * disty) >> 8) & 0xff00ff; + x++; + } + // Now interpolate the values from the intermediate_buffer to get the final result. + fx &= fixed_scale - 1; + Q_ASSERT((fx >> 16) == 0); + while (b < end) { + register int x1 = (fx >> 16); + register int x2 = x1 + 1; + Q_ASSERT(x1 >= 0); + Q_ASSERT(x2 < count); + + register int distx = (fx & 0x0000ffff) >> 8; + register int idistx = 256 - distx; + int rb = ((intermediate_buffer[0][x1] * idistx + intermediate_buffer[0][x2] * distx) >> 8) & 0xff00ff; + int ag = (intermediate_buffer[1][x1] * idistx + intermediate_buffer[1][x2] * distx) & 0xff00ff00; + *b = rb | ag; + b++; + fx += fdx; + } + } else if ((fdx < 0 && fdx > -(fixed_scale / 8)) || fabs(data->m22) < (1./8.)) { // scale up more than 8x + int y1 = (fy >> 16); + int y2; + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); + int disty = (fy & 0x0000ffff) >> 8; + int idisty = 256 - disty; + while (b < end) { + int x1 = (fx >> 16); + int x2; + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + uint tl = fetch(s1, x1, data->texture.colorTable); + uint tr = fetch(s1, x2, data->texture.colorTable); + uint bl = fetch(s2, x1, data->texture.colorTable); + uint br = fetch(s2, x2, data->texture.colorTable); + + int distx = (fx & 0x0000ffff) >> 8; + int idistx = 256 - distx; + + uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); + uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); + *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); + + fx += fdx; + ++b; + } + } else { //scale down + int y1 = (fy >> 16); + int y2; + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); + int disty = (fy & 0x0000ffff) >> 12; + + if (blendType != BlendTransformedBilinearTiled && + (format == QImage::Format_ARGB32_Premultiplied || format == QImage::Format_RGB32)) { + +#define BILINEAR_DOWNSCALE_BOUNDS_PROLOG \ + while (b < end) { \ + int x1 = (fx >> 16); \ + int x2; \ + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); \ + if (x1 != x2) \ + break; \ + uint tl = fetch(s1, x1, data->texture.colorTable); \ + uint tr = fetch(s1, x2, data->texture.colorTable); \ + uint bl = fetch(s2, x1, data->texture.colorTable); \ + uint br = fetch(s2, x2, data->texture.colorTable); \ + int distx = (fx & 0x0000ffff) >> 12; \ + *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty); \ + fx += fdx; \ + ++b; \ + } \ + uint *boundedEnd; \ + if (fdx > 0) \ + boundedEnd = qMin(end, buffer + uint((image_x2 - (fx >> 16)) / data->m11)); \ + else \ + boundedEnd = qMin(end, buffer + uint((image_x1 - (fx >> 16)) / data->m11)); \ + boundedEnd -= 3; + +#if defined(QT_ALWAYS_HAVE_SSE2) + BILINEAR_DOWNSCALE_BOUNDS_PROLOG + + const __m128i colorMask = _mm_set1_epi32(0x00ff00ff); + const __m128i v_256 = _mm_set1_epi16(256); + const __m128i v_disty = _mm_set1_epi16(disty); + __m128i v_fdx = _mm_set1_epi32(fdx*4); + + ptrdiff_t secondLine = reinterpret_cast<const uint *>(s2) - reinterpret_cast<const uint *>(s1); + + union Vect_buffer { __m128i vect; quint32 i[4]; }; + Vect_buffer v_fx; + + for (int i = 0; i < 4; i++) { + v_fx.i[i] = fx; + fx += fdx; + } + + while (b < boundedEnd) { + + Vect_buffer tl, tr, bl, br; + + for (int i = 0; i < 4; i++) { + int x1 = v_fx.i[i] >> 16; + const uint *addr_tl = reinterpret_cast<const uint *>(s1) + x1; + const uint *addr_tr = addr_tl + 1; + tl.i[i] = *addr_tl; + tr.i[i] = *addr_tr; + bl.i[i] = *(addr_tl+secondLine); + br.i[i] = *(addr_tr+secondLine); + } + __m128i v_distx = _mm_srli_epi16(v_fx.vect, 12); + v_distx = _mm_shufflehi_epi16(v_distx, _MM_SHUFFLE(2,2,0,0)); + v_distx = _mm_shufflelo_epi16(v_distx, _MM_SHUFFLE(2,2,0,0)); + + interpolate_4_pixels_16_sse2(tl.vect, tr.vect, bl.vect, br.vect, v_distx, v_disty, colorMask, v_256, b); + b+=4; + v_fx.vect = _mm_add_epi32(v_fx.vect, v_fdx); + } + fx = v_fx.i[0]; +#elif defined(QT_ALWAYS_HAVE_NEON) + BILINEAR_DOWNSCALE_BOUNDS_PROLOG + + const int16x8_t colorMask = vdupq_n_s16(0x00ff); + const int16x8_t invColorMask = vmvnq_s16(colorMask); + const int16x8_t v_256 = vdupq_n_s16(256); + const int16x8_t v_disty = vdupq_n_s16(disty); + const int16x8_t v_disty_ = vshlq_n_s16(v_disty, 4); + int32x4_t v_fdx = vdupq_n_s32(fdx*4); + + ptrdiff_t secondLine = reinterpret_cast<const uint *>(s2) - reinterpret_cast<const uint *>(s1); + + union Vect_buffer { int32x4_t vect; quint32 i[4]; }; + Vect_buffer v_fx; + + for (int i = 0; i < 4; i++) { + v_fx.i[i] = fx; + fx += fdx; + } + + const int32x4_t v_ffff_mask = vdupq_n_s32(0x0000ffff); + + while (b < boundedEnd) { + + Vect_buffer tl, tr, bl, br; + + Vect_buffer v_fx_shifted; + v_fx_shifted.vect = vshrq_n_s32(v_fx.vect, 16); + + int32x4_t v_distx = vshrq_n_s32(vandq_s32(v_fx.vect, v_ffff_mask), 12); + + for (int i = 0; i < 4; i++) { + int x1 = v_fx_shifted.i[i]; + const uint *addr_tl = reinterpret_cast<const uint *>(s1) + x1; + const uint *addr_tr = addr_tl + 1; + tl.i[i] = *addr_tl; + tr.i[i] = *addr_tr; + bl.i[i] = *(addr_tl+secondLine); + br.i[i] = *(addr_tr+secondLine); + } + + v_distx = vorrq_s32(v_distx, vshlq_n_s32(v_distx, 16)); + + interpolate_4_pixels_16_neon(vreinterpretq_s16_s32(tl.vect), vreinterpretq_s16_s32(tr.vect), vreinterpretq_s16_s32(bl.vect), vreinterpretq_s16_s32(br.vect), vreinterpretq_s16_s32(v_distx), v_disty, v_disty_, colorMask, invColorMask, v_256, b); + b+=4; + v_fx.vect = vaddq_s32(v_fx.vect, v_fdx); + } + fx = v_fx.i[0]; +#endif + } + + while (b < end) { + int x1 = (fx >> 16); + int x2; + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + uint tl = fetch(s1, x1, data->texture.colorTable); + uint tr = fetch(s1, x2, data->texture.colorTable); + uint bl = fetch(s2, x1, data->texture.colorTable); + uint br = fetch(s2, x2, data->texture.colorTable); + int distx = (fx & 0x0000ffff) >> 12; + *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty); + fx += fdx; + ++b; + } + } + } else { //rotation + if (fabs(data->m11) > 8 || fabs(data->m22) > 8) { + //if we are zooming more than 8 times, we use 8bit precision for the position. + while (b < end) { + int x1 = (fx >> 16); + int x2; + int y1 = (fy >> 16); + int y2; + + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); + + uint tl = fetch(s1, x1, data->texture.colorTable); + uint tr = fetch(s1, x2, data->texture.colorTable); + uint bl = fetch(s2, x1, data->texture.colorTable); + uint br = fetch(s2, x2, data->texture.colorTable); + + int distx = (fx & 0x0000ffff) >> 8; + int disty = (fy & 0x0000ffff) >> 8; + int idistx = 256 - distx; + int idisty = 256 - disty; + + uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); + uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); + *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); + + fx += fdx; + fy += fdy; + ++b; + } + } else { + //we are zooming less than 8x, use 4bit precision + while (b < end) { + int x1 = (fx >> 16); + int x2; + int y1 = (fy >> 16); + int y2; + + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); + + uint tl = fetch(s1, x1, data->texture.colorTable); + uint tr = fetch(s1, x2, data->texture.colorTable); + uint bl = fetch(s2, x1, data->texture.colorTable); + uint br = fetch(s2, x2, data->texture.colorTable); + + int distx = (fx & 0x0000ffff) >> 12; + int disty = (fy & 0x0000ffff) >> 12; + + *b = interpolate_4_pixels_16(tl, tr, bl, br, distx, disty); + + fx += fdx; + fy += fdy; + ++b; + } + } + } + } else { + const qreal fdx = data->m11; + const qreal fdy = data->m12; + const qreal fdw = data->m13; + + qreal fx = data->m21 * cy + data->m11 * cx + data->dx; + qreal fy = data->m22 * cy + data->m12 * cx + data->dy; + qreal fw = data->m23 * cy + data->m13 * cx + data->m33; + + while (b < end) { + const qreal iw = fw == 0 ? 1 : 1 / fw; + const qreal px = fx * iw - qreal(0.5); + const qreal py = fy * iw - qreal(0.5); + + int x1 = int(px) - (px < 0); + int x2; + int y1 = int(py) - (py < 0); + int y2; + + int distx = int((px - x1) * 256); + int disty = int((py - y1) * 256); + int idistx = 256 - distx; + int idisty = 256 - disty; + + fetchTransformedBilinear_pixelBounds<blendType>(image_width, image_x1, image_x2, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image_height, image_y1, image_y2, y1, y2); + + const uchar *s1 = data->texture.scanLine(y1); + const uchar *s2 = data->texture.scanLine(y2); + + uint tl = fetch(s1, x1, data->texture.colorTable); + uint tr = fetch(s1, x2, data->texture.colorTable); + uint bl = fetch(s2, x1, data->texture.colorTable); + uint br = fetch(s2, x2, data->texture.colorTable); + + uint xtop = INTERPOLATE_PIXEL_256(tl, idistx, tr, distx); + uint xbot = INTERPOLATE_PIXEL_256(bl, idistx, br, distx); + *b = INTERPOLATE_PIXEL_256(xtop, idisty, xbot, disty); + + fx += fdx; + fy += fdy; + fw += fdw; + //force increment to avoid /0 + if (!fw) { + fw += fdw; + } + ++b; + } + } + + return buffer; +} + +#define SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Arg) qt_fetchUntransformed<QImage::Arg> + +static const SourceFetchProc sourceFetch[NBlendTypes][QImage::NImageFormats] = { + // Untransformed + { + 0, // Invalid + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Mono), // Mono + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_MonoLSB), // MonoLsb + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Indexed8), // Indexed8 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // RGB32 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32), // ARGB32 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // ARGB32_Premultiplied + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB16), // RGB16 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8565_Premultiplied),// ARGB8565_Premultiplied + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB666), // RGB666 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB6666_Premultiplied),// ARGB6666_Premultiplied + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB555), // RGB555 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8555_Premultiplied),// ARGB8555_Premultiplied + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB888), // RGB888 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB444), // RGB444 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB4444_Premultiplied) // ARGB4444_Premultiplied + }, + // Tiled + { + 0, // Invalid + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Mono), // Mono + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_MonoLSB), // MonoLsb + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_Indexed8), // Indexed8 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // RGB32 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32), // ARGB32 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB32_Premultiplied), // ARGB32_Premultiplied + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB16), // RGB16 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8565_Premultiplied),// ARGB8565_Premultiplied + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB666), // RGB666 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB6666_Premultiplied),// ARGB6666_Premultiplied + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB555), // RGB555 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB8555_Premultiplied),// ARGB8555_Premultiplied + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB888), // RGB888 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_RGB444), // RGB444 + SPANFUNC_POINTER_FETCHHUNTRANSFORMED(Format_ARGB4444_Premultiplied) // ARGB4444_Premultiplied + }, + // Transformed + { + 0, // Invalid + fetchTransformed<BlendTransformed>, // Mono + fetchTransformed<BlendTransformed>, // MonoLsb + fetchTransformed<BlendTransformed>, // Indexed8 + fetchTransformed<BlendTransformed>, // RGB32 + fetchTransformed<BlendTransformed>, // ARGB32 + fetchTransformed<BlendTransformed>, // ARGB32_Premultiplied + fetchTransformed<BlendTransformed>, // RGB16 + fetchTransformed<BlendTransformed>, // ARGB8565_Premultiplied + fetchTransformed<BlendTransformed>, // RGB666 + fetchTransformed<BlendTransformed>, // ARGB6666_Premultiplied + fetchTransformed<BlendTransformed>, // RGB555 + fetchTransformed<BlendTransformed>, // ARGB8555_Premultiplied + fetchTransformed<BlendTransformed>, // RGB888 + fetchTransformed<BlendTransformed>, // RGB444 + fetchTransformed<BlendTransformed>, // ARGB4444_Premultiplied + }, + { + 0, // TransformedTiled + fetchTransformed<BlendTransformedTiled>, // Mono + fetchTransformed<BlendTransformedTiled>, // MonoLsb + fetchTransformed<BlendTransformedTiled>, // Indexed8 + fetchTransformed<BlendTransformedTiled>, // RGB32 + fetchTransformed<BlendTransformedTiled>, // ARGB32 + fetchTransformed<BlendTransformedTiled>, // ARGB32_Premultiplied + fetchTransformed<BlendTransformedTiled>, // RGB16 + fetchTransformed<BlendTransformedTiled>, // ARGB8565_Premultiplied + fetchTransformed<BlendTransformedTiled>, // RGB666 + fetchTransformed<BlendTransformedTiled>, // ARGB6666_Premultiplied + fetchTransformed<BlendTransformedTiled>, // RGB555 + fetchTransformed<BlendTransformedTiled>, // ARGB8555_Premultiplied + fetchTransformed<BlendTransformedTiled>, // RGB888 + fetchTransformed<BlendTransformedTiled>, // RGB444 + fetchTransformed<BlendTransformedTiled>, // ARGB4444_Premultiplied + }, + { + 0, // Bilinear + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // Mono + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // MonoLsb + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // Indexed8 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_ARGB32_Premultiplied>, // RGB32 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_ARGB32>, // ARGB32 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_ARGB32_Premultiplied>, // ARGB32_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB16 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // ARGB8565_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB666 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // ARGB6666_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB555 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // ARGB8555_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB888 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid>, // RGB444 + fetchTransformedBilinear<BlendTransformedBilinear, QImage::Format_Invalid> // ARGB4444_Premultiplied + }, + { + 0, // BilinearTiled + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // Mono + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // MonoLsb + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // Indexed8 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_ARGB32_Premultiplied>, // RGB32 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_ARGB32>, // ARGB32 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_ARGB32_Premultiplied>, // ARGB32_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB16 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // ARGB8565_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB666 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // ARGB6666_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB555 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // ARGB8555_Premultiplied + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB888 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid>, // RGB444 + fetchTransformedBilinear<BlendTransformedBilinearTiled, QImage::Format_Invalid> // ARGB4444_Premultiplied + }, +}; + + +static inline uint qt_gradient_pixel(const QGradientData *data, qreal pos) +{ + int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5)); + + // calculate the actual offset. + if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) { + if (data->spread == QGradient::RepeatSpread) { + ipos = ipos % GRADIENT_STOPTABLE_SIZE; + ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos; + + } else if (data->spread == QGradient::ReflectSpread) { + const int limit = GRADIENT_STOPTABLE_SIZE * 2 - 1; + ipos = ipos % limit; + ipos = ipos < 0 ? limit + ipos : ipos; + ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos; + + } else { + if (ipos < 0) ipos = 0; + else if (ipos >= GRADIENT_STOPTABLE_SIZE) ipos = GRADIENT_STOPTABLE_SIZE-1; + } + } + + Q_ASSERT(ipos >= 0); + Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE); + + return data->colorTable[ipos]; +} + +#define FIXPT_BITS 8 +#define FIXPT_SIZE (1<<FIXPT_BITS) + +static uint qt_gradient_pixel_fixed(const QGradientData *data, int fixed_pos) +{ + int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS; + + // calculate the actual offset. + if (ipos < 0 || ipos >= GRADIENT_STOPTABLE_SIZE) { + if (data->spread == QGradient::RepeatSpread) { + ipos = ipos % GRADIENT_STOPTABLE_SIZE; + ipos = ipos < 0 ? GRADIENT_STOPTABLE_SIZE + ipos : ipos; + + } else if (data->spread == QGradient::ReflectSpread) { + const int limit = GRADIENT_STOPTABLE_SIZE * 2 - 1; + ipos = ipos % limit; + ipos = ipos < 0 ? limit + ipos : ipos; + ipos = ipos >= GRADIENT_STOPTABLE_SIZE ? limit - ipos : ipos; + + } else { + if (ipos < 0) ipos = 0; + else if (ipos >= GRADIENT_STOPTABLE_SIZE) ipos = GRADIENT_STOPTABLE_SIZE-1; + } + } + + Q_ASSERT(ipos >= 0); + Q_ASSERT(ipos < GRADIENT_STOPTABLE_SIZE); + + return data->colorTable[ipos]; +} + +static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data) +{ + v->dx = data->gradient.linear.end.x - data->gradient.linear.origin.x; + v->dy = data->gradient.linear.end.y - data->gradient.linear.origin.y; + v->l = v->dx * v->dx + v->dy * v->dy; + v->off = 0; + if (v->l != 0) { + v->dx /= v->l; + v->dy /= v->l; + v->off = -v->dx * data->gradient.linear.origin.x - v->dy * data->gradient.linear.origin.y; + } +} + +static const uint * QT_FASTCALL fetchLinearGradient(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) +{ + const uint *b = buffer; + qreal t, inc; + + bool affine = true; + qreal rx=0, ry=0; + if (op->linear.l == 0) { + t = inc = 0; + } else { + rx = data->m21 * (y + qreal(0.5)) + data->m11 * (x + qreal(0.5)) + data->dx; + ry = data->m22 * (y + qreal(0.5)) + data->m12 * (x + qreal(0.5)) + data->dy; + t = op->linear.dx*rx + op->linear.dy*ry + op->linear.off; + inc = op->linear.dx * data->m11 + op->linear.dy * data->m12; + affine = !data->m13 && !data->m23; + + if (affine) { + t *= (GRADIENT_STOPTABLE_SIZE - 1); + inc *= (GRADIENT_STOPTABLE_SIZE - 1); + } + } + + const uint *end = buffer + length; + if (affine) { + if (inc > qreal(-1e-5) && inc < qreal(1e-5)) { + QT_MEMFILL_UINT(buffer, length, qt_gradient_pixel_fixed(&data->gradient, int(t * FIXPT_SIZE))); + } else { + if (t+inc*length < qreal(INT_MAX >> (FIXPT_BITS + 1)) && + t+inc*length > qreal(INT_MIN >> (FIXPT_BITS + 1))) { + // we can use fixed point math + int t_fixed = int(t * FIXPT_SIZE); + int inc_fixed = int(inc * FIXPT_SIZE); + while (buffer < end) { + *buffer = qt_gradient_pixel_fixed(&data->gradient, t_fixed); + t_fixed += inc_fixed; + ++buffer; + } + } else { + // we have to fall back to float math + while (buffer < end) { + *buffer = qt_gradient_pixel(&data->gradient, t/GRADIENT_STOPTABLE_SIZE); + t += inc; + ++buffer; + } + } + } + } else { // fall back to float math here as well + qreal rw = data->m23 * (y + qreal(0.5)) + data->m13 * (x + qreal(0.5)) + data->m33; + while (buffer < end) { + qreal x = rx/rw; + qreal y = ry/rw; + t = (op->linear.dx*x + op->linear.dy *y) + op->linear.off; + + *buffer = qt_gradient_pixel(&data->gradient, t); + rx += data->m11; + ry += data->m12; + rw += data->m13; + if (!rw) { + rw += data->m13; + } + ++buffer; + } + } + + return b; +} + +static inline qreal determinant(qreal a, qreal b, qreal c) +{ + return (b * b) - (4 * a * c); +} + +// function to evaluate real roots +static inline qreal realRoots(qreal a, qreal b, qreal detSqrt) +{ + return (-b + detSqrt)/(2 * a); +} + +static inline qreal qSafeSqrt(qreal x) +{ + return x > 0 ? qSqrt(x) : 0; +} + +static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data) +{ + v->dx = data->gradient.radial.center.x - data->gradient.radial.focal.x; + v->dy = data->gradient.radial.center.y - data->gradient.radial.focal.y; + v->a = data->gradient.radial.radius*data->gradient.radial.radius - v->dx*v->dx - v->dy*v->dy; +} + +static const uint * QT_FASTCALL fetchRadialGradient(uint *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) +{ + const uint *b = buffer; + qreal rx = data->m21 * (y + qreal(0.5)) + + data->dx + data->m11 * (x + qreal(0.5)); + qreal ry = data->m22 * (y + qreal(0.5)) + + data->dy + data->m12 * (x + qreal(0.5)); + bool affine = !data->m13 && !data->m23; + //qreal r = data->gradient.radial.radius; + + const uint *end = buffer + length; + if (affine) { + rx -= data->gradient.radial.focal.x; + ry -= data->gradient.radial.focal.y; + + qreal inv_a = 1 / qreal(2 * op->radial.a); + + const qreal delta_rx = data->m11; + const qreal delta_ry = data->m12; + + qreal b = 2*(rx * op->radial.dx + ry * op->radial.dy); + qreal delta_b = 2*(delta_rx * op->radial.dx + delta_ry * op->radial.dy); + const qreal b_delta_b = 2 * b * delta_b; + const qreal delta_b_delta_b = 2 * delta_b * delta_b; + + const qreal bb = b * b; + const qreal delta_bb = delta_b * delta_b; + + b *= inv_a; + delta_b *= inv_a; + + const qreal rxrxryry = rx * rx + ry * ry; + const qreal delta_rxrxryry = delta_rx * delta_rx + delta_ry * delta_ry; + const qreal rx_plus_ry = 2*(rx * delta_rx + ry * delta_ry); + const qreal delta_rx_plus_ry = 2 * delta_rxrxryry; + + inv_a *= inv_a; + + qreal det = (bb + 4 * op->radial.a * rxrxryry) * inv_a; + qreal delta_det = (b_delta_b + delta_bb + 4 * op->radial.a * (rx_plus_ry + delta_rxrxryry)) * inv_a; + const qreal delta_delta_det = (delta_b_delta_b + 4 * op->radial.a * delta_rx_plus_ry) * inv_a; + + while (buffer < end) { + *buffer = qt_gradient_pixel(&data->gradient, qSafeSqrt(det) - b); + + det += delta_det; + delta_det += delta_delta_det; + b += delta_b; + + ++buffer; + } + } else { + qreal rw = data->m23 * (y + qreal(0.5)) + + data->m33 + data->m13 * (x + qreal(0.5)); + if (!rw) + rw = 1; + while (buffer < end) { + qreal gx = rx/rw - data->gradient.radial.focal.x; + qreal gy = ry/rw - data->gradient.radial.focal.y; + qreal b = 2*(gx*op->radial.dx + gy*op->radial.dy); + qreal det = determinant(op->radial.a, b , -(gx*gx + gy*gy)); + qreal s = realRoots(op->radial.a, b, qSafeSqrt(det)); + + *buffer = qt_gradient_pixel(&data->gradient, s); + + rx += data->m11; + ry += data->m12; + rw += data->m13; + if (!rw) { + rw += data->m13; + } + ++buffer; + } + } + + return b; +} + +static const uint * QT_FASTCALL fetchConicalGradient(uint *buffer, const Operator *, const QSpanData *data, + int y, int x, int length) +{ + const uint *b = buffer; + qreal rx = data->m21 * (y + qreal(0.5)) + + data->dx + data->m11 * (x + qreal(0.5)); + qreal ry = data->m22 * (y + qreal(0.5)) + + data->dy + data->m12 * (x + qreal(0.5)); + bool affine = !data->m13 && !data->m23; + + const uint *end = buffer + length; + if (affine) { + rx -= data->gradient.conical.center.x; + ry -= data->gradient.conical.center.y; + while (buffer < end) { + qreal angle = qAtan2(ry, rx) + data->gradient.conical.angle; + + *buffer = qt_gradient_pixel(&data->gradient, 1 - angle / (2*Q_PI)); + + rx += data->m11; + ry += data->m12; + ++buffer; + } + } else { + qreal rw = data->m23 * (y + qreal(0.5)) + + data->m33 + data->m13 * (x + qreal(0.5)); + if (!rw) + rw = 1; + while (buffer < end) { + qreal angle = qAtan2(ry/rw - data->gradient.conical.center.x, + rx/rw - data->gradient.conical.center.y) + + data->gradient.conical.angle; + + *buffer = qt_gradient_pixel(&data->gradient, 1. - angle / (2*Q_PI)); + + rx += data->m11; + ry += data->m12; + rw += data->m13; + if (!rw) { + rw += data->m13; + } + ++buffer; + } + } + return b; +} + +#if defined(Q_CC_RVCT) +// Force ARM code generation for comp_func_* -methods +# pragma push +# pragma arm +# if defined(QT_HAVE_ARMV6) +static __forceinline void preload(const uint *start) +{ + asm( "pld [start]" ); +} +static const uint L2CacheLineLength = 32; +static const uint L2CacheLineLengthInInts = L2CacheLineLength/sizeof(uint); +# define PRELOAD_INIT(x) preload(x); +# define PRELOAD_INIT2(x,y) PRELOAD_INIT(x) PRELOAD_INIT(y) +# define PRELOAD_COND(x) if (((uint)&x[i])%L2CacheLineLength == 0) preload(&x[i] + L2CacheLineLengthInInts); +// Two consecutive preloads stall, so space them out a bit by using different modulus. +# define PRELOAD_COND2(x,y) if (((uint)&x[i])%L2CacheLineLength == 0) preload(&x[i] + L2CacheLineLengthInInts); \ + if (((uint)&y[i])%L2CacheLineLength == 16) preload(&y[i] + L2CacheLineLengthInInts); +# endif // QT_HAVE_ARMV6 +#endif // Q_CC_RVCT + +#if !defined(Q_CC_RVCT) || !defined(QT_HAVE_ARMV6) +# define PRELOAD_INIT(x) +# define PRELOAD_INIT2(x,y) +# define PRELOAD_COND(x) +# define PRELOAD_COND2(x,y) +#endif + +/* The constant alpha factor describes an alpha factor that gets applied + to the result of the composition operation combining it with the destination. + + The intent is that if const_alpha == 0. we get back dest, and if const_alpha == 1. + we get the unmodified operation + + result = src op dest + dest = result * const_alpha + dest * (1. - const_alpha) + + This means that in the comments below, the first line is the const_alpha==255 case, the + second line the general one. + + In the lines below: + s == src, sa == alpha(src), sia = 1 - alpha(src) + d == dest, da == alpha(dest), dia = 1 - alpha(dest) + ca = const_alpha, cia = 1 - const_alpha + + The methods exist in two variants. One where we have a constant source, the other + where the source is an array of pixels. +*/ + +/* + result = 0 + d = d * cia +*/ +#define comp_func_Clear_impl(dest, length, const_alpha)\ +{\ + if (const_alpha == 255) {\ + QT_MEMFILL_UINT(dest, length, 0);\ + } else {\ + int ialpha = 255 - const_alpha;\ + PRELOAD_INIT(dest)\ + for (int i = 0; i < length; ++i) {\ + PRELOAD_COND(dest)\ + dest[i] = BYTE_MUL(dest[i], ialpha);\ + }\ + }\ +} + +void QT_FASTCALL comp_func_solid_Clear(uint *dest, int length, uint, uint const_alpha) +{ + comp_func_Clear_impl(dest, length, const_alpha); +} + +void QT_FASTCALL comp_func_Clear(uint *dest, const uint *, int length, uint const_alpha) +{ + comp_func_Clear_impl(dest, length, const_alpha); +} + +/* + result = s + dest = s * ca + d * cia +*/ +void QT_FASTCALL comp_func_solid_Source(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) { + QT_MEMFILL_UINT(dest, length, color); + } else { + int ialpha = 255 - const_alpha; + color = BYTE_MUL(color, const_alpha); + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + dest[i] = color + BYTE_MUL(dest[i], ialpha); + } + } +} + +void QT_FASTCALL comp_func_Source(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) { + ::memcpy(dest, src, length * sizeof(uint)); + } else { + int ialpha = 255 - const_alpha; + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + dest[i] = INTERPOLATE_PIXEL_255(src[i], const_alpha, dest[i], ialpha); + } + } +} + +void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint) +{ +} + +void QT_FASTCALL comp_func_Destination(uint *, const uint *, int, uint) +{ +} + +/* + result = s + d * sia + dest = (s + d * sia) * ca + d * cia + = s * ca + d * (sia * ca + cia) + = s * ca + d * (1 - sa*ca) +*/ +void QT_FASTCALL comp_func_solid_SourceOver(uint *dest, int length, uint color, uint const_alpha) +{ + if ((const_alpha & qAlpha(color)) == 255) { + QT_MEMFILL_UINT(dest, length, color); + } else { + if (const_alpha != 255) + color = BYTE_MUL(color, const_alpha); + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + dest[i] = color + BYTE_MUL(dest[i], qAlpha(~color)); + } + } +} + +void QT_FASTCALL comp_func_SourceOver(uint *dest, const uint *src, int length, uint const_alpha) +{ + PRELOAD_INIT2(dest, src) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint s = src[i]; + if (s >= 0xff000000) + dest[i] = s; + else if (s != 0) + dest[i] = s + BYTE_MUL(dest[i], qAlpha(~s)); + } + } else { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint s = BYTE_MUL(src[i], const_alpha); + dest[i] = s + BYTE_MUL(dest[i], qAlpha(~s)); + } + } +} + +/* + result = d + s * dia + dest = (d + s * dia) * ca + d * cia + = d + s * dia * ca +*/ +void QT_FASTCALL comp_func_solid_DestinationOver(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha != 255) + color = BYTE_MUL(color, const_alpha); + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + dest[i] = d + BYTE_MUL(color, qAlpha(~d)); + } +} + +void QT_FASTCALL comp_func_DestinationOver(uint *dest, const uint *src, int length, uint const_alpha) +{ + PRELOAD_INIT2(dest, src) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + dest[i] = d + BYTE_MUL(src[i], qAlpha(~d)); + } + } else { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = BYTE_MUL(src[i], const_alpha); + dest[i] = d + BYTE_MUL(s, qAlpha(~d)); + } + } +} + +/* + result = s * da + dest = s * da * ca + d * cia +*/ +void QT_FASTCALL comp_func_solid_SourceIn(uint *dest, int length, uint color, uint const_alpha) +{ + PRELOAD_INIT(dest) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + dest[i] = BYTE_MUL(color, qAlpha(dest[i])); + } + } else { + color = BYTE_MUL(color, const_alpha); + uint cia = 255 - const_alpha; + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(d), d, cia); + } + } +} + +void QT_FASTCALL comp_func_SourceIn(uint *dest, const uint *src, int length, uint const_alpha) +{ + PRELOAD_INIT2(dest, src) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + dest[i] = BYTE_MUL(src[i], qAlpha(dest[i])); + } + } else { + uint cia = 255 - const_alpha; + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = BYTE_MUL(src[i], const_alpha); + dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, cia); + } + } +} + +/* + result = d * sa + dest = d * sa * ca + d * cia + = d * (sa * ca + cia) +*/ +void QT_FASTCALL comp_func_solid_DestinationIn(uint *dest, int length, uint color, uint const_alpha) +{ + uint a = qAlpha(color); + if (const_alpha != 255) { + a = BYTE_MUL(a, const_alpha) + 255 - const_alpha; + } + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + dest[i] = BYTE_MUL(dest[i], a); + } +} + +void QT_FASTCALL comp_func_DestinationIn(uint *dest, const uint *src, int length, uint const_alpha) +{ + PRELOAD_INIT2(dest, src) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + dest[i] = BYTE_MUL(dest[i], qAlpha(src[i])); + } + } else { + int cia = 255 - const_alpha; + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint a = BYTE_MUL(qAlpha(src[i]), const_alpha) + cia; + dest[i] = BYTE_MUL(dest[i], a); + } + } +} + +/* + result = s * dia + dest = s * dia * ca + d * cia +*/ + +void QT_FASTCALL comp_func_solid_SourceOut(uint *dest, int length, uint color, uint const_alpha) +{ + PRELOAD_INIT(dest) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + dest[i] = BYTE_MUL(color, qAlpha(~dest[i])); + } + } else { + color = BYTE_MUL(color, const_alpha); + int cia = 255 - const_alpha; + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(~d), d, cia); + } + } +} + +void QT_FASTCALL comp_func_SourceOut(uint *dest, const uint *src, int length, uint const_alpha) +{ + PRELOAD_INIT2(dest, src) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + dest[i] = BYTE_MUL(src[i], qAlpha(~dest[i])); + } + } else { + int cia = 255 - const_alpha; + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint s = BYTE_MUL(src[i], const_alpha); + uint d = dest[i]; + dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, cia); + } + } +} + +/* + result = d * sia + dest = d * sia * ca + d * cia + = d * (sia * ca + cia) +*/ +void QT_FASTCALL comp_func_solid_DestinationOut(uint *dest, int length, uint color, uint const_alpha) +{ + uint a = qAlpha(~color); + if (const_alpha != 255) + a = BYTE_MUL(a, const_alpha) + 255 - const_alpha; + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + dest[i] = BYTE_MUL(dest[i], a); + } +} + +void QT_FASTCALL comp_func_DestinationOut(uint *dest, const uint *src, int length, uint const_alpha) +{ + PRELOAD_INIT2(dest, src) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + dest[i] = BYTE_MUL(dest[i], qAlpha(~src[i])); + } + } else { + int cia = 255 - const_alpha; + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint sia = BYTE_MUL(qAlpha(~src[i]), const_alpha) + cia; + dest[i] = BYTE_MUL(dest[i], sia); + } + } +} + +/* + result = s*da + d*sia + dest = s*da*ca + d*sia*ca + d *cia + = s*ca * da + d * (sia*ca + cia) + = s*ca * da + d * (1 - sa*ca) +*/ +void QT_FASTCALL comp_func_solid_SourceAtop(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha != 255) { + color = BYTE_MUL(color, const_alpha); + } + uint sia = qAlpha(~color); + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(dest[i]), dest[i], sia); + } +} + +void QT_FASTCALL comp_func_SourceAtop(uint *dest, const uint *src, int length, uint const_alpha) +{ + PRELOAD_INIT2(dest, src) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint s = src[i]; + uint d = dest[i]; + dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, qAlpha(~s)); + } + } else { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint s = BYTE_MUL(src[i], const_alpha); + uint d = dest[i]; + dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(d), d, qAlpha(~s)); + } + } +} + +/* + result = d*sa + s*dia + dest = d*sa*ca + s*dia*ca + d *cia + = s*ca * dia + d * (sa*ca + cia) +*/ +void QT_FASTCALL comp_func_solid_DestinationAtop(uint *dest, int length, uint color, uint const_alpha) +{ + uint a = qAlpha(color); + if (const_alpha != 255) { + color = BYTE_MUL(color, const_alpha); + a = qAlpha(color) + 255 - const_alpha; + } + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + dest[i] = INTERPOLATE_PIXEL_255(d, a, color, qAlpha(~d)); + } +} + +void QT_FASTCALL comp_func_DestinationAtop(uint *dest, const uint *src, int length, uint const_alpha) +{ + PRELOAD_INIT2(dest, src) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint s = src[i]; + uint d = dest[i]; + dest[i] = INTERPOLATE_PIXEL_255(d, qAlpha(s), s, qAlpha(~d)); + } + } else { + int cia = 255 - const_alpha; + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint s = BYTE_MUL(src[i], const_alpha); + uint d = dest[i]; + uint a = qAlpha(s) + cia; + dest[i] = INTERPOLATE_PIXEL_255(d, a, s, qAlpha(~d)); + } + } +} + +/* + result = d*sia + s*dia + dest = d*sia*ca + s*dia*ca + d *cia + = s*ca * dia + d * (sia*ca + cia) + = s*ca * dia + d * (1 - sa*ca) +*/ +void QT_FASTCALL comp_func_solid_XOR(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha != 255) + color = BYTE_MUL(color, const_alpha); + uint sia = qAlpha(~color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + dest[i] = INTERPOLATE_PIXEL_255(color, qAlpha(~d), d, sia); + } +} + +void QT_FASTCALL comp_func_XOR(uint *dest, const uint *src, int length, uint const_alpha) +{ + PRELOAD_INIT2(dest, src) + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, qAlpha(~s)); + } + } else { + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = BYTE_MUL(src[i], const_alpha); + dest[i] = INTERPOLATE_PIXEL_255(s, qAlpha(~d), d, qAlpha(~s)); + } + } +} + +struct QFullCoverage { + inline void store(uint *dest, const uint src) const + { + *dest = src; + } +}; + +struct QPartialCoverage { + inline QPartialCoverage(uint const_alpha) + : ca(const_alpha) + , ica(255 - const_alpha) + { + } + + inline void store(uint *dest, const uint src) const + { + *dest = INTERPOLATE_PIXEL_255(src, ca, *dest, ica); + } + +private: + const uint ca; + const uint ica; +}; + +static inline int mix_alpha(int da, int sa) +{ + return 255 - ((255 - sa) * (255 - da) >> 8); +} + +/* + Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa) + = Sca + Dca +*/ +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Plus_impl(uint *dest, int length, uint color, const T &coverage) +{ + uint s = color; + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + d = comp_func_Plus_one_pixel(d, s); + coverage.store(&dest[i], d); + } +} + +void QT_FASTCALL comp_func_solid_Plus(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Plus_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Plus_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Plus_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + d = comp_func_Plus_one_pixel(d, s); + + coverage.store(&dest[i], d); + } +} + +void QT_FASTCALL comp_func_Plus(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Plus_impl(dest, src, length, QFullCoverage()); + else + comp_func_Plus_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) +*/ +static inline int multiply_op(int dst, int src, int da, int sa) +{ + return qt_div_255(src * dst + src * (255 - da) + dst * (255 - sa)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Multiply_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a, b) multiply_op(a, b, da, sa) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Multiply(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Multiply_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Multiply_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) multiply_op(a, b, da, sa) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Multiply(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Multiply_impl(dest, src, length, QFullCoverage()); + else + comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + Dca' = (Sca.Da + Dca.Sa - Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) + = Sca + Dca - Sca.Dca +*/ +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Screen_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a, b) 255 - qt_div_255((255-a) * (255-b)) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Screen(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Screen_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Screen_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) 255 - (((255-a) * (255-b)) >> 8) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Screen(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Screen_impl(dest, src, length, QFullCoverage()); + else + comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + if 2.Dca < Da + Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) + otherwise + Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa) +*/ +static inline int overlay_op(int dst, int src, int da, int sa) +{ + const int temp = src * (255 - da) + dst * (255 - sa); + if (2 * dst < da) + return qt_div_255(2 * src * dst + temp); + else + return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Overlay_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a, b) overlay_op(a, b, da, sa) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Overlay(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Overlay_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Overlay_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) overlay_op(a, b, da, sa) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Overlay(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Overlay_impl(dest, src, length, QFullCoverage()); + else + comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) + Da' = Sa + Da - Sa.Da +*/ +static inline int darken_op(int dst, int src, int da, int sa) +{ + return qt_div_255(qMin(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Darken_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a, b) darken_op(a, b, da, sa) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Darken(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Darken_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Darken_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) darken_op(a, b, da, sa) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Darken(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Darken_impl(dest, src, length, QFullCoverage()); + else + comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) + Da' = Sa + Da - Sa.Da +*/ +static inline int lighten_op(int dst, int src, int da, int sa) +{ + return qt_div_255(qMax(src * da, dst * sa) + src * (255 - da) + dst * (255 - sa)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Lighten_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a, b) lighten_op(a, b, da, sa) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Lighten(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Lighten_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Lighten_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) lighten_op(a, b, da, sa) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Lighten(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Lighten_impl(dest, src, length, QFullCoverage()); + else + comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + if Sca.Da + Dca.Sa >= Sa.Da + Dca' = Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa) + otherwise + Dca' = Dca.Sa/(1-Sca/Sa) + Sca.(1 - Da) + Dca.(1 - Sa) +*/ +static inline int color_dodge_op(int dst, int src, int da, int sa) +{ + const int sa_da = sa * da; + const int dst_sa = dst * sa; + const int src_da = src * da; + + const int temp = src * (255 - da) + dst * (255 - sa); + if (src_da + dst_sa >= sa_da) + return qt_div_255(sa_da + temp); + else + return qt_div_255(255 * dst_sa / (255 - 255 * src / sa) + temp); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorDodge_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a,b) color_dodge_op(a, b, da, sa) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_ColorDodge(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_ColorDodge_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorDodge_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) color_dodge_op(a, b, da, sa) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_ColorDodge(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_ColorDodge_impl(dest, src, length, QFullCoverage()); + else + comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + if Sca.Da + Dca.Sa <= Sa.Da + Dca' = Sca.(1 - Da) + Dca.(1 - Sa) + otherwise + Dca' = Sa.(Sca.Da + Dca.Sa - Sa.Da)/Sca + Sca.(1 - Da) + Dca.(1 - Sa) +*/ +static inline int color_burn_op(int dst, int src, int da, int sa) +{ + const int src_da = src * da; + const int dst_sa = dst * sa; + const int sa_da = sa * da; + + const int temp = src * (255 - da) + dst * (255 - sa); + + if (src == 0 || src_da + dst_sa <= sa_da) + return qt_div_255(temp); + return qt_div_255(sa * (src_da + dst_sa - sa_da) / src + temp); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_ColorBurn_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a, b) color_burn_op(a, b, da, sa) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_ColorBurn(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_ColorBurn_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_ColorBurn_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) color_burn_op(a, b, da, sa) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_ColorBurn(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_ColorBurn_impl(dest, src, length, QFullCoverage()); + else + comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + if 2.Sca < Sa + Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) + otherwise + Dca' = Sa.Da - 2.(Da - Dca).(Sa - Sca) + Sca.(1 - Da) + Dca.(1 - Sa) +*/ +static inline uint hardlight_op(int dst, int src, int da, int sa) +{ + const uint temp = src * (255 - da) + dst * (255 - sa); + + if (2 * src < sa) + return qt_div_255(2 * src * dst + temp); + else + return qt_div_255(sa * da - 2 * (da - dst) * (sa - src) + temp); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_HardLight_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a, b) hardlight_op(a, b, da, sa) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_HardLight(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_HardLight_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_HardLight_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) hardlight_op(a, b, da, sa) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_HardLight(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_HardLight_impl(dest, src, length, QFullCoverage()); + else + comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + if 2.Sca <= Sa + Dca' = Dca.(Sa + (2.Sca - Sa).(1 - Dca/Da)) + Sca.(1 - Da) + Dca.(1 - Sa) + otherwise if 2.Sca > Sa and 4.Dca <= Da + Dca' = Dca.Sa + Da.(2.Sca - Sa).(4.Dca/Da.(4.Dca/Da + 1).(Dca/Da - 1) + 7.Dca/Da) + Sca.(1 - Da) + Dca.(1 - Sa) + otherwise if 2.Sca > Sa and 4.Dca > Da + Dca' = Dca.Sa + Da.(2.Sca - Sa).((Dca/Da)^0.5 - Dca/Da) + Sca.(1 - Da) + Dca.(1 - Sa) +*/ +static inline int soft_light_op(int dst, int src, int da, int sa) +{ + const int src2 = src << 1; + const int dst_np = da != 0 ? (255 * dst) / da : 0; + const int temp = (src * (255 - da) + dst * (255 - sa)) * 255; + + if (src2 < sa) + return (dst * (sa * 255 + (src2 - sa) * (255 - dst_np)) + temp) / 65025; + else if (4 * dst <= da) + return (dst * sa * 255 + da * (src2 - sa) * ((((16 * dst_np - 12 * 255) * dst_np + 3 * 65025) * dst_np) / 65025) + temp) / 65025; + else { +# ifdef Q_CC_RVCT // needed to avoid compiler crash in RVCT 2.2 + return (dst * sa * 255 + da * (src2 - sa) * (qIntSqrtInt(dst_np * 255) - dst_np) + temp) / 65025; +# else + return (dst * sa * 255 + da * (src2 - sa) * (int(qSqrt(qreal(dst_np * 255))) - dst_np) + temp) / 65025; +# endif + } +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_SoftLight_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a, b) soft_light_op(a, b, da, sa) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_SoftLight_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) soft_light_op(a, b, da, sa) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_SoftLight(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_SoftLight_impl(dest, src, length, QFullCoverage()); + else + comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa) + = Sca + Dca - 2.min(Sca.Da, Dca.Sa) +*/ +static inline int difference_op(int dst, int src, int da, int sa) +{ + return src + dst - qt_div_255(2 * qMin(src * da, dst * sa)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_solid_Difference_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a, b) difference_op(a, b, da, sa) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Difference(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Difference_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Difference_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) difference_op(a, b, da, sa) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Difference(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Difference_impl(dest, src, length, QFullCoverage()); + else + comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +/* + Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) +*/ +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void QT_FASTCALL comp_func_solid_Exclusion_impl(uint *dest, int length, uint color, const T &coverage) +{ + int sa = qAlpha(color); + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + PRELOAD_INIT(dest) + for (int i = 0; i < length; ++i) { + PRELOAD_COND(dest) + uint d = dest[i]; + int da = qAlpha(d); + +#define OP(a, b) (a + b - qt_div_255(2*(a*b))) + int r = OP( qRed(d), sr); + int b = OP( qBlue(d), sb); + int g = OP(qGreen(d), sg); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Exclusion(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Exclusion_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + +template <typename T> +Q_STATIC_TEMPLATE_FUNCTION inline void comp_func_Exclusion_impl(uint *dest, const uint *src, int length, const T &coverage) +{ + PRELOAD_INIT2(dest, src) + for (int i = 0; i < length; ++i) { + PRELOAD_COND2(dest, src) + uint d = dest[i]; + uint s = src[i]; + + int da = qAlpha(d); + int sa = qAlpha(s); + +#define OP(a, b) (a + b - ((a*b) >> 7)) + int r = OP( qRed(d), qRed(s)); + int b = OP( qBlue(d), qBlue(s)); + int g = OP(qGreen(d), qGreen(s)); + int a = mix_alpha(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Exclusion(uint *dest, const uint *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Exclusion_impl(dest, src, length, QFullCoverage()); + else + comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha)); +} + +#if defined(Q_CC_RVCT) +// Restore pragma state from previous #pragma arm +# pragma pop +#endif + +void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest, + int length, + uint color, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) + *dest++ |= color; +} + +void QT_FASTCALL rasterop_SourceOrDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) + *dest++ |= *src++; +} + +void QT_FASTCALL rasterop_solid_SourceAndDestination(uint *dest, + int length, + uint color, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + color |= 0xff000000; + while (length--) + *dest++ &= color; +} + +void QT_FASTCALL rasterop_SourceAndDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) { + *dest = (*src & *dest) | 0xff000000; + ++dest; ++src; + } +} + +void QT_FASTCALL rasterop_solid_SourceXorDestination(uint *dest, + int length, + uint color, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + color &= 0x00ffffff; + while (length--) + *dest++ ^= color; +} + +void QT_FASTCALL rasterop_SourceXorDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) { + *dest = (*src ^ *dest) | 0xff000000; + ++dest; ++src; + } +} + +void QT_FASTCALL rasterop_solid_NotSourceAndNotDestination(uint *dest, + int length, + uint color, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + color = ~color; + while (length--) { + *dest = (color & ~(*dest)) | 0xff000000; + ++dest; + } +} + +void QT_FASTCALL rasterop_NotSourceAndNotDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) { + *dest = (~(*src) & ~(*dest)) | 0xff000000; + ++dest; ++src; + } +} + +void QT_FASTCALL rasterop_solid_NotSourceOrNotDestination(uint *dest, + int length, + uint color, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + color = ~color | 0xff000000; + while (length--) { + *dest = color | ~(*dest); + ++dest; + } +} + +void QT_FASTCALL rasterop_NotSourceOrNotDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) { + *dest = ~(*src) | ~(*dest) | 0xff000000; + ++dest; ++src; + } +} + +void QT_FASTCALL rasterop_solid_NotSourceXorDestination(uint *dest, + int length, + uint color, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + color = ~color & 0x00ffffff; + while (length--) { + *dest = color ^ (*dest); + ++dest; + } +} + +void QT_FASTCALL rasterop_NotSourceXorDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) { + *dest = ((~(*src)) ^ (*dest)) | 0xff000000; + ++dest; ++src; + } +} + +void QT_FASTCALL rasterop_solid_NotSource(uint *dest, int length, + uint color, uint const_alpha) +{ + Q_UNUSED(const_alpha); + qt_memfill(dest, ~color | 0xff000000, length); +} + +void QT_FASTCALL rasterop_NotSource(uint *dest, const uint *src, + int length, uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) + *dest++ = ~(*src++) | 0xff000000; +} + +void QT_FASTCALL rasterop_solid_NotSourceAndDestination(uint *dest, + int length, + uint color, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + color = ~color | 0xff000000; + while (length--) { + *dest = color & *dest; + ++dest; + } +} + +void QT_FASTCALL rasterop_NotSourceAndDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) { + *dest = (~(*src) & *dest) | 0xff000000; + ++dest; ++src; + } +} + +void QT_FASTCALL rasterop_solid_SourceAndNotDestination(uint *dest, + int length, + uint color, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) { + *dest = (color & ~(*dest)) | 0xff000000; + ++dest; + } +} + +void QT_FASTCALL rasterop_SourceAndNotDestination(uint *dest, + const uint *src, + int length, + uint const_alpha) +{ + Q_UNUSED(const_alpha); + while (length--) { + *dest = (*src & ~(*dest)) | 0xff000000; + ++dest; ++src; + } +} + +static CompositionFunctionSolid functionForModeSolid_C[] = { + comp_func_solid_SourceOver, + comp_func_solid_DestinationOver, + comp_func_solid_Clear, + comp_func_solid_Source, + comp_func_solid_Destination, + comp_func_solid_SourceIn, + comp_func_solid_DestinationIn, + comp_func_solid_SourceOut, + comp_func_solid_DestinationOut, + comp_func_solid_SourceAtop, + comp_func_solid_DestinationAtop, + comp_func_solid_XOR, + comp_func_solid_Plus, + comp_func_solid_Multiply, + comp_func_solid_Screen, + comp_func_solid_Overlay, + comp_func_solid_Darken, + comp_func_solid_Lighten, + comp_func_solid_ColorDodge, + comp_func_solid_ColorBurn, + comp_func_solid_HardLight, + comp_func_solid_SoftLight, + comp_func_solid_Difference, + comp_func_solid_Exclusion, + rasterop_solid_SourceOrDestination, + rasterop_solid_SourceAndDestination, + rasterop_solid_SourceXorDestination, + rasterop_solid_NotSourceAndNotDestination, + rasterop_solid_NotSourceOrNotDestination, + rasterop_solid_NotSourceXorDestination, + rasterop_solid_NotSource, + rasterop_solid_NotSourceAndDestination, + rasterop_solid_SourceAndNotDestination +}; + +static const CompositionFunctionSolid *functionForModeSolid = functionForModeSolid_C; + +static CompositionFunction functionForMode_C[] = { + comp_func_SourceOver, + comp_func_DestinationOver, + comp_func_Clear, + comp_func_Source, + comp_func_Destination, + comp_func_SourceIn, + comp_func_DestinationIn, + comp_func_SourceOut, + comp_func_DestinationOut, + comp_func_SourceAtop, + comp_func_DestinationAtop, + comp_func_XOR, + comp_func_Plus, + comp_func_Multiply, + comp_func_Screen, + comp_func_Overlay, + comp_func_Darken, + comp_func_Lighten, + comp_func_ColorDodge, + comp_func_ColorBurn, + comp_func_HardLight, + comp_func_SoftLight, + comp_func_Difference, + comp_func_Exclusion, + rasterop_SourceOrDestination, + rasterop_SourceAndDestination, + rasterop_SourceXorDestination, + rasterop_NotSourceAndNotDestination, + rasterop_NotSourceOrNotDestination, + rasterop_NotSourceXorDestination, + rasterop_NotSource, + rasterop_NotSourceAndDestination, + rasterop_SourceAndNotDestination +}; + +static const CompositionFunction *functionForMode = functionForMode_C; + +static TextureBlendType getBlendType(const QSpanData *data) +{ + TextureBlendType ft; + if (data->txop <= QTransform::TxTranslate) + if (data->texture.type == QTextureData::Tiled) + ft = BlendTiled; + else + ft = BlendUntransformed; + else if (data->bilinear) + if (data->texture.type == QTextureData::Tiled) + ft = BlendTransformedBilinearTiled; + else + ft = BlendTransformedBilinear; + else + if (data->texture.type == QTextureData::Tiled) + ft = BlendTransformedTiled; + else + ft = BlendTransformed; + return ft; +} + +static inline Operator getOperator(const QSpanData *data, const QSpan *spans, int spanCount) +{ + Operator op; + bool solidSource = false; + + switch(data->type) { + case QSpanData::Solid: + solidSource = (qAlpha(data->solid.color) == 255); + break; + case QSpanData::LinearGradient: + solidSource = !data->gradient.alphaColor; + getLinearGradientValues(&op.linear, data); + op.src_fetch = fetchLinearGradient; + break; + case QSpanData::RadialGradient: + solidSource = !data->gradient.alphaColor; + getRadialGradientValues(&op.radial, data); + op.src_fetch = fetchRadialGradient; + break; + case QSpanData::ConicalGradient: + solidSource = !data->gradient.alphaColor; + op.src_fetch = fetchConicalGradient; + break; + case QSpanData::Texture: + op.src_fetch = sourceFetch[getBlendType(data)][data->texture.format]; + solidSource = !data->texture.hasAlpha; + default: + break; + } + + op.mode = data->rasterBuffer->compositionMode; + if (op.mode == QPainter::CompositionMode_SourceOver && solidSource) + op.mode = QPainter::CompositionMode_Source; + + op.dest_fetch = destFetchProc[data->rasterBuffer->format]; + if (op.mode == QPainter::CompositionMode_Source) { + switch (data->rasterBuffer->format) { + case QImage::Format_RGB32: + case QImage::Format_ARGB32_Premultiplied: + // don't clear dest_fetch as it sets up the pointer correctly to save one copy + break; + default: { + const QSpan *lastSpan = spans + spanCount; + bool alphaSpans = false; + while (spans < lastSpan) { + if (spans->coverage != 255) { + alphaSpans = true; + break; + } + ++spans; + } + if (!alphaSpans) + op.dest_fetch = 0; + } + } + } + + op.dest_store = destStoreProc[data->rasterBuffer->format]; + + op.funcSolid = functionForModeSolid[op.mode]; + op.func = functionForMode[op.mode]; + + return op; +} + + + +// -------------------- blend methods --------------------- + +enum SpanMethod { + RegularSpans, + CallbackSpans +}; + +#if !defined(Q_CC_SUN) +static +#endif +void drawBufferSpan(QSpanData *data, const uint *buffer, int bufsize, + int x, int y, int length, uint const_alpha) +{ +#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS) + data->rasterEngine->drawBufferSpan(buffer, bufsize, x, y, length, const_alpha); +#else + Q_UNUSED(data); + Q_UNUSED(buffer); + Q_UNUSED(bufsize); + Q_UNUSED(x); + Q_UNUSED(y); + Q_UNUSED(length); + Q_UNUSED(const_alpha); +#endif +} + +#if !defined(Q_CC_SUN) +static +#endif +void blend_color_generic(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + uint buffer[buffer_size]; + Operator op = getOperator(data, spans, count); + + while (count--) { + int x = spans->x; + int length = spans->len; + while (length) { + int l = qMin(buffer_size, length); + uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer; + op.funcSolid(dest, l, data->solid.color, spans->coverage); + if (op.dest_store) + op.dest_store(data->rasterBuffer, x, spans->y, dest, l); + length -= l; + x += l; + } + ++spans; + } +} + +#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS) +static void blend_color_generic_callback(int count, const QSpan *spans, void *userData) +{ + // ### Falcon + Q_UNUSED(count); + Q_UNUSED(spans); + Q_UNUSED(userData); +// QSpanData *data = reinterpret_cast<QSpanData*>(userData); +// data->rasterEngine->drawColorSpans(spans, count, data->solid.color); +} +#endif // QT_NO_RASTERCALLBACKS + +static void blend_color_argb(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + Operator op = getOperator(data, spans, count); + + if (op.mode == QPainter::CompositionMode_Source) { + // inline for performance + while (count--) { + uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x; + if (spans->coverage == 255) { + QT_MEMFILL_UINT(target, spans->len, data->solid.color); + } else { + uint c = BYTE_MUL(data->solid.color, spans->coverage); + int ialpha = 255 - spans->coverage; + for (int i = 0; i < spans->len; ++i) + target[i] = c + BYTE_MUL(target[i], ialpha); + } + ++spans; + } + return; + } + + while (count--) { + uint *target = ((uint *)data->rasterBuffer->scanLine(spans->y)) + spans->x; + op.funcSolid(target, spans->len, data->solid.color, spans->coverage); + ++spans; + } +} + +template <class T> +Q_STATIC_TEMPLATE_FUNCTION void blendColor(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + Operator op = getOperator(data, spans, count); + + if (op.mode == QPainter::CompositionMode_Source) { + const T c = qt_colorConvert<T, quint32p>(quint32p::fromRawData(data->solid.color), 0); + while (count--) { + T *target = ((T*)data->rasterBuffer->scanLine(spans->y)) + + spans->x; + if (spans->coverage == 255) { + qt_memfill(target, c, spans->len); + } else { + const quint8 alpha = T::alpha(spans->coverage); + const T color = c.byte_mul(alpha); + const int ialpha = T::ialpha(spans->coverage); + const T *end = target + spans->len; + while (target < end) { + *target = color + target->byte_mul(ialpha); + ++target; + } + } + ++spans; + } + return; + } + + if (op.mode == QPainter::CompositionMode_SourceOver) { + while (count--) { + const quint32 color = BYTE_MUL(data->solid.color, spans->coverage); + const T c = qt_colorConvert<T, quint32p>(quint32p::fromRawData(color), 0); + const quint8 ialpha = T::alpha(qAlpha(~color)); + T *target = ((T*)data->rasterBuffer->scanLine(spans->y)) + spans->x; + const T *end = target + spans->len; + while (target != end) { + *target = c + target->byte_mul(ialpha); + ++target; + } + ++spans; + } + return; + } + + blend_color_generic(count, spans, userData); +} + +#define SPANFUNC_POINTER_BLENDCOLOR(DST) blendColor<DST> + +static void blend_color_rgb16(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + /* + We duplicate a little logic from getOperator() and calculate the + composition mode directly. This allows blend_color_rgb16 to be used + from qt_gradient_quint16 with minimal overhead. + */ + QPainter::CompositionMode mode = data->rasterBuffer->compositionMode; + if (mode == QPainter::CompositionMode_SourceOver && + qAlpha(data->solid.color) == 255) + mode = QPainter::CompositionMode_Source; + + if (mode == QPainter::CompositionMode_Source) { + // inline for performance + ushort c = qConvertRgb32To16(data->solid.color); + while (count--) { + ushort *target = ((ushort *)data->rasterBuffer->scanLine(spans->y)) + spans->x; + if (spans->coverage == 255) { + QT_MEMFILL_USHORT(target, spans->len, c); + } else { + ushort color = BYTE_MUL_RGB16(c, spans->coverage); + int ialpha = 255 - spans->coverage; + const ushort *end = target + spans->len; + while (target < end) { + *target = color + BYTE_MUL_RGB16(*target, ialpha); + ++target; + } + } + ++spans; + } + return; + } + + if (mode == QPainter::CompositionMode_SourceOver) { + while (count--) { + uint color = BYTE_MUL(data->solid.color, spans->coverage); + int ialpha = qAlpha(~color); + ushort c = qConvertRgb32To16(color); + ushort *target = ((ushort *)data->rasterBuffer->scanLine(spans->y)) + spans->x; + int len = spans->len; + bool pre = (((quintptr)target) & 0x3) != 0; + bool post = false; + if (pre) { + // skip to word boundary + *target = c + BYTE_MUL_RGB16(*target, ialpha); + ++target; + --len; + } + if (len & 0x1) { + post = true; + --len; + } + uint *target32 = (uint*)target; + uint c32 = c | (c<<16); + len >>= 1; + uint salpha = (ialpha+1) >> 3; // calculate here rather than in loop + while (len--) { + // blend full words + *target32 = c32 + BYTE_MUL_RGB16_32(*target32, salpha); + ++target32; + target += 2; + } + if (post) { + // one last pixel beyond a full word + *target = c + BYTE_MUL_RGB16(*target, ialpha); + } + ++spans; + } + return; + } + + blend_color_generic(count, spans, userData); +} + +template <typename T> +void handleSpans(int count, const QSpan *spans, const QSpanData *data, T &handler) +{ + uint const_alpha = 256; + if (data->type == QSpanData::Texture) + const_alpha = data->texture.const_alpha; + + int coverage = 0; + while (count) { + int x = spans->x; + const int y = spans->y; + int right = x + spans->len; + + // compute length of adjacent spans + for (int i = 1; i < count && spans[i].y == y && spans[i].x == right; ++i) + right += spans[i].len; + int length = right - x; + + while (length) { + int l = qMin(buffer_size, length); + length -= l; + + int process_length = l; + int process_x = x; + + const uint *src = handler.fetch(process_x, y, process_length); + int offset = 0; + while (l > 0) { + if (x == spans->x) // new span? + coverage = (spans->coverage * const_alpha) >> 8; + + int right = spans->x + spans->len; + int len = qMin(l, right - x); + + handler.process(x, y, len, coverage, src, offset); + + l -= len; + x += len; + offset += len; + + if (x == right) { // done with current span? + ++spans; + --count; + } + } + handler.store(process_x, y, process_length); + } + } +} + +struct QBlendBase +{ + QBlendBase(QSpanData *d, Operator o) + : data(d) + , op(o) + , dest(0) + { + } + + QSpanData *data; + Operator op; + + uint *dest; + + uint buffer[buffer_size]; + uint src_buffer[buffer_size]; +}; + +template <SpanMethod spanMethod> +class BlendSrcGeneric : public QBlendBase +{ +public: + BlendSrcGeneric(QSpanData *d, Operator o) + : QBlendBase(d, o) + { + } + + const uint *fetch(int x, int y, int len) + { + if (spanMethod == RegularSpans) + dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, y, len) : buffer; + + return op.src_fetch(src_buffer, &op, data, y, x, len); + } + + void process(int x, int y, int len, int coverage, const uint *src, int offset) + { + if (spanMethod == RegularSpans) + op.func(dest + offset, src + offset, len, coverage); + else + drawBufferSpan(data, src + offset, len, x, y, len, coverage); + } + + void store(int x, int y, int len) + { + if (spanMethod == RegularSpans && op.dest_store) { + op.dest_store(data->rasterBuffer, x, y, dest, len); + } + } +}; + +template <SpanMethod spanMethod> +Q_STATIC_TEMPLATE_FUNCTION void blend_src_generic(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + BlendSrcGeneric<spanMethod> blend(data, getOperator(data, spans, count)); + handleSpans(count, spans, data, blend); +} + +template <SpanMethod spanMethod> +Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_generic(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + uint buffer[buffer_size]; + uint src_buffer[buffer_size]; + Operator op = getOperator(data, spans, count); + + const int image_width = data->texture.width; + const int image_height = data->texture.height; + int xoff = -qRound(-data->dx); + int yoff = -qRound(-data->dy); + + while (count--) { + int x = spans->x; + int length = spans->len; + int sx = xoff + x; + int sy = yoff + spans->y; + if (sy >= 0 && sy < image_height && sx < image_width) { + if (sx < 0) { + x -= sx; + length += sx; + sx = 0; + } + if (sx + length > image_width) + length = image_width - sx; + if (length > 0) { + const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; + while (length) { + int l = qMin(buffer_size, length); + const uint *src = op.src_fetch(src_buffer, &op, data, sy, sx, l); + if (spanMethod == RegularSpans) { + uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer; + op.func(dest, src, l, coverage); + if (op.dest_store) + op.dest_store(data->rasterBuffer, x, spans->y, dest, l); + } else { + drawBufferSpan(data, src, l, x, spans->y, + l, coverage); + } + x += l; + sx += l; + length -= l; + } + } + } + ++spans; + } +} + +template <SpanMethod spanMethod> +Q_STATIC_TEMPLATE_FUNCTION void blend_untransformed_argb(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + if (data->texture.format != QImage::Format_ARGB32_Premultiplied + && data->texture.format != QImage::Format_RGB32) { + blend_untransformed_generic<spanMethod>(count, spans, userData); + return; + } + + Operator op = getOperator(data, spans, count); + + const int image_width = data->texture.width; + const int image_height = data->texture.height; + int xoff = -qRound(-data->dx); + int yoff = -qRound(-data->dy); + + while (count--) { + int x = spans->x; + int length = spans->len; + int sx = xoff + x; + int sy = yoff + spans->y; + if (sy >= 0 && sy < image_height && sx < image_width) { + if (sx < 0) { + x -= sx; + length += sx; + sx = 0; + } + if (sx + length > image_width) + length = image_width - sx; + if (length > 0) { + const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; + const uint *src = (uint *)data->texture.scanLine(sy) + sx; + if (spanMethod == RegularSpans) { + uint *dest = ((uint *)data->rasterBuffer->scanLine(spans->y)) + x; + op.func(dest, src, length, coverage); + } else { + drawBufferSpan(data, src, length, x, + spans->y, length, coverage); + } + } + } + ++spans; + } +} + +static inline quint16 interpolate_pixel_rgb16_255(quint16 x, quint8 a, + quint16 y, quint8 b) +{ + quint16 t = ((((x & 0x07e0) * a) + ((y & 0x07e0) * b)) >> 5) & 0x07e0; + t |= ((((x & 0xf81f) * a) + ((y & 0xf81f) * b)) >> 5) & 0xf81f; + + return t; +} + +static inline quint32 interpolate_pixel_rgb16x2_255(quint32 x, quint8 a, + quint32 y, quint8 b) +{ + uint t; + t = ((((x & 0xf81f07e0) >> 5) * a) + (((y & 0xf81f07e0) >> 5) * b)) & 0xf81f07e0; + t |= ((((x & 0x07e0f81f) * a) + ((y & 0x07e0f81f) * b)) >> 5) & 0x07e0f81f; + return t; +} + +static inline void blend_sourceOver_rgb16_rgb16(quint16 *dest, + const quint16 *src, + int length, + const quint8 alpha, + const quint8 ialpha) +{ + const int dstAlign = ((quintptr)dest) & 0x3; + if (dstAlign) { + *dest = interpolate_pixel_rgb16_255(*src, alpha, *dest, ialpha); + ++dest; + ++src; + --length; + } + const int srcAlign = ((quintptr)src) & 0x3; + int length32 = length >> 1; + if (length32 && srcAlign == 0) { + while (length32--) { + const quint32 *src32 = reinterpret_cast<const quint32*>(src); + quint32 *dest32 = reinterpret_cast<quint32*>(dest); + *dest32 = interpolate_pixel_rgb16x2_255(*src32, alpha, + *dest32, ialpha); + dest += 2; + src += 2; + } + length &= 0x1; + } + while (length--) { + *dest = interpolate_pixel_rgb16_255(*src, alpha, *dest, ialpha); + ++dest; + ++src; + } +} + +template <class DST, class SRC> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline void madd_2(DST *dest, const quint16 alpha, const SRC *src) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + dest[0] = dest[0].byte_mul(alpha >> 8) + DST(src[0]); + dest[1] = dest[1].byte_mul(alpha & 0xff) + DST(src[1]); +} + +template <class DST, class SRC> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline void madd_4(DST *dest, const quint32 alpha, const SRC *src) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + dest[0] = dest[0].byte_mul(alpha >> 24) + DST(src[0]); + dest[1] = dest[1].byte_mul((alpha >> 16) & 0xff) + DST(src[1]); + dest[2] = dest[2].byte_mul((alpha >> 8) & 0xff) + DST(src[2]); + dest[3] = dest[3].byte_mul(alpha & 0xff) + DST(src[3]); +} + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline void madd_4(qargb8565 *dest, const quint32 a, const qargb8565 *src) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + const quint32 *src32 = reinterpret_cast<const quint32*>(src); + quint32 *dest32 = reinterpret_cast<quint32*>(dest); + quint32 x, y, t; + quint8 a8; + + { + x = dest32[0]; + y = src32[0]; + + a8 = a >> 24; + + // a0,g0 + t = ((((x & 0x0007e0ff) * a8) >> 5) & 0x0007e0ff) + (y & 0x0007c0f8); + + // r0,b0 + t |= ((((x & 0x00f81f00) * a8) >> 5) & 0x00f81f00) + (y & 0x00f81f00); + + a8 = (a >> 16) & 0xff; + + // a1 + t |= ((((x & 0xff000000) >> 5) * a8) & 0xff000000) + (y & 0xf8000000); + + dest32[0] = t; + } + { + x = dest32[1]; + y = src32[1]; + + // r1,b1 + t = ((((x & 0x0000f81f) * a8) >> 5) & 0x0000f81f) + (y & 0x0000f81f); + + // g1 + t |= ((((x & 0x000007e0) * a8) >> 5) & 0x000007e0) + (y & 0x000007c0); + + a8 = (a >> 8) & 0xff; + + // a2 + t |= ((((x & 0x00ff0000) * a8) >> 5) & 0x00ff0000) + (y & 0x00f80000); + + { + // rgb2 + quint16 x16 = (x >> 24) | ((dest32[2] & 0x000000ff) << 8); + quint16 y16 = (y >> 24) | ((src32[2] & 0x000000ff) << 8); + quint16 t16; + + t16 = ((((x16 & 0xf81f) * a8) >> 5) & 0xf81f) + (y16 & 0xf81f); + t16 |= ((((x16 & 0x07e0) * a8) >> 5) & 0x07e0) + (y16 & 0x07c0); + + // rg2 + t |= ((t16 & 0x00ff) << 24); + + dest32[1] = t; + + x = dest32[2]; + y = src32[2]; + + // gb2 + t = (t16 >> 8); + } + } + { + a8 = a & 0xff; + + // g3,a3 + t |= ((((x & 0x07e0ff00) * a8) >> 5) & 0x07e0ff00) + (y & 0x07c0f800); + + // r3,b3 + t |= ((((x & 0xf81f0000) >> 5) * a8) & 0xf81f0000)+ (y & 0xf81f0000); + + dest32[2] = t; + } +} +#endif + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline void madd_4(qargb8555 *dest, const quint32 a, const qargb8555 *src) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + const quint32 *src32 = reinterpret_cast<const quint32*>(src); + quint32 *dest32 = reinterpret_cast<quint32*>(dest); + quint32 x, y, t; + quint8 a8; + + { + x = dest32[0]; + y = src32[0]; + + a8 = a >> 24; + + // a0,g0 + t = ((((x & 0x0003e0ff) * a8) >> 5) & 0x0003e0ff) + (y & 0x0003e0f8); + + // r0,b0 + t |= ((((x & 0x007c1f00) * a8) >> 5) & 0x007c1f00) + (y & 0x007c1f00); + + a8 = (a >> 16) & 0xff; + + // a1 + t |= ((((x & 0xff000000) >> 5) * a8) & 0xff000000) + (y & 0xf8000000); + + dest32[0] = t; + } + { + x = dest32[1]; + y = src32[1]; + + // r1,b1 + t = ((((x & 0x00007c1f) * a8) >> 5) & 0x00007c1f) + (y & 0x00007c1f); + + // g1 + t |= ((((x & 0x000003e0) * a8) >> 5) & 0x000003e0) + (y & 0x000003e0); + + a8 = (a >> 8) & 0xff; + + // a2 + t |= ((((x & 0x00ff0000) * a8) >> 5) & 0x00ff0000) + (y & 0x00f80000); + + { + // rgb2 + quint16 x16 = (x >> 24) | ((dest32[2] & 0x000000ff) << 8); + quint16 y16 = (y >> 24) | ((src32[2] & 0x000000ff) << 8); + quint16 t16; + + t16 = ((((x16 & 0x7c1f) * a8) >> 5) & 0x7c1f) + (y16 & 0x7c1f); + t16 |= ((((x16 & 0x03e0) * a8) >> 5) & 0x03e0) + (y16 & 0x03e0); + + // rg2 + t |= ((t16 & 0x00ff) << 24); + + dest32[1] = t; + + x = dest32[2]; + y = src32[2]; + + // gb2 + t = (t16 >> 8); + } + } + { + a8 = a & 0xff; + + // g3,a3 + t |= ((((x & 0x03e0ff00) * a8) >> 5) & 0x03e0ff00) + (y & 0x03e0f800); + + // r3,b3 + t |= ((((x & 0x7c1f0000) >> 5) * a8) & 0x7c1f0000)+ (y & 0x7c1f0000); + + dest32[2] = t; + } +} +#endif + +template <class T> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint16 alpha_2(const T *src) +{ + Q_ASSERT((quintptr(src) & 0x3) == 0); + + if (T::hasAlpha()) + return (src[0].alpha() << 8) | src[1].alpha(); + else + return 0xffff; +} + +template <class T> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 alpha_4(const T *src) +{ + Q_ASSERT((quintptr(src) & 0x3) == 0); + + if (T::hasAlpha()) { + return (src[0].alpha() << 24) | (src[1].alpha() << 16) + | (src[2].alpha() << 8) | src[3].alpha(); + } else { + return 0xffffffff; + } +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 alpha_4(const qargb8565 *src) +{ + const quint8 *src8 = reinterpret_cast<const quint8*>(src); + return src8[0] << 24 | src8[3] << 16 | src8[6] << 8 | src8[9]; +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 alpha_4(const qargb6666 *src) +{ + const quint8 *src8 = reinterpret_cast<const quint8*>(src); + return ((src8[2] & 0xfc) | (src8[2] >> 6)) << 24 + | ((src8[5] & 0xfc) | (src8[5] >> 6)) << 16 + | ((src8[8] & 0xfc) | (src8[8] >> 6)) << 8 + | ((src8[11] & 0xfc) | (src8[11] >> 6)); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 alpha_4(const qargb8555 *src) +{ + Q_ASSERT((quintptr(src) & 0x3) == 0); + const quint8 *src8 = reinterpret_cast<const quint8*>(src); + return src8[0] << 24 | src8[3] << 16 | src8[6] << 8 | src8[9]; +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint16 alpha_2(const qargb4444 *src) +{ + const quint32 *src32 = reinterpret_cast<const quint32*>(src); + const quint32 t = (*src32 & 0xf000f000) | + ((*src32 & 0xf000f000) >> 4); + return (t >> 24) | (t & 0xff00); +} + +template <class T> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint16 eff_alpha_2(quint16 alpha, const T*) +{ + return (T::alpha((alpha >> 8) & 0xff) << 8) + | T::alpha(alpha & 0xff); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint16 eff_alpha_2(quint16 a, const qrgb565*) +{ + return ((((a & 0xff00) + 0x0100) >> 3) & 0xff00) + | ((((a & 0x00ff) + 0x0001) >> 3) & 0x00ff); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint16 eff_alpha_2(quint16 a, const qrgb444*) +{ + return (((a & 0x00ff) + 0x0001) >> 4) + | ((((a & 0xff00) + 0x0100) >> 4) & 0xff00); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint16 eff_alpha_2(quint16 a, const qargb4444*) +{ + return (((a & 0x00ff) + 0x0001) >> 4) + | ((((a & 0xff00) + 0x0100) >> 4) & 0xff00); +} + +template <class T> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint16 eff_ialpha_2(quint16 alpha, const T*) +{ + return (T::ialpha((alpha >> 8) & 0xff) << 8) + | T::ialpha(alpha & 0xff); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint16 eff_ialpha_2(quint16 a, const qrgb565 *dummy) +{ + return 0x2020 - eff_alpha_2(a, dummy); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint16 eff_ialpha_2(quint16 a, const qargb4444 *dummy) +{ + return 0x1010 - eff_alpha_2(a, dummy); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint16 eff_ialpha_2(quint16 a, const qrgb444 *dummy) +{ + return 0x1010 - eff_alpha_2(a, dummy); +} + +template <class T> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_alpha_4(quint32 alpha, const T*) +{ + return (T::alpha(alpha >> 24) << 24) + | (T::alpha((alpha >> 16) & 0xff) << 16) + | (T::alpha((alpha >> 8) & 0xff) << 8) + | T::alpha(alpha & 0xff); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_alpha_4(quint32 a, const qrgb888*) +{ + return a; +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_alpha_4(quint32 a, const qargb8565*) +{ + return ((((a & 0xff00ff00) + 0x01000100) >> 3) & 0xff00ff00) + | ((((a & 0x00ff00ff) + 0x00010001) >> 3) & 0x00ff00ff); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_alpha_4(quint32 a, const qargb6666*) +{ + return ((((a & 0xff00ff00) >> 2) + 0x00400040) & 0xff00ff00) + | ((((a & 0x00ff00ff) + 0x00010001) >> 2) & 0x00ff00ff); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_alpha_4(quint32 a, const qrgb666*) +{ + return ((((a & 0xff00ff00) >> 2) + 0x00400040) & 0xff00ff00) + | ((((a & 0x00ff00ff) + 0x00010001) >> 2) & 0x00ff00ff); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_alpha_4(quint32 a, const qargb8555*) +{ + return ((((a & 0xff00ff00) + 0x01000100) >> 3) & 0xff00ff00) + | ((((a & 0x00ff00ff) + 0x00010001) >> 3) & 0x00ff00ff); +} + +template <class T> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_ialpha_4(quint32 alpha, const T*) +{ + return (T::ialpha(alpha >> 24) << 24) + | (T::ialpha((alpha >> 16) & 0xff) << 16) + | (T::ialpha((alpha >> 8) & 0xff) << 8) + | T::ialpha(alpha & 0xff); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_ialpha_4(quint32 a, const qrgb888*) +{ + return ~a; +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_ialpha_4(quint32 a, const qargb8565 *dummy) +{ + return 0x20202020 - eff_alpha_4(a, dummy); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_ialpha_4(quint32 a, const qargb6666 *dummy) +{ + return 0x40404040 - eff_alpha_4(a, dummy); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_ialpha_4(quint32 a, const qrgb666 *dummy) +{ + return 0x40404040 - eff_alpha_4(a, dummy); +} + +template <> +Q_STATIC_TEMPLATE_SPECIALIZATION +inline quint32 eff_ialpha_4(quint32 a, const qargb8555 *dummy) +{ + return 0x20202020 - eff_alpha_4(a, dummy); +} + +template <class DST, class SRC> +inline void interpolate_pixel_unaligned_2(DST *dest, const SRC *src, + quint16 alpha) +{ + const quint16 a = eff_alpha_2(alpha, dest); + const quint16 ia = eff_ialpha_2(alpha, dest); + dest[0] = DST(src[0]).byte_mul(a >> 8) + dest[0].byte_mul(ia >> 8); + dest[1] = DST(src[1]).byte_mul(a & 0xff) + dest[1].byte_mul(ia & 0xff); +} + +template <class DST, class SRC> +inline void interpolate_pixel_2(DST *dest, const SRC *src, quint16 alpha) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + const quint16 a = eff_alpha_2(alpha, dest); + const quint16 ia = eff_ialpha_2(alpha, dest); + + dest[0] = DST(src[0]).byte_mul(a >> 8) + dest[0].byte_mul(ia >> 8); + dest[1] = DST(src[1]).byte_mul(a & 0xff) + dest[1].byte_mul(ia & 0xff); +} + +template <class DST, class SRC> +inline void interpolate_pixel(DST &dest, quint8 a, const SRC &src, quint8 b) +{ + if (SRC::hasAlpha() && !DST::hasAlpha()) + interpolate_pixel(dest, a, DST(src), b); + else + dest = dest.byte_mul(a) + DST(src).byte_mul(b); +} + +template <> +inline void interpolate_pixel(qargb8565 &dest, quint8 a, + const qargb8565 &src, quint8 b) +{ + quint8 *d = reinterpret_cast<quint8*>(&dest); + const quint8 *s = reinterpret_cast<const quint8*>(&src); + d[0] = (d[0] * a + s[0] * b) >> 5; + + const quint16 x = (d[2] << 8) | d[1]; + const quint16 y = (s[2] << 8) | s[1]; + quint16 t = (((x & 0x07e0) * a + (y & 0x07e0) * b) >> 5) & 0x07e0; + t |= (((x & 0xf81f) * a + (y & 0xf81f) * b) >> 5) & 0xf81f; + + d[1] = t & 0xff; + d[2] = t >> 8; +} + +template <> +inline void interpolate_pixel(qrgb565 &dest, quint8 a, + const qrgb565 &src, quint8 b) +{ + const quint16 x = dest.rawValue(); + const quint16 y = src.rawValue(); + quint16 t = (((x & 0x07e0) * a + (y & 0x07e0) * b) >> 5) & 0x07e0; + t |= (((x & 0xf81f) * a + (y & 0xf81f) * b) >> 5) & 0xf81f; + dest = t; +} + +template <> +inline void interpolate_pixel(qrgb555 &dest, quint8 a, + const qrgb555 &src, quint8 b) +{ + const quint16 x = dest.rawValue(); + const quint16 y = src.rawValue(); + quint16 t = (((x & 0x03e0) * a + (y & 0x03e0) * b) >> 5) & 0x03e0; + t |= ((((x & 0x7c1f) * a) + ((y & 0x7c1f) * b)) >> 5) & 0x7c1f; + dest = t; +} + +template <> +inline void interpolate_pixel(qrgb444 &dest, quint8 a, + const qrgb444 &src, quint8 b) +{ + const quint16 x = dest.rawValue(); + const quint16 y = src.rawValue(); + quint16 t = ((x & 0x00f0) * a + (y & 0x00f0) * b) & 0x0f00; + t |= ((x & 0x0f0f) * a + (y & 0x0f0f) * b) & 0xf0f0; + quint16 *d = reinterpret_cast<quint16*>(&dest); + *d = (t >> 4); +} + +template <class DST, class SRC> +inline void interpolate_pixel_2(DST *dest, quint8 a, + const SRC *src, quint8 b) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + Q_ASSERT(!SRC::hasAlpha()); + + dest[0] = dest[0].byte_mul(a) + DST(src[0]).byte_mul(b); + dest[1] = dest[1].byte_mul(a) + DST(src[1]).byte_mul(b); +} + +template <> +inline void interpolate_pixel_2(qrgb565 *dest, quint8 a, + const qrgb565 *src, quint8 b) +{ + quint32 *x = reinterpret_cast<quint32*>(dest); + const quint32 *y = reinterpret_cast<const quint32*>(src); + quint32 t = (((*x & 0xf81f07e0) >> 5) * a + + ((*y & 0xf81f07e0) >> 5) * b) & 0xf81f07e0; + t |= (((*x & 0x07e0f81f) * a + + (*y & 0x07e0f81f) * b) >> 5) & 0x07e0f81f; + *x = t; +} + +template <> +inline void interpolate_pixel_2(qrgb555 *dest, quint8 a, + const qrgb555 *src, quint8 b) +{ + quint32 *x = reinterpret_cast<quint32*>(dest); + const quint32 *y = reinterpret_cast<const quint32*>(src); + quint32 t = (((*x & 0x7c1f03e0) >> 5) * a + + ((*y & 0x7c1f03e0) >> 5) * b) & 0x7c1f03e0; + t |= (((*x & 0x03e07c1f) * a + + (*y & 0x03e07c1f) * b) >> 5) & 0x03e07c1f; + *x = t; +} + +template <> +inline void interpolate_pixel_2(qrgb444 *dest, quint8 a, + const qrgb444 *src, quint8 b) +{ + quint32 *x = reinterpret_cast<quint32*>(dest); + const quint32 *y = reinterpret_cast<const quint32*>(src); + quint32 t = ((*x & 0x0f0f0f0f) * a + (*y & 0x0f0f0f0f) * b) & 0xf0f0f0f0; + t |= ((*x & 0x00f000f0) * a + (*y & 0x00f000f0) * b) & 0x0f000f00; + *x = t >> 4; +} + +template <class DST, class SRC> +inline void interpolate_pixel_4(DST *dest, const SRC *src, quint32 alpha) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + const quint32 a = eff_alpha_4(alpha, dest); + const quint32 ia = eff_ialpha_4(alpha, dest); + dest[0] = DST(src[0]).byte_mul(a >> 24) + + dest[0].byte_mul(ia >> 24); + dest[1] = DST(src[1]).byte_mul((a >> 16) & 0xff) + + dest[1].byte_mul((ia >> 16) & 0xff); + dest[2] = DST(src[2]).byte_mul((a >> 8) & 0xff) + + dest[2].byte_mul((ia >> 8) & 0xff); + dest[3] = DST(src[3]).byte_mul(a & 0xff) + + dest[3].byte_mul(ia & 0xff); +} + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN +template <> +inline void interpolate_pixel_4(qargb8565 *dest, const qargb8565 *src, + quint32 alpha) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + const quint32 a = eff_alpha_4(alpha, dest); + const quint32 ia = eff_ialpha_4(alpha, dest); + const quint32 *src32 = reinterpret_cast<const quint32*>(src); + quint32 *dest32 = reinterpret_cast<quint32*>(dest); + + quint32 x, y, t; + quint8 a8, ia8; + { + x = src32[0]; + y = dest32[0]; + + a8 = a >> 24; + ia8 = ia >> 24; + + // a0,g0 + t = (((x & 0x0007e0ff) * a8 + (y & 0x0007e0ff) * ia8) >> 5) + & 0x0007e0ff; + + // r0,b0 + t |= (((x & 0x00f81f00) * a8 + (y & 0x00f81f00) * ia8) >> 5) + & 0x00f81f00; + + a8 = (a >> 16) & 0xff; + ia8 = (ia >> 16) & 0xff; + + // a1 + t |= (((x & 0xff000000) >> 5) * a8 + ((y & 0xff000000) >> 5) * ia8) + & 0xff000000; + + dest32[0] = t; + } + { + x = src32[1]; + y = dest32[1]; + + // r1,b1 + t = (((x & 0x0000f81f) * a8 + (y & 0x0000f81f) * ia8) >> 5) + & 0x0000f81f; + + // g1 + t |= (((x & 0x000007e0) * a8 + (y & 0x000007e0) * ia8) >> 5) + & 0x000007e0; + + a8 = (a >> 8) & 0xff; + ia8 = (ia >> 8) & 0xff; + + // a2 + t |= (((x & 0x00ff0000) * a8 + (y & 0x00ff0000) * ia8) >> 5) + & 0x00ff0000; + + { + // rgb2 + quint16 x16 = (x >> 24) | ((src32[2] & 0x000000ff) << 8); + quint16 y16 = (y >> 24) | ((dest32[2] & 0x000000ff) << 8); + quint16 t16; + + t16 = (((x16 & 0xf81f) * a8 + (y16 & 0xf81f) * ia8) >> 5) & 0xf81f; + t16 |= (((x16 & 0x07e0) * a8 + (y16 & 0x07e0) * ia8) >> 5) & 0x07e0; + + // rg2 + t |= ((t16 & 0x00ff) << 24); + + dest32[1] = t; + + x = src32[2]; + y = dest32[2]; + + // gb2 + t = (t16 >> 8); + } + } + { + a8 = a & 0xff; + ia8 = ia & 0xff; + + // g3,a3 + t |= (((x & 0x07e0ff00) * a8 + (y & 0x07e0ff00) * ia8) >> 5) + & 0x07e0ff00; + + // r3,b3 + t |= (((x & 0xf81f0000) >> 5) * a8 + ((y & 0xf81f0000) >> 5) * ia8) + & 0xf81f0000; + + dest32[2] = t; + } +} +#endif + +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN +template <> +inline void interpolate_pixel_4(qargb8555 *dest, const qargb8555 *src, + quint32 alpha) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + + const quint32 a = eff_alpha_4(alpha, dest); + const quint32 ia = eff_ialpha_4(alpha, dest); + const quint32 *src32 = reinterpret_cast<const quint32*>(src); + quint32 *dest32 = reinterpret_cast<quint32*>(dest); + + quint32 x, y, t; + quint8 a8, ia8; + { + x = src32[0]; + y = dest32[0]; + + a8 = a >> 24; + ia8 = ia >> 24; + + // a0,g0 + t = (((x & 0x0003e0ff) * a8 + (y & 0x0003e0ff) * ia8) >> 5) + & 0x0003e0ff; + + // r0,b0 + t |= (((x & 0x007c1f00) * a8 + (y & 0x007c1f00) * ia8) >> 5) + & 0x007c1f00; + + a8 = (a >> 16) & 0xff; + ia8 = (ia >> 16) & 0xff; + + // a1 + t |= (((x & 0xff000000) >> 5) * a8 + ((y & 0xff000000) >> 5) * ia8) + & 0xff000000; + + dest32[0] = t; + } + { + x = src32[1]; + y = dest32[1]; + + // r1,b1 + t = (((x & 0x00007c1f) * a8 + (y & 0x00007c1f) * ia8) >> 5) + & 0x00007c1f; + + // g1 + t |= (((x & 0x000003e0) * a8 + (y & 0x000003e0) * ia8) >> 5) + & 0x000003e0; + + a8 = (a >> 8) & 0xff; + ia8 = (ia >> 8) & 0xff; + + // a2 + t |= (((x & 0x00ff0000) * a8 + (y & 0x00ff0000) * ia8) >> 5) + & 0x00ff0000; + + { + // rgb2 + quint16 x16 = (x >> 24) | ((src32[2] & 0x000000ff) << 8); + quint16 y16 = (y >> 24) | ((dest32[2] & 0x000000ff) << 8); + quint16 t16; + + t16 = (((x16 & 0x7c1f) * a8 + (y16 & 0x7c1f) * ia8) >> 5) & 0x7c1f; + t16 |= (((x16 & 0x03e0) * a8 + (y16 & 0x03e0) * ia8) >> 5) & 0x03e0; + + // rg2 + t |= ((t16 & 0x00ff) << 24); + + dest32[1] = t; + + x = src32[2]; + y = dest32[2]; + + // gb2 + t = (t16 >> 8); + } + } + { + a8 = a & 0xff; + ia8 = ia & 0xff; + + // g3,a3 + t |= (((x & 0x03e0ff00) * a8 + (y & 0x03e0ff00) * ia8) >> 5) + & 0x03e0ff00; + + // r3,b3 + t |= (((x & 0x7c1f0000) >> 5) * a8 + ((y & 0x7c1f0000) >> 5) * ia8) + & 0x7c1f0000; + + dest32[2] = t; + } +} +#endif + +template <> +inline void interpolate_pixel_4(qrgb888 *dest, const qrgb888 *src, + quint32 alpha) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + const quint32 a = eff_alpha_4(alpha, dest); + const quint32 ia = eff_ialpha_4(alpha, dest); + const quint32 *src32 = reinterpret_cast<const quint32*>(src); + quint32 *dest32 = reinterpret_cast<quint32*>(dest); + + { + quint32 x = src32[0]; + quint32 y = dest32[0]; + + quint32 t; + t = ((x >> 8) & 0xff00ff) * (a >> 24) + + ((y >> 8) & 0xff00ff) * (ia >> 24); + t = (t + ((t >> 8) & 0xff00ff) + 0x800080); + t &= 0xff00ff00; + + x = (x & 0xff0000) * (a >> 24) + + (x & 0x0000ff) * ((a >> 16) & 0xff) + + (y & 0xff0000) * (ia >> 24) + + (y & 0x0000ff) * ((ia >> 16) & 0xff); + x = (x + ((x >> 8) & 0xff00ff) + 0x800080) >> 8; + x &= 0x00ff00ff; + + dest32[0] = x | t; + } + { + quint32 x = src32[1]; + quint32 y = dest32[1]; + + quint32 t; + t = ((x >> 8) & 0xff0000) * ((a >> 16) & 0xff) + + ((x >> 8) & 0x0000ff) * ((a >> 8) & 0xff) + + ((y >> 8) & 0xff0000) * ((ia >> 16) & 0xff) + + ((y >> 8) & 0x0000ff) * ((ia >> 8) & 0xff); + t = (t + ((t >> 8) & 0xff00ff) + 0x800080); + t &= 0xff00ff00; + + x = (x & 0xff0000) * ((a >> 16) & 0xff) + + (x & 0x0000ff) * ((a >> 8) & 0xff) + + (y & 0xff0000) * ((ia >> 16) & 0xff) + + (y & 0x0000ff) * ((ia >> 8) & 0xff); + x = (x + ((x >> 8) & 0xff00ff) + 0x800080) >> 8; + x &= 0x00ff00ff; + + dest32[1] = x | t; + } + { + quint32 x = src32[2]; + quint32 y = dest32[2]; + + quint32 t; + t = ((x >> 8) & 0xff0000) * ((a >> 8) & 0xff) + + ((x >> 8) & 0x0000ff) * (a & 0xff) + + ((y >> 8) & 0xff0000) * ((ia >> 8) & 0xff) + + ((y >> 8) & 0x0000ff) * (ia & 0xff); + t = (t + ((t >> 8) & 0xff00ff) + 0x800080); + t &= 0xff00ff00; + + x = (x & 0xff00ff) * (a & 0xff) + + (y & 0xff00ff) * (ia & 0xff); + x = (x + ((x >> 8) & 0xff00ff) + 0x800080) >> 8; + x &= 0x00ff00ff; + + dest32[2] = x | t; + } +} + +template <class DST, class SRC> +inline void interpolate_pixel_4(DST *dest, quint8 a, + const SRC *src, quint8 b) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + dest[0] = dest[0].byte_mul(a) + DST(src[0]).byte_mul(b); + dest[1] = dest[1].byte_mul(a) + DST(src[1]).byte_mul(b); + dest[2] = dest[2].byte_mul(a) + DST(src[2]).byte_mul(b); + dest[3] = dest[3].byte_mul(a) + DST(src[3]).byte_mul(b); +} + +template <class DST, class SRC> +inline void blend_sourceOver_4(DST *dest, const SRC *src) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + const quint32 a = alpha_4(src); + if (a == 0xffffffff) { + qt_memconvert(dest, src, 4); + } else if (a > 0) { + quint32 buf[3]; // array of quint32 to get correct alignment + qt_memconvert((DST*)(void*)buf, src, 4); + madd_4(dest, eff_ialpha_4(a, dest), (DST*)(void*)buf); + } +} + +template <> +inline void blend_sourceOver_4(qargb8565 *dest, const qargb8565 *src) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + const quint32 a = alpha_4(src); + if (a == 0xffffffff) { + qt_memconvert(dest, src, 4); + } else if (a > 0) { + madd_4(dest, eff_ialpha_4(a, dest), src); + } +} + +template <> +inline void blend_sourceOver_4(qargb8555 *dest, const qargb8555 *src) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + const quint32 a = alpha_4(src); + if (a == 0xffffffff) { + qt_memconvert(dest, src, 4); + } else if (a > 0) { + madd_4(dest, eff_ialpha_4(a, dest), src); + } +} + +template <> +inline void blend_sourceOver_4(qargb6666 *dest, const qargb6666 *src) +{ + Q_ASSERT((quintptr(dest) & 0x3) == 0); + Q_ASSERT((quintptr(src) & 0x3) == 0); + + const quint32 a = alpha_4(src); + if (a == 0xffffffff) { + qt_memconvert(dest, src, 4); + } else if (a > 0) { + madd_4(dest, eff_ialpha_4(a, dest), src); + } +} + +template <class DST, class SRC> +void QT_FASTCALL blendUntransformed_unaligned(DST *dest, const SRC *src, + quint8 coverage, int length) +{ + Q_ASSERT(coverage > 0); + + if (coverage < 255) { + if (SRC::hasAlpha()) { + for (int i = 0; i < length; ++i) { + if (src[i].alpha()) { + const quint8 alpha = qt_div_255(int(src[i].alpha()) * int(coverage)); + interpolate_pixel(dest[i], DST::ialpha(alpha), + src[i], DST::alpha(alpha)); + } + } + } else { + const quint8 alpha = DST::alpha(coverage); + const quint8 ialpha = DST::ialpha(coverage); + if (alpha) { + for (int i = 0; i < length; ++i) + interpolate_pixel(dest[i], ialpha, src[i], alpha); + } + } + return; + } + + Q_ASSERT(coverage == 0xff); + Q_ASSERT(SRC::hasAlpha()); + + if (SRC::hasAlpha()) { + for (int i = 0; i < length; ++i) { + const quint8 a = src->alpha(); + if (a == 0xff) + *dest = DST(*src); + else if (a > 0) { + if (DST::hasAlpha()) + *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(a)); + else + *dest = DST(SRC(*src).truncedAlpha()) + dest->byte_mul(DST::ialpha(a)); + } + ++src; + ++dest; + } + } +} + +template <class DST, class SRC> +void QT_FASTCALL blendUntransformed_dest16(DST *dest, const SRC *src, + quint8 coverage, int length) +{ + Q_ASSERT(sizeof(DST) == 2); + Q_ASSERT(sizeof(SRC) == 2); + Q_ASSERT((quintptr(dest) & 0x3) == (quintptr(src) & 0x3)); + Q_ASSERT(coverage > 0); + + const int align = quintptr(dest) & 0x3; + + if (coverage < 255) { + // align + if (align) { + const quint8 alpha = SRC::hasAlpha() + ? qt_div_255(int(src->alpha()) * int(coverage)) + : coverage; + if (alpha) { + interpolate_pixel(*dest, DST::ialpha(alpha), + *src, DST::alpha(alpha)); + } + ++dest; + ++src; + --length; + } + + if (SRC::hasAlpha()) { + while (length >= 2) { + const quint16 alpha16 = BYTE_MUL(uint(alpha_2(src)), uint(coverage)); + interpolate_pixel_2(dest, src, alpha16); + length -= 2; + src += 2; + dest += 2; + } + } else { + const quint8 alpha = DST::alpha(coverage); + const quint8 ialpha = DST::ialpha(coverage); + + while (length >= 2) { + interpolate_pixel_2(dest, ialpha, src, alpha); + length -= 2; + src += 2; + dest += 2; + } + } + + // tail + if (length) { + const quint8 alpha = SRC::hasAlpha() + ? qt_div_255(int(src->alpha()) * int(coverage)) + : coverage; + if (alpha) { + interpolate_pixel(*dest, DST::ialpha(alpha), + *src, DST::alpha(alpha)); + } + } + + return; + } + + Q_ASSERT(SRC::hasAlpha()); + if (SRC::hasAlpha()) { + if (align) { + const quint8 alpha = src->alpha(); + if (alpha == 0xff) + *dest = DST(*src); + else if (alpha > 0) + *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(alpha)); + ++dest; + ++src; + --length; + } + + while (length >= 2) { + Q_ASSERT((quintptr(dest) & 3) == 0); + Q_ASSERT((quintptr(src) & 3) == 0); + + const quint16 a = alpha_2(src); + if (a == 0xffff) { + qt_memconvert(dest, src, 2); + } else if (a > 0) { + quint32 buf; + if (sizeof(DST) == 2) + qt_memconvert((DST*)(void*)&buf, src, 2); + madd_2(dest, eff_ialpha_2(a, dest), (DST*)(void*)&buf); + } + + length -= 2; + src += 2; + dest += 2; + } + + if (length) { + const quint8 alpha = src->alpha(); + if (alpha == 0xff) + *dest = DST(*src); + else if (alpha > 0) + *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(alpha)); + } + } +} + +template <class DST, class SRC> +void QT_FASTCALL blendUntransformed_dest24(DST *dest, const SRC *src, + quint8 coverage, int length) +{ + Q_ASSERT((quintptr(dest) & 0x3) == (quintptr(src) & 0x3)); + Q_ASSERT(sizeof(DST) == 3); + Q_ASSERT(coverage > 0); + + const int align = quintptr(dest) & 0x3; + + if (coverage < 255) { + // align + for (int i = 0; i < align; ++i) { + if (SRC::hasAlpha()) { + const quint8 alpha = qt_div_255(int(src->alpha()) * int(coverage)); + if (alpha) + interpolate_pixel(*dest, DST::ialpha(alpha), + *src, DST::alpha(alpha)); + } else { + interpolate_pixel(*dest, DST::ialpha(coverage), + *src, DST::alpha(coverage)); + } + ++dest; + ++src; + --length; + } + + if (SRC::hasAlpha()) { + while (length >= 4) { + const quint32 alpha = QT_PREPEND_NAMESPACE(BYTE_MUL)(uint(alpha_4(src)), uint(coverage)); + if (alpha) + interpolate_pixel_4(dest, src, alpha); + length -= 4; + src += 4; + dest += 4; + } + } else { + const quint8 alpha = DST::alpha(coverage); + const quint8 ialpha = DST::ialpha(coverage); + while (length >= 4) { + interpolate_pixel_4(dest, ialpha, src, alpha); + length -= 4; + src += 4; + dest += 4; + } + } + + // tail + while (length--) { + if (SRC::hasAlpha()) { + const quint8 alpha = qt_div_255(int(src->alpha()) * int(coverage)); + if (alpha) + interpolate_pixel(*dest, DST::ialpha(alpha), + *src, DST::alpha(alpha)); + } else { + interpolate_pixel(*dest, DST::ialpha(coverage), + *src, DST::alpha(coverage)); + } + ++dest; + ++src; + } + + return; + } + + + Q_ASSERT(coverage == 255); + Q_ASSERT(SRC::hasAlpha()); + + if (SRC::hasAlpha()) { + // align + for (int i = 0; i < align; ++i) { + const quint8 a = src->alpha(); + if (a == 0xff) { + *dest = DST(*src); + } else if (a > 0) { + *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(a)); + } + ++dest; + ++src; + --length; + } + + while (length >= 4) { + blend_sourceOver_4(dest, src); + length -= 4; + src += 4; + dest += 4; + } + + // tail + while (length--) { + const quint8 a = src->alpha(); + if (a == 0xff) { + *dest = DST(*src); + } else if (a > 0) { + *dest = DST(*src).truncedAlpha() + dest->byte_mul(DST::ialpha(a)); + } + ++dest; + ++src; + } + } +} + +template <class DST, class SRC> +Q_STATIC_TEMPLATE_SPECIALIZATION +void QT_FASTCALL blendUntransformed(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData*>(userData); + QPainter::CompositionMode mode = data->rasterBuffer->compositionMode; + + if (mode != QPainter::CompositionMode_SourceOver && + mode != QPainter::CompositionMode_Source) + { + blend_src_generic<RegularSpans>(count, spans, userData); + return; + } + + const bool modeSource = !SRC::hasAlpha() || + mode == QPainter::CompositionMode_Source; + const int image_width = data->texture.width; + const int image_height = data->texture.height; + int xoff = -qRound(-data->dx); + int yoff = -qRound(-data->dy); + + while (count--) { + const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; + if (coverage == 0) { + ++spans; + continue; + } + + int x = spans->x; + int length = spans->len; + int sx = xoff + x; + int sy = yoff + spans->y; + if (sy >= 0 && sy < image_height && sx < image_width) { + if (sx < 0) { + x -= sx; + length += sx; + sx = 0; + } + if (sx + length > image_width) + length = image_width - sx; + if (length > 0) { + DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x; + const SRC *src = (SRC*)data->texture.scanLine(sy) + sx; + if (modeSource && coverage == 255) { + qt_memconvert<DST, SRC>(dest, src, length); + } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && length >= 3 && + (quintptr(dest) & 3) == (quintptr(src) & 3)) + { + blendUntransformed_dest24(dest, src, coverage, length); + } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && length >= 3 && + (quintptr(dest) & 3) == (quintptr(src) & 3)) + { + blendUntransformed_dest16(dest, src, coverage, length); + } else { + blendUntransformed_unaligned(dest, src, coverage, length); + } + } + } + ++spans; + } +} + +static void blend_untransformed_rgb888(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_24) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_RGB888) + blendUntransformed<qrgb888, qrgb888>(count, spans, userData); + else +#endif + blend_untransformed_generic<RegularSpans>(count, spans, userData); +} + +static void blend_untransformed_argb6666(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_18) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB6666_Premultiplied) + blendUntransformed<qargb6666, qargb6666>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB666) + blendUntransformed<qargb6666, qrgb666>(count, spans, userData); + else +#endif + blend_untransformed_generic<RegularSpans>(count, spans, userData); +} + +static void blend_untransformed_rgb666(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_18) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB6666_Premultiplied) + blendUntransformed<qrgb666, qargb6666>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB666) + blendUntransformed<qrgb666, qrgb666>(count, spans, userData); + else +#endif + blend_untransformed_generic<RegularSpans>(count, spans, userData); +} + +static void blend_untransformed_argb8565(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_16) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) + blendUntransformed<qargb8565, qargb8565>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB16) + blendUntransformed<qargb8565, qrgb565>(count, spans, userData); + else +#endif + blend_untransformed_generic<RegularSpans>(count, spans, userData); +} + +static void blend_untransformed_rgb565(int count, const QSpan *spans, + void *userData) +{ +#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) + blendUntransformed<qrgb565, qargb8565>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB16) + blendUntransformed<qrgb565, qrgb565>(count, spans, userData); + else +#endif + blend_untransformed_generic<RegularSpans>(count, spans, userData); +} + +static void blend_untransformed_argb8555(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_15) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8555_Premultiplied) + blendUntransformed<qargb8555, qargb8555>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB555) + blendUntransformed<qargb8555, qrgb555>(count, spans, userData); + else +#endif + blend_untransformed_generic<RegularSpans>(count, spans, userData); +} + +static void blend_untransformed_rgb555(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_15) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8555_Premultiplied) + blendUntransformed<qrgb555, qargb8555>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB555) + blendUntransformed<qrgb555, qrgb555>(count, spans, userData); + else +#endif + blend_untransformed_generic<RegularSpans>(count, spans, userData); +} + +static void blend_untransformed_argb4444(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_12) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB4444_Premultiplied) + blendUntransformed<qargb4444, qargb4444>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB444) + blendUntransformed<qargb4444, qrgb444>(count, spans, userData); + else +#endif + blend_untransformed_generic<RegularSpans>(count, spans, userData); +} + +static void blend_untransformed_rgb444(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_12) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB4444_Premultiplied) + blendUntransformed<qrgb444, qargb4444>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB444) + blendUntransformed<qrgb444, qrgb444>(count, spans, userData); + else +#endif + blend_untransformed_generic<RegularSpans>(count, spans, userData); +} + +template <SpanMethod spanMethod> +Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_generic(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + uint buffer[buffer_size]; + uint src_buffer[buffer_size]; + Operator op = getOperator(data, spans, count); + + const int image_width = data->texture.width; + const int image_height = data->texture.height; + int xoff = -qRound(-data->dx) % image_width; + int yoff = -qRound(-data->dy) % image_height; + + if (xoff < 0) + xoff += image_width; + if (yoff < 0) + yoff += image_height; + + while (count--) { + int x = spans->x; + int length = spans->len; + int sx = (xoff + spans->x) % image_width; + int sy = (spans->y + yoff) % image_height; + if (sx < 0) + sx += image_width; + if (sy < 0) + sy += image_height; + + const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; + while (length) { + int l = qMin(image_width - sx, length); + if (buffer_size < l) + l = buffer_size; + const uint *src = op.src_fetch(src_buffer, &op, data, sy, sx, l); + if (spanMethod == RegularSpans) { + uint *dest = op.dest_fetch ? op.dest_fetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer; + op.func(dest, src, l, coverage); + if (op.dest_store) + op.dest_store(data->rasterBuffer, x, spans->y, dest, l); + } else { + drawBufferSpan(data, src, l, x, spans->y, l, + coverage); + } + x += l; + sx += l; + length -= l; + if (sx >= image_width) + sx = 0; + } + ++spans; + } +} + +template <SpanMethod spanMethod> +Q_STATIC_TEMPLATE_FUNCTION void blend_tiled_argb(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + if (data->texture.format != QImage::Format_ARGB32_Premultiplied + && data->texture.format != QImage::Format_RGB32) { + blend_tiled_generic<spanMethod>(count, spans, userData); + return; + } + + Operator op = getOperator(data, spans, count); + + int image_width = data->texture.width; + int image_height = data->texture.height; + int xoff = -qRound(-data->dx) % image_width; + int yoff = -qRound(-data->dy) % image_height; + + if (xoff < 0) + xoff += image_width; + if (yoff < 0) + yoff += image_height; + + while (count--) { + int x = spans->x; + int length = spans->len; + int sx = (xoff + spans->x) % image_width; + int sy = (spans->y + yoff) % image_height; + if (sx < 0) + sx += image_width; + if (sy < 0) + sy += image_height; + + const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; + while (length) { + int l = qMin(image_width - sx, length); + if (buffer_size < l) + l = buffer_size; + const uint *src = (uint *)data->texture.scanLine(sy) + sx; + if (spanMethod == RegularSpans) { + uint *dest = ((uint *)data->rasterBuffer->scanLine(spans->y)) + x; + op.func(dest, src, l, coverage); + } else { + drawBufferSpan(data, src, buffer_size, + x, spans->y, l, coverage); + } + x += l; + length -= l; + sx = 0; + } + ++spans; + } +} + +template <class DST, class SRC> +Q_STATIC_TEMPLATE_FUNCTION void blendTiled(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData*>(userData); + QPainter::CompositionMode mode = data->rasterBuffer->compositionMode; + + if (mode != QPainter::CompositionMode_SourceOver && + mode != QPainter::CompositionMode_Source) + { + blend_src_generic<RegularSpans>(count, spans, userData); + return; + } + + const bool modeSource = !SRC::hasAlpha() || + mode == QPainter::CompositionMode_Source; + const int image_width = data->texture.width; + const int image_height = data->texture.height; + int xoff = -qRound(-data->dx) % image_width; + int yoff = -qRound(-data->dy) % image_height; + + if (xoff < 0) + xoff += image_width; + if (yoff < 0) + yoff += image_height; + + while (count--) { + const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; + if (coverage == 0) { + ++spans; + continue; + } + + int x = spans->x; + int length = spans->len; + int sx = (xoff + spans->x) % image_width; + int sy = (spans->y + yoff) % image_height; + if (sx < 0) + sx += image_width; + if (sy < 0) + sy += image_height; + + if (modeSource && coverage == 255) { + // Copy the first texture block + length = qMin(image_width,length); + int tx = x; + while (length) { + int l = qMin(image_width - sx, length); + if (buffer_size < l) + l = buffer_size; + DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + tx; + const SRC *src = (SRC*)data->texture.scanLine(sy) + sx; + + qt_memconvert<DST, SRC>(dest, src, l); + length -= l; + tx += l; + sx = 0; + } + + // Now use the rasterBuffer as the source of the texture, + // We can now progressively copy larger blocks + // - Less cpu time in code figuring out what to copy + // We are dealing with one block of data + // - More likely to fit in the cache + // - can use memcpy + int copy_image_width = qMin(image_width, int(spans->len)); + length = spans->len - copy_image_width; + DST *src = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x; + DST *dest = src + copy_image_width; + while (copy_image_width < length) { + qt_memconvert(dest, src, copy_image_width); + dest += copy_image_width; + length -= copy_image_width; + copy_image_width *= 2; + } + if (length > 0) + qt_memconvert(dest, src, length); + } else { + while (length) { + int l = qMin(image_width - sx, length); + if (buffer_size < l) + l = buffer_size; + DST *dest = ((DST*)data->rasterBuffer->scanLine(spans->y)) + x; + const SRC *src = (SRC*)data->texture.scanLine(sy) + sx; + if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 && + (quintptr(dest) & 3) == (quintptr(src) & 3)) + { + blendUntransformed_dest24(dest, src, coverage, l); + } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 && + (quintptr(dest) & 3) == (quintptr(src) & 3)) + { + blendUntransformed_dest16(dest, src, coverage, l); + } else { + blendUntransformed_unaligned(dest, src, coverage, l); + } + + x += l; + length -= l; + sx = 0; + } + } + ++spans; + } +} + +static void blend_tiled_rgb888(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_24) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_RGB888) + blendTiled<qrgb888, qrgb888>(count, spans, userData); + else +#endif + blend_tiled_generic<RegularSpans>(count, spans, userData); +} + +static void blend_tiled_argb6666(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_18) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB6666_Premultiplied) + blendTiled<qargb6666, qargb6666>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB666) + blendTiled<qargb6666, qrgb666>(count, spans, userData); + else +#endif + blend_tiled_generic<RegularSpans>(count, spans, userData); +} + +static void blend_tiled_rgb666(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_18) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB6666_Premultiplied) + blendTiled<qrgb666, qargb6666>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB666) + blendTiled<qrgb666, qrgb666>(count, spans, userData); + else +#endif + blend_tiled_generic<RegularSpans>(count, spans, userData); +} + +static void blend_tiled_argb8565(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_16) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) + blendTiled<qargb8565, qargb8565>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB16) + blendTiled<qargb8565, qrgb565>(count, spans, userData); + else +#endif + blend_tiled_generic<RegularSpans>(count, spans, userData); +} + +static void blend_tiled_rgb565(int count, const QSpan *spans, void *userData) +{ +#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) + blendTiled<qrgb565, qargb8565>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB16) + blendTiled<qrgb565, qrgb565>(count, spans, userData); + else +#endif + blend_tiled_generic<RegularSpans>(count, spans, userData); +} + +static void blend_tiled_argb8555(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_15) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8555_Premultiplied) + blendTiled<qargb8555, qargb8555>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB555) + blendTiled<qargb8555, qrgb555>(count, spans, userData); + else +#endif + blend_tiled_generic<RegularSpans>(count, spans, userData); +} + +static void blend_tiled_rgb555(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_15) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8555_Premultiplied) + blendTiled<qrgb555, qargb8555>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB555) + blendTiled<qrgb555, qrgb555>(count, spans, userData); + else +#endif + blend_tiled_generic<RegularSpans>(count, spans, userData); +} + +static void blend_tiled_argb4444(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_12) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB4444_Premultiplied) + blendTiled<qargb4444, qargb4444>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB444) + blendTiled<qargb4444, qrgb444>(count, spans, userData); + else +#endif + blend_tiled_generic<RegularSpans>(count, spans, userData); +} + +static void blend_tiled_rgb444(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_12) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB4444_Premultiplied) + blendTiled<qrgb444, qargb4444>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB444) + blendTiled<qrgb444, qrgb444>(count, spans, userData); + else +#endif + blend_tiled_generic<RegularSpans>(count, spans, userData); +} + +template <class DST, class SRC> +Q_STATIC_TEMPLATE_FUNCTION void blendTransformedBilinear(int count, const QSpan *spans, + void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData*>(userData); + QPainter::CompositionMode mode = data->rasterBuffer->compositionMode; + + + if (mode != QPainter::CompositionMode_SourceOver) { + blend_src_generic<RegularSpans>(count, spans, userData); + return; + } + + SRC buffer[buffer_size]; + + const int src_minx = data->texture.x1; + const int src_miny = data->texture.y1; + const int src_maxx = data->texture.x2 - 1; + const int src_maxy = data->texture.y2 - 1; + + if (data->fast_matrix) { + // The increment pr x in the scanline + const int fdx = (int)(data->m11 * fixed_scale); + const int fdy = (int)(data->m12 * fixed_scale); + + while (count--) { + const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; + if (coverage == 0) { + ++spans; + continue; + } + + DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y) + + spans->x; + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); + int x = int((data->m21 * cy + + data->m11 * cx + data->dx) * fixed_scale) - half_point; + int y = int((data->m22 * cy + + data->m12 * cx + data->dy) * fixed_scale) - half_point; + int length = spans->len; + + while (length) { + const int l = qMin(length, buffer_size); + + const SRC *end = buffer + l; + SRC *b = buffer; + while (b < end) { + int x1 = (x >> 16); + int x2; + int y1 = (y >> 16); + int y2; + + const int distx = (x & 0x0000ffff) >> 8; + const int disty = (y & 0x0000ffff) >> 8; + + if (x1 < src_minx) { + x2 = x1 = src_minx; + } else if (x1 >= src_maxx) { + x2 = x1 = src_maxx; + } else { + x2 = x1 + 1; + } + if (y1 < src_miny) { + y2 = y1 = src_miny; + } else if (y1 >= src_maxy) { + y2 = y1 = src_maxy; + } else { + y2 = y1 + 1; + } +#if 0 + if (x1 == x2) { + if (y1 == y2) { + *b = ((SRC*)data->texture.scanLine(y1))[x1]; + } else { + *b = ((SRC*)data->texture.scanLine(y1))[x1]; + const SRC t = data->texture.scanLine(y2)[x1]; + interpolate_pixel(*b, SRC::ialpha(disty), + t, SRC::alpha(disty)); + } + } else if (y1 == y2) { + *b = ((SRC*)data->texture.scanLine(y1))[x1]; + const SRC t = ((SRC*)data->texture.scanLine(y1))[x2]; + interpolate_pixel(*b, SRC::ialpha(distx), + t, SRC::alpha(distx)); + } else +#endif + { + const SRC *src1 = (SRC*)data->texture.scanLine(y1); + const SRC *src2 = (SRC*)data->texture.scanLine(y2); + SRC tl = src1[x1]; + const SRC tr = src1[x2]; + SRC bl = src2[x1]; + const SRC br = src2[x2]; + const quint8 ax = SRC::alpha(distx); + const quint8 iax = SRC::ialpha(distx); + + interpolate_pixel(tl, iax, tr, ax); + interpolate_pixel(bl, iax, br, ax); + interpolate_pixel(tl, SRC::ialpha(disty), + bl, SRC::alpha(disty)); + *b = tl; + } + ++b; + + x += fdx; + y += fdy; + } + + if (!SRC::hasAlpha() && coverage == 255) { + qt_memconvert(dest, buffer, l); + } else if (sizeof(DST) == 3 && l >= 4 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) + { + blendUntransformed_dest24(dest, buffer, coverage, l); + } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) { + blendUntransformed_dest16(dest, buffer, coverage, l); + } else { + blendUntransformed_unaligned(dest, buffer, coverage, l); + } + + dest += l; + length -= l; + } + ++spans; + } + } else { + const qreal fdx = data->m11; + const qreal fdy = data->m12; + const qreal fdw = data->m13; + + while (count--) { + const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; + if (coverage == 0) { + ++spans; + continue; + } + + DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y) + + spans->x; + + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); + + qreal x = data->m21 * cy + data->m11 * cx + data->dx; + qreal y = data->m22 * cy + data->m12 * cx + data->dy; + qreal w = data->m23 * cy + data->m13 * cx + data->m33; + + int length = spans->len; + while (length) { + const int l = qMin(length, buffer_size); + const SRC *end = buffer + l; + SRC *b = buffer; + while (b < end) { + const qreal iw = w == 0 ? 1 : 1 / w; + const qreal px = x * iw - qreal(0.5); + const qreal py = y * iw - qreal(0.5); + + int x1 = int(px) - (px < 0); + int x2; + int y1 = int(py) - (py < 0); + int y2; + + const int distx = int((px - x1) * 256); + const int disty = int((py - y1) * 256); + + if (x1 < src_minx) { + x2 = x1 = src_minx; + } else if (x1 >= src_maxx) { + x2 = x1 = src_maxx; + } else { + x2 = x1 + 1; + } + if (y1 < src_miny) { + y2 = y1 = src_miny; + } else if (y1 >= src_maxy) { + y2 = y1 = src_maxy; + } else { + y2 = y1 + 1; + } + + const SRC *src1 = (SRC*)data->texture.scanLine(y1); + const SRC *src2 = (SRC*)data->texture.scanLine(y2); + SRC tl = src1[x1]; + const SRC tr = src1[x2]; + SRC bl = src2[x1]; + const SRC br = src2[x2]; + const quint8 ax = SRC::alpha(distx); + const quint8 iax = SRC::ialpha(distx); + + interpolate_pixel(tl, iax, tr, ax); + interpolate_pixel(bl, iax, br, ax); + interpolate_pixel(tl, SRC::ialpha(disty), + bl, SRC::alpha(disty)); + *b = tl; + ++b; + + x += fdx; + y += fdy; + w += fdw; + } + if (!SRC::hasAlpha() && coverage == 255) { + qt_memconvert(dest, buffer, l); + } else if (sizeof(DST) == 3 && l >= 4 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) + { + blendUntransformed_dest24(dest, buffer, coverage, l); + } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) { + blendUntransformed_dest16(dest, buffer, coverage, l); + } else { + blendUntransformed_unaligned(dest, buffer, coverage, l); + } + + dest += l; + length -= l; + } + ++spans; + } + } +} + +static void blend_transformed_bilinear_rgb888(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_24) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_RGB888) + blendTransformedBilinear<qrgb888, qrgb888>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_bilinear_argb6666(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_18) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB6666_Premultiplied) + blendTransformedBilinear<qargb6666, qargb6666>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB666) + blendTransformedBilinear<qargb6666, qrgb666>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_bilinear_rgb666(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_18) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB6666_Premultiplied) + blendTransformedBilinear<qrgb666, qargb6666>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB666) + blendTransformedBilinear<qrgb666, qrgb666>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_bilinear_argb8565(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_16) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) + blendTransformedBilinear<qargb8565, qargb8565>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB16) + blendTransformedBilinear<qargb8565, qrgb565>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_bilinear_rgb565(int count, const QSpan *spans, + void *userData) +{ +#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_RGB16) + blendTransformedBilinear<qrgb565, qrgb565>(count, spans, userData); + else if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) + blendTransformedBilinear<qrgb565, qargb8565>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_bilinear_argb8555(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_15) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8555_Premultiplied) + blendTransformedBilinear<qargb8555, qargb8555>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB555) + blendTransformedBilinear<qargb8555, qrgb555>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_bilinear_rgb555(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_15) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8555_Premultiplied) + blendTransformedBilinear<qrgb555, qargb8555>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB555) + blendTransformedBilinear<qrgb555, qrgb555>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_bilinear_argb4444(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_12) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB4444_Premultiplied) + blendTransformedBilinear<qargb4444, qargb4444>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB444) + blendTransformedBilinear<qargb4444, qrgb444>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_bilinear_rgb444(int count, const QSpan *spans, void *userData) +{ +#if defined(QT_QWS_DEPTH_12) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB4444_Premultiplied) + blendTransformedBilinear<qrgb444, qargb4444>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB444) + blendTransformedBilinear<qrgb444, qrgb444>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +template <SpanMethod spanMethod> +Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_argb(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + if (data->texture.format != QImage::Format_ARGB32_Premultiplied + && data->texture.format != QImage::Format_RGB32) { + blend_src_generic<spanMethod>(count, spans, userData); + return; + } + + CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode]; + uint buffer[buffer_size]; + + int image_width = data->texture.width; + int image_height = data->texture.height; + const int scanline_offset = data->texture.bytesPerLine / 4; + + if (data->fast_matrix) { + // The increment pr x in the scanline + int fdx = (int)(data->m11 * fixed_scale); + int fdy = (int)(data->m12 * fixed_scale); + + while (count--) { + void *t = data->rasterBuffer->scanLine(spans->y); + + uint *target = ((uint *)t) + spans->x; + uint *image_bits = (uint *)data->texture.imageData; + + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); + + int x = int((data->m21 * cy + + data->m11 * cx + data->dx) * fixed_scale); + int y = int((data->m22 * cy + + data->m12 * cx + data->dy) * fixed_scale); + + int length = spans->len; + const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; + while (length) { + int l = qMin(length, buffer_size); + const uint *end = buffer + l; + uint *b = buffer; + while (b < end) { + int px = x >> 16; + int py = y >> 16; + + bool out = (px < 0) || (px >= image_width) + || (py < 0) || (py >= image_height); + + int y_offset = py * scanline_offset; + *b = out ? uint(0) : image_bits[y_offset + px]; + x += fdx; + y += fdy; + ++b; + } + if (spanMethod == RegularSpans) + func(target, buffer, l, coverage); + else + drawBufferSpan(data, buffer, buffer_size, + spans->x + spans->len - length, + spans->y, l, coverage); + target += l; + length -= l; + } + ++spans; + } + } else { + const qreal fdx = data->m11; + const qreal fdy = data->m12; + const qreal fdw = data->m13; + while (count--) { + void *t = data->rasterBuffer->scanLine(spans->y); + + uint *target = ((uint *)t) + spans->x; + uint *image_bits = (uint *)data->texture.imageData; + + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); + + qreal x = data->m21 * cy + data->m11 * cx + data->dx; + qreal y = data->m22 * cy + data->m12 * cx + data->dy; + qreal w = data->m23 * cy + data->m13 * cx + data->m33; + + int length = spans->len; + const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; + while (length) { + int l = qMin(length, buffer_size); + const uint *end = buffer + l; + uint *b = buffer; + while (b < end) { + const qreal iw = w == 0 ? 1 : 1 / w; + const qreal tx = x * iw; + const qreal ty = y * iw; + const int px = int(tx) - (tx < 0); + const int py = int(ty) - (ty < 0); + + bool out = (px < 0) || (px >= image_width) + || (py < 0) || (py >= image_height); + + int y_offset = py * scanline_offset; + *b = out ? uint(0) : image_bits[y_offset + px]; + x += fdx; + y += fdy; + w += fdw; + + ++b; + } + if (spanMethod == RegularSpans) + func(target, buffer, l, coverage); + else + drawBufferSpan(data, buffer, buffer_size, + spans->x + spans->len - length, + spans->y, l, coverage); + target += l; + length -= l; + } + ++spans; + } + } +} + +template <class DST, class SRC> +Q_STATIC_TEMPLATE_FUNCTION void blendTransformed(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData*>(userData); + QPainter::CompositionMode mode = data->rasterBuffer->compositionMode; + + if (mode != QPainter::CompositionMode_SourceOver) { + blend_src_generic<RegularSpans>(count, spans, userData); + return; + } + + SRC buffer[buffer_size]; + const int image_width = data->texture.width; + const int image_height = data->texture.height; + + if (data->fast_matrix) { + // The increment pr x in the scanline + const int fdx = (int)(data->m11 * fixed_scale); + const int fdy = (int)(data->m12 * fixed_scale); + + while (count--) { + const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; + if (coverage == 0) { + ++spans; + continue; + } + + DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y) + + spans->x; + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); + int x = int((data->m21 * cy + + data->m11 * cx + data->dx) * fixed_scale); + int y = int((data->m22 * cy + + data->m12 * cx + data->dy) * fixed_scale); + int length = spans->len; + + while (length) { + const int l = qMin(length, buffer_size); + + const SRC *end = buffer + l; + SRC *b = buffer; + while (b < end) { + const int px = (x >> 16); + const int py = (y >> 16); + + if ((px < 0) || (px >= image_width) || + (py < 0) || (py >= image_height)) + { + *b = 0; + } else { + *b = ((SRC*)data->texture.scanLine(py))[px]; + } + ++b; + + x += fdx; + y += fdy; + } + + if (!SRC::hasAlpha() && coverage == 255) { + qt_memconvert(dest, buffer, l); + } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) + { + blendUntransformed_dest24(dest, buffer, coverage, l); + } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) { + blendUntransformed_dest16(dest, buffer, coverage, l); + } else { + blendUntransformed_unaligned(dest, buffer, coverage, l); + } + + dest += l; + length -= l; + } + ++spans; + } + } else { + const qreal fdx = data->m11; + const qreal fdy = data->m12; + const qreal fdw = data->m13; + + while (count--) { + const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; + if (coverage == 0) { + ++spans; + continue; + } + + DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y) + + spans->x; + + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); + + qreal x = data->m21 * cy + data->m11 * cx + data->dx; + qreal y = data->m22 * cy + data->m12 * cx + data->dy; + qreal w = data->m23 * cy + data->m13 * cx + data->m33; + + int length = spans->len; + while (length) { + const int l = qMin(length, buffer_size); + const SRC *end = buffer + l; + SRC *b = buffer; + while (b < end) { + const qreal iw = w == 0 ? 1 : 1 / w; + const qreal tx = x * iw; + const qreal ty = y * iw; + + const int px = int(tx) - (tx < 0); + const int py = int(ty) - (ty < 0); + + if ((px < 0) || (px >= image_width) || + (py < 0) || (py >= image_height)) + { + *b = 0; + } else { + *b = ((SRC*)data->texture.scanLine(py))[px]; + } + ++b; + + x += fdx; + y += fdy; + w += fdw; + } + if (!SRC::hasAlpha() && coverage == 255) { + qt_memconvert(dest, buffer, l); + } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) + { + blendUntransformed_dest24(dest, buffer, coverage, l); + } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) { + blendUntransformed_dest16(dest, buffer, coverage, l); + } else { + blendUntransformed_unaligned(dest, buffer, coverage, l); + } + + dest += l; + length -= l; + } + ++spans; + } + } +} + +static void blend_transformed_rgb888(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_24) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_RGB888) + blendTransformed<qrgb888, qrgb888>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_argb6666(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_18) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB6666_Premultiplied) + blendTransformed<qargb6666, qargb6666>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB666) + blendTransformed<qargb6666, qrgb666>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_rgb666(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_18) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB6666_Premultiplied) + blendTransformed<qrgb666, qargb6666>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB666) + blendTransformed<qrgb666, qrgb666>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_argb8565(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_16) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) + blendTransformed<qargb8565, qargb8565>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB16) + blendTransformed<qargb8565, qrgb565>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_rgb565(int count, const QSpan *spans, + void *userData) +{ +#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) + blendTransformed<qrgb565, qargb8565>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB16) + blendTransformed<qrgb565, qrgb565>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_argb8555(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_15) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8555_Premultiplied) + blendTransformed<qargb8555, qargb8555>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB555) + blendTransformed<qargb8555, qrgb555>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_rgb555(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_15) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8555_Premultiplied) + blendTransformed<qrgb555, qargb8555>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB555) + blendTransformed<qrgb555, qrgb555>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_argb4444(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_12) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB4444_Premultiplied) + blendTransformed<qargb4444, qargb4444>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB444) + blendTransformed<qargb4444, qrgb444>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_rgb444(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_12) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB4444_Premultiplied) + blendTransformed<qrgb444, qargb4444>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB444) + blendTransformed<qrgb444, qrgb444>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +template <SpanMethod spanMethod> +Q_STATIC_TEMPLATE_FUNCTION void blend_transformed_tiled_argb(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + if (data->texture.format != QImage::Format_ARGB32_Premultiplied + && data->texture.format != QImage::Format_RGB32) { + blend_src_generic<spanMethod>(count, spans, userData); + return; + } + + CompositionFunction func = functionForMode[data->rasterBuffer->compositionMode]; + uint buffer[buffer_size]; + + int image_width = data->texture.width; + int image_height = data->texture.height; + const int scanline_offset = data->texture.bytesPerLine / 4; + + if (data->fast_matrix) { + // The increment pr x in the scanline + int fdx = (int)(data->m11 * fixed_scale); + int fdy = (int)(data->m12 * fixed_scale); + + while (count--) { + void *t = data->rasterBuffer->scanLine(spans->y); + + uint *target = ((uint *)t) + spans->x; + uint *image_bits = (uint *)data->texture.imageData; + + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); + + int x = int((data->m21 * cy + + data->m11 * cx + data->dx) * fixed_scale); + int y = int((data->m22 * cy + + data->m12 * cx + data->dy) * fixed_scale); + + const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; + int length = spans->len; + while (length) { + int l = qMin(length, buffer_size); + const uint *end = buffer + l; + uint *b = buffer; + while (b < end) { + int px = x >> 16; + int py = y >> 16; + px %= image_width; + py %= image_height; + if (px < 0) px += image_width; + if (py < 0) py += image_height; + int y_offset = py * scanline_offset; + + Q_ASSERT(px >= 0 && px < image_width); + Q_ASSERT(py >= 0 && py < image_height); + + *b = image_bits[y_offset + px]; + x += fdx; + y += fdy; + ++b; + } + if (spanMethod == RegularSpans) + func(target, buffer, l, coverage); + else + drawBufferSpan(data, buffer, buffer_size, + spans->x + spans->len - length, + spans->y, l, coverage); + target += l; + length -= l; + } + ++spans; + } + } else { + const qreal fdx = data->m11; + const qreal fdy = data->m12; + const qreal fdw = data->m13; + while (count--) { + void *t = data->rasterBuffer->scanLine(spans->y); + + uint *target = ((uint *)t) + spans->x; + uint *image_bits = (uint *)data->texture.imageData; + + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); + + qreal x = data->m21 * cy + data->m11 * cx + data->dx; + qreal y = data->m22 * cy + data->m12 * cx + data->dy; + qreal w = data->m23 * cy + data->m13 * cx + data->m33; + + const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; + int length = spans->len; + while (length) { + int l = qMin(length, buffer_size); + const uint *end = buffer + l; + uint *b = buffer; + while (b < end) { + const qreal iw = w == 0 ? 1 : 1 / w; + const qreal tx = x * iw; + const qreal ty = y * iw; + int px = int(tx) - (tx < 0); + int py = int(ty) - (ty < 0); + + px %= image_width; + py %= image_height; + if (px < 0) px += image_width; + if (py < 0) py += image_height; + int y_offset = py * scanline_offset; + + Q_ASSERT(px >= 0 && px < image_width); + Q_ASSERT(py >= 0 && py < image_height); + + *b = image_bits[y_offset + px]; + x += fdx; + y += fdy; + w += fdw; + //force increment to avoid /0 + if (!w) { + w += fdw; + } + ++b; + } + if (spanMethod == RegularSpans) + func(target, buffer, l, coverage); + else + drawBufferSpan(data, buffer, buffer_size, + spans->x + spans->len - length, + spans->y, l, coverage); + target += l; + length -= l; + } + ++spans; + } + } +} + +template <class DST, class SRC> +Q_STATIC_TEMPLATE_FUNCTION void blendTransformedTiled(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData*>(userData); + QPainter::CompositionMode mode = data->rasterBuffer->compositionMode; + + if (mode != QPainter::CompositionMode_SourceOver) { + blend_src_generic<RegularSpans>(count, spans, userData); + return; + } + + SRC buffer[buffer_size]; + const int image_width = data->texture.width; + const int image_height = data->texture.height; + + if (data->fast_matrix) { + // The increment pr x in the scanline + const int fdx = (int)(data->m11 * fixed_scale); + const int fdy = (int)(data->m12 * fixed_scale); + + while (count--) { + const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; + if (coverage == 0) { + ++spans; + continue; + } + + DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y) + + spans->x; + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); + int x = int((data->m21 * cy + + data->m11 * cx + data->dx) * fixed_scale); + int y = int((data->m22 * cy + + data->m12 * cx + data->dy) * fixed_scale); + int length = spans->len; + + while (length) { + const int l = qMin(length, buffer_size); + + const SRC *end = buffer + l; + SRC *b = buffer; + while (b < end) { + int px = (x >> 16) % image_width; + int py = (y >> 16) % image_height; + + if (px < 0) + px += image_width; + if (py < 0) + py += image_height; + + *b = ((SRC*)data->texture.scanLine(py))[px]; + ++b; + + x += fdx; + y += fdy; + } + + if (!SRC::hasAlpha() && coverage == 255) { + qt_memconvert(dest, buffer, l); + } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) + { + blendUntransformed_dest24(dest, buffer, coverage, l); + } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) { + blendUntransformed_dest16(dest, buffer, coverage, l); + } else { + blendUntransformed_unaligned(dest, buffer, coverage, l); + } + + dest += l; + length -= l; + } + ++spans; + } + } else { + const qreal fdx = data->m11; + const qreal fdy = data->m12; + const qreal fdw = data->m13; + + while (count--) { + const quint8 coverage = (data->texture.const_alpha * spans->coverage) >> 8; + if (coverage == 0) { + ++spans; + continue; + } + + DST *dest = (DST*)data->rasterBuffer->scanLine(spans->y) + + spans->x; + + const qreal cx = spans->x + qreal(0.5); + const qreal cy = spans->y + qreal(0.5); + + qreal x = data->m21 * cy + data->m11 * cx + data->dx; + qreal y = data->m22 * cy + data->m12 * cx + data->dy; + qreal w = data->m23 * cy + data->m13 * cx + data->m33; + + int length = spans->len; + while (length) { + const int l = qMin(length, buffer_size); + const SRC *end = buffer + l; + SRC *b = buffer; + while (b < end) { + const qreal iw = w == 0 ? 1 : 1 / w; + const qreal tx = x * iw; + const qreal ty = y * iw; + + int px = int(tx) - (tx < 0); + int py = int(ty) - (ty < 0); + + px %= image_width; + py %= image_height; + if (px < 0) + px += image_width; + if (py < 0) + py += image_height; + + *b = ((SRC*)data->texture.scanLine(py))[px]; + ++b; + + x += fdx; + y += fdy; + w += fdw; + // force increment to avoid /0 + if (!w) + w += fdw; + } + if (!SRC::hasAlpha() && coverage == 255) { + qt_memconvert(dest, buffer, l); + } else if (sizeof(DST) == 3 && sizeof(SRC) == 3 && l >= 4 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) + { + blendUntransformed_dest24(dest, buffer, coverage, l); + } else if (sizeof(DST) == 2 && sizeof(SRC) == 2 && l >= 2 && + (quintptr(dest) & 3) == (quintptr(buffer) & 3)) { + blendUntransformed_dest16(dest, buffer, coverage, l); + } else { + blendUntransformed_unaligned(dest, buffer, coverage, l); + } + + dest += l; + length -= l; + } + ++spans; + } + } +} + +static void blend_transformed_tiled_rgb888(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_24) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_RGB888) + blendTransformedTiled<qrgb888, qrgb888>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_tiled_argb6666(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_18) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB6666_Premultiplied) + blendTransformedTiled<qargb6666, qargb6666>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB666) + blendTransformedTiled<qargb6666, qrgb666>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_tiled_rgb666(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_18) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB6666_Premultiplied) + blendTransformedTiled<qrgb666, qargb6666>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB666) + blendTransformedTiled<qrgb666, qrgb666>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_tiled_argb8565(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_16) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) + blendTransformedTiled<qargb8565, qargb8565>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB16) + blendTransformedTiled<qargb8565, qrgb565>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_tiled_rgb565(int count, const QSpan *spans, + void *userData) +{ +#if !defined(Q_WS_QWS) || defined(QT_QWS_DEPTH_16) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8565_Premultiplied) + blendTransformedTiled<qrgb565, qargb8565>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB16) + blendTransformedTiled<qrgb565, qrgb565>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_tiled_argb8555(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_15) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8555_Premultiplied) + blendTransformedTiled<qargb8555, qargb8555>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB555) + blendTransformedTiled<qargb8555, qrgb555>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_tiled_rgb555(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_15) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB8555_Premultiplied) + blendTransformedTiled<qrgb555, qargb8555>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB555) + blendTransformedTiled<qrgb555, qrgb555>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_tiled_argb4444(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_12) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB4444_Premultiplied) + blendTransformedTiled<qargb4444, qargb4444>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB444) + blendTransformedTiled<qargb4444, qrgb444>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +static void blend_transformed_tiled_rgb444(int count, const QSpan *spans, + void *userData) +{ +#if defined(QT_QWS_DEPTH_12) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + if (data->texture.format == QImage::Format_ARGB4444_Premultiplied) + blendTransformedTiled<qrgb444, qargb4444>(count, spans, userData); + else if (data->texture.format == QImage::Format_RGB444) + blendTransformedTiled<qrgb444, qrgb444>(count, spans, userData); + else +#endif + blend_src_generic<RegularSpans>(count, spans, userData); +} + +# define SPANFUNC_POINTER(Name, Arg) Name<Arg> + + +/* Image formats here are target formats */ +static const ProcessSpans processTextureSpans[NBlendTypes][QImage::NImageFormats] = { + // Untransformed + { + 0, // Invalid + SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // Mono + SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // MonoLsb + SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // Indexed8 + SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // RGB32 + SPANFUNC_POINTER(blend_untransformed_generic, RegularSpans), // ARGB32 + SPANFUNC_POINTER(blend_untransformed_argb, RegularSpans), // ARGB32_Premultiplied + blend_untransformed_rgb565, + blend_untransformed_argb8565, + blend_untransformed_rgb666, + blend_untransformed_argb6666, + blend_untransformed_rgb555, + blend_untransformed_argb8555, + blend_untransformed_rgb888, + blend_untransformed_rgb444, + blend_untransformed_argb4444, + }, + // Tiled + { + 0, // Invalid + SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // Mono + SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // MonoLsb + SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // Indexed8 + SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // RGB32 + SPANFUNC_POINTER(blend_tiled_generic, RegularSpans), // ARGB32 + SPANFUNC_POINTER(blend_tiled_argb, RegularSpans), // ARGB32_Premultiplied + blend_tiled_rgb565, + blend_tiled_argb8565, + blend_tiled_rgb666, + blend_tiled_argb6666, + blend_tiled_rgb555, + blend_tiled_argb8555, + blend_tiled_rgb888, + blend_tiled_rgb444, + blend_tiled_argb4444, + }, + // Transformed + { + 0, // Invalid + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32 + SPANFUNC_POINTER(blend_transformed_argb, RegularSpans), // ARGB32_Premultiplied + blend_transformed_rgb565, + blend_transformed_argb8565, + blend_transformed_rgb666, + blend_transformed_argb6666, + blend_transformed_rgb555, + blend_transformed_argb8555, + blend_transformed_rgb888, + blend_transformed_rgb444, + blend_transformed_argb4444, + }, + // TransformedTiled + { + 0, + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32 + SPANFUNC_POINTER(blend_transformed_tiled_argb, RegularSpans), // ARGB32_Premultiplied + blend_transformed_tiled_rgb565, + blend_transformed_tiled_argb8565, + blend_transformed_tiled_rgb666, + blend_transformed_tiled_argb6666, + blend_transformed_tiled_rgb555, + blend_transformed_tiled_argb8555, + blend_transformed_tiled_rgb888, + blend_transformed_tiled_rgb444, + blend_transformed_tiled_argb4444 + }, + // Bilinear + { + 0, + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32_Premultiplied + blend_transformed_bilinear_rgb565, + blend_transformed_bilinear_argb8565, + blend_transformed_bilinear_rgb666, + blend_transformed_bilinear_argb6666, + blend_transformed_bilinear_rgb555, + blend_transformed_bilinear_argb8555, + blend_transformed_bilinear_rgb888, + blend_transformed_bilinear_rgb444, + blend_transformed_bilinear_argb4444, + }, + // BilinearTiled + { + 0, + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Mono + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // MonoLsb + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // Indexed8 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB32 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB32_Premultiplied + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB16 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB8565_Premultiplied + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB666 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB6666_Premultiplied + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB555 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB8555_Premultiplied + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB888 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // RGB444 + SPANFUNC_POINTER(blend_src_generic, RegularSpans), // ARGB4444_Premultiplied + } +}; + +#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS) +static const ProcessSpans processTextureSpansCallback[NBlendTypes][QImage::NImageFormats] = { + // Untransformed + { + 0, // Invalid + blend_untransformed_generic<CallbackSpans>, // Mono + blend_untransformed_generic<CallbackSpans>, // MonoLsb + blend_untransformed_generic<CallbackSpans>, // Indexed8 + blend_untransformed_generic<CallbackSpans>, // RGB32 + blend_untransformed_generic<CallbackSpans>, // ARGB32 + blend_untransformed_argb<CallbackSpans>, // ARGB32_Premultiplied + blend_untransformed_generic<CallbackSpans>, // RGB16 + blend_untransformed_generic<CallbackSpans>, // ARGB8565_Premultiplied + blend_untransformed_generic<CallbackSpans>, // RGB666 + blend_untransformed_generic<CallbackSpans>, // ARGB6666_Premultiplied + blend_untransformed_generic<CallbackSpans>, // RGB555 + blend_untransformed_generic<CallbackSpans>, // ARGB8555_Premultiplied + blend_untransformed_generic<CallbackSpans>, // RGB888 + blend_untransformed_generic<CallbackSpans>, // RGB444 + blend_untransformed_generic<CallbackSpans> // ARGB4444_Premultiplied + }, + // Tiled + { + 0, // Invalid + blend_tiled_generic<CallbackSpans>, // Mono + blend_tiled_generic<CallbackSpans>, // MonoLsb + blend_tiled_generic<CallbackSpans>, // Indexed8 + blend_tiled_generic<CallbackSpans>, // RGB32 + blend_tiled_generic<CallbackSpans>, // ARGB32 + blend_tiled_argb<CallbackSpans>, // ARGB32_Premultiplied + blend_tiled_generic<CallbackSpans>, // RGB16 + blend_tiled_generic<CallbackSpans>, // ARGB8565_Premultiplied + blend_tiled_generic<CallbackSpans>, // RGB666 + blend_tiled_generic<CallbackSpans>, // ARGB6666_Premultiplied + blend_tiled_generic<CallbackSpans>, // RGB555 + blend_tiled_generic<CallbackSpans>, // ARGB8555_Premultiplied + blend_tiled_generic<CallbackSpans>, // RGB888 + blend_tiled_generic<CallbackSpans>, // RGB444 + blend_tiled_generic<CallbackSpans> // ARGB4444_Premultiplied + }, + // Transformed + { + 0, // Invalid + blend_src_generic<CallbackSpans>, // Mono + blend_src_generic<CallbackSpans>, // MonoLsb + blend_src_generic<CallbackSpans>, // Indexed8 + blend_src_generic<CallbackSpans>, // RGB32 + blend_src_generic<CallbackSpans>, // ARGB32 + blend_transformed_argb<CallbackSpans>, // ARGB32_Premultiplied + blend_src_generic<CallbackSpans>, // RGB16 + blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied + blend_src_generic<CallbackSpans>, // RGB666 + blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied + blend_src_generic<CallbackSpans>, // RGB555 + blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied + blend_src_generic<CallbackSpans>, // RGB888 + blend_src_generic<CallbackSpans>, // RGB444 + blend_src_generic<CallbackSpans>, // ARGB4444_Premultiplied + }, + // TransformedTiled + { + 0, + blend_src_generic<CallbackSpans>, // Mono + blend_src_generic<CallbackSpans>, // MonoLsb + blend_src_generic<CallbackSpans>, // Indexed8 + blend_src_generic<CallbackSpans>, // RGB32 + blend_src_generic<CallbackSpans>, // ARGB32 + blend_transformed_tiled_argb<CallbackSpans>, // ARGB32_Premultiplied + blend_src_generic<CallbackSpans>, // RGB16 + blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied + blend_src_generic<CallbackSpans>, // RGB666 + blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied + blend_src_generic<CallbackSpans>, // RGB555 + blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied + blend_src_generic<CallbackSpans>, // RGB888 + blend_src_generic<CallbackSpans>, // RGB444 + blend_src_generic<CallbackSpans> // ARGB4444_Premultiplied + }, + // Bilinear + { + 0, + blend_src_generic<CallbackSpans>, // Mono + blend_src_generic<CallbackSpans>, // MonoLsb + blend_src_generic<CallbackSpans>, // Indexed8 + blend_src_generic<CallbackSpans>, // RGB32 + blend_src_generic<CallbackSpans>, // ARGB32 + blend_src_generic<CallbackSpans>, // ARGB32_Premultiplied + blend_src_generic<CallbackSpans>, // RGB16 + blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied + blend_src_generic<CallbackSpans>, // RGB666 + blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied + blend_src_generic<CallbackSpans>, // RGB555 + blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied + blend_src_generic<CallbackSpans>, // RGB888 + blend_src_generic<CallbackSpans>, // RGB444 + blend_src_generic<CallbackSpans> // ARGB4444_Premultiplied + }, + // BilinearTiled + { + 0, + blend_src_generic<CallbackSpans>, // Mono + blend_src_generic<CallbackSpans>, // MonoLsb + blend_src_generic<CallbackSpans>, // Indexed8 + blend_src_generic<CallbackSpans>, // RGB32 + blend_src_generic<CallbackSpans>, // ARGB32 + blend_src_generic<CallbackSpans>, // ARGB32_Premultiplied + blend_src_generic<CallbackSpans>, // RGB16 + blend_src_generic<CallbackSpans>, // ARGB8565_Premultiplied + blend_src_generic<CallbackSpans>, // RGB666 + blend_src_generic<CallbackSpans>, // ARGB6666_Premultiplied + blend_src_generic<CallbackSpans>, // RGB555 + blend_src_generic<CallbackSpans>, // ARGB8555_Premultiplied + blend_src_generic<CallbackSpans>, // RGB888 + blend_src_generic<CallbackSpans>, // RGB444 + blend_src_generic<CallbackSpans> // ARGB4444_Premultiplied + } +}; +#endif // QT_NO_RASTERCALLBACKS + +void qBlendTexture(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + ProcessSpans proc = processTextureSpans[getBlendType(data)][data->rasterBuffer->format]; + proc(count, spans, userData); +} + +#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS) +void qBlendTextureCallback(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + ProcessSpans proc = processTextureSpansCallback[getBlendType(data)][data->rasterBuffer->format]; + proc(count, spans, userData); +} +#endif // QT_NO_RASTERCALLBACKS + +template <class DST> +inline void qt_bitmapblit_template(QRasterBuffer *rasterBuffer, + int x, int y, quint32 color, + const uchar *map, + int mapWidth, int mapHeight, int mapStride, + DST dummy = 0) +{ + Q_UNUSED(dummy); + const DST c = qt_colorConvert<DST, quint32>(color, 0); + DST *dest = reinterpret_cast<DST*>(rasterBuffer->scanLine(y)) + x; + const int destStride = rasterBuffer->bytesPerLine() / sizeof(DST); + + if (mapWidth > 8) { + while (mapHeight--) { + int x0 = 0; + int n = 0; + for (int x = 0; x < mapWidth; x += 8) { + uchar s = map[x >> 3]; + for (int i = 0; i < 8; ++i) { + if (s & 0x80) { + ++n; + } else { + if (n) { + qt_memfill(dest + x0, c, n); + x0 += n + 1; + n = 0; + } else { + ++x0; + } + if (!s) { + x0 += 8 - 1 - i; + break; + } + } + s <<= 1; + } + } + if (n) + qt_memfill(dest + x0, c, n); + dest += destStride; + map += mapStride; + } + } else { + while (mapHeight--) { + int x0 = 0; + int n = 0; + for (uchar s = *map; s; s <<= 1) { + if (s & 0x80) { + ++n; + } else if (n) { + qt_memfill(dest + x0, c, n); + x0 += n + 1; + n = 0; + } else { + ++x0; + } + } + if (n) + qt_memfill(dest + x0, c, n); + dest += destStride; + map += mapStride; + } + } +} + +static void qt_gradient_quint32(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + bool isVerticalGradient = + data->txop <= QTransform::TxScale && + data->type == QSpanData::LinearGradient && + data->gradient.linear.end.x == data->gradient.linear.origin.x; + + if (isVerticalGradient) { + LinearGradientValues linear; + getLinearGradientValues(&linear, data); + + CompositionFunctionSolid funcSolid = + functionForModeSolid[data->rasterBuffer->compositionMode]; + + /* + The logic for vertical gradient calculations is a mathematically + reduced copy of that in fetchLinearGradient() - which is basically: + + qreal ry = data->m22 * (y + 0.5) + data->dy; + qreal t = linear.dy*ry + linear.off; + t *= (GRADIENT_STOPTABLE_SIZE - 1); + quint32 color = + qt_gradient_pixel_fixed(&data->gradient, + int(t * FIXPT_SIZE)); + + This has then been converted to fixed point to improve performance. + */ + const int gss = GRADIENT_STOPTABLE_SIZE - 1; + int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE); + int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE)); + + while (count--) { + int y = spans->y; + int x = spans->x; + + quint32 *dst = (quint32 *)(data->rasterBuffer->scanLine(y)) + x; + quint32 color = + qt_gradient_pixel_fixed(&data->gradient, yinc * y + off); + + funcSolid(dst, spans->len, color, spans->coverage); + ++spans; + } + + } else { + blend_src_generic<RegularSpans>(count, spans, userData); + } +} + +static void qt_gradient_quint16(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + bool isVerticalGradient = + data->txop <= QTransform::TxScale && + data->type == QSpanData::LinearGradient && + data->gradient.linear.end.x == data->gradient.linear.origin.x; + + if (isVerticalGradient) { + + LinearGradientValues linear; + getLinearGradientValues(&linear, data); + + /* + The logic for vertical gradient calculations is a mathematically + reduced copy of that in fetchLinearGradient() - which is basically: + + qreal ry = data->m22 * (y + 0.5) + data->dy; + qreal t = linear.dy*ry + linear.off; + t *= (GRADIENT_STOPTABLE_SIZE - 1); + quint32 color = + qt_gradient_pixel_fixed(&data->gradient, + int(t * FIXPT_SIZE)); + + This has then been converted to fixed point to improve performance. + */ + const int gss = GRADIENT_STOPTABLE_SIZE - 1; + int yinc = int((linear.dy * data->m22 * gss) * FIXPT_SIZE); + int off = int((((linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss) * FIXPT_SIZE)); + + uint oldColor = data->solid.color; + while (count--) { + int y = spans->y; + + quint32 color = qt_gradient_pixel_fixed(&data->gradient, yinc * y + off); + + data->solid.color = color; + blend_color_rgb16(1, spans, userData); + ++spans; + } + data->solid.color = oldColor; + + } else { + blend_src_generic<RegularSpans>(count, spans, userData); + } +} + +inline static void qt_bitmapblit_quint32(QRasterBuffer *rasterBuffer, + int x, int y, quint32 color, + const uchar *map, + int mapWidth, int mapHeight, int mapStride) +{ + qt_bitmapblit_template<quint32>(rasterBuffer, x, y, color, + map, mapWidth, mapHeight, mapStride); +} + +inline static void qt_bitmapblit_quint16(QRasterBuffer *rasterBuffer, + int x, int y, quint32 color, + const uchar *map, + int mapWidth, int mapHeight, int mapStride) +{ + qt_bitmapblit_template<quint16>(rasterBuffer, x, y, color, + map, mapWidth, mapHeight, mapStride); +} + + +uchar qt_pow_rgb_gamma[256]; +uchar qt_pow_rgb_invgamma[256]; + +uint qt_pow_gamma[256]; +uchar qt_pow_invgamma[2048]; + +static void qt_alphamapblit_quint16(QRasterBuffer *rasterBuffer, + int x, int y, quint32 color, + const uchar *map, + int mapWidth, int mapHeight, int mapStride, + const QClipData *) +{ + const quint16 c = qt_colorConvert<quint16, quint32>(color, 0); + quint16 *dest = reinterpret_cast<quint16*>(rasterBuffer->scanLine(y)) + x; + const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint16); + + while (mapHeight--) { + for (int i = 0; i < mapWidth; ++i) { + const int coverage = map[i]; + + if (coverage == 0) { + // nothing + } else if (coverage == 255) { + dest[i] = c; + } else { + int ialpha = 255 - coverage; + dest[i] = BYTE_MUL_RGB16(c, coverage) + + BYTE_MUL_RGB16(dest[i], ialpha); + } + } + dest += destStride; + map += mapStride; + } +} + +void qt_build_pow_tables() { + qreal smoothing = qreal(1.7); + +#ifdef Q_WS_MAC + // decided by testing a few things on an iMac, should probably get this from the + // system... + smoothing = qreal(2.0); +#endif + +#ifdef Q_WS_WIN + int winSmooth; + if (SystemParametersInfo(0x200C /* SPI_GETFONTSMOOTHINGCONTRAST */, 0, &winSmooth, 0)) + smoothing = winSmooth / qreal(1000.0); + + // Safeguard ourselves against corrupt registry values... + if (smoothing > 5 || smoothing < 1) + smoothing = qreal(1.4); + +#endif + +#ifdef Q_WS_X11 + Q_UNUSED(smoothing); + for (int i=0; i<256; ++i) { + qt_pow_rgb_gamma[i] = uchar(i); + qt_pow_rgb_invgamma[i] = uchar(i); + } +#else + for (int i=0; i<256; ++i) { + qt_pow_rgb_gamma[i] = uchar(qRound(qPow(i / qreal(255.0), smoothing) * 255)); + qt_pow_rgb_invgamma[i] = uchar(qRound(qPow(i / qreal(255.), 1 / smoothing) * 255)); + } +#endif + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + const qreal gray_gamma = 2.31; + for (int i=0; i<256; ++i) + qt_pow_gamma[i] = uint(qRound(qPow(i / qreal(255.), gray_gamma) * 2047)); + for (int i=0; i<2048; ++i) + qt_pow_invgamma[i] = uchar(qRound(qPow(i / qreal(2047.0), 1 / gray_gamma) * 255)); +#endif +} + +static inline void rgbBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb) +{ + // Do a gray alphablend... + int da = qAlpha(*dst); + int dr = qRed(*dst); + int dg = qGreen(*dst); + int db = qBlue(*dst); + + if (da != 255 +#if defined (Q_WS_WIN) + // Work around GDI messing up alpha channel + && qRed(*dst) <= da && qBlue(*dst) <= da && qGreen(*dst) <= da +#endif + ) { + + int a = qGray(coverage); + sr = qt_div_255(qt_pow_rgb_invgamma[sr] * a); + sg = qt_div_255(qt_pow_rgb_invgamma[sg] * a); + sb = qt_div_255(qt_pow_rgb_invgamma[sb] * a); + + int ia = 255 - a; + dr = qt_div_255(dr * ia); + dg = qt_div_255(dg * ia); + db = qt_div_255(db * ia); + + *dst = ((a + qt_div_255((255 - a) * da)) << 24) + | ((sr + dr) << 16) + | ((sg + dg) << 8) + | ((sb + db)); + return; + } + + int mr = qRed(coverage); + int mg = qGreen(coverage); + int mb = qBlue(coverage); + + dr = qt_pow_rgb_gamma[dr]; + dg = qt_pow_rgb_gamma[dg]; + db = qt_pow_rgb_gamma[db]; + + int nr = qt_div_255((sr - dr) * mr) + dr; + int ng = qt_div_255((sg - dg) * mg) + dg; + int nb = qt_div_255((sb - db) * mb) + db; + + nr = qt_pow_rgb_invgamma[nr]; + ng = qt_pow_rgb_invgamma[ng]; + nb = qt_pow_rgb_invgamma[nb]; + + *dst = qRgb(nr, ng, nb); +} + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) +static inline void grayBlendPixel(quint32 *dst, int coverage, int sr, int sg, int sb) +{ + // Do a gammacorrected gray alphablend... + int dr = qRed(*dst); + int dg = qGreen(*dst); + int db = qBlue(*dst); + + dr = qt_pow_gamma[dr]; + dg = qt_pow_gamma[dg]; + db = qt_pow_gamma[db]; + + int alpha = coverage; + int ialpha = 255 - alpha; + int nr = (sr * alpha + ialpha * dr) / 255; + int ng = (sg * alpha + ialpha * dg) / 255; + int nb = (sb * alpha + ialpha * db) / 255; + + nr = qt_pow_invgamma[nr]; + ng = qt_pow_invgamma[ng]; + nb = qt_pow_invgamma[nb]; + + *dst = qRgb(nr, ng, nb); +} +#endif + +static void qt_alphamapblit_quint32(QRasterBuffer *rasterBuffer, + int x, int y, quint32 color, + const uchar *map, + int mapWidth, int mapHeight, int mapStride, + const QClipData *clip) +{ + const quint32 c = color; + const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32); + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + + sr = qt_pow_gamma[sr]; + sg = qt_pow_gamma[sg]; + sb = qt_pow_gamma[sb]; + bool opaque_src = (qAlpha(color) == 255); +#endif + + if (!clip) { + quint32 *dest = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x; + while (mapHeight--) { + for (int i = 0; i < mapWidth; ++i) { + const int coverage = map[i]; + + if (coverage == 0) { + // nothing + } else if (coverage == 255) { + dest[i] = c; + } else { +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && opaque_src + && qAlpha(dest[i]) == 255) { + grayBlendPixel(dest+i, coverage, sr, sg, sb); + } else +#endif + { + int ialpha = 255 - coverage; + dest[i] = INTERPOLATE_PIXEL_255(c, coverage, dest[i], ialpha); + } + } + } + dest += destStride; + map += mapStride; + } + } else { + int bottom = qMin(y + mapHeight, rasterBuffer->height()); + + int top = qMax(y, 0); + map += (top - y) * mapStride; + + const_cast<QClipData *>(clip)->initialize(); + for (int yp = top; yp<bottom; ++yp) { + const QClipData::ClipLine &line = clip->m_clipLines[yp]; + + quint32 *dest = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp)); + + for (int i=0; i<line.count; ++i) { + const QSpan &clip = line.spans[i]; + + int start = qMax<int>(x, clip.x); + int end = qMin<int>(x + mapWidth, clip.x + clip.len); + + for (int xp=start; xp<end; ++xp) { + const int coverage = map[xp - x]; + + if (coverage == 0) { + // nothing + } else if (coverage == 255) { + dest[xp] = c; + } else { +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + if (QSysInfo::WindowsVersion >= QSysInfo::WV_XP && opaque_src + && qAlpha(dest[xp]) == 255) { + grayBlendPixel(dest+xp, coverage, sr, sg, sb); + } else +#endif + { + int ialpha = 255 - coverage; + dest[xp] = INTERPOLATE_PIXEL_255(c, coverage, dest[xp], ialpha); + } + } + + } // for (i -> line.count) + } // for (yp -> bottom) + map += mapStride; + } + } +} + +static void qt_alphargbblit_quint32(QRasterBuffer *rasterBuffer, + int x, int y, quint32 color, + const uint *src, int mapWidth, int mapHeight, int srcStride, + const QClipData *clip) +{ + const quint32 c = color; + + int sr = qRed(color); + int sg = qGreen(color); + int sb = qBlue(color); + int sa = qAlpha(color); + + sr = qt_pow_rgb_gamma[sr]; + sg = qt_pow_rgb_gamma[sg]; + sb = qt_pow_rgb_gamma[sb]; + + if (sa == 0) + return; + + if (!clip) { + quint32 *dst = reinterpret_cast<quint32*>(rasterBuffer->scanLine(y)) + x; + const int destStride = rasterBuffer->bytesPerLine() / sizeof(quint32); + while (mapHeight--) { + for (int i = 0; i < mapWidth; ++i) { + const uint coverage = src[i]; + if (coverage == 0xffffffff) { + dst[i] = c; + } else if (coverage != 0xff000000) { + rgbBlendPixel(dst+i, coverage, sr, sg, sb); + } + } + + dst += destStride; + src += srcStride; + } + } else { + int bottom = qMin(y + mapHeight, rasterBuffer->height()); + + int top = qMax(y, 0); + src += (top - y) * srcStride; + + const_cast<QClipData *>(clip)->initialize(); + for (int yp = top; yp<bottom; ++yp) { + const QClipData::ClipLine &line = clip->m_clipLines[yp]; + + quint32 *dst = reinterpret_cast<quint32 *>(rasterBuffer->scanLine(yp)); + + for (int i=0; i<line.count; ++i) { + const QSpan &clip = line.spans[i]; + + int start = qMax<int>(x, clip.x); + int end = qMin<int>(x + mapWidth, clip.x + clip.len); + + for (int xp=start; xp<end; ++xp) { + const uint coverage = src[xp - x]; + if (coverage == 0xffffffff) { + dst[xp] = c; + } else if (coverage != 0xff000000) { + rgbBlendPixel(dst+xp, coverage, sr, sg, sb); + } + } + } // for (i -> line.count) + src += srcStride; + } // for (yp -> bottom) + + } +} + +template <class T> +inline void qt_rectfill_template(QRasterBuffer *rasterBuffer, + int x, int y, int width, int height, + quint32 color, T dummy = 0) +{ + Q_UNUSED(dummy); + + qt_rectfill<T>(reinterpret_cast<T*>(rasterBuffer->buffer()), + qt_colorConvert<T, quint32p>(quint32p::fromRawData(color), 0), + x, y, width, height, rasterBuffer->bytesPerLine()); +} + +#define QT_RECTFILL(T) \ + inline static void qt_rectfill_##T(QRasterBuffer *rasterBuffer, \ + int x, int y, int width, int height, \ + quint32 color) \ + { \ + qt_rectfill_template<T>(rasterBuffer, x, y, width, height, color); \ + } + +QT_RECTFILL(quint32) +QT_RECTFILL(quint16) +QT_RECTFILL(qargb8565) +QT_RECTFILL(qrgb666) +QT_RECTFILL(qargb6666) +QT_RECTFILL(qrgb555) +QT_RECTFILL(qargb8555) +QT_RECTFILL(qrgb888) +QT_RECTFILL(qrgb444) +QT_RECTFILL(qargb4444) +#undef QT_RECTFILL + +inline static void qt_rectfill_nonpremul_quint32(QRasterBuffer *rasterBuffer, + int x, int y, int width, int height, + quint32 color) +{ + qt_rectfill<quint32>(reinterpret_cast<quint32 *>(rasterBuffer->buffer()), + INV_PREMUL(color), x, y, width, height, rasterBuffer->bytesPerLine()); +} + + +// Map table for destination image format. Contains function pointers +// for blends of various types unto the destination + +DrawHelper qDrawHelper[QImage::NImageFormats] = +{ + // Format_Invalid, + { 0, 0, 0, 0, 0, 0 }, + // Format_Mono, + { + blend_color_generic, + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, 0 + }, + // Format_MonoLSB, + { + blend_color_generic, + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, 0 + }, + // Format_Indexed8, + { + blend_color_generic, + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, 0 + }, + // Format_RGB32, + { + blend_color_argb, + qt_gradient_quint32, + qt_bitmapblit_quint32, + qt_alphamapblit_quint32, + qt_alphargbblit_quint32, + qt_rectfill_quint32 + }, + // Format_ARGB32, + { + blend_color_generic, + qt_gradient_quint32, + qt_bitmapblit_quint32, + qt_alphamapblit_quint32, + qt_alphargbblit_quint32, + qt_rectfill_nonpremul_quint32 + }, + // Format_ARGB32_Premultiplied + { + blend_color_argb, + qt_gradient_quint32, + qt_bitmapblit_quint32, + qt_alphamapblit_quint32, + qt_alphargbblit_quint32, + qt_rectfill_quint32 + }, + // Format_RGB16 + { + blend_color_rgb16, + qt_gradient_quint16, + qt_bitmapblit_quint16, + qt_alphamapblit_quint16, + 0, + qt_rectfill_quint16 + }, + // Format_ARGB8565_Premultiplied + { + SPANFUNC_POINTER_BLENDCOLOR(qargb8565), + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, + qt_rectfill_qargb8565 + }, + // Format_RGB666 + { + SPANFUNC_POINTER_BLENDCOLOR(qrgb666), + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, + qt_rectfill_qrgb666 + }, + // Format_ARGB6666_Premultiplied + { + SPANFUNC_POINTER_BLENDCOLOR(qargb6666), + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, + qt_rectfill_qargb6666 + }, + // Format_RGB555 + { + SPANFUNC_POINTER_BLENDCOLOR(qrgb555), + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, + qt_rectfill_qrgb555 + }, + // Format_ARGB8555_Premultiplied + { + SPANFUNC_POINTER_BLENDCOLOR(qargb8555), + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, + qt_rectfill_qargb8555 + }, + // Format_RGB888 + { + SPANFUNC_POINTER_BLENDCOLOR(qrgb888), + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, + qt_rectfill_qrgb888 + }, + // Format_RGB444 + { + SPANFUNC_POINTER_BLENDCOLOR(qrgb444), + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, + qt_rectfill_qrgb444 + }, + // Format_ARGB4444_Premultiplied + { + SPANFUNC_POINTER_BLENDCOLOR(qargb4444), + SPANFUNC_POINTER(blend_src_generic, RegularSpans), + 0, 0, 0, + qt_rectfill_qargb4444 + } +}; + +#if defined (Q_WS_QWS) && !defined(QT_NO_RASTERCALLBACKS) +DrawHelper qDrawHelperCallback[QImage::NImageFormats] = +{ + // Format_Invalid, + { 0, 0, 0, 0, 0, 0 }, + // Format_Mono, + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_MonoLSB, + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_Indexed8, + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_RGB32, + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_ARGB32, + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_ARGB32_Premultiplied + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_RGB16 + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_ARGB8565_Premultiplied + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_RGB666 + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_ARGB6666_Premultiplied + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_RGB555 + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_ARGB8555_Premultiplied + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_RGB888 + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_RGB444 + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + }, + // Format_ARGB4444_Premultiplied + { + blend_color_generic_callback, + blend_src_generic<CallbackSpans>, + 0, 0, 0, 0 + } +}; +#endif + + + +#if defined(Q_CC_MSVC) && !defined(_MIPS_) +template <class DST, class SRC> +inline void qt_memfill_template(DST *dest, SRC color, int count) +{ + const DST c = qt_colorConvert<DST, SRC>(color, 0); + while (count--) + *dest++ = c; +} + +#else + +template <class DST, class SRC> +inline void qt_memfill_template(DST *dest, SRC color, int count) +{ + const DST c = qt_colorConvert<DST, SRC>(color, 0); + int n = (count + 7) / 8; + switch (count & 0x07) + { + case 0: do { *dest++ = c; + case 7: *dest++ = c; + case 6: *dest++ = c; + case 5: *dest++ = c; + case 4: *dest++ = c; + case 3: *dest++ = c; + case 2: *dest++ = c; + case 1: *dest++ = c; + } while (--n > 0); + } +} + +template <> +inline void qt_memfill_template(quint16 *dest, quint16 value, int count) +{ + if (count < 3) { + switch (count) { + case 2: *dest++ = value; + case 1: *dest = value; + } + return; + } + + const int align = (quintptr)(dest) & 0x3; + switch (align) { + case 2: *dest++ = value; --count; + } + + const quint32 value32 = (value << 16) | value; + qt_memfill(reinterpret_cast<quint32*>(dest), value32, count / 2); + if (count & 0x1) + dest[count - 1] = value; +} +#endif + +static void qt_memfill_quint16(quint16 *dest, quint16 color, int count) +{ + qt_memfill_template<quint16, quint16>(dest, color, count); +} + +typedef void (*qt_memfill32_func)(quint32 *dest, quint32 value, int count); +typedef void (*qt_memfill16_func)(quint16 *dest, quint16 value, int count); +static void qt_memfill32_setup(quint32 *dest, quint32 value, int count); +static void qt_memfill16_setup(quint16 *dest, quint16 value, int count); + +qt_memfill32_func qt_memfill32 = qt_memfill32_setup; +qt_memfill16_func qt_memfill16 = qt_memfill16_setup; + +void qInitDrawhelperAsm() +{ + + qt_memfill32 = qt_memfill_template<quint32, quint32>; + qt_memfill16 = qt_memfill_quint16; //qt_memfill_template<quint16, quint16>; + + CompositionFunction *functionForModeAsm = 0; + CompositionFunctionSolid *functionForModeSolidAsm = 0; + + const uint features = qDetectCPUFeatures(); + if (false) { +#ifdef QT_HAVE_SSE2 + } else if (features & SSE2) { + qt_memfill32 = qt_memfill32_sse2; + qt_memfill16 = qt_memfill16_sse2; + qDrawHelper[QImage::Format_RGB32].bitmapBlit = qt_bitmapblit32_sse2; + qDrawHelper[QImage::Format_ARGB32].bitmapBlit = qt_bitmapblit32_sse2; + qDrawHelper[QImage::Format_ARGB32_Premultiplied].bitmapBlit = qt_bitmapblit32_sse2; + qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse2; +#endif +#ifdef QT_HAVE_SSE + } else if (features & SSE) { +// qt_memfill32 = qt_memfill32_sse; + qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse; +#ifdef QT_HAVE_3DNOW + if (features & MMX3DNOW) { + qt_memfill32 = qt_memfill32_sse3dnow; + qDrawHelper[QImage::Format_RGB16].bitmapBlit = qt_bitmapblit16_sse3dnow; + } +#endif +#endif // SSE + } +#ifdef QT_HAVE_MMX + if (features & MMX) { + functionForModeAsm = qt_functionForMode_MMX; + + functionForModeSolidAsm = qt_functionForModeSolid_MMX; + qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_mmx; +#ifdef QT_HAVE_3DNOW + if (features & MMX3DNOW) { + functionForModeAsm = qt_functionForMode_MMX3DNOW; + functionForModeSolidAsm = qt_functionForModeSolid_MMX3DNOW; + qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_mmx3dnow; + } +#endif // 3DNOW + + extern void qt_blend_rgb32_on_rgb32_mmx(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + extern void qt_blend_argb32_on_argb32_mmx(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mmx; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_mmx; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mmx; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_mmx; + + } +#endif // MMX + +#ifdef QT_HAVE_SSE + if (features & SSE) { + extern void qt_blend_rgb32_on_rgb32_sse(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + extern void qt_blend_argb32_on_argb32_sse(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse; + } +#endif // SSE + +#ifdef QT_HAVE_SSE2 + if (features & SSE2) { + extern void qt_blend_rgb32_on_rgb32_sse2(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + extern void qt_blend_argb32_on_argb32_sse2(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_sse2; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_sse2; + } + +#ifdef QT_HAVE_SSSE3 + if (features & SSSE3) { + extern void qt_blend_argb32_on_argb32_ssse3(uchar *destPixels, int dbpl, + const uchar *srcPixels, int sbpl, + int w, int h, + int const_alpha); + + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; + } +#endif // SSSE3 + +#endif // SSE2 + +#ifdef QT_HAVE_SSE + if (features & SSE) { + functionForModeAsm = qt_functionForMode_SSE; + functionForModeSolidAsm = qt_functionForModeSolid_SSE; + qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_sse; +#ifdef QT_HAVE_3DNOW + if (features & MMX3DNOW) { + functionForModeAsm = qt_functionForMode_SSE3DNOW; + functionForModeSolidAsm = qt_functionForModeSolid_SSE3DNOW; + qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_sse3dnow; + } +#endif // 3DNOW + + +#ifdef QT_HAVE_SSE2 + if (features & SSE2) { + extern void QT_FASTCALL comp_func_SourceOver_sse2(uint *destPixels, + const uint *srcPixels, + int length, + uint const_alpha); + extern void QT_FASTCALL comp_func_solid_SourceOver_sse2(uint *destPixels, int length, uint color, uint const_alpha); + extern void QT_FASTCALL comp_func_Plus_sse2(uint *dst, const uint *src, int length, uint const_alpha); + extern void QT_FASTCALL comp_func_Source_sse2(uint *dst, const uint *src, int length, uint const_alpha); + + functionForModeAsm[0] = comp_func_SourceOver_sse2; + functionForModeAsm[QPainter::CompositionMode_Source] = comp_func_Source_sse2; + functionForModeAsm[QPainter::CompositionMode_Plus] = comp_func_Plus_sse2; + functionForModeSolidAsm[0] = comp_func_solid_SourceOver_sse2; + } +#endif + } +#elif defined(QT_HAVE_SSE2) + // this is the special case when SSE2 is usable but MMX/SSE is not usable (e.g.: Windows x64 + visual studio) + if (features & SSE2) { + functionForModeAsm = qt_functionForMode_onlySSE2; + functionForModeSolidAsm = qt_functionForModeSolid_onlySSE2; + } +#endif + +#ifdef QT_HAVE_IWMMXT + if (features & IWMMXT) { + functionForModeAsm = qt_functionForMode_IWMMXT; + functionForModeSolidAsm = qt_functionForModeSolid_IWMMXT; + qDrawHelper[QImage::Format_ARGB32_Premultiplied].blendColor = qt_blend_color_argb_iwmmxt; + } +#endif // IWMMXT + +#if defined(QT_HAVE_ARM_SIMD) + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_arm_simd; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_arm_simd; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_arm_simd; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_arm_simd; +#elif defined(QT_HAVE_NEON) + if (features & NEON) { + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon; + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon; + qBlendFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_rgb16_neon; + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB16] = qt_blend_rgb16_on_argb32_neon; + qBlendFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_blend_rgb16_on_rgb16_neon; + + qScaleFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_scale_image_argb32_on_rgb16_neon; + qScaleFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_scale_image_rgb16_on_rgb16_neon; + + qTransformFunctions[QImage::Format_RGB16][QImage::Format_ARGB32_Premultiplied] = qt_transform_image_argb32_on_rgb16_neon; + qTransformFunctions[QImage::Format_RGB16][QImage::Format_RGB16] = qt_transform_image_rgb16_on_rgb16_neon; + + qDrawHelper[QImage::Format_RGB16].alphamapBlit = qt_alphamapblit_quint16_neon; + + functionForMode_C[QPainter::CompositionMode_SourceOver] = qt_blend_argb32_on_argb32_scanline_neon; + functionForModeSolid_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_neon; + functionForMode_C[QPainter::CompositionMode_Plus] = comp_func_Plus_neon; + destFetchProc[QImage::Format_RGB16] = qt_destFetchRGB16_neon; + destStoreProc[QImage::Format_RGB16] = qt_destStoreRGB16_neon; + + qMemRotateFunctions[QImage::Format_RGB16][0] = qt_memrotate90_16_neon; + qMemRotateFunctions[QImage::Format_RGB16][2] = qt_memrotate270_16_neon; + qt_memfill32 = qt_memfill32_neon; + } +#endif + + if (functionForModeSolidAsm) { + const int destinationMode = QPainter::CompositionMode_Destination; + functionForModeSolidAsm[destinationMode] = functionForModeSolid_C[destinationMode]; + + // use the default qdrawhelper implementation for the + // extended composition modes + for (int mode = 12; mode < 24; ++mode) + functionForModeSolidAsm[mode] = functionForModeSolid_C[mode]; + + functionForModeSolid = functionForModeSolidAsm; + } + if (functionForModeAsm) + functionForMode = functionForModeAsm; + + qt_build_pow_tables(); +} + +static void qt_memfill32_setup(quint32 *dest, quint32 value, int count) +{ + qInitDrawhelperAsm(); + qt_memfill32(dest, value, count); +} + +static void qt_memfill16_setup(quint16 *dest, quint16 value, int count) +{ + qInitDrawhelperAsm(); + qt_memfill16(dest, value, count); +} + +#ifdef QT_QWS_DEPTH_GENERIC + +int qrgb::bpp = 0; +int qrgb::len_red = 0; +int qrgb::len_green = 0; +int qrgb::len_blue = 0; +int qrgb::len_alpha = 0; +int qrgb::off_red = 0; +int qrgb::off_green = 0; +int qrgb::off_blue = 0; +int qrgb::off_alpha = 0; + +template <typename SRC> +Q_STATIC_TEMPLATE_FUNCTION inline void qt_rectconvert_rgb(qrgb *dest, const SRC *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + quint8 *dest8 = reinterpret_cast<quint8*>(dest) + + y * dstStride + x * qrgb::bpp; + + srcStride = srcStride / sizeof(SRC) - width; + dstStride -= (width * qrgb::bpp); + + for (int j = 0; j < height; ++j) { + for (int i = 0; i < width; ++i) { + const quint32 v = qt_convertToRgb<SRC>(*src++); +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + for (int j = qrgb::bpp - 1; j >= 0; --j) + *dest8++ = (v >> (8 * j)) & 0xff; +#else + for (int j = 0; j < qrgb::bpp; ++j) + *dest8++ = (v >> (8 * j)) & 0xff; +#endif + } + + dest8 += dstStride; + src += srcStride; + } +} + +template <> +void qt_rectconvert(qrgb *dest, const quint32 *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + qt_rectconvert_rgb<quint32>(dest, src, x, y, width, height, + dstStride, srcStride); +} + +template <> +void qt_rectconvert(qrgb *dest, const quint16 *src, + int x, int y, int width, int height, + int dstStride, int srcStride) +{ + qt_rectconvert_rgb<quint16>(dest, src, x, y, width, height, + dstStride, srcStride); +} + +#endif // QT_QWS_DEPTH_GENERIC + +QT_END_NAMESPACE |