diff options
20 files changed, 1918 insertions, 490 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 85f0d2b251..32ef67763a 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -277,6 +277,16 @@ bool QImageData::checkForAlphaPixels() const bits += bytes_per_line; } } break; + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: { + uchar *bits = data; + for (int y=0; y<height && !has_alpha_pixels; ++y) { + for (int x=0; x<width; ++x) { + has_alpha_pixels |= !(((QRgba64 *)bits)[x].isOpaque()); + } + bits += bytes_per_line; + } + } break; case QImage::Format_RGB32: case QImage::Format_RGB16: @@ -288,6 +298,7 @@ bool QImageData::checkForAlphaPixels() const case QImage::Format_BGR30: case QImage::Format_RGB30: case QImage::Format_Grayscale8: + case QImage::Format_RGBX64: break; case QImage::Format_Invalid: case QImage::NImageFormats: @@ -651,11 +662,7 @@ bool QImageData::checkForAlphaPixels() const /*! \enum QImage::Format - The following image formats are available in Qt. Values from Format_ARGB8565_Premultiplied - to Format_ARGB4444_Premultiplied were added in Qt 4.4. Values Format_RGBX8888, Format_RGBA8888 - and Format_RGBA8888_Premultiplied were added in Qt 5.2. Values Format_BGR30, Format_A2BGR30_Premultiplied, - Format_RGB30, Format_A2RGB30_Premultiplied were added in Qt 5.4. Format_Alpha8 and Format_Grayscale8 - were added in Qt 5.5. + The following image formats are available in Qt. See the notes after the table. \value Format_Invalid The image is invalid. @@ -699,29 +706,32 @@ bool QImageData::checkForAlphaPixels() const \value Format_ARGB4444_Premultiplied The image is stored using a premultiplied 16-bit ARGB format (4-4-4-4). \value Format_RGBX8888 The image is stored using a 32-bit byte-ordered RGB(x) format (8-8-8-8). - This is the same as the Format_RGBA8888 except alpha must always be 255. + This is the same as the Format_RGBA8888 except alpha must always be 255. (added in Qt 5.2) \value Format_RGBA8888 The image is stored using a 32-bit byte-ordered RGBA format (8-8-8-8). Unlike ARGB32 this is a byte-ordered format, which means the 32bit encoding differs between big endian and little endian architectures, being respectively (0xRRGGBBAA) and (0xAABBGGRR). The order of the colors - is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA. + is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA. (added in Qt 5.2) \value Format_RGBA8888_Premultiplied The image is stored using a - premultiplied 32-bit byte-ordered RGBA format (8-8-8-8). - \value Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10). - \value Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10). - \value Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10). - \value Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10). - \value Format_Alpha8 The image is stored using an 8-bit alpha only format. - \value Format_Grayscale8 The image is stored using an 8-bit grayscale format. + premultiplied 32-bit byte-ordered RGBA format (8-8-8-8). (added in Qt 5.2) + \value Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10). (added in Qt 5.4) + \value Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10). (added in Qt 5.4) + \value Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10). (added in Qt 5.4) + \value Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10). (added in Qt 5.4) + \value Format_Alpha8 The image is stored using an 8-bit alpha only format. (added in Qt 5.5) + \value Format_Grayscale8 The image is stored using an 8-bit grayscale format. (added in Qt 5.5) + \value Format_RGBX64 The image is stored using a 64-bit halfword-ordered RGB(x) format (16-16-16-16). + This is the same as the Format_RGBX64 except alpha must always be 65535. (added in Qt 5.12) + \value Format_RGBA64 The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16). (added in Qt 5.12) + \value Format_RGBA64_Premultiplied The image is stored using a premultiplied 64-bit halfword-ordered + RGBA format (16-16-16-16). (added in Qt 5.12) \note Drawing into a QImage with QImage::Format_Indexed8 is not supported. - \note Do not render into ARGB32 images using QPainter. Using - QImage::Format_ARGB32_Premultiplied is significantly faster. - - \note Formats with more than 8 bit per color channel will only be processed by the raster engine using 8 bit - per color. + \note Avoid most rendering directly to most of these formats using QPainter. Rendering + is best optimized to the \c Format_RGB32 and \c Format_ARGB32_Premultiplied formats, and secondarily for rendering to the + \c Format_RGB16, \c Format_RGBX8888, \c Format_RGBA8888_Premultiplied, \c Format_RGBX64 and \c Format_RGBA64_Premultiplied formats \sa format(), convertToFormat() */ @@ -1708,6 +1718,10 @@ void QImage::fill(uint pixel) qt_rectfill<quint24>(reinterpret_cast<quint24*>(d->data), pixel, 0, 0, d->width, d->height, d->bytes_per_line); return; + } else if (d->depth == 64) { + qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), QRgba64::fromArgb32(pixel), + 0, 0, d->width, d->height, d->bytes_per_line); + return; } if (d->format == Format_RGB32) @@ -1815,6 +1829,12 @@ void QImage::fill(const QColor &color) else fill((uint) 0); break; + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), color.rgba64(), + 0, 0, d->width, d->height, d->bytes_per_line); + break; default: { QPainter p(this); p.setCompositionMode(QPainter::CompositionMode_Source); @@ -1838,7 +1858,8 @@ void QImage::fill(const QColor &color) changed. If the image has a premultiplied alpha channel, the image is first - converted to ARGB32 to be inverted and then converted back. + converted to an unpremultiplied image format to be inverted and + then converted back. \sa {QImage#Image Transformations}{Image Transformations} */ @@ -1857,8 +1878,13 @@ void QImage::invertPixels(InvertMode mode) QImage::Format originalFormat = d->format; // Inverting premultiplied pixels would produce invalid image data. if (hasAlphaChannel() && qPixelLayouts[d->format].premultiplied) { - if (!d->convertInPlace(QImage::Format_ARGB32, 0)) - *this = convertToFormat(QImage::Format_ARGB32); + if (depth() > 32) { + if (!d->convertInPlace(QImage::Format_RGBA64, 0)) + *this = convertToFormat(QImage::Format_RGBA64); + } else { + if (!d->convertInPlace(QImage::Format_ARGB32, 0)) + *this = convertToFormat(QImage::Format_ARGB32); + } } if (depth() < 32) { @@ -1871,6 +1897,20 @@ void QImage::invertPixels(InvertMode mode) *sl++ ^= 0xff; sl += pad; } + } + else if (depth() == 64) { + quint16 *p = (quint16*)d->data; + quint16 *end = (quint16*)(d->data + d->nbytes); + quint16 xorbits = 0xffff; + while (p < end) { + *p++ ^= xorbits; + *p++ ^= xorbits; + *p++ ^= xorbits; + if (mode == InvertRgba) + *p++ ^= xorbits; + else + p++; + } } else { quint32 *p = (quint32*)d->data; quint32 *end = (quint32*)(d->data + d->nbytes); @@ -1987,6 +2027,26 @@ QImage::Format QImage::format() const \sa {Image Formats} */ +static bool highColorPrecision(QImage::Format format) +{ + // Formats with higher color precision than ARGB32_Premultiplied. + switch (format) { + case QImage::Format_ARGB32: + case QImage::Format_RGBA8888: + case QImage::Format_BGR30: + case QImage::Format_RGB30: + case QImage::Format_A2BGR30_Premultiplied: + case QImage::Format_A2RGB30_Premultiplied: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + return true; + default: + break; + } + return false; +} + /*! \internal */ @@ -1999,8 +2059,18 @@ QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags fl return QImage(); Image_Converter converter = qimage_converter_map[d->format][format]; - if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8) - converter = convert_generic; + if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8) { + if (highColorPrecision(format) && highColorPrecision(d->format)) { + // Convert over RGBA64_Premultiplied + if (format == QImage::Format_RGBA64_Premultiplied) + converter = convert_generic_to_rgb64; + else { + Q_ASSERT(d->format != QImage::Format_RGBA64_Premultiplied); + return convertToFormat(Format_RGBA64_Premultiplied, flags).convertToFormat(format, flags); + } + } else + converter = convert_generic; + } if (converter) { QImage image(d->width, d->height, format); @@ -2298,6 +2368,10 @@ QRgb QImage::pixel(int x, int y) const return qConvertA2rgb30ToArgb32<PixelOrderRGB>(reinterpret_cast<const quint32 *>(s)[x]); case Format_RGB16: return qConvertRgb16To32(reinterpret_cast<const quint16 *>(s)[x]); + case Format_RGBX64: + case Format_RGBA64: // Match ARGB32 behavior. + case Format_RGBA64_Premultiplied: + return reinterpret_cast<const QRgba64 *>(s)[x].toArgb32(); default: break; } @@ -2447,6 +2521,11 @@ QColor QImage::pixelColor(int x, int y) const case Format_A2RGB30_Premultiplied: c = qConvertA2rgb30ToRgb64<PixelOrderRGB>(reinterpret_cast<const quint32 *>(s)[x]); break; + case Format_RGBX64: + case Format_RGBA64: + case Format_RGBA64_Premultiplied: + c = reinterpret_cast<const QRgba64 *>(s)[x]; + break; default: c = QRgba64::fromArgb32(pixel(x, y)); break; @@ -2517,6 +2596,14 @@ void QImage::setPixelColor(int x, int y, const QColor &color) case Format_A2RGB30_Premultiplied: ((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderRGB>(c); return; + case Format_RGBX64: + ((QRgba64 *)s)[x] = color.rgba64(); + ((QRgba64 *)s)[x].setAlpha(65535); + return; + case Format_RGBA64: + case Format_RGBA64_Premultiplied: + ((QRgba64 *)s)[x] = color.rgba64(); + return; default: setPixel(x, y, c.toArgb32()); return; @@ -3100,6 +3187,9 @@ inline void do_mirror(QImageData *dst, QImageData *src, bool horizontal, bool ve } switch (depth) { + case 64: + do_mirror_data<quint64>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h); + break; case 32: do_mirror_data<quint32>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h); break; @@ -3308,6 +3398,23 @@ QImage QImage::rgbSwapped_helper() const } } break; + case Format_RGBX64: + case Format_RGBA64: + case Format_RGBA64_Premultiplied: + res = QImage(d->width, d->height, d->format); + QIMAGE_SANITYCHECK_MEMORY(res); + for (int i = 0; i < d->height; i++) { + QRgba64 *q = reinterpret_cast<QRgba64 *>(res.scanLine(i)); + const QRgba64 *p = reinterpret_cast<const QRgba64 *>(constScanLine(i)); + const QRgba64 *end = p + d->width; + while (p < end) { + QRgba64 c = *p; + *q = QRgba64::fromRgba64(c.blue(), c.green(), c.red(), c.alpha()); + p++; + q++; + } + } + break; default: res = QImage(d->width, d->height, d->format); rgbSwapped_generic(d->width, d->height, this, &res, &qPixelLayouts[d->format]); @@ -3400,6 +3507,19 @@ void QImage::rgbSwapped_inplace() } } break; + case Format_RGBX64: + case Format_RGBA64: + case Format_RGBA64_Premultiplied: + for (int i = 0; i < d->height; i++) { + QRgba64 *p = reinterpret_cast<QRgba64 *>(scanLine(i)); + QRgba64 *end = p + d->width; + while (p < end) { + QRgba64 c = *p; + *p = QRgba64::fromRgba64(c.blue(), c.green(), c.red(), c.alpha()); + p++; + } + } + break; default: rgbSwapped_generic(d->width, d->height, this, this, &qPixelLayouts[d->format]); break; @@ -4473,6 +4593,9 @@ int QImage::bitPlaneCount() const case QImage::Format_RGB444: bpc = 12; break; + case QImage::Format_RGBX64: + bpc = 48; + break; default: bpc = qt_depthForFormat(d->format); break; @@ -5171,6 +5294,45 @@ static Q_CONSTEXPR QPixelFormat pixelformats[] = { /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, /*INTERPRETATION*/ QPixelFormat::UnsignedByte, /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBX64: + QPixelFormat(QPixelFormat::RGB, + /*RED*/ 16, + /*GREEN*/ 16, + /*BLUE*/ 16, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 16, + /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtEnd, + /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, + /*INTERPRETATION*/ QPixelFormat::UnsignedShort, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBA64: + QPixelFormat(QPixelFormat::RGB, + /*RED*/ 16, + /*GREEN*/ 16, + /*BLUE*/ 16, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 16, + /*ALPHA USAGE*/ QPixelFormat::UsesAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtEnd, + /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, + /*INTERPRETATION*/ QPixelFormat::UnsignedShort, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBA64_Premultiplied: + QPixelFormat(QPixelFormat::RGB, + /*RED*/ 16, + /*GREEN*/ 16, + /*BLUE*/ 16, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 16, + /*ALPHA USAGE*/ QPixelFormat::UsesAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtEnd, + /*PREMULTIPLIED*/ QPixelFormat::Premultiplied, + /*INTERPRETATION*/ QPixelFormat::UnsignedShort, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), }; Q_STATIC_ASSERT(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats); diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 1f39569dda..7764c19452 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -125,6 +125,9 @@ public: Format_A2RGB30_Premultiplied, Format_Alpha8, Format_Grayscale8, + Format_RGBX64, + Format_RGBA64, + Format_RGBA64_Premultiplied, #if 0 // reserved for future use Format_Grayscale16, diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index e1d3101174..dd21ade2fd 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -179,7 +179,7 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio store = storeRGB32FromARGB32PM; } } - if ((src->format == QImage::Format_ARGB32 || src->format == QImage::Format_RGBA8888) && + if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied && !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) { // Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format. fetch = qPixelLayouts[src->format + 1].fetchToARGB32PM; @@ -212,6 +212,26 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio } } +void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(dest->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(src->format > QImage::Format_Indexed8); + const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; + const uchar *srcData = src->data; + uchar *destData = dest->data; + + const FetchAndConvertPixelsFunc64 fetch = srcLayout->fetchToRGBA64PM; + + for (int y = 0; y < src->height; ++y) { + const QRgba64 *ptr = fetch((QRgba64*)destData, srcData, 0, src->width, nullptr, nullptr); + if (ptr != (const QRgba64*)destData) { + memcpy(destData, ptr, dest->bytes_per_line); + } + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; + } +} + bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags flags) { // Cannot be used with indexed formats or between formats with different pixel depths. @@ -226,6 +246,8 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im const QPixelLayout *destLayout = &qPixelLayouts[dst_format]; uchar *srcData = data->data; + Q_ASSERT(srcLayout->bpp == destLayout->bpp); + Q_ASSERT(srcLayout->bpp != QPixelLayout::BPP64); FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM; ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM; if (!srcLayout->hasAlphaChannel && destLayout->storeFromRGB32) { @@ -243,7 +265,7 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im store = storeRGB32FromARGB32PM; } } - if ((data->format == QImage::Format_ARGB32 || data->format == QImage::Format_RGBA8888) && + if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied && !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) { // Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format. fetch = qPixelLayouts[data->format + 1].fetchToARGB32PM; @@ -282,20 +304,15 @@ static void convert_passthrough(QImageData *dest, const QImageData *src, Qt::Ima Q_ASSERT(src->width == dest->width); Q_ASSERT(src->height == dest->height); - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const quint32 *src_data = (quint32 *) src->data; - quint32 *dest_data = (quint32 *) dest->data; + const int src_bpl = src->bytes_per_line; + const int dest_bpl = dest->bytes_per_line; + const uchar *src_data = src->data; + uchar *dest_data = dest->data; for (int i = 0; i < src->height; ++i) { - const quint32 *end = src_data + src->width; - while (src_data < end) { - *dest_data = *src_data; - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; + memcpy(dest_data, src_data, src_bpl); + src_data += src_bpl; + dest_data += dest_bpl; } } @@ -1138,6 +1155,270 @@ static bool mask_alpha_converter_rgbx_inplace(QImageData *data, Qt::ImageConvers #endif } +template<bool RGBA> +static void convert_RGBA64_to_ARGB32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64); + Q_ASSERT(RGBA || dest->format == QImage::Format_ARGB32); + Q_ASSERT(!RGBA || dest->format == QImage::Format_RGBA8888); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const uchar *srcData = src->data; + uchar *destData = dest->data; + + for (int i = 0; i < src->height; ++i) { + uint *d = reinterpret_cast<uint *>(destData); + const QRgba64 *s = reinterpret_cast<const QRgba64 *>(srcData); + qt_convertRGBA64ToARGB32<RGBA>(d, s, src->width); + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; + } +} + +template<bool RGBA> +static void convert_RGBA64PM_to_ARGB32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(RGBA || dest->format == QImage::Format_ARGB32); + Q_ASSERT(!RGBA || dest->format == QImage::Format_RGBA8888); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + uint *dest_data = reinterpret_cast<uint *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + QRgba64 s = src_data->unpremultiplied(); + *dest_data = RGBA ? ARGB2RGBA(s.toArgb32()) : s.toArgb32(); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +template<bool RGBA> +static void convert_ARGB32_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(RGBA || src->format == QImage::Format_ARGB32); + Q_ASSERT(!RGBA || src->format == QImage::Format_RGBA8888); + Q_ASSERT(dest->format == QImage::Format_RGBA64); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 2) - src->width; + const int dest_pad = (dest->bytes_per_line >> 3) - dest->width; + const uint *src_data = reinterpret_cast<const uint *>(src->data); + QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const uint *end = src_data + src->width; + while (src_data < end) { + if (RGBA) + *dest_data = QRgba64::fromArgb32(RGBA2ARGB(*src_data)); + else + *dest_data = QRgba64::fromArgb32(*src_data); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +template<QtPixelOrder PixelOrder> +static void convert_RGBA64PM_to_RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_RGB30 || dest->format == QImage::Format_BGR30); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + uint *dest_data = reinterpret_cast<uint *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + *dest_data = 0xc0000000 | qConvertRgb64ToRgb30<PixelOrder>(src_data->unpremultiplied()); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +template<QtPixelOrder PixelOrder> +static void convert_RGBA64PM_to_A2RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_A2RGB30_Premultiplied + || dest->format == QImage::Format_A2BGR30_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + uint *dest_data = reinterpret_cast<uint *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + *dest_data = qConvertRgb64ToRgb30<PixelOrder>(*src_data); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static void convert_RGBA64_to_RGBx64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64); + Q_ASSERT(dest->format == QImage::Format_RGBX64); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 3) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + *dest_data = *src_data; + dest_data->setAlpha(65535); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static bool convert_RGBA64_to_RGBx64_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGBA64); + + const int pad = (data->bytes_per_line >> 3) - data->width; + QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data); + + for (int i = 0; i < data->height; ++i) { + const QRgba64 *end = rgb_data + data->width; + while (rgb_data < end) { + rgb_data->setAlpha(65535); + ++rgb_data; + } + rgb_data += pad; + } + data->format = QImage::Format_RGBX64; + return true; +} + +static void convert_RGBA64_to_RGBA64PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64); + Q_ASSERT(dest->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 3) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + *dest_data = src_data->premultiplied(); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +static bool convert_RGBA64_to_RGBA64PM_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGBA64); + + const int pad = (data->bytes_per_line >> 3) - data->width; + QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data); + + for (int i = 0; i < data->height; ++i) { + const QRgba64 *end = rgb_data + data->width; + while (rgb_data < end) { + *rgb_data = rgb_data->premultiplied(); + ++rgb_data; + } + rgb_data += pad; + } + data->format = QImage::Format_RGBA64_Premultiplied; + return true; +} + +template<bool MaskAlpha> +static void convert_RGBA64PM_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_RGBA64 || dest->format == QImage::Format_RGBX64); + Q_ASSERT(src->width == dest->width); + Q_ASSERT(src->height == dest->height); + + const int src_pad = (src->bytes_per_line >> 3) - src->width; + const int dest_pad = (dest->bytes_per_line >> 3) - dest->width; + const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data); + QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba64 *end = src_data + src->width; + while (src_data < end) { + *dest_data = src_data->unpremultiplied(); + if (MaskAlpha) + dest_data->setAlpha(65535); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +template<bool MaskAlpha> +static bool convert_RGBA64PM_to_RGBA64_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGBA64_Premultiplied); + + const int pad = (data->bytes_per_line >> 3) - data->width; + QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data); + + for (int i = 0; i < data->height; ++i) { + const QRgba64 *end = rgb_data + data->width; + while (rgb_data < end) { + *rgb_data = rgb_data->unpremultiplied(); + if (MaskAlpha) + rgb_data->setAlpha(65535); + ++rgb_data; + } + rgb_data += pad; + } + data->format = MaskAlpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64; + return true; +} + static QVector<QRgb> fix_color_table(const QVector<QRgb> &ctbl, QImage::Format format) { QVector<QRgb> colorTable = ctbl; @@ -1982,7 +2263,7 @@ static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageCo Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = { { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, @@ -2003,7 +2284,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Mono { @@ -2025,7 +2306,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_MonoLSB { @@ -2050,6 +2331,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, 0, 0, convert_Indexed8_to_Alpha8, convert_Indexed8_to_Grayscale8, + 0, 0, 0 }, // Format_Indexed8 { @@ -2076,7 +2358,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, convert_RGB_to_RGB30<PixelOrderRGB>, 0, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_RGB32 { @@ -2103,7 +2386,10 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, convert_RGB_to_RGB30<PixelOrderRGB>, 0, - 0, 0 + 0, 0, + 0, + convert_ARGB32_to_RGBA64<false>, + 0 }, // Format_ARGB32 { @@ -2126,11 +2412,9 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, convert_ARGB_to_RGBA, - 0, - 0, - 0, - 0, - 0, 0 + 0, 0, 0, 0, + 0, 0, + 0, 0, 0 }, // Format_ARGB32_Premultiplied { @@ -2152,7 +2436,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB16 { @@ -2174,7 +2458,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8565_Premultiplied { @@ -2196,7 +2480,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB666 { @@ -2218,7 +2502,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB6666_Premultiplied { @@ -2240,7 +2524,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB555 { @@ -2262,7 +2546,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8555_Premultiplied { @@ -2285,7 +2569,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_RGB888_to_RGB<true>, convert_RGB888_to_RGB<true>, convert_RGB888_to_RGB<true>, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB888 { @@ -2307,7 +2591,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB444 { @@ -2328,7 +2612,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB4444_Premultiplied { 0, @@ -2348,9 +2632,11 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - mask_alpha_converter_RGBx, - mask_alpha_converter_RGBx, - 0, 0, 0, 0, 0, 0 + convert_passthrough, + convert_passthrough, + 0, 0, 0, 0, + 0, 0, + 0, 0, 0 }, // Format_RGBX8888 { 0, @@ -2372,7 +2658,10 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat mask_alpha_converter_RGBx, 0, 0, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, + 0, + convert_ARGB32_to_RGBA64<true>, + 0 }, // Format_RGBA8888 { @@ -2394,7 +2683,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGBA8888_Premultiplied { @@ -2421,7 +2710,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_passthrough, convert_BGR30_to_RGB30, convert_BGR30_to_RGB30, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_BGR30 { 0, @@ -2447,7 +2737,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, convert_A2RGB30_PM_to_RGB30<true>, convert_BGR30_to_RGB30, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_BGR30A2_Premultiplied { 0, @@ -2473,7 +2764,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_BGR30_to_RGB30, 0, convert_passthrough, - 0, 0 + 0, 0, 0, 0, 0 }, // Format_RGB30 { 0, @@ -2499,7 +2790,8 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat convert_BGR30_to_RGB30, convert_A2RGB30_PM_to_RGB30<false>, 0, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_RGB30A2_Premultiplied { 0, @@ -2519,7 +2811,7 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Alpha8 { 0, @@ -2539,20 +2831,81 @@ Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormat 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 - } // Format_Grayscale8 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }, // Format_Grayscale8 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // self + convert_passthrough, + convert_passthrough + }, // Format_RGBX64 + { + 0, + 0, + 0, + 0, + 0, + convert_RGBA64_to_ARGB32<false>, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_RGBA64_to_ARGB32<true>, + 0, + 0, 0, 0, 0, + 0, 0, + convert_RGBA64_to_RGBx64, + 0, // self + convert_RGBA64_to_RGBA64PM + }, // Format_RGBA64 + { + 0, + 0, + 0, + 0, + 0, + convert_RGBA64PM_to_ARGB32<false>, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + convert_RGBA64PM_to_ARGB32<true>, + 0, + convert_RGBA64PM_to_RGB30<PixelOrderBGR>, + convert_RGBA64PM_to_A2RGB30<PixelOrderBGR>, + convert_RGBA64PM_to_RGB30<PixelOrderRGB>, + convert_RGBA64PM_to_A2RGB30<PixelOrderRGB>, + 0, 0, + convert_RGBA64PM_to_RGBA64<true>, + convert_RGBA64PM_to_RGBA64<false>, + 0 // self + } // Format_RGBA64_Premultiplied }; InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] = { { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_Mono { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_MonoLSB { 0, @@ -2576,6 +2929,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, 0, 0, convert_Indexed8_to_Alpha8_inplace, convert_Indexed8_to_Grayscale8_inplace, + 0, 0, 0 }, // Format_Indexed8 { 0, @@ -2601,7 +2955,8 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, convert_RGB_to_RGB30_inplace<PixelOrderRGB>, 0, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_RGB32 { 0, @@ -2627,7 +2982,8 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, convert_RGB_to_RGB30_inplace<PixelOrderRGB>, 0, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_ARGB32 { 0, @@ -2649,38 +3005,36 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888_Premultiplied>, - 0, - 0, - 0, - 0, - 0, 0 + 0, 0, 0, 0, + 0, 0, + 0, 0, 0 }, // Format_ARGB32_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB16 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8565_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB666 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB6666_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB555 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB8555_Premultiplied { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB888 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGB444 { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_ARGB4444_Premultiplied { 0, @@ -2702,7 +3056,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, convert_passthrough_inplace<QImage::Format_RGBA8888>, convert_passthrough_inplace<QImage::Format_RGBA8888_Premultiplied>, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGBX8888 { 0, @@ -2724,7 +3078,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma mask_alpha_converter_rgbx_inplace, 0, 0, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGBA8888 { 0, @@ -2746,7 +3100,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, - 0, 0, 0, 0, 0, 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Format_RGBA8888_Premultiplied { 0, @@ -2772,7 +3126,8 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_passthrough_inplace<QImage::Format_A2BGR30_Premultiplied>, convert_BGR30_to_RGB30_inplace, convert_BGR30_to_A2RGB30_inplace, - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_BGR30 { 0, @@ -2798,7 +3153,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, // self convert_A2RGB30_PM_to_RGB30_inplace<true>, convert_BGR30_to_RGB30_inplace, - 0, 0 + 0, 0, 0, 0, 0 }, // Format_BGR30A2_Premultiplied { 0, @@ -2824,7 +3179,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_BGR30_to_A2RGB30_inplace, 0, // self convert_passthrough_inplace<QImage::Format_A2RGB30_Premultiplied>, - 0, 0 + 0, 0, 0, 0, 0 }, // Format_RGB30 { 0, @@ -2850,7 +3205,8 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma convert_BGR30_to_RGB30_inplace, convert_A2RGB30_PM_to_RGB30_inplace<false>, 0, // self - 0, 0 + 0, 0, + 0, 0, 0 }, // Format_RGB30A2_Premultiplied { 0, @@ -2872,11 +3228,10 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, + 0, 0, 0, 0, + 0, // self 0, - 0, - 0, - 0, - 0, 0 + 0, 0, 0 }, // Format_Alpha8 { 0, @@ -2898,12 +3253,29 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, + 0, 0, 0, 0, 0, - 0, - 0, - 0, - 0, 0 - } // Format_Grayscale8 + 0, // self + 0, 0, 0 + }, // Format_Grayscale8 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, // self + convert_passthrough_inplace<QImage::Format_RGBA64>, + convert_passthrough_inplace<QImage::Format_RGBA64_Premultiplied>, + }, // Format_RGBX64 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + convert_RGBA64_to_RGBx64_inplace, + 0, // self + convert_RGBA64_to_RGBA64PM_inplace + }, // Format_RGBA64 + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + convert_RGBA64PM_to_RGBA64_inplace<true>, + convert_RGBA64PM_to_RGBA64_inplace<false>, + 0 // self + } // Format_RGBA64_Premultiplied }; static void qInitImageConversions() diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index f5fea2ed00..2fe29a88d3 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -113,6 +113,7 @@ extern Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImag extern InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats]; void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); +void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags); void dither_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags, bool fromalpha); @@ -164,6 +165,11 @@ inline int qt_depthForFormat(QImage::Format format) case QImage::Format_RGB888: depth = 24; break; + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + depth = 64; + break; } return depth; } @@ -190,6 +196,9 @@ inline QImage::Format qt_opaqueVersion(QImage::Format format) return QImage::Format_BGR30; case QImage::Format_A2RGB30_Premultiplied: return QImage::Format_RGB30; + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + return QImage::Format_RGBX64; case QImage::Format_ARGB32_Premultiplied: case QImage::Format_ARGB32: default: @@ -214,6 +223,8 @@ inline QImage::Format qt_alphaVersion(QImage::Format format) return QImage::Format_A2BGR30_Premultiplied; case QImage::Format_RGB30: return QImage::Format_A2RGB30_Premultiplied; + case QImage::Format_RGBX64: + return QImage::Format_RGBA64_Premultiplied; default: break; } diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 42b9e71087..94479b4f8e 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -352,6 +352,24 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal ); i++; } + // Qt==ARGB==Big(ARGB)==Little(BGRA) + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + png_set_bgr(png_ptr); + } + } else if (bit_depth == 16 && (color_type & PNG_COLOR_MASK_COLOR)) { + QImage::Format format = QImage::Format_RGBA64; + if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER); + format = QImage::Format_RGBX64; + } + if (image.size() != QSize(width, height) || image.format() != format) { + image = QImage(width, height, format); + if (image.isNull()) + return; + } + png_read_update_info(png_ptr, info_ptr); + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) + png_set_swap(png_ptr); } else { // 32-bit if (bit_depth == 16) @@ -388,12 +406,12 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal if (QSysInfo::ByteOrder == QSysInfo::BigEndian) png_set_swap_alpha(png_ptr); - png_read_update_info(png_ptr, info_ptr); - } + // Qt==ARGB==Big(ARGB)==Little(BGRA) + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + png_set_bgr(png_ptr); + } - // Qt==ARGB==Big(ARGB)==Little(BGRA) - if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { - png_set_bgr(png_ptr); + png_read_update_info(png_ptr, info_ptr); } } @@ -678,6 +696,10 @@ QImage::Format QPngHandlerPrivate::readImageFormat() { // 1-bit and 8-bit color format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8; + } else if (bit_depth == 16 && (color_type & PNG_COLOR_MASK_COLOR)) { + format = QImage::Format_RGBA64; + if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + format = QImage::Format_RGBX64; } else { // 32-bit format = QImage::Format_ARGB32; @@ -843,8 +865,24 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c else color_type = PNG_COLOR_TYPE_RGB; + int bpc = 0; + switch (image.format()) { + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + bpc = 1; + break; + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + bpc = 16; + break; + default: + bpc = 8; + break; + } + png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), - image.depth() == 1 ? 1 : 8, // per channel + bpc, // per channel color_type, 0, 0, 0); // sets #channels if (gamma != 0.0) { @@ -880,13 +918,31 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c // Swap ARGB to RGBA (normal PNG format) before saving on // BigEndian machines if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - png_set_swap_alpha(png_ptr); + switch (image.format()) { + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + break; + default: + png_set_swap_alpha(png_ptr); + } } // Qt==ARGB==Big(ARGB)==Little(BGRA). But RGB888 is RGB regardless - if (QSysInfo::ByteOrder == QSysInfo::LittleEndian - && image.format() != QImage::Format_RGB888) { - png_set_bgr(png_ptr); + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + switch (image.format()) { + case QImage::Format_RGB888: + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + break; + default: + png_set_bgr(png_ptr); + } } if (off_x || off_y) { @@ -909,10 +965,32 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c if (image.depth() != 1) png_set_packing(png_ptr); - if (color_type == PNG_COLOR_TYPE_RGB && image.format() != QImage::Format_RGB888) - png_set_filler(png_ptr, 0, - QSysInfo::ByteOrder == QSysInfo::BigEndian ? - PNG_FILLER_BEFORE : PNG_FILLER_AFTER); + if (color_type == PNG_COLOR_TYPE_RGB) { + switch (image.format()) { + case QImage::Format_RGB888: + break; + case QImage::Format_RGBX8888: + case QImage::Format_RGBX64: + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + break; + default: + png_set_filler(png_ptr, 0, + QSysInfo::ByteOrder == QSysInfo::BigEndian ? + PNG_FILLER_BEFORE : PNG_FILLER_AFTER); + } + } + + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + switch (image.format()) { + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + png_set_swap(png_ptr); + break; + default: + break; + } + } if (looping >= 0 && frames_written == 0) { uchar data[13] = "NETSCAPE2.0"; @@ -940,6 +1018,10 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c case QImage::Format_RGB32: case QImage::Format_ARGB32: case QImage::Format_RGB888: + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: { png_bytep* row_pointers = new png_bytep[height]; for (int y=0; y<height; y++) @@ -948,6 +1030,17 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c delete [] row_pointers; } break; + case QImage::Format_RGBA64_Premultiplied: + { + QImage row; + png_bytep row_pointers[1]; + for (int y=0; y<height; y++) { + row = image.copy(0, y, width, 1).convertToFormat(QImage::Format_RGBA64); + row_pointers[0] = const_cast<png_bytep>(row.constScanLine(0)); + png_write_rows(png_ptr, row_pointers, 1); + } + } + break; default: { QImage::Format fmt = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32; diff --git a/src/gui/opengl/qopenglframebufferobject.cpp b/src/gui/opengl/qopenglframebufferobject.cpp index 21ed029dfc..400888cc21 100644 --- a/src/gui/opengl/qopenglframebufferobject.cpp +++ b/src/gui/opengl/qopenglframebufferobject.cpp @@ -106,6 +106,10 @@ QT_BEGIN_NAMESPACE #define GL_RGB10 0x8052 #endif +#ifndef GL_RGB16 +#define GL_RGB16 0x8054 +#endif + #ifndef GL_RGBA8 #define GL_RGBA8 0x8058 #endif @@ -114,6 +118,10 @@ QT_BEGIN_NAMESPACE #define GL_RGB10_A2 0x8059 #endif +#ifndef GL_RGBA16 +#define GL_RGBA16 0x805B +#endif + #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif @@ -127,6 +135,7 @@ QT_BEGIN_NAMESPACE #endif + /*! \class QOpenGLFramebufferObjectFormat \brief The QOpenGLFramebufferObjectFormat class specifies the format of an OpenGL @@ -522,6 +531,8 @@ void QOpenGLFramebufferObjectPrivate::initTexture(int idx) GLuint pixelType = GL_UNSIGNED_BYTE; if (color.internalFormat == GL_RGB10_A2 || color.internalFormat == GL_RGB10) pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; + else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16) + pixelType = GL_UNSIGNED_SHORT; funcs.glTexImage2D(target, 0, color.internalFormat, color.size.width(), color.size.height(), 0, GL_RGBA, pixelType, NULL); @@ -1304,6 +1315,14 @@ static inline QImage qt_gl_read_framebuffer_rgb10a2(const QSize &size, bool incl return img; } +static inline QImage qt_gl_read_framebuffer_rgba16(const QSize &size, bool include_alpha, QOpenGLContext *context) +{ + // We assume OpenGL 1.2+ or ES 3.0+ here. + QImage img(size, include_alpha ? QImage::Format_RGBA64_Premultiplied : QImage::Format_RGBX64); + context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_UNSIGNED_SHORT, img.bits()); + return img; +} + static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, bool include_alpha, bool flip) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); @@ -1318,6 +1337,10 @@ static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, return qt_gl_read_framebuffer_rgb10a2(size, false, ctx).mirrored(false, flip); case GL_RGB10_A2: return qt_gl_read_framebuffer_rgb10a2(size, include_alpha, ctx).mirrored(false, flip); + case GL_RGB16: + return qt_gl_read_framebuffer_rgba16(size, false, ctx).mirrored(false, flip); + case GL_RGBA16: + return qt_gl_read_framebuffer_rgba16(size, include_alpha, ctx).mirrored(false, flip); case GL_RGBA: case GL_RGBA8: default: @@ -1346,7 +1369,8 @@ Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, is used only when internalTextureFormat() is set to \c GL_RGB. Since Qt 5.2 the function will fall back to premultiplied RGBA8888 or RGBx8888 when reading to (A)RGB32 is not supported, and this includes OpenGL ES. Since Qt - 5.4 an A2BGR30 image is returned if the internal format is RGB10_A2. + 5.4 an A2BGR30 image is returned if the internal format is RGB10_A2, and since + Qt 5.12 a RGBA64 image is return if the internal format is RGBA16. If the rendering in the framebuffer was not done with premultiplied alpha in mind, create a wrapper QImage with a non-premultiplied format. This is necessary before diff --git a/src/gui/opengl/qopenglpaintengine.cpp b/src/gui/opengl/qopenglpaintengine.cpp index aca57d664a..3680b3c3c2 100644 --- a/src/gui/opengl/qopenglpaintengine.cpp +++ b/src/gui/opengl/qopenglpaintengine.cpp @@ -1567,6 +1567,7 @@ void QOpenGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, c switch (image.format()) { case QImage::Format_RGBA8888: case QImage::Format_ARGB32: + case QImage::Format_RGBA64: d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::NonPremultipliedImageSrc); bindOption = 0; break; diff --git a/src/gui/opengl/qopengltextureuploader.cpp b/src/gui/opengl/qopengltextureuploader.cpp index 0204d4852f..fc449d8090 100644 --- a/src/gui/opengl/qopengltextureuploader.cpp +++ b/src/gui/opengl/qopengltextureuploader.cpp @@ -61,6 +61,10 @@ #define GL_RGB10_A2 0x8059 #endif +#ifndef GL_RGBA16 +#define GL_RGBA16 0x805B +#endif + #ifndef GL_BGRA #define GL_BGRA 0x80E1 #endif @@ -193,6 +197,15 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag pixelType = GL_UNSIGNED_BYTE; targetFormat = image.format(); break; + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + externalFormat = internalFormat = GL_RGBA; + if (isOpenGL12orBetter || (context->isOpenGLES() && context->format().majorVersion() >= 3)) + internalFormat = GL_RGBA16; + pixelType = GL_UNSIGNED_SHORT; + targetFormat = image.format(); + break; case QImage::Format_Indexed8: if (sRgbBinding) { // Always needs conversion @@ -262,11 +275,15 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag targetFormat = QImage::Format_ARGB32_Premultiplied; else if (targetFormat == QImage::Format_RGBA8888) targetFormat = QImage::Format_RGBA8888_Premultiplied; + else if (targetFormat == QImage::Format_RGBA64) + targetFormat = QImage::Format_RGBA64_Premultiplied; } else { if (targetFormat == QImage::Format_ARGB32_Premultiplied) targetFormat = QImage::Format_ARGB32; else if (targetFormat == QImage::Format_RGBA8888_Premultiplied) targetFormat = QImage::Format_RGBA8888; + else if (targetFormat == QImage::Format_RGBA64_Premultiplied) + targetFormat = QImage::Format_RGBA64; } if (sRgbBinding) { diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 1110f951a9..b1424b5b0a 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -267,18 +267,19 @@ inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP32>(const uchar *src, int in return reinterpret_cast<const uint *>(src)[index]; } -template <QPixelLayout::BPP bpp> -inline const uint *QT_FASTCALL fetchPixels(uint *buffer, const uchar *src, int index, int count) +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP64>(const uchar *src, int index) { - for (int i = 0; i < count; ++i) - buffer[i] = fetchPixel<bpp>(src, index + i); - return buffer; + // We have to do the conversion in fetch to fit into a 32bit uint + QRgba64 c = reinterpret_cast<const QRgba64 *>(src)[index]; + return c.toArgb32(); } -template <> -inline const uint *QT_FASTCALL fetchPixels<QPixelLayout::BPP32>(uint *, const uchar *src, int index, int) +template <QPixelLayout::BPP bpp> +static quint64 QT_FASTCALL fetchPixel64(const uchar *src, int index) { - return reinterpret_cast<const uint *>(src) + index; + Q_STATIC_ASSERT(bpp != QPixelLayout::BPP64); + return fetchPixel<bpp>(src, index); } template <QPixelLayout::BPP width> static @@ -296,16 +297,6 @@ inline void QT_FASTCALL storePixel<QPixelLayout::BPP24>(uchar *dest, int index, reinterpret_cast<quint24 *>(dest)[index] = quint24(pixel); } -static FetchPixelsFunc qFetchPixels[QPixelLayout::BPPCount] = { - 0, // BPPNone - fetchPixels<QPixelLayout::BPP1MSB>, // BPP1MSB - fetchPixels<QPixelLayout::BPP1LSB>, // BPP1LSB - fetchPixels<QPixelLayout::BPP8>, // BPP8 - fetchPixels<QPixelLayout::BPP16>, // BPP16 - fetchPixels<QPixelLayout::BPP24>, // BPP24 - fetchPixels<QPixelLayout::BPP32> // BPP32 -}; - typedef uint (QT_FASTCALL *FetchPixelFunc)(const uchar *src, int index); static const FetchPixelFunc qFetchPixel[QPixelLayout::BPPCount] = { @@ -315,7 +306,8 @@ static const FetchPixelFunc qFetchPixel[QPixelLayout::BPPCount] = { fetchPixel<QPixelLayout::BPP8>, // BPP8 fetchPixel<QPixelLayout::BPP16>, // BPP16 fetchPixel<QPixelLayout::BPP24>, // BPP24 - fetchPixel<QPixelLayout::BPP32> // BPP32 + fetchPixel<QPixelLayout::BPP32>, // BPP32 + fetchPixel<QPixelLayout::BPP64> // BPP64 }; template<QImage::Format Format> @@ -350,16 +342,20 @@ static void QT_FASTCALL convertToRGB32(uint *buffer, int count, const QVector<QR buffer[i] = convertPixelToRGB32<Format>(buffer[i]); } +#if defined(__SSE2__) && !defined(__SSSE3__) +extern const uint * QT_FASTCALL fetchPixelsBPP24_ssse3(uint *dest, const uchar*src, int index, int count); +#endif + template<QImage::Format Format> static const uint *QT_FASTCALL fetchRGBToRGB32(uint *buffer, const uchar *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { constexpr QPixelLayout::BPP BPP = bitsPerPixel<Format>(); #if defined(__SSE2__) && !defined(__SSSE3__) - if (BPP == QPixelLayout::BPP24) { + if (BPP == QPixelLayout::BPP24 && qCpuHasFeature(SSSE3)) { // With SSE2 can convertToRGB32 be vectorized, but it takes SSSE3 // to vectorize the deforested version below. - qFetchPixels[BPP](buffer, src, index, count); + fetchPixelsBPP24_ssse3(buffer, src, index, count); convertToRGB32<Format>(buffer, count, nullptr); return buffer; } @@ -370,12 +366,26 @@ static const uint *QT_FASTCALL fetchRGBToRGB32(uint *buffer, const uchar *src, i } template<QImage::Format Format> +static Q_ALWAYS_INLINE QRgba64 convertPixelToRGB64(uint s) +{ + return QRgba64::fromArgb32(convertPixelToRGB32<Format>(s)); +} + +template<QImage::Format Format> static const QRgba64 *QT_FASTCALL convertToRGB64(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) - buffer[i] = QRgba64::fromArgb32(convertPixelToRGB32<Format>(src[i])); + buffer[i] = convertPixelToRGB64<Format>(src[i]); + return buffer; +} +template<QImage::Format Format> +static const QRgba64 *QT_FASTCALL fetchRGBToRGB64(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = convertPixelToRGB64<Format>(fetchPixel<bitsPerPixel<Format>()>(src, index + i)); return buffer; } @@ -433,10 +443,10 @@ static const uint *QT_FASTCALL fetchARGBPMToARGB32PM(uint *buffer, const uchar * { constexpr QPixelLayout::BPP BPP = bitsPerPixel<Format>(); #if defined(__SSE2__) && !defined(__SSSE3__) - if (BPP == QPixelLayout::BPP24) { + if (BPP == QPixelLayout::BPP24 && qCpuHasFeature(SSSE3)) { // With SSE2 can convertToRGB32 be vectorized, but it takes SSSE3 // to vectorize the deforested version below. - qFetchPixels[BPP](buffer, src, index, count); + fetchPixelsBPP24_ssse3(buffer, src, index, count); convertARGBPMToARGB32PM<Format>(buffer, count, nullptr); return buffer; } @@ -447,12 +457,27 @@ static const uint *QT_FASTCALL fetchARGBPMToARGB32PM(uint *buffer, const uchar * } template<QImage::Format Format> -static const QRgba64 *QT_FASTCALL convertARGBPMToARGB64PM(QRgba64 *buffer, const uint *src, int count, +static Q_ALWAYS_INLINE QRgba64 convertPixelToRGBA64PM(uint s) +{ + return QRgba64::fromArgb32(convertPixelToARGB32PM<Format>(s)); +} + +template<QImage::Format Format> +static const QRgba64 *QT_FASTCALL convertARGBPMToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) - buffer[i] = QRgba64::fromArgb32(convertPixelToARGB32PM<Format>(src[i])); + buffer[i] = convertPixelToRGB64<Format>(src[i]); + return buffer; +} +template<QImage::Format Format> +static const QRgba64 *QT_FASTCALL fetchARGBPMToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + constexpr QPixelLayout::BPP bpp = bitsPerPixel<Format>(); + for (int i = 0; i < count; ++i) + buffer[i] = convertPixelToRGBA64PM<Format>(fetchPixel<bpp>(src, index + i)); return buffer; } @@ -635,6 +660,7 @@ template<QImage::Format Format> Q_DECL_CONSTEXPR static inline QPixelLayout pixe convertToRGB32<Format>, convertToRGB64<Format>, fetchRGBToRGB32<Format>, + fetchRGBToRGB64<Format>, storeRGBFromARGB32PM<Format, false>, storeRGBFromARGB32PM<Format, true> }; @@ -648,8 +674,9 @@ template<QImage::Format Format> Q_DECL_CONSTEXPR static inline QPixelLayout pixe bitsPerPixel<Format>(), rbSwap<Format>, convertARGBPMToARGB32PM<Format>, - convertARGBPMToARGB64PM<Format>, + convertARGBPMToRGBA64PM<Format>, fetchARGBPMToARGB32PM<Format>, + fetchARGBPMToRGBA64PM<Format>, storeARGBPMFromARGB32PM<Format, false>, storeARGBPMFromARGB32PM<Format, true> }; @@ -672,7 +699,18 @@ static const uint *QT_FASTCALL fetchIndexedToARGB32PM(uint *buffer, const uchar return buffer; } -static const QRgba64 *QT_FASTCALL convertIndexedToARGB64PM(QRgba64 *buffer, const uint *src, int count, +template<QPixelLayout::BPP BPP> +static const QRgba64 *QT_FASTCALL fetchIndexedToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *clut, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) { + const uint s = fetchPixel<BPP>(src, index + i); + buffer[i] = QRgba64::fromArgb32(clut->at(s)).premultiplied(); + } + return buffer; +} + +static const QRgba64 *QT_FASTCALL convertIndexedToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *clut, QDitherInfo *) { for (int i = 0; i < count; ++i) @@ -690,6 +728,12 @@ static const uint *QT_FASTCALL fetchPassThrough(uint *, const uchar *src, int in return reinterpret_cast<const uint *>(src) + index; } +static const QRgba64 *QT_FASTCALL fetchPassThrough64(QRgba64 *, const uchar *src, int index, int, + const QVector<QRgb> *, QDitherInfo *) +{ + return reinterpret_cast<const QRgba64 *>(src) + index; +} + static void QT_FASTCALL storePassThrough(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { @@ -755,6 +799,13 @@ static const QRgba64 *QT_FASTCALL convertAlpha8ToRGB64(QRgba64 *buffer, const ui buffer[i] = QRgba64::fromRgba(0, 0, 0, src[i]); return buffer; } +static const QRgba64 *QT_FASTCALL fetchAlpha8ToRGB64(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = QRgba64::fromRgba(0, 0, 0, src[index + i]); + return buffer; +} static void QT_FASTCALL convertGrayscale8ToRGB32(uint *buffer, int count, const QVector<QRgb> *) { @@ -782,6 +833,16 @@ static const QRgba64 *QT_FASTCALL convertGrayscale8ToRGB64(QRgba64 *buffer, cons return buffer; } +static const QRgba64 *QT_FASTCALL fetchGrayscale8ToRGB64(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) { + const uint s = src[index + i]; + buffer[i] = QRgba64::fromRgba(s, s, s, 255); + } + return buffer; +} + static void QT_FASTCALL storeARGB32FromARGB32PM(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { @@ -798,7 +859,7 @@ static void QT_FASTCALL storeRGBA8888PMFromARGB32PM(uchar *dest, const uint *src #ifdef __SSE2__ template<bool RGBA, bool maskAlpha> -static inline void qConvertARGB32PMToARGB64PM_sse2(QRgba64 *buffer, const uint *src, int count) +static inline void qConvertARGB32PMToRGBA64PM_sse2(QRgba64 *buffer, const uint *src, int count) { if (count <= 0) return; @@ -841,6 +902,66 @@ static inline void qConvertARGB32PMToARGB64PM_sse2(QRgba64 *buffer, const uint * *buffer++ = QRgba64::fromArgb32(s); } } + +template<QtPixelOrder PixelOrder> +static inline void qConvertRGBA64PMToA2RGB30PM_sse2(uint *dest, const QRgba64 *buffer, int count) +{ + const __m128i gmask = _mm_set1_epi32(0x000ffc00); + const __m128i cmask = _mm_set1_epi32(0x000003ff); + int i = 0; + __m128i vr, vg, vb, va; + for (; i < count && uintptr_t(buffer) & 0xF; ++i) { + *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++); + } + + for (; i < count-15; i += 16) { + // Repremultiplying is really expensive and hard to do in SIMD without AVX2, + // so we try to avoid it by checking if it is needed 16 samples at a time. + __m128i vOr = _mm_set1_epi32(0); + __m128i vAnd = _mm_set1_epi32(0xffffffff); + for (int j = 0; j < 16; j += 2) { + __m128i vs = _mm_load_si128((const __m128i*)(buffer + j)); + vOr = _mm_or_si128(vOr, vs); + vAnd = _mm_and_si128(vAnd, vs); + } + const quint16 orAlpha = ((uint)_mm_extract_epi16(vOr, 3)) | ((uint)_mm_extract_epi16(vOr, 7)); + const quint16 andAlpha = ((uint)_mm_extract_epi16(vAnd, 3)) & ((uint)_mm_extract_epi16(vAnd, 7)); + + if (andAlpha == 0xffff) { + for (int j = 0; j < 16; j += 2) { + __m128i vs = _mm_load_si128((const __m128i*)buffer); + buffer += 2; + vr = _mm_srli_epi64(vs, 6); + vg = _mm_srli_epi64(vs, 16 + 6 - 10); + vb = _mm_srli_epi64(vs, 32 + 6); + vr = _mm_and_si128(vr, cmask); + vg = _mm_and_si128(vg, gmask); + vb = _mm_and_si128(vb, cmask); + va = _mm_srli_epi64(vs, 48 + 14); + if (PixelOrder == PixelOrderRGB) + vr = _mm_slli_epi32(vr, 20); + else + vb = _mm_slli_epi32(vb, 20); + va = _mm_slli_epi32(va, 30); + __m128i vd = _mm_or_si128(_mm_or_si128(vr, vg), _mm_or_si128(vb, va)); + vd = _mm_shuffle_epi32(vd, _MM_SHUFFLE(3, 1, 2, 0)); + _mm_storel_epi64((__m128i*)dest, vd); + dest += 2; + } + } else if (orAlpha == 0) { + for (int j = 0; j < 16; ++j) { + *dest++ = 0; + buffer++; + } + } else { + for (int j = 0; j < 16; ++j) + *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++); + } + } + + SIMD_EPILOGUE(i, count, 15) + *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++); +} #elif defined(__ARM_NEON__) template<bool RGBA, bool maskAlpha> static inline void qConvertARGB32PMToRGBA64PM_neon(QRgba64 *buffer, const uint *src, int count) @@ -894,7 +1015,7 @@ static const QRgba64 *QT_FASTCALL convertRGB32ToRGB64(QRgba64 *buffer, const uin const QVector<QRgb> *, QDitherInfo *) { #ifdef __SSE2__ - qConvertARGB32PMToARGB64PM_sse2<false, true>(buffer, src, count); + qConvertARGB32PMToRGBA64PM_sse2<false, true>(buffer, src, count); #elif defined(__ARM_NEON__) qConvertARGB32PMToRGBA64PM_neon<false, true>(buffer, src, count); #else @@ -904,11 +1025,17 @@ static const QRgba64 *QT_FASTCALL convertRGB32ToRGB64(QRgba64 *buffer, const uin return buffer; } -static const QRgba64 *QT_FASTCALL convertARGB32ToARGB64PM(QRgba64 *buffer, const uint *src, int count, +static const QRgba64 *QT_FASTCALL fetchRGB32ToRGB64(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + return convertRGB32ToRGB64(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr); +} + +static const QRgba64 *QT_FASTCALL convertARGB32ToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { #ifdef __SSE2__ - qConvertARGB32PMToARGB64PM_sse2<false, false>(buffer, src, count); + qConvertARGB32PMToRGBA64PM_sse2<false, false>(buffer, src, count); for (int i = 0; i < count; ++i) buffer[i] = buffer[i].premultiplied(); #elif defined(__ARM_NEON__) @@ -922,11 +1049,17 @@ static const QRgba64 *QT_FASTCALL convertARGB32ToARGB64PM(QRgba64 *buffer, const return buffer; } -static const QRgba64 *QT_FASTCALL convertARGB32PMToARGB64PM(QRgba64 *buffer, const uint *src, int count, +static const QRgba64 *QT_FASTCALL fetchARGB32ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + return convertARGB32ToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr); +} + +static const QRgba64 *QT_FASTCALL convertARGB32PMToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { #ifdef __SSE2__ - qConvertARGB32PMToARGB64PM_sse2<false, false>(buffer, src, count); + qConvertARGB32PMToRGBA64PM_sse2<false, false>(buffer, src, count); #elif defined(__ARM_NEON__) qConvertARGB32PMToRGBA64PM_neon<false, false>(buffer, src, count); #else @@ -936,11 +1069,36 @@ static const QRgba64 *QT_FASTCALL convertARGB32PMToARGB64PM(QRgba64 *buffer, con return buffer; } -static const QRgba64 *QT_FASTCALL convertRGBA8888ToARGB64PM(QRgba64 *buffer, const uint *src, int count, +static const QRgba64 *QT_FASTCALL fetchARGB32PMToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + return convertARGB32PMToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr); +} + +static void convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count) +{ + for (int i = 0; i < count; ++i) + buffer[i] = buffer[i].premultiplied(); +} + +static void convertRGBA64PMToRGBA64PM(QRgba64 *, int) +{ +} + +static const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + const QRgba64 *s = reinterpret_cast<const QRgba64 *>(src) + index; + for (int i = 0; i < count; ++i) + buffer[i] = QRgba64::fromRgba64(s[i]).premultiplied(); + return buffer; +} + +static const QRgba64 *QT_FASTCALL convertRGBA8888ToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { #ifdef __SSE2__ - qConvertARGB32PMToARGB64PM_sse2<true, false>(buffer, src, count); + qConvertARGB32PMToRGBA64PM_sse2<true, false>(buffer, src, count); for (int i = 0; i < count; ++i) buffer[i] = buffer[i].premultiplied(); #elif defined(__ARM_NEON__) @@ -954,11 +1112,17 @@ static const QRgba64 *QT_FASTCALL convertRGBA8888ToARGB64PM(QRgba64 *buffer, con return buffer; } -static const QRgba64 *QT_FASTCALL convertRGBA8888PMToARGB64PM(QRgba64 *buffer, const uint *src, int count, +static const QRgba64 *QT_FASTCALL fetchRGBA8888ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + return convertRGBA8888ToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr); +} + +static const QRgba64 *QT_FASTCALL convertRGBA8888PMToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { #ifdef __SSE2__ - qConvertARGB32PMToARGB64PM_sse2<true, false>(buffer, src, count); + qConvertARGB32PMToRGBA64PM_sse2<true, false>(buffer, src, count); #elif defined(__ARM_NEON__) qConvertARGB32PMToRGBA64PM_neon<true, false>(buffer, src, count); #else @@ -968,6 +1132,12 @@ static const QRgba64 *QT_FASTCALL convertRGBA8888PMToARGB64PM(QRgba64 *buffer, c return buffer; } +static const QRgba64 *QT_FASTCALL fetchRGBA8888PMToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + return convertRGBA8888PMToRGBA64PM(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr); +} + static void QT_FASTCALL storeRGBA8888FromARGB32PM(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { @@ -1025,7 +1195,7 @@ static const uint *QT_FASTCALL fetchA2RGB30PMToARGB32PM(uint *buffer, const ucha #ifdef __SSE2__ template<QtPixelOrder PixelOrder> -static inline void qConvertA2RGB30PMToARGB64PM_sse2(QRgba64 *buffer, const uint *src, int count) +static inline void qConvertA2RGB30PMToRGBA64PM_sse2(QRgba64 *buffer, const uint *src, int count) { if (count <= 0) return; @@ -1068,11 +1238,11 @@ static inline void qConvertA2RGB30PMToARGB64PM_sse2(QRgba64 *buffer, const uint #endif template<QtPixelOrder PixelOrder> -static const QRgba64 *QT_FASTCALL convertA2RGB30PMToARGB64PM(QRgba64 *buffer, const uint *src, int count, +static const QRgba64 *QT_FASTCALL convertA2RGB30PMToRGBA64PM(QRgba64 *buffer, const uint *src, int count, const QVector<QRgb> *, QDitherInfo *) { #ifdef __SSE2__ - qConvertA2RGB30PMToARGB64PM_sse2<PixelOrder>(buffer, src, count); + qConvertA2RGB30PMToRGBA64PM_sse2<PixelOrder>(buffer, src, count); #else for (int i = 0; i < count; ++i) buffer[i] = qConvertA2rgb30ToRgb64<PixelOrder>(src[i]); @@ -1081,6 +1251,13 @@ static const QRgba64 *QT_FASTCALL convertA2RGB30PMToARGB64PM(QRgba64 *buffer, co } template<QtPixelOrder PixelOrder> +static const QRgba64 *QT_FASTCALL fetchA2RGB30PMToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + return convertA2RGB30PMToRGBA64PM<PixelOrder>(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr); +} + +template<QtPixelOrder PixelOrder> static void QT_FASTCALL storeA2RGB30PMFromARGB32PM(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { @@ -1104,6 +1281,52 @@ static void QT_FASTCALL storeRGB30FromARGB32PM(uchar *dest, const uint *src, int UNALIASED_CONVERSION_LOOP(d, src, count, qConvertRgb32ToRgb30<PixelOrder>); } +template<bool RGBA> +void qt_convertRGBA64ToARGB32(uint *dst, const QRgba64 *src, int count) +{ + int i = 0; +#ifdef __SSE2__ + if (((uintptr_t)dst & 0x7) && count > 0) { + uint s = (*src++).toArgb32(); + if (RGBA) + s = ARGB2RGBA(s); + *dst++ = s; + i++; + } + const __m128i vhalf = _mm_set1_epi32(0x80); + const __m128i vzero = _mm_setzero_si128(); + for (; i < count-1; i += 2) { + __m128i vs = _mm_loadu_si128((const __m128i*)src); + src += 2; + if (!RGBA) { + vs = _mm_shufflelo_epi16(vs, _MM_SHUFFLE(3, 0, 1, 2)); + vs = _mm_shufflehi_epi16(vs, _MM_SHUFFLE(3, 0, 1, 2)); + } + __m128i v1 = _mm_unpacklo_epi16(vs, vzero); + __m128i v2 = _mm_unpackhi_epi16(vs, vzero); + v1 = _mm_add_epi32(v1, vhalf); + v2 = _mm_add_epi32(v2, vhalf); + v1 = _mm_sub_epi32(v1, _mm_srli_epi32(v1, 8)); + v2 = _mm_sub_epi32(v2, _mm_srli_epi32(v2, 8)); + v1 = _mm_srli_epi32(v1, 8); + v2 = _mm_srli_epi32(v2, 8); + v1 = _mm_packs_epi32(v1, v2); + v1 = _mm_packus_epi16(v1, vzero); + _mm_storel_epi64((__m128i*)(dst), v1); + dst += 2; + } +#endif + for (; i < count; i++) { + uint s = (*src++).toArgb32(); + if (RGBA) + s = ARGB2RGBA(s); + *dst++ = s; + } +} +template void qt_convertRGBA64ToARGB32<false>(uint *dst, const QRgba64 *src, int count); +template void qt_convertRGBA64ToARGB32<true>(uint *dst, const QRgba64 *src, int count); + + static void QT_FASTCALL storeAlpha8FromARGB32PM(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *) { @@ -1125,27 +1348,66 @@ static void QT_FASTCALL storeGrayscale8FromARGB32PM(uchar *dest, const uint *src dest[index + i] = qGray(qUnpremultiply(src[i])); } +static const uint *QT_FASTCALL fetchRGB64ToRGB32(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + const QRgba64 *s = reinterpret_cast<const QRgba64 *>(src) + index; + for (int i = 0; i < count; ++i) + buffer[i] = toArgb32(s[i]); + return buffer; +} + +static void QT_FASTCALL storeRGB64FromRGB32(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + QRgba64 *d = reinterpret_cast<QRgba64 *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = QRgba64::fromArgb32(src[i]); +} + +static const uint *QT_FASTCALL fetchRGBA64ToARGB32PM(uint *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + const QRgba64 *s = reinterpret_cast<const QRgba64 *>(src) + index; + for (int i = 0; i < count; ++i) + buffer[i] = toArgb32(s[i].premultiplied()); + return buffer; +} + +static void QT_FASTCALL storeRGBA64FromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + QRgba64 *d = reinterpret_cast<QRgba64 *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = QRgba64::fromArgb32(src[i]).unpremultiplied(); +} // Note: // convertToArgb32() assumes that no color channel is less than 4 bits. // storeRGBFromARGB32PM() assumes that no color channel is more than 8 bits. // QImage::rgbSwapped() assumes that the red and blue color channels have the same number of bits. QPixelLayout qPixelLayouts[QImage::NImageFormats] = { - { false, false, QPixelLayout::BPPNone, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }, // Format_Invalid - { false, false, QPixelLayout::BPP1MSB, nullptr, convertIndexedToARGB32PM, - convertIndexedToARGB64PM, fetchIndexedToARGB32PM<QPixelLayout::BPP1MSB>, nullptr, nullptr }, // Format_Mono - { false, false, QPixelLayout::BPP1LSB, nullptr, convertIndexedToARGB32PM, - convertIndexedToARGB64PM, fetchIndexedToARGB32PM<QPixelLayout::BPP1LSB>, nullptr, nullptr }, // Format_MonoLSB - { false, false, QPixelLayout::BPP8, nullptr, convertIndexedToARGB32PM, - convertIndexedToARGB64PM, fetchIndexedToARGB32PM<QPixelLayout::BPP8>, nullptr, nullptr }, // Format_Indexed8 + { false, false, QPixelLayout::BPPNone, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }, // Format_Invalid + { false, false, QPixelLayout::BPP1MSB, nullptr, + convertIndexedToARGB32PM, convertIndexedToRGBA64PM, + fetchIndexedToARGB32PM<QPixelLayout::BPP1MSB>, fetchIndexedToRGBA64PM<QPixelLayout::BPP1MSB>, + nullptr, nullptr }, // Format_Mono + { false, false, QPixelLayout::BPP1LSB, nullptr, + convertIndexedToARGB32PM, convertIndexedToRGBA64PM, + fetchIndexedToARGB32PM<QPixelLayout::BPP1LSB>, fetchIndexedToRGBA64PM<QPixelLayout::BPP1LSB>, + nullptr, nullptr }, // Format_MonoLSB + { false, false, QPixelLayout::BPP8, nullptr, + convertIndexedToARGB32PM, convertIndexedToRGBA64PM, + fetchIndexedToARGB32PM<QPixelLayout::BPP8>, fetchIndexedToRGBA64PM<QPixelLayout::BPP8>, + nullptr, nullptr }, // Format_Indexed8 // Technically using convertPassThrough to convert from ARGB32PM to RGB32 is wrong, // but everywhere this generic conversion would be wrong is currently overloaded. { false, false, QPixelLayout::BPP32, rbSwap_rgb32, convertPassThrough, - convertRGB32ToRGB64, fetchPassThrough, storePassThrough, storePassThrough }, // Format_RGB32 + convertRGB32ToRGB64, fetchPassThrough, fetchRGB32ToRGB64, storePassThrough, storePassThrough }, // Format_RGB32 { true, false, QPixelLayout::BPP32, rbSwap_rgb32, convertARGB32ToARGB32PM, - convertARGB32ToARGB64PM, fetchARGB32ToARGB32PM, storeARGB32FromARGB32PM, storePassThrough }, // Format_ARGB32 + convertARGB32ToRGBA64PM, fetchARGB32ToARGB32PM, fetchARGB32ToRGBA64PM, storeARGB32FromARGB32PM, storePassThrough }, // Format_ARGB32 { true, true, QPixelLayout::BPP32, rbSwap_rgb32, convertPassThrough, - convertARGB32PMToARGB64PM, fetchPassThrough, storePassThrough, storePassThrough }, // Format_ARGB32_Premultiplied + convertARGB32PMToRGBA64PM, fetchPassThrough, fetchARGB32PMToRGBA64PM, storePassThrough, storePassThrough }, // Format_ARGB32_Premultiplied pixelLayoutRGB<QImage::Format_RGB16>(), pixelLayoutARGBPM<QImage::Format_ARGB8565_Premultiplied>(), pixelLayoutRGB<QImage::Format_RGB666>(), @@ -1156,43 +1418,167 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = { pixelLayoutRGB<QImage::Format_RGB444>(), pixelLayoutARGBPM<QImage::Format_ARGB4444_Premultiplied>(), { false, false, QPixelLayout::BPP32, rbSwap<QImage::Format_RGBA8888>, convertRGBA8888PMToARGB32PM, - convertRGBA8888PMToARGB64PM, fetchRGBA8888PMToARGB32PM, storeRGBXFromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBX8888 + convertRGBA8888PMToRGBA64PM, fetchRGBA8888PMToARGB32PM, fetchRGBA8888PMToRGBA64PM, storeRGBXFromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBX8888 { true, false, QPixelLayout::BPP32, rbSwap<QImage::Format_RGBA8888>, convertRGBA8888ToARGB32PM, - convertRGBA8888ToARGB64PM, fetchRGBA8888ToARGB32PM, storeRGBA8888FromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBA8888 + convertRGBA8888ToRGBA64PM, fetchRGBA8888ToARGB32PM, fetchRGBA8888ToRGBA64PM, storeRGBA8888FromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBA8888 { true, true, QPixelLayout::BPP32, rbSwap<QImage::Format_RGBA8888>, convertRGBA8888PMToARGB32PM, - convertRGBA8888PMToARGB64PM, fetchRGBA8888PMToARGB32PM, storeRGBA8888PMFromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBA8888_Premultiplied + convertRGBA8888PMToRGBA64PM, fetchRGBA8888PMToARGB32PM, fetchRGBA8888PMToRGBA64PM, storeRGBA8888PMFromARGB32PM, storeRGBXFromRGB32 }, // Format_RGBA8888_Premultiplied { false, false, QPixelLayout::BPP32, rbSwap_rgb30, convertA2RGB30PMToARGB32PM<PixelOrderBGR>, - convertA2RGB30PMToARGB64PM<PixelOrderBGR>, + convertA2RGB30PMToRGBA64PM<PixelOrderBGR>, fetchA2RGB30PMToARGB32PM<PixelOrderBGR>, + fetchA2RGB30PMToRGBA64PM<PixelOrderBGR>, storeRGB30FromARGB32PM<PixelOrderBGR>, storeRGB30FromRGB32<PixelOrderBGR> }, // Format_BGR30 { true, true, QPixelLayout::BPP32, rbSwap_rgb30, convertA2RGB30PMToARGB32PM<PixelOrderBGR>, - convertA2RGB30PMToARGB64PM<PixelOrderBGR>, + convertA2RGB30PMToRGBA64PM<PixelOrderBGR>, fetchA2RGB30PMToARGB32PM<PixelOrderBGR>, + fetchA2RGB30PMToRGBA64PM<PixelOrderBGR>, storeA2RGB30PMFromARGB32PM<PixelOrderBGR>, storeRGB30FromRGB32<PixelOrderBGR> }, // Format_A2BGR30_Premultiplied { false, false, QPixelLayout::BPP32, rbSwap_rgb30, convertA2RGB30PMToARGB32PM<PixelOrderRGB>, - convertA2RGB30PMToARGB64PM<PixelOrderRGB>, + convertA2RGB30PMToRGBA64PM<PixelOrderRGB>, fetchA2RGB30PMToARGB32PM<PixelOrderRGB>, + fetchA2RGB30PMToRGBA64PM<PixelOrderRGB>, storeRGB30FromARGB32PM<PixelOrderRGB>, storeRGB30FromRGB32<PixelOrderRGB> }, // Format_RGB30 { true, true, QPixelLayout::BPP32, rbSwap_rgb30, convertA2RGB30PMToARGB32PM<PixelOrderRGB>, - convertA2RGB30PMToARGB64PM<PixelOrderRGB>, + convertA2RGB30PMToRGBA64PM<PixelOrderRGB>, fetchA2RGB30PMToARGB32PM<PixelOrderRGB>, + fetchA2RGB30PMToRGBA64PM<PixelOrderRGB>, storeA2RGB30PMFromARGB32PM<PixelOrderRGB>, storeRGB30FromRGB32<PixelOrderRGB> }, // Format_A2RGB30_Premultiplied - { true, true, QPixelLayout::BPP8, nullptr, convertAlpha8ToRGB32, - convertAlpha8ToRGB64, fetchAlpha8ToRGB32, storeAlpha8FromARGB32PM, nullptr }, // Format_Alpha8 - { false, false, QPixelLayout::BPP8, nullptr, convertGrayscale8ToRGB32, - convertGrayscale8ToRGB64, fetchGrayscale8ToRGB32, storeGrayscale8FromARGB32PM, storeGrayscale8FromRGB32 } // Format_Grayscale8 + { true, true, QPixelLayout::BPP8, nullptr, + convertAlpha8ToRGB32, convertAlpha8ToRGB64, + fetchAlpha8ToRGB32, fetchAlpha8ToRGB64, + storeAlpha8FromARGB32PM, nullptr }, // Format_Alpha8 + { false, false, QPixelLayout::BPP8, nullptr, + convertGrayscale8ToRGB32, convertGrayscale8ToRGB64, + fetchGrayscale8ToRGB32, fetchGrayscale8ToRGB64, + storeGrayscale8FromARGB32PM, storeGrayscale8FromRGB32 }, // Format_Grayscale8 + { false, false, QPixelLayout::BPP64, nullptr, + convertPassThrough, nullptr, + fetchRGB64ToRGB32, fetchPassThrough64, + storeRGB64FromRGB32, storeRGB64FromRGB32 }, // Format_RGBX64 + { true, false, QPixelLayout::BPP64, nullptr, + convertARGB32ToARGB32PM, nullptr, + fetchRGBA64ToARGB32PM, fetchRGBA64ToRGBA64PM, + storeRGBA64FromARGB32PM, storeRGB64FromRGB32 }, // Format_RGBA64 + { true, true, QPixelLayout::BPP64, nullptr, + convertPassThrough, nullptr, + fetchRGB64ToRGB32, fetchPassThrough64, + storeRGB64FromRGB32, storeRGB64FromRGB32 } // Format_RGBA64_Premultiplied +}; + +Q_STATIC_ASSERT(sizeof(qPixelLayouts) / sizeof(*qPixelLayouts) == QImage::NImageFormats); + +static void QT_FASTCALL convertFromRgb64(uint *dest, const QRgba64 *src, int length) +{ + for (int i = 0; i < length; ++i) { + dest[i] = toArgb32(src[i]); + } +} + +template<QImage::Format format> +static void QT_FASTCALL storeGenericFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *clut, QDitherInfo *dither) +{ + uint buffer[BufferSize]; + convertFromRgb64(buffer, src, count); + qPixelLayouts[format].storeFromARGB32PM(dest, buffer, index, count, clut, dither); +} + +static void QT_FASTCALL storeARGB32FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = (uint*)dest + index; + for (int i = 0; i < count; ++i) + d[i] = toArgb32(src[i].unpremultiplied()); +} + +static void QT_FASTCALL storeRGBA8888FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = (uint*)dest + index; + for (int i = 0; i < count; ++i) + d[i] = toRgba8888(src[i].unpremultiplied()); +} + +template<QtPixelOrder PixelOrder> +static void QT_FASTCALL storeRGB30FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = (uint*)dest + index; +#ifdef __SSE2__ + qConvertRGBA64PMToA2RGB30PM_sse2<PixelOrder>(d, src, count); +#else + for (int i = 0; i < count; ++i) + d[i] = qConvertRgb64ToRgb30<PixelOrder>(src[i]); +#endif +} + +static void QT_FASTCALL storeRGBX64FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + QRgba64 *d = reinterpret_cast<QRgba64*>(dest) + index; + for (int i = 0; i < count; ++i) { + d[i] = src[i].unpremultiplied(); + d[i].setAlpha(65535); + } +} + +static void QT_FASTCALL storeRGBA64FromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + QRgba64 *d = reinterpret_cast<QRgba64*>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = src[i].unpremultiplied(); +} + +static void QT_FASTCALL storeRGBA64PMFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + QRgba64 *d = reinterpret_cast<QRgba64*>(dest) + index; + if (d != src) + memcpy(d, src, count * sizeof(QRgba64)); +} + +ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = { + nullptr, + nullptr, + nullptr, + nullptr, + storeGenericFromRGBA64PM<QImage::Format_RGB32>, + storeARGB32FromRGBA64PM, + storeGenericFromRGBA64PM<QImage::Format_ARGB32_Premultiplied>, + storeGenericFromRGBA64PM<QImage::Format_RGB16>, + storeGenericFromRGBA64PM<QImage::Format_ARGB8565_Premultiplied>, + storeGenericFromRGBA64PM<QImage::Format_RGB666>, + storeGenericFromRGBA64PM<QImage::Format_ARGB6666_Premultiplied>, + storeGenericFromRGBA64PM<QImage::Format_RGB555>, + storeGenericFromRGBA64PM<QImage::Format_ARGB8555_Premultiplied>, + storeGenericFromRGBA64PM<QImage::Format_RGB888>, + storeGenericFromRGBA64PM<QImage::Format_RGB444>, + storeGenericFromRGBA64PM<QImage::Format_ARGB4444_Premultiplied>, + storeGenericFromRGBA64PM<QImage::Format_RGBX8888>, + storeRGBA8888FromRGBA64PM, + storeGenericFromRGBA64PM<QImage::Format_RGBA8888_Premultiplied>, + storeRGB30FromRGBA64PM<PixelOrderBGR>, + storeRGB30FromRGBA64PM<PixelOrderBGR>, + storeRGB30FromRGBA64PM<PixelOrderRGB>, + storeRGB30FromRGBA64PM<PixelOrderRGB>, + storeGenericFromRGBA64PM<QImage::Format_Alpha8>, + storeGenericFromRGBA64PM<QImage::Format_Grayscale8>, + storeRGBX64FromRGBA64PM, + storeRGBA64FromRGBA64PM, + storeRGBA64PMFromRGBA64PM }; /* @@ -1245,19 +1631,20 @@ static uint *QT_FASTCALL destFetch(uint *buffer, QRasterBuffer *rasterBuffer, in return const_cast<uint *>(layout->fetchToARGB32PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr)); } +static uint *QT_FASTCALL destFetchUndefined(uint *buffer, QRasterBuffer *, int, int, int) +{ + return buffer; +} + static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) { const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; - uint buffer32[BufferSize]; - const uint *ptr = qFetchPixels[layout->bpp](buffer32, rasterBuffer->scanLine(y), x, length); - return const_cast<QRgba64 *>(layout->convertToARGB64PM(buffer, ptr, length, 0, 0)); + return const_cast<QRgba64 *>(layout->fetchToRGBA64PM(buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr)); } -static QRgba64 *QT_FASTCALL destFetch64uint32(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) +static QRgba64 * QT_FASTCALL destFetchRGB64(QRgba64 *, QRasterBuffer *rasterBuffer, int x, int y, int) { - const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; - const uint *src = ((const uint *)rasterBuffer->scanLine(y)) + x; - return const_cast<QRgba64 *>(layout->convertToARGB64PM(buffer, src, length, 0, 0)); + return (QRgba64 *)rasterBuffer->scanLine(y) + x; } static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer *, int, int, int) @@ -1292,6 +1679,9 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] = destFetch, // Format_A2RGB30_Premultiplied destFetch, // Format_Alpha8 destFetch, // Format_Grayscale8 + destFetch, // Format_RGBX64 + destFetch, // Format_RGBA64 + destFetch, // Format_RGBA64_Premultiplied }; static DestFetchProc64 destFetchProc64[QImage::NImageFormats] = @@ -1300,9 +1690,9 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] = 0, // Format_Mono, 0, // Format_MonoLSB 0, // Format_Indexed8 - destFetch64uint32, // Format_RGB32 - destFetch64uint32, // Format_ARGB32, - destFetch64uint32, // Format_ARGB32_Premultiplied + destFetch64, // Format_RGB32 + destFetch64, // Format_ARGB32, + destFetch64, // Format_ARGB32_Premultiplied destFetch64, // Format_RGB16 destFetch64, // Format_ARGB8565_Premultiplied destFetch64, // Format_RGB666 @@ -1312,15 +1702,18 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] = destFetch64, // Format_RGB888 destFetch64, // Format_RGB444 destFetch64, // Format_ARGB4444_Premultiplied - destFetch64uint32, // Format_RGBX8888 - destFetch64uint32, // Format_RGBA8888 - destFetch64uint32, // Format_RGBA8888_Premultiplied - destFetch64uint32, // Format_BGR30 - destFetch64uint32, // Format_A2BGR30_Premultiplied - destFetch64uint32, // Format_RGB30 - destFetch64uint32, // Format_A2RGB30_Premultiplied + destFetch64, // Format_RGBX8888 + destFetch64, // Format_RGBA8888 + destFetch64, // Format_RGBA8888_Premultiplied + destFetch64, // Format_BGR30 + destFetch64, // Format_A2BGR30_Premultiplied + destFetch64, // Format_RGB30 + destFetch64, // Format_A2RGB30_Premultiplied destFetch64, // Format_Alpha8 destFetch64, // Format_Grayscale8 + destFetchRGB64, // Format_RGBX64 + destFetch64, // Format_RGBA64 + destFetchRGB64, // Format_RGBA64_Premultiplied }; /* @@ -1429,120 +1822,19 @@ static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, con store(dest, buffer, x, length, nullptr, nullptr); } -static void QT_FASTCALL convertFromRgb64(uint *dest, const QRgba64 *src, int length) -{ - for (int i = 0; i < length; ++i) { - dest[i] = toArgb32(src[i]); - } -} - static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) { - uint buf[BufferSize]; - const QPixelLayout *layout = &qPixelLayouts[rasterBuffer->format]; - ConvertAndStorePixelsFunc store = layout->storeFromARGB32PM; - if (!layout->premultiplied && !layout->hasAlphaChannel) - store = layout->storeFromRGB32; + auto store = qStoreFromRGBA64PM[rasterBuffer->format]; uchar *dest = rasterBuffer->scanLine(y); - while (length) { - int l = qMin(length, BufferSize); - convertFromRgb64(buf, buffer, l); - store(dest, buf, x, l, nullptr, nullptr); - length -= l; - buffer += l; - x += l; - } -} - -#ifdef __SSE2__ -template<QtPixelOrder PixelOrder> -static inline void qConvertARGB64PMToA2RGB30PM_sse2(uint *dest, const QRgba64 *buffer, int count) -{ - const __m128i gmask = _mm_set1_epi32(0x000ffc00); - const __m128i cmask = _mm_set1_epi32(0x000003ff); - int i = 0; - __m128i vr, vg, vb, va; - for (; i < count && uintptr_t(buffer) & 0xF; ++i) { - *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++); - } - - for (; i < count-15; i += 16) { - // Repremultiplying is really expensive and hard to do in SIMD without AVX2, - // so we try to avoid it by checking if it is needed 16 samples at a time. - __m128i vOr = _mm_set1_epi32(0); - __m128i vAnd = _mm_set1_epi32(0xffffffff); - for (int j = 0; j < 16; j += 2) { - __m128i vs = _mm_load_si128((const __m128i*)(buffer + j)); - vOr = _mm_or_si128(vOr, vs); - vAnd = _mm_and_si128(vAnd, vs); - } - const quint16 orAlpha = ((uint)_mm_extract_epi16(vOr, 3)) | ((uint)_mm_extract_epi16(vOr, 7)); - const quint16 andAlpha = ((uint)_mm_extract_epi16(vAnd, 3)) & ((uint)_mm_extract_epi16(vAnd, 7)); - - if (andAlpha == 0xffff) { - for (int j = 0; j < 16; j += 2) { - __m128i vs = _mm_load_si128((const __m128i*)buffer); - buffer += 2; - vr = _mm_srli_epi64(vs, 6); - vg = _mm_srli_epi64(vs, 16 + 6 - 10); - vb = _mm_srli_epi64(vs, 32 + 6); - vr = _mm_and_si128(vr, cmask); - vg = _mm_and_si128(vg, gmask); - vb = _mm_and_si128(vb, cmask); - va = _mm_srli_epi64(vs, 48 + 14); - if (PixelOrder == PixelOrderRGB) - vr = _mm_slli_epi32(vr, 20); - else - vb = _mm_slli_epi32(vb, 20); - va = _mm_slli_epi32(va, 30); - __m128i vd = _mm_or_si128(_mm_or_si128(vr, vg), _mm_or_si128(vb, va)); - vd = _mm_shuffle_epi32(vd, _MM_SHUFFLE(3, 1, 2, 0)); - _mm_storel_epi64((__m128i*)dest, vd); - dest += 2; - } - } else if (orAlpha == 0) { - for (int j = 0; j < 16; ++j) { - *dest++ = 0; - buffer++; - } - } else { - for (int j = 0; j < 16; ++j) - *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++); - } - } - - SIMD_EPILOGUE(i, count, 15) - *dest++ = qConvertRgb64ToRgb30<PixelOrder>(*buffer++); -} -#endif - -static void QT_FASTCALL destStore64ARGB32(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) -{ - uint *dest = (uint*)rasterBuffer->scanLine(y) + x; - for (int i = 0; i < length; ++i) { - dest[i] = toArgb32(buffer[i].unpremultiplied()); - } -} - -static void QT_FASTCALL destStore64RGBA8888(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) -{ - uint *dest = (uint*)rasterBuffer->scanLine(y) + x; - for (int i = 0; i < length; ++i) { - dest[i] = toRgba8888(buffer[i].unpremultiplied()); - } + store(dest, buffer, x, length, nullptr, nullptr); } -template<QtPixelOrder PixelOrder> -static void QT_FASTCALL destStore64RGB30(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) +static void QT_FASTCALL destStore64RGBA64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) { - uint *dest = (uint*)rasterBuffer->scanLine(y) + x; -#ifdef __SSE2__ - qConvertARGB64PMToA2RGB30PM_sse2<PixelOrder>(dest, buffer, length); -#else + QRgba64 *dest = reinterpret_cast<QRgba64*>(rasterBuffer->scanLine(y)) + x; for (int i = 0; i < length; ++i) { - dest[i] = qConvertRgb64ToRgb30<PixelOrder>(buffer[i]); + dest[i] = buffer[i].unpremultiplied(); } -#endif } static DestStoreProc destStoreProc[QImage::NImageFormats] = @@ -1572,16 +1864,19 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] = destStore, // Format_A2RGB30_Premultiplied destStore, // Format_Alpha8 destStore, // Format_Grayscale8 + destStore, // Format_RGBX64 + destStore, // Format_RGBA64 + destStore, // Format_RGBA64_Premultiplied }; static DestStoreProc64 destStoreProc64[QImage::NImageFormats] = { 0, // Format_Invalid - destStore64, // Format_Mono, - destStore64, // Format_MonoLSB + 0, // Format_Mono, + 0, // Format_MonoLSB 0, // Format_Indexed8 destStore64, // Format_RGB32 - destStore64ARGB32, // Format_ARGB32, + destStore64, // Format_ARGB32, destStore64, // Format_ARGB32_Premultiplied destStore64, // Format_RGB16 destStore64, // Format_ARGB8565_Premultiplied @@ -1593,14 +1888,17 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] = destStore64, // Format_RGB444 destStore64, // Format_ARGB4444_Premultiplied destStore64, // Format_RGBX8888 - destStore64RGBA8888, // Format_RGBA8888 + destStore64, // Format_RGBA8888 destStore64, // Format_RGBA8888_Premultiplied - destStore64RGB30<PixelOrderBGR>, // Format_BGR30 - destStore64RGB30<PixelOrderBGR>, // Format_A2BGR30_Premultiplied - destStore64RGB30<PixelOrderRGB>, // Format_RGB30 - destStore64RGB30<PixelOrderRGB>, // Format_A2RGB30_Premultiplied + destStore64, // Format_BGR30 + destStore64, // Format_A2BGR30_Premultiplied + destStore64, // Format_RGB30 + destStore64, // Format_A2RGB30_Premultiplied destStore64, // Format_Alpha8 destStore64, // Format_Grayscale8 + 0, // Format_RGBX64 + destStore64RGBA64, // Format_RGBA64 + 0 // Format_RGBA64_Premultiplied }; /* @@ -1639,7 +1937,7 @@ static const uint *QT_FASTCALL fetchUntransformedARGB32PM(uint *, const Operator const QSpanData *data, int y, int x, int) { const uchar *scanLine = data->texture.scanLine(y); - return ((const uint *)scanLine) + x; + return reinterpret_cast<const uint *>(scanLine) + x; } static const uint *QT_FASTCALL fetchUntransformedRGB16(uint *buffer, const Operator *, @@ -1656,14 +1954,14 @@ static const QRgba64 *QT_FASTCALL fetchUntransformed64(QRgba64 *buffer, const Op const QSpanData *data, int y, int x, int length) { const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; - if (layout->bpp != QPixelLayout::BPP32) { - uint buffer32[BufferSize]; - const uint *ptr = qFetchPixels[layout->bpp](buffer32, data->texture.scanLine(y), x, length); - return layout->convertToARGB64PM(buffer, ptr, length, data->texture.colorTable, 0); - } else { - const uint *src = (const uint *)data->texture.scanLine(y) + x; - return layout->convertToARGB64PM(buffer, src, length, data->texture.colorTable, 0); - } + return layout->fetchToRGBA64PM(buffer, data->texture.scanLine(y), x, length, data->texture.colorTable, nullptr); +} + +static const QRgba64 *QT_FASTCALL fetchUntransformedRGBA64PM(QRgba64 *, const Operator *, + const QSpanData *data, int y, int x, int) +{ + const uchar *scanLine = data->texture.scanLine(y); + return reinterpret_cast<const QRgba64 *>(scanLine) + x; } template<TextureBlendType blendType> @@ -1680,8 +1978,8 @@ inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v) } } -template<TextureBlendType blendType, QPixelLayout::BPP bpp> -static void QT_FASTCALL fetchTransformed_fetcher(uint *buffer, const QSpanData *data, +template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T> +static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *data, int y, int x, int length) { Q_STATIC_ASSERT(blendType == BlendTransformed || blendType == BlendTransformedTiled); @@ -1690,8 +1988,9 @@ static void QT_FASTCALL fetchTransformed_fetcher(uint *buffer, const QSpanData * const qreal cx = x + qreal(0.5); const qreal cy = y + qreal(0.5); + constexpr bool useFetch = (bpp < QPixelLayout::BPP32) && sizeof(T) == sizeof(uint); const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; - if (bpp != QPixelLayout::BPPNone) // Like this to not ICE on GCC 5.3.1 + if (!useFetch) Q_ASSERT(layout->bpp == bpp); // When templated 'fetch' should be inlined at compile time: const FetchPixelFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : FetchPixelFunc(fetchPixel<bpp>); @@ -1725,13 +2024,19 @@ static void QT_FASTCALL fetchTransformed_fetcher(uint *buffer, const QSpanData * fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1); if (x1 == x2) break; - buffer[i] = fetch(src, x1); + if (useFetch) + buffer[i] = fetch(src, x1); + else + buffer[i] = reinterpret_cast<const T*>(src)[x1]; fx += fdx; } for (; i < fastLen; ++i) { int px = (fx >> 16); - buffer[i] = fetch(src, px); + if (useFetch) + buffer[i] = fetch(src, px); + else + buffer[i] = reinterpret_cast<const T*>(src)[px]; fx += fdx; } } @@ -1739,7 +2044,10 @@ static void QT_FASTCALL fetchTransformed_fetcher(uint *buffer, const QSpanData * for (; i < length; ++i) { int px = (fx >> 16); fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px); - buffer[i] = fetch(src, px); + if (useFetch) + buffer[i] = fetch(src, px); + else + buffer[i] = reinterpret_cast<const T*>(src)[px]; fx += fdx; } } else { // rotation or shear @@ -1764,7 +2072,10 @@ static void QT_FASTCALL fetchTransformed_fetcher(uint *buffer, const QSpanData * fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1); if (x1 == x2 && y1 == y2) break; - buffer[i] = fetch(image.scanLine(y1), x1); + if (useFetch) + buffer[i] = fetch(image.scanLine(y1), x1); + else + buffer[i] = reinterpret_cast<const T*>(image.scanLine(y1))[x1]; fx += fdx; fy += fdy; } @@ -1772,7 +2083,10 @@ static void QT_FASTCALL fetchTransformed_fetcher(uint *buffer, const QSpanData * for (; i < fastLen; ++i) { int px = (fx >> 16); int py = (fy >> 16); - buffer[i] = fetch(image.scanLine(py), px); + if (useFetch) + buffer[i] = fetch(image.scanLine(py), px); + else + buffer[i] = reinterpret_cast<const T*>(image.scanLine(py))[px]; fx += fdx; fy += fdy; } @@ -1783,7 +2097,10 @@ static void QT_FASTCALL fetchTransformed_fetcher(uint *buffer, const QSpanData * int py = (fy >> 16); fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px); fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, py); - buffer[i] = fetch(image.scanLine(py), px); + if (useFetch) + buffer[i] = fetch(image.scanLine(py), px); + else + buffer[i] = reinterpret_cast<const T*>(image.scanLine(py))[px]; fx += fdx; fy += fdy; } @@ -1797,8 +2114,8 @@ static void QT_FASTCALL fetchTransformed_fetcher(uint *buffer, const QSpanData * qreal fy = data->m22 * cy + data->m12 * cx + data->dy; qreal fw = data->m23 * cy + data->m13 * cx + data->m33; - uint *const end = buffer + length; - uint *b = buffer; + T *const end = buffer + length; + T *b = buffer; while (b < end) { const qreal iw = fw == 0 ? 1 : 1 / fw; const qreal tx = fx * iw; @@ -1808,7 +2125,10 @@ static void QT_FASTCALL fetchTransformed_fetcher(uint *buffer, const QSpanData * fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, py); fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px); - *b = fetch(image.scanLine(py), px); + if (useFetch) + *b = fetch(image.scanLine(py), px); + else + *b = reinterpret_cast<const T*>(image.scanLine(py))[px]; fx += fdx; fy += fdy; @@ -1828,23 +2148,29 @@ static const uint *QT_FASTCALL fetchTransformed(uint *buffer, const Operator *, { Q_STATIC_ASSERT(blendType == BlendTransformed || blendType == BlendTransformedTiled); const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; - fetchTransformed_fetcher<blendType, bpp>(buffer, data, y, x, length); + fetchTransformed_fetcher<blendType, bpp, uint>(buffer, data, y, x, length); layout->convertToARGB32PM(buffer, length, data->texture.colorTable); return buffer; } -template<TextureBlendType blendType> +template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Operator *, const QSpanData *data, int y, int x, int length) { - Q_STATIC_ASSERT(blendType == BlendTransformed || blendType == BlendTransformedTiled); const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; - uint buffer32[BufferSize]; - if (layout->bpp == QPixelLayout::BPP32) - fetchTransformed_fetcher<blendType, QPixelLayout::BPP32>(buffer32, data, y, x, length); - else - fetchTransformed_fetcher<blendType, QPixelLayout::BPPNone>(buffer32, data, y, x, length); - layout->convertToARGB64PM(buffer, buffer32, length, data->texture.colorTable, nullptr); + if (layout->bpp != QPixelLayout::BPP64) { + uint buffer32[BufferSize]; + Q_ASSERT(length <= BufferSize); + if (layout->bpp == QPixelLayout::BPP32) + fetchTransformed_fetcher<blendType, QPixelLayout::BPP32, uint>(buffer32, data, y, x, length); + else + fetchTransformed_fetcher<blendType, QPixelLayout::BPPNone, uint>(buffer32, data, y, x, length); + return layout->convertToRGBA64PM(buffer, buffer32, length, data->texture.colorTable, nullptr); + } + + fetchTransformed_fetcher<blendType, QPixelLayout::BPP64, QRgba64>(buffer, data, y, x, length); + if (data->texture.format == QImage::Format_RGBA64) + convertRGBA64ToRGBA64PM(buffer, length); return buffer; } @@ -1939,7 +2265,7 @@ static inline uint interpolate_4_pixels_16(uint tl, uint tr, uint bl, uint br, u #endif #if defined(__SSE2__) -static inline QRgba64 interpolate_4_pixels_rgb64(QRgba64 t[], QRgba64 b[], uint distx, uint disty) +static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty) { __m128i vt = _mm_loadu_si128((const __m128i*)t); if (disty) { @@ -1963,7 +2289,7 @@ static inline QRgba64 interpolate_4_pixels_rgb64(QRgba64 t[], QRgba64 b[], uint #endif } #else -static inline QRgba64 interpolate_4_pixels_rgb64(QRgba64 t[], QRgba64 b[], uint distx, uint disty) +static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba64 b[], uint distx, uint disty) { const uint dx = distx>>8; const uint dy = disty>>8; @@ -2844,16 +3170,16 @@ static void QT_FASTCALL fetchTransformedBilinear_simple_scale_helper(uint *b, ui } -typedef void (QT_FASTCALL *BilinearFastTransformFetcher)(uint *buf1, uint *buf2, const int len, const QTextureData &image, - int fx, int fy, const int fdx, const int fdy); - -template<TextureBlendType blendType, QPixelLayout::BPP bpp> -static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2, const int len, const QTextureData &image, +template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T> +static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const int len, const QTextureData &image, int fx, int fy, const int fdx, const int fdy) { const QPixelLayout &layout = qPixelLayouts[image.format]; - Q_ASSERT(bpp == QPixelLayout::BPPNone || bpp == layout.bpp); - // When templated 'fetch1' should be inlined at compile time: + constexpr bool useFetch = (bpp < QPixelLayout::BPP32); + if (useFetch) + Q_ASSERT(sizeof(T) == sizeof(uint)); + else + Q_ASSERT(layout.bpp == bpp); const FetchPixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout.bpp] : fetchPixel<bpp>; if (fdy == 0) { int y1 = (fy >> 16); @@ -2870,8 +3196,13 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2, fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); if (x1 != x2) break; - buf1[i * 2 + 0] = buf1[i * 2 + 1] = fetch1(s1, x1); - buf2[i * 2 + 0] = buf2[i * 2 + 1] = fetch1(s2, x1); + if (useFetch) { + buf1[i * 2 + 0] = buf1[i * 2 + 1] = fetch1(s1, x1); + buf2[i * 2 + 0] = buf2[i * 2 + 1] = fetch1(s2, x1); + } else { + buf1[i * 2 + 0] = buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x1]; + buf2[i * 2 + 0] = buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x1]; + } fx += fdx; } int fastLen = len; @@ -2882,10 +3213,17 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2, for (; i < fastLen; ++i) { int x = (fx >> 16); - buf1[i * 2 + 0] = fetch1(s1, x); - buf1[i * 2 + 1] = fetch1(s1, x + 1); - buf2[i * 2 + 0] = fetch1(s2, x); - buf2[i * 2 + 1] = fetch1(s2, x + 1); + if (useFetch) { + buf1[i * 2 + 0] = fetch1(s1, x); + buf1[i * 2 + 1] = fetch1(s1, x + 1); + buf2[i * 2 + 0] = fetch1(s2, x); + buf2[i * 2 + 1] = fetch1(s2, x + 1); + } else { + buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x]; + buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x + 1]; + buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x]; + buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x + 1]; + } fx += fdx; } } @@ -2894,10 +3232,17 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2, int x1 = (fx >> 16); int x2; fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); - buf1[i * 2 + 0] = fetch1(s1, x1); - buf1[i * 2 + 1] = fetch1(s1, x2); - buf2[i * 2 + 0] = fetch1(s2, x1); - buf2[i * 2 + 1] = fetch1(s2, x2); + if (useFetch) { + buf1[i * 2 + 0] = fetch1(s1, x1); + buf1[i * 2 + 1] = fetch1(s1, x2); + buf2[i * 2 + 0] = fetch1(s2, x1); + buf2[i * 2 + 1] = fetch1(s2, x2); + } else { + buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1]; + buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2]; + buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1]; + buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2]; + } fx += fdx; } } else { @@ -2914,10 +3259,17 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2, break; const uchar *s1 = image.scanLine(y1); const uchar *s2 = image.scanLine(y2); - buf1[i * 2 + 0] = fetch1(s1, x1); - buf1[i * 2 + 1] = fetch1(s1, x2); - buf2[i * 2 + 0] = fetch1(s2, x1); - buf2[i * 2 + 1] = fetch1(s2, x2); + if (useFetch) { + buf1[i * 2 + 0] = fetch1(s1, x1); + buf1[i * 2 + 1] = fetch1(s1, x2); + buf2[i * 2 + 0] = fetch1(s2, x1); + buf2[i * 2 + 1] = fetch1(s2, x2); + } else { + buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1]; + buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2]; + buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1]; + buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2]; + } fx += fdx; fy += fdy; } @@ -2936,10 +3288,17 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2, int y = (fy >> 16); const uchar *s1 = image.scanLine(y); const uchar *s2 = s1 + image.bytesPerLine; - buf1[i * 2 + 0] = fetch1(s1, x); - buf1[i * 2 + 1] = fetch1(s1, x + 1); - buf2[i * 2 + 0] = fetch1(s2, x); - buf2[i * 2 + 1] = fetch1(s2, x + 1); + if (useFetch) { + buf1[i * 2 + 0] = fetch1(s1, x); + buf1[i * 2 + 1] = fetch1(s1, x + 1); + buf2[i * 2 + 0] = fetch1(s2, x); + buf2[i * 2 + 1] = fetch1(s2, x + 1); + } else { + buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x]; + buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x + 1]; + buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x]; + buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x + 1]; + } fx += fdx; fy += fdy; } @@ -2955,10 +3314,17 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(uint *buf1, uint *buf2, const uchar *s1 = image.scanLine(y1); const uchar *s2 = image.scanLine(y2); - buf1[i * 2 + 0] = fetch1(s1, x1); - buf1[i * 2 + 1] = fetch1(s1, x2); - buf2[i * 2 + 0] = fetch1(s2, x1); - buf2[i * 2 + 1] = fetch1(s2, x2); + if (useFetch) { + buf1[i * 2 + 0] = fetch1(s1, x1); + buf1[i * 2 + 1] = fetch1(s1, x2); + buf2[i * 2 + 0] = fetch1(s2, x1); + buf2[i * 2 + 1] = fetch1(s2, x2); + } else { + buf1[i * 2 + 0] = reinterpret_cast<const T *>(s1)[x1]; + buf1[i * 2 + 1] = reinterpret_cast<const T *>(s1)[x2]; + buf2[i * 2 + 0] = reinterpret_cast<const T *>(s2)[x1]; + buf2[i * 2 + 1] = reinterpret_cast<const T *>(s2)[x2]; + } fx += fdx; fy += fdy; } @@ -2997,7 +3363,7 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper if (mid != length) fetchTransformedBilinear_simple_scale_helper<blendType>(buffer + mid, buffer + length, data->texture, fx, fy, fdx, fdy); } else { - const BilinearFastTransformFetcher fetcher = fetchTransformedBilinear_fetcher<blendType,bpp>; + const auto fetcher = fetchTransformedBilinear_fetcher<blendType,bpp,uint>; uint buf1[BufferSize]; uint buf2[BufferSize]; @@ -3032,7 +3398,7 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper } } } else { // rotation or shear - const BilinearFastTransformFetcher fetcher = fetchTransformedBilinear_fetcher<blendType,bpp>; + const auto fetcher = fetchTransformedBilinear_fetcher<blendType,bpp,uint>; uint buf1[BufferSize]; uint buf2[BufferSize]; @@ -3146,19 +3512,27 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper } template<TextureBlendType blendType> -static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, const Operator *, - const QSpanData *data, int y, int x, int length) +static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buffer, const QSpanData *data, + int y, int x, int length) { - const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; + const QTextureData &texture = data->texture; + const QPixelLayout *layout = &qPixelLayouts[texture.format]; const QVector<QRgb> *clut = data->texture.colorTable; const qreal cx = x + qreal(0.5); const qreal cy = y + qreal(0.5); + uint sbuf1[BufferSize]; + uint sbuf2[BufferSize]; + QRgba64 buf1[BufferSize]; + QRgba64 buf2[BufferSize]; + QRgba64 *end = buffer + length; + QRgba64 *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); + const int fdx = (int)(data->m11 * fixed_scale); + const 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); @@ -3166,18 +3540,12 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co fx -= half_point; fy -= half_point; - const BilinearFastTransformFetcher fetcher = + const auto fetcher = (layout->bpp == QPixelLayout::BPP32) - ? fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32> - : fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPPNone>; + ? fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32, uint> + : fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPPNone, uint>; if (fdy == 0) { //simple scale, no rotation - - uint sbuf1[BufferSize]; - uint sbuf2[BufferSize]; - quint64 buf1[BufferSize]; - quint64 buf2[BufferSize]; - QRgba64 *b = buffer; while (length) { int len = qMin(length, BufferSize / 2); int disty = (fy & 0x0000ffff); @@ -3187,9 +3555,9 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co #endif fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy); - layout->convertToARGB64PM((QRgba64 *)buf1, sbuf1, len * 2, clut, 0); + layout->convertToRGBA64PM(buf1, sbuf1, len * 2, clut, 0); if (disty) - layout->convertToARGB64PM((QRgba64 *)buf2, sbuf2, len * 2, clut, 0); + layout->convertToRGBA64PM(buf2, sbuf2, len * 2, clut, 0); for (int i = 0; i < len; ++i) { int distx = (fx & 0x0000ffff); @@ -3209,33 +3577,26 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co } _mm_storel_epi64((__m128i*)(b+i), vt); #else - b[i] = interpolate_4_pixels_rgb64((QRgba64 *)buf1 + i*2, (QRgba64 *)buf2 + i*2, distx, disty); + b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty); #endif fx += fdx; } length -= len; b += len; } - } else { //rotation - uint sbuf1[BufferSize]; - uint sbuf2[BufferSize]; - quint64 buf1[BufferSize]; - quint64 buf2[BufferSize]; - QRgba64 *end = buffer + length; - QRgba64 *b = buffer; - + } else { // rotation or shear while (b < end) { int len = qMin(length, BufferSize / 2); fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy); - layout->convertToARGB64PM((QRgba64 *)buf1, sbuf1, len * 2, clut, 0); - layout->convertToARGB64PM((QRgba64 *)buf2, sbuf2, len * 2, clut, 0); + layout->convertToRGBA64PM(buf1, sbuf1, len * 2, clut, 0); + layout->convertToRGBA64PM(buf2, sbuf2, len * 2, clut, 0); for (int i = 0; i < len; ++i) { int distx = (fx & 0x0000ffff); int disty = (fy & 0x0000ffff); - b[i] = interpolate_4_pixels_rgb64((QRgba64 *)buf1 + i*2, (QRgba64 *)buf2 + i*2, distx, disty); + b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty); fx += fdx; fy += fdy; } @@ -3244,7 +3605,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co b += len; } } - } else { + } else { // !(data->fast_matrix) const QTextureData &image = data->texture; const qreal fdx = data->m11; @@ -3256,16 +3617,11 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co qreal fw = data->m23 * cy + data->m13 * cx + data->m33; FetchPixelFunc fetch = qFetchPixel[layout->bpp]; - uint sbuf1[BufferSize]; - uint sbuf2[BufferSize]; - quint64 buf1[BufferSize]; - quint64 buf2[BufferSize]; - QRgba64 *b = buffer; int distxs[BufferSize / 2]; int distys[BufferSize / 2]; - while (length) { + while (b < end) { int len = qMin(length, BufferSize / 2); for (int i = 0; i < len; ++i) { const qreal iw = fw == 0 ? 1 : 1 / fw; @@ -3283,21 +3639,165 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2); - const uchar *s1 = data->texture.scanLine(y1); - const uchar *s2 = data->texture.scanLine(y2); + const uchar *s1 = texture.scanLine(y1); + const uchar *s2 = texture.scanLine(y2); - if (layout->bpp == QPixelLayout::BPP32) { - sbuf1[i * 2 + 0] = ((const uint*)s1)[x1]; - sbuf1[i * 2 + 1] = ((const uint*)s1)[x2]; - sbuf2[i * 2 + 0] = ((const uint*)s2)[x1]; - sbuf2[i * 2 + 1] = ((const uint*)s2)[x2]; + sbuf1[i * 2 + 0] = fetch(s1, x1); + sbuf1[i * 2 + 1] = fetch(s1, x2); + sbuf2[i * 2 + 0] = fetch(s2, x1); + sbuf2[i * 2 + 1] = fetch(s2, x2); - } else { - sbuf1[i * 2 + 0] = fetch(s1, x1); - sbuf1[i * 2 + 1] = fetch(s1, x2); - sbuf2[i * 2 + 0] = fetch(s2, x1); - sbuf2[i * 2 + 1] = fetch(s2, x2); + fx += fdx; + fy += fdy; + fw += fdw; + //force increment to avoid /0 + if (!fw) + fw += fdw; + } + + layout->convertToRGBA64PM(buf1, sbuf1, len * 2, clut, 0); + layout->convertToRGBA64PM(buf2, sbuf2, len * 2, clut, 0); + + for (int i = 0; i < len; ++i) { + int distx = distxs[i]; + int disty = distys[i]; + b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty); + } + + length -= len; + b += len; + } + } + return buffer; +} + +template<TextureBlendType blendType> +static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buffer, const QSpanData *data, + int y, int x, int length) +{ + const QTextureData &texture = data->texture; + Q_ASSERT(qPixelLayouts[texture.format].bpp == QPixelLayout::BPP64); + const auto convert = (data->texture.format == QImage::Format_RGBA64) ? convertRGBA64ToRGBA64PM : convertRGBA64PMToRGBA64PM; + + const qreal cx = x + qreal(0.5); + const qreal cy = y + qreal(0.5); + + QRgba64 buf1[BufferSize]; + QRgba64 buf2[BufferSize]; + QRgba64 *end = buffer + length; + QRgba64 *b = buffer; + + 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); + + 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; + const auto fetcher = fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP64, QRgba64>; + + if (fdy == 0) { //simple scale, no rotation + while (length) { + int len = qMin(length, BufferSize / 2); + int disty = (fy & 0x0000ffff); +#if defined(__SSE2__) + const __m128i vdy = _mm_set1_epi16(disty); + const __m128i vidy = _mm_set1_epi16(0x10000 - disty); +#endif + fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy); + + convert(buf1, len * 2); + if (disty) + convert(buf2, len * 2); + + for (int i = 0; i < len; ++i) { + int distx = (fx & 0x0000ffff); +#if defined(__SSE2__) + __m128i vt = _mm_loadu_si128((const __m128i*)(buf1 + i*2)); + if (disty) { + __m128i vb = _mm_loadu_si128((const __m128i*)(buf2 + i*2)); + vt = _mm_mulhi_epu16(vt, vidy); + vb = _mm_mulhi_epu16(vb, vdy); + vt = _mm_add_epi16(vt, vb); + } + if (distx) { + const __m128i vdistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(distx), _MM_SHUFFLE(0, 0, 0, 0)); + const __m128i vidistx = _mm_shufflelo_epi16(_mm_cvtsi32_si128(0x10000 - distx), _MM_SHUFFLE(0, 0, 0, 0)); + vt = _mm_mulhi_epu16(vt, _mm_unpacklo_epi64(vidistx, vdistx)); + vt = _mm_add_epi16(vt, _mm_srli_si128(vt, 8)); + } + _mm_storel_epi64((__m128i*)(b+i), vt); +#else + b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty); +#endif + fx += fdx; } + length -= len; + b += len; + } + } else { // rotation or shear + while (b < end) { + int len = qMin(length, BufferSize / 2); + + fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy); + + convert(buf1, len * 2); + convert(buf2, len * 2); + + for (int i = 0; i < len; ++i) { + int distx = (fx & 0x0000ffff); + int disty = (fy & 0x0000ffff); + b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty); + fx += fdx; + fy += fdy; + } + + length -= len; + b += len; + } + } + } else { // !(data->fast_matrix) + const QTextureData &image = data->texture; + + 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; + + int distxs[BufferSize / 2]; + int distys[BufferSize / 2]; + + while (b < end) { + int len = qMin(length, BufferSize / 2); + for (int i = 0; i < len; ++i) { + 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; + + distxs[i] = int((px - x1) * (1<<16)); + distys[i] = int((py - y1) * (1<<16)); + + fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); + fetchTransformedBilinear_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1, y2); + + const uchar *s1 = texture.scanLine(y1); + const uchar *s2 = texture.scanLine(y2); + + buf1[i * 2 + 0] = reinterpret_cast<const QRgba64 *>(s1)[x1]; + buf1[i * 2 + 1] = reinterpret_cast<const QRgba64 *>(s1)[x2]; + buf2[i * 2 + 0] = reinterpret_cast<const QRgba64 *>(s2)[x1]; + buf2[i * 2 + 1] = reinterpret_cast<const QRgba64 *>(s2)[x2]; fx += fdx; fy += fdy; @@ -3307,23 +3807,31 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, co fw += fdw; } - layout->convertToARGB64PM((QRgba64 *)buf1, sbuf1, len * 2, clut, 0); - layout->convertToARGB64PM((QRgba64 *)buf2, sbuf2, len * 2, clut, 0); + convert(buf1, len * 2); + convert(buf2, len * 2); for (int i = 0; i < len; ++i) { int distx = distxs[i]; int disty = distys[i]; - b[i] = interpolate_4_pixels_rgb64((QRgba64 *)buf1 + i*2, (QRgba64 *)buf2 + i*2, distx, disty); + b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty); } length -= len; b += len; } } - return buffer; } +template<TextureBlendType blendType> +static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64(QRgba64 *buffer, const Operator *, + const QSpanData *data, int y, int x, int length) +{ + if (qPixelLayouts[data->texture.format].bpp == QPixelLayout::BPP64) + return fetchTransformedBilinear64_uint64<blendType>(buffer, data, y, x, length); + return fetchTransformedBilinear64_uint32<blendType>(buffer, data, y, x, length); +} + // FetchUntransformed can have more specialized methods added depending on SIMD features. static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = { 0, // Invalid @@ -3351,6 +3859,9 @@ static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = { fetchUntransformed, // Format_A2RGB30_Premultiplied fetchUntransformed, // Alpha8 fetchUntransformed, // Grayscale8 + fetchUntransformed, // RGBX64 + fetchUntransformed, // RGBA64 + fetchUntransformed, // RGBA64_Premultiplied }; static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = { @@ -3398,6 +3909,15 @@ static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = { fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled }; +static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = { + fetchUntransformedRGBA64PM, // Untransformed + fetchUntransformedRGBA64PM, // Tiled + fetchTransformed64<BlendTransformed>, // Transformed + fetchTransformed64<BlendTransformedTiled>, // TransformedTiled + fetchTransformedBilinear64<BlendTransformedBilinear>, // Bilinear + fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled +}; + static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format) { if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied) @@ -3411,6 +3931,13 @@ static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage: return sourceFetchGeneric[blendType]; } +static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format) +{ + if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64_Premultiplied) + return sourceFetchRGBA64PM[blendType]; + return sourceFetchGeneric64[blendType]; +} + #define FIXPT_BITS 8 #define FIXPT_SIZE (1<<FIXPT_BITS) @@ -3753,7 +4280,7 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in case QSpanData::Texture: solidSource = !data->texture.hasAlpha; op.srcFetch = getSourceFetch(getBlendType(data), data->texture.format); - op.srcFetch64 = sourceFetchGeneric64[getBlendType(data)]; + op.srcFetch64 = getSourceFetch64(getBlendType(data), data->texture.format);; break; default: Q_UNREACHABLE(); @@ -3781,8 +4308,9 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in // If all spans are opaque we do not need to fetch dest. // But don't clear passthrough destFetch as they are just as fast and save destStore. if (op.destFetch != destFetchARGB32P) - op.destFetch = 0; - op.destFetch64 = destFetch64Undefined; + op.destFetch = destFetchUndefined; + if (op.destFetch64 != destFetchRGB64) + op.destFetch64 = destFetch64Undefined; } } @@ -3816,7 +4344,7 @@ void blend_color_generic(int count, const QSpan *spans, void *userData) int length = spans->len; while (length) { int l = qMin(BufferSize, length); - uint *dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer; + uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans->y, l); op.funcSolid(dest, l, color, spans->coverage); if (op.destStore) op.destStore(data->rasterBuffer, x, spans->y, dest, l); @@ -3831,7 +4359,7 @@ static void blend_color_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); - Operator op = getOperator(data, spans, count); + const Operator op = getOperator(data, spans, count); const uint color = data->solid.color.toArgb32(); if (op.mode == QPainter::CompositionMode_Source) { @@ -3890,7 +4418,8 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) int l = qMin(BufferSize, length); QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l); op.funcSolid64(dest, l, color, spans->coverage); - op.destStore64(data->rasterBuffer, x, spans->y, dest, l); + if (op.destStore64) + op.destStore64(data->rasterBuffer, x, spans->y, dest, l); length -= l; x += l; } @@ -4053,7 +4582,7 @@ public: const uint *fetch(int x, int y, int len) { - dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, y, len) : buffer; + dest = op.destFetch(buffer, data->rasterBuffer, x, y, len); return op.srcFetch(src_buffer, &op, data, y, x, len); } @@ -4079,7 +4608,7 @@ public: bool isSupported() const { - return op.func64 && op.destFetch64 && op.destStore64; + return op.func64 && op.destFetch64; } const quint64 *fetch(int x, int y, int len) @@ -4095,7 +4624,8 @@ public: void store(int x, int y, int len) { - op.destStore64(data->rasterBuffer, x, y, (QRgba64 *)dest, len); + if (op.destStore64) + op.destStore64(data->rasterBuffer, x, y, (QRgba64 *)dest, len); } }; @@ -4151,7 +4681,7 @@ static void blend_untransformed_generic(int count, const QSpan *spans, void *use while (length) { int l = qMin(BufferSize, length); const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l); - uint *dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer; + uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans->y, l); op.func(dest, src, l, coverage); if (op.destStore) op.destStore(data->rasterBuffer, x, spans->y, dest, l); @@ -4202,7 +4732,8 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l); QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l); op.func64(dest, src, l, coverage); - op.destStore64(data->rasterBuffer, x, spans->y, dest, l); + if (op.destStore64) + op.destStore64(data->rasterBuffer, x, spans->y, dest, l); x += l; sx += l; length -= l; @@ -4392,7 +4923,7 @@ static void blend_tiled_generic(int count, const QSpan *spans, void *userData) if (BufferSize < l) l = BufferSize; const uint *src = op.srcFetch(src_buffer, &op, data, sy, sx, l); - uint *dest = op.destFetch ? op.destFetch(buffer, data->rasterBuffer, x, spans->y, l) : buffer; + uint *dest = op.destFetch(buffer, data->rasterBuffer, x, spans->y, l); op.func(dest, src, l, coverage); if (op.destStore) op.destStore(data->rasterBuffer, x, spans->y, dest, l); @@ -4490,7 +5021,8 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD const QRgba64 *src = op.srcFetch64((QRgba64 *)src_buffer, &op, data, sy, sx, l); QRgba64 *dest = op.destFetch64((QRgba64 *)buffer, data->rasterBuffer, x, spans->y, l); op.func64(dest, src, l, coverage); - op.destStore64(data->rasterBuffer, x, spans->y, dest, l); + if (op.destStore64) + op.destStore64(data->rasterBuffer, x, spans->y, dest, l); x += l; sx += l; length -= l; @@ -4703,6 +5235,9 @@ void qBlendTexture(int count, const QSpan *spans, void *userData) case QImage::Format_A2BGR30_Premultiplied: case QImage::Format_RGB30: case QImage::Format_A2RGB30_Premultiplied: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: proc = processTextureSpansGeneric64[blendType]; break; case QImage::Format_Invalid: @@ -4974,7 +5509,8 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, const int coverage = map[j + (i - x)]; alphamapblend_generic(coverage, dest, j, srcColor, color, colorProfile); } - destStore64(rasterBuffer, i, y + ly, dest, l); + if (destStore64) + destStore64(rasterBuffer, i, y + ly, dest, l); length -= l; i += l; } @@ -5004,7 +5540,8 @@ static void qt_alphamapblit_generic(QRasterBuffer *rasterBuffer, const int coverage = map[xp - x]; alphamapblend_generic(coverage, dest, xp - start, srcColor, color, colorProfile); } - destStore64(rasterBuffer, start, clip.y, dest, end - start); + if (destStore64) + destStore64(rasterBuffer, start, clip.y, dest, end - start); } // for (i -> line.count) map += mapStride; } // for (yp -> bottom) @@ -5251,7 +5788,8 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, const uint coverage = src[j + (i - x)]; alphargbblend_generic(coverage, dest, j, srcColor, color, colorProfile); } - destStore64(rasterBuffer, i, y + ly, dest, l); + if (destStore64) + destStore64(rasterBuffer, i, y + ly, dest, l); length -= l; i += l; } @@ -5281,7 +5819,8 @@ static void qt_alphargbblit_generic(QRasterBuffer *rasterBuffer, const uint coverage = src[xp - x]; alphargbblend_generic(coverage, dest, xp - start, srcColor, color, colorProfile); } - destStore64(rasterBuffer, start, clip.y, dest, end - start); + if (destStore64) + destStore64(rasterBuffer, start, clip.y, dest, end - start); } // for (i -> line.count) src += srcStride; } // for (yp -> bottom) @@ -5433,6 +5972,17 @@ static void qt_rectfill_gray(QRasterBuffer *rasterBuffer, qGray(color.toArgb32()), x, y, width, height, rasterBuffer->bytesPerLine()); } +static void qt_rectfill_quint64(QRasterBuffer *rasterBuffer, + int x, int y, int width, int height, + const QRgba64 &color) +{ + const auto store = qStoreFromRGBA64PM[rasterBuffer->format]; + quint64 c64; + store(reinterpret_cast<uchar *>(&c64), &color, 0, 1, nullptr, nullptr); + qt_rectfill<quint64>(reinterpret_cast<quint64 *>(rasterBuffer->buffer()), + c64, x, y, width, height, rasterBuffer->bytesPerLine()); +} + // Map table for destination image format. Contains function pointers // for blends of various types unto the destination @@ -5647,6 +6197,33 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = qt_alphargbblit_generic, qt_rectfill_gray }, + // Format_RGBX64 + { + blend_color_generic_rgb64, + blend_src_generic_rgb64, + 0, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_quint64 + }, + // Format_RGBA64 + { + blend_color_generic_rgb64, + blend_src_generic_rgb64, + 0, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_quint64 + }, + // Format_RGBA64_Premultiplied + { + blend_color_generic_rgb64, + blend_src_generic_rgb64, + 0, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_quint64 + }, }; #if defined(Q_CC_MSVC) && !defined(_MIPS_) @@ -5795,10 +6372,6 @@ static void qInitDrawhelperFunctions() qBlendFunctions[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; qBlendFunctions[QImage::Format_RGBA8888_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = qt_blend_argb32_on_argb32_ssse3; sourceFetchUntransformed[QImage::Format_RGB888] = qt_fetchUntransformed_888_ssse3; -#ifndef __SSSE3__ - extern const uint * QT_FASTCALL fetchPixelsBPP24_ssse3(uint *dest, const uchar*src, int index, int count); - qFetchPixels[QPixelLayout::BPP24] = fetchPixelsBPP24_ssse3; -#endif } #endif // SSSE3 @@ -5816,6 +6389,10 @@ static void qInitDrawhelperFunctions() const QVector<QRgb> *, QDitherInfo *); extern void QT_FASTCALL storeRGBXFromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); + extern void QT_FASTCALL storeARGB32FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); + extern void QT_FASTCALL storeRGBA8888FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *); extern void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length); extern void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length); qPixelLayouts[QImage::Format_ARGB32].fetchToARGB32PM = fetchARGB32ToARGB32PM_sse4; @@ -5827,6 +6404,8 @@ static void qInitDrawhelperFunctions() qPixelLayouts[QImage::Format_RGBX8888].storeFromARGB32PM = storeRGBXFromARGB32PM_sse4; qPixelLayouts[QImage::Format_A2BGR30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>; qPixelLayouts[QImage::Format_A2RGB30_Premultiplied].storeFromARGB32PM = storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>; + qStoreFromRGBA64PM[QImage::Format_ARGB32] = storeARGB32FromRGBA64PM_sse4; + qStoreFromRGBA64PM[QImage::Format_RGBA8888] = storeRGBA8888FromRGBA64PM_sse4; destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4; destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4; } diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index bddd6899e3..078ab62251 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -1143,6 +1143,8 @@ static Q_ALWAYS_INLINE const uint *qt_convertRGBA8888ToARGB32PM(uint *buffer, co return buffer; } +template<bool RGBA> void qt_convertRGBA64ToARGB32(uint *dst, const QRgba64 *src, int count); + const uint qt_bayer_matrix[16][16] = { { 0x1, 0xc0, 0x30, 0xf0, 0xc, 0xcc, 0x3c, 0xfc, 0x3, 0xc3, 0x33, 0xf3, 0xf, 0xcf, 0x3f, 0xff}, @@ -1232,9 +1234,15 @@ typedef const uint *(QT_FASTCALL *FetchAndConvertPixelsFunc)(uint *buffer, const typedef void (QT_FASTCALL *ConvertAndStorePixelsFunc)(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *clut, QDitherInfo *dither); +typedef const QRgba64 *(QT_FASTCALL *FetchAndConvertPixelsFunc64)(QRgba64 *buffer, const uchar *src, int index, int count, + const QVector<QRgb> *clut, QDitherInfo *dither); +typedef void (QT_FASTCALL *ConvertAndStorePixelsFunc64)(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *clut, QDitherInfo *dither); + typedef void (QT_FASTCALL *ConvertFunc)(uint *buffer, int count, const QVector<QRgb> *clut); -typedef const QRgba64 *(QT_FASTCALL *ConvertFunc64)(QRgba64 *buffer, const uint *src, int count, - const QVector<QRgb> *clut, QDitherInfo *dither); +typedef void (QT_FASTCALL *Convert64Func)(quint64 *buffer, int count, const QVector<QRgb> *clut); +typedef const QRgba64 *(QT_FASTCALL *ConvertTo64Func)(QRgba64 *buffer, const uint *src, int count, + const QVector<QRgb> *clut, QDitherInfo *dither); typedef void (QT_FASTCALL *RbSwapFunc)(uchar *dst, const uchar *src, int count); @@ -1249,6 +1257,7 @@ struct QPixelLayout BPP16, BPP24, BPP32, + BPP64, BPPCount }; @@ -1257,17 +1266,19 @@ struct QPixelLayout BPP bpp; RbSwapFunc rbSwap; ConvertFunc convertToARGB32PM; - ConvertFunc64 convertToARGB64PM; + ConvertTo64Func convertToRGBA64PM; FetchAndConvertPixelsFunc fetchToARGB32PM; + FetchAndConvertPixelsFunc64 fetchToRGBA64PM; ConvertAndStorePixelsFunc storeFromARGB32PM; ConvertAndStorePixelsFunc storeFromRGB32; }; +extern ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats]; + extern QPixelLayout qPixelLayouts[QImage::NImageFormats]; extern MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3]; - QT_END_NAMESPACE #endif // QDRAWHELPER_P_H diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp index 0b6f963168..e3cc1dd43e 100644 --- a/src/gui/painting/qdrawhelper_sse4.cpp +++ b/src/gui/painting/qdrawhelper_sse4.cpp @@ -332,6 +332,20 @@ void QT_FASTCALL destStore64RGBA8888_sse4(QRasterBuffer *rasterBuffer, int x, in convertARGBFromRGBA64PM_sse4<true>(dest, buffer, length); } +void QT_FASTCALL storeARGB32FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = (uint*)dest + index; + convertARGBFromRGBA64PM_sse4<false>(d, src, count); +} + +void QT_FASTCALL storeRGBA8888FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, int index, int count, + const QVector<QRgb> *, QDitherInfo *) +{ + uint *d = (uint*)dest + index; + convertARGBFromRGBA64PM_sse4<true>(d, src, count); +} + template void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>(uchar *dest, const uint *src, int index, int count, const QVector<QRgb> *, QDitherInfo *); diff --git a/src/gui/painting/qmemrotate.cpp b/src/gui/painting/qmemrotate.cpp index 25aa6a3122..43aeff3268 100644 --- a/src/gui/painting/qmemrotate.cpp +++ b/src/gui/painting/qmemrotate.cpp @@ -239,6 +239,12 @@ inline void qt_memrotate90_template<quint32>(const quint32 *src, int w, int h, i qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride); } +template <> +inline void qt_memrotate90_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride) +{ + qt_memrotate90_tiled_unpacked(src, w, h, sstride, dest, dstride); +} + template <class T> Q_STATIC_TEMPLATE_FUNCTION inline void qt_memrotate180_template(const T *src, int w, int h, int sstride, T *dest, int dstride) @@ -275,6 +281,12 @@ inline void qt_memrotate270_template<quint32>(const quint32 *src, int w, int h, qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); } +template <> +inline void qt_memrotate270_template<quint64>(const quint64 *src, int w, int h, int sstride, quint64 *dest, int dstride) +{ + qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); +} + #define QT_IMPL_MEMROTATE(type) \ Q_GUI_EXPORT void qt_memrotate90(const type *src, int w, int h, int sstride, \ type *dest, int dstride) \ @@ -309,9 +321,7 @@ Q_GUI_EXPORT void qt_memrotate270(const type *src, int w, int h, int sstride, \ qt_memrotate270_tiled_unpacked(src, w, h, sstride, dest, dstride); \ } - - - +QT_IMPL_MEMROTATE(quint64) QT_IMPL_MEMROTATE(quint32) QT_IMPL_MEMROTATE(quint16) QT_IMPL_MEMROTATE(quint24) @@ -377,6 +387,22 @@ void qt_memrotate270_32(const uchar *srcPixels, int w, int h, int sbpl, uchar *d qt_memrotate270((const uint *)srcPixels, w, h, sbpl, (uint *)destPixels, dbpl); } + +void qt_memrotate90_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate90((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl); +} + +void qt_memrotate180_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate180((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl); +} + +void qt_memrotate270_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate270((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl); +} + MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3] = // 90, 180, 270 { @@ -387,6 +413,7 @@ MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3] = { qt_memrotate90_16, qt_memrotate180_16, qt_memrotate270_16 }, // BPP16, { qt_memrotate90_24, qt_memrotate180_24, qt_memrotate270_24 }, // BPP24 { qt_memrotate90_32, qt_memrotate180_32, qt_memrotate270_32 }, // BPP32 + { qt_memrotate90_64, qt_memrotate180_64, qt_memrotate270_64 }, // BPP64 }; QT_END_NAMESPACE diff --git a/src/gui/painting/qmemrotate_p.h b/src/gui/painting/qmemrotate_p.h index 9bc3fd1010..677fd57af9 100644 --- a/src/gui/painting/qmemrotate_p.h +++ b/src/gui/painting/qmemrotate_p.h @@ -65,6 +65,7 @@ QT_DECL_MEMROTATE(quint32); QT_DECL_MEMROTATE(quint16); QT_DECL_MEMROTATE(quint24); QT_DECL_MEMROTATE(quint8); +QT_DECL_MEMROTATE(quint64); #undef QT_DECL_MEMROTATE diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index d32913b822..08aa00df56 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -291,6 +291,12 @@ static QLatin1String formatToString(QImage::Format format) return QLatin1String("Alpha8"); case QImage::Format_Grayscale8: return QLatin1String("Grayscale8"); + case QImage::Format_RGBX64: + return QLatin1String("RGBx64"); + case QImage::Format_RGBA64: + return QLatin1String("RGBA64"); + case QImage::Format_RGBA64_Premultiplied: + return QLatin1String("RGBA64pm"); default: break; }; @@ -2347,7 +2353,9 @@ void tst_QImage::rgbSwapped_data() { QTest::addColumn<QImage::Format>("format"); - for (int i = QImage::Format_Indexed8; i < QImage::Format_Alpha8; ++i) { + for (int i = QImage::Format_Indexed8; i < QImage::NImageFormats; ++i) { + if (i == QImage::Format_Alpha8 || i == QImage::Format_Grayscale8) + continue; QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i); } } @@ -2515,14 +2523,7 @@ void tst_QImage::mirrored() void tst_QImage::inplaceRgbSwapped_data() { - QTest::addColumn<QImage::Format>("format"); - - QTest::newRow("Format_ARGB32_Premultiplied") << QImage::Format_ARGB32_Premultiplied; - QTest::newRow("Format_RGBA8888") << QImage::Format_RGBA8888; - QTest::newRow("Format_A2RGB30_Premultiplied") << QImage::Format_A2RGB30_Premultiplied; - QTest::newRow("Format_RGB888") << QImage::Format_RGB888; - QTest::newRow("Format_RGB16") << QImage::Format_RGB16; - QTest::newRow("Format_Indexed8") << QImage::Format_Indexed8; + rgbSwapped_data(); } void tst_QImage::inplaceRgbSwapped() @@ -2553,9 +2554,9 @@ void tst_QImage::inplaceRgbSwapped() for (int i = 0; i < imageSwapped.width(); ++i) { QRgb referenceColor = testColor[i]; QRgb swappedColor = imageSwapped.pixel(i, 0); - QCOMPARE(qRed(swappedColor) & 0xf8, qBlue(referenceColor) & 0xf8); - QCOMPARE(qGreen(swappedColor) & 0xf8, qGreen(referenceColor) & 0xf8); - QCOMPARE(qBlue(swappedColor) & 0xf8, qRed(referenceColor) & 0xf8); + QCOMPARE(qRed(swappedColor) & 0xf0, qBlue(referenceColor) & 0xf0); + QCOMPARE(qGreen(swappedColor) & 0xf0, qGreen(referenceColor) & 0xf0); + QCOMPARE(qBlue(swappedColor) & 0xf0, qRed(referenceColor) & 0xf0); } QCOMPARE(imageSwapped.constScanLine(0), orginalPtr); @@ -2771,9 +2772,13 @@ void tst_QImage::genericRgbConversion_data() QTest::addColumn<QImage::Format>("format"); QTest::addColumn<QImage::Format>("dest_format"); - for (int i = QImage::Format_RGB32; i < QImage::Format_Alpha8; ++i) { + for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) { + if (i == QImage::Format_Alpha8 || i == QImage::Format_Grayscale8) + continue; const QLatin1String formatI = formatToString(QImage::Format(i)); - for (int j = QImage::Format_RGB32; j < QImage::Format_Alpha8; ++j) { + for (int j = QImage::Format_RGB32; j < QImage::NImageFormats; ++j) { + if (j == QImage::Format_Alpha8 || j == QImage::Format_Grayscale8) + continue; if (i == j) continue; QTest::addRow("%s -> %s", formatI.data(), formatToString(QImage::Format(j)).data()) @@ -2810,8 +2815,12 @@ void tst_QImage::inplaceRgbConversion_data() QTest::addColumn<QImage::Format>("format"); QTest::addColumn<QImage::Format>("dest_format"); - for (int i = QImage::Format_RGB32; i < QImage::Format_Alpha8; ++i) { - for (int j = QImage::Format_RGB32; j < QImage::Format_Alpha8; ++j) { + for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) { + if (i == QImage::Format_Alpha8 || i == QImage::Format_Grayscale8) + continue; + for (int j = QImage::Format_RGB32; j < QImage::NImageFormats; ++j) { + if (j == QImage::Format_Alpha8 || j == QImage::Format_Grayscale8) + continue; if (i == j) continue; QTest::addRow("%s -> %s", formatToString(QImage::Format(i)).data(), formatToString(QImage::Format(j)).data()) @@ -2844,10 +2853,10 @@ void tst_QImage::inplaceRgbConversion() QCOMPARE(qGreen(convertedColor) & 0xF0, i * 16); } } - if (image.depth() == imageConverted.depth()) + if (qt_depthForFormat(format) == qt_depthForFormat(dest_format)) QCOMPARE(imageConverted.constScanLine(0), originalPtr); - { + if (qt_depthForFormat(format) <= 32) { // Test attempted inplace conversion of images created on existing buffer static const quint32 readOnlyData[] = { 0xff0102ffU, 0xff0506ffU, 0xff0910ffU, 0xff1314ffU }; quint32 readWriteData[] = { 0xff0102ffU, 0xff0506ffU, 0xff0910ffU, 0xff1314ffU }; @@ -2980,7 +2989,9 @@ void tst_QImage::invertPixelsRGB_data() { QTest::addColumn<QImage::Format>("image_format"); - for (int i = QImage::Format_RGB32; i < QImage::Format_Alpha8; ++i) { + for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) { + if (i == QImage::Format_Alpha8 || i == QImage::Format_Grayscale8) + continue; QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i); } } diff --git a/tests/auto/gui/image/qimagereader/images/kollada-16bpc.png b/tests/auto/gui/image/qimagereader/images/kollada-16bpc.png Binary files differnew file mode 100644 index 0000000000..b99d5f8a6f --- /dev/null +++ b/tests/auto/gui/image/qimagereader/images/kollada-16bpc.png diff --git a/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp b/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp index 170f551dbf..0f8afe63b3 100644 --- a/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp +++ b/tests/auto/gui/image/qimagereader/tst_qimagereader.cpp @@ -234,6 +234,7 @@ void tst_QImageReader::readImage_data() QTest::newRow("BMP: high mask bit set") << QString("rgb32bf.bmp") << true << QByteArray("bmp"); QTest::newRow("XPM: marble") << QString("marble.xpm") << true << QByteArray("xpm"); QTest::newRow("PNG: kollada") << QString("kollada.png") << true << QByteArray("png"); + QTest::newRow("PNG: kollada 16bpc") << QString("kollada-16bpc.png") << true << QByteArray("png"); QTest::newRow("PPM: teapot") << QString("teapot.ppm") << true << QByteArray("ppm"); QTest::newRow("PPM: runners") << QString("runners.ppm") << true << QByteArray("ppm"); QTest::newRow("PPM: test") << QString("test.ppm") << true << QByteArray("ppm"); @@ -523,6 +524,7 @@ void tst_QImageReader::imageFormat_data() QTest::newRow("bmp-4") << QString("test32v5.bmp") << QByteArray("bmp") << QImage::Format_RGB32; QTest::newRow("png") << QString("kollada.png") << QByteArray("png") << QImage::Format_ARGB32; QTest::newRow("png-2") << QString("YCbCr_cmyk.png") << QByteArray("png") << QImage::Format_RGB32; + QTest::newRow("png-3") << QString("kollada-16bpc.png") << QByteArray("png") << QImage::Format_RGBA64; QTest::newRow("svg") << QString("rect.svg") << QByteArray("svg") << QImage::Format_ARGB32_Premultiplied; QTest::newRow("svgz") << QString("rect.svgz") << QByteArray("svgz") << QImage::Format_ARGB32_Premultiplied; } @@ -1850,6 +1852,8 @@ void tst_QImageReader::saveFormat_data() QTest::newRow("Format_RGB888") << QImage::Format_RGB888; QTest::newRow("Format_RGB444") << QImage::Format_RGB444; QTest::newRow("Format_ARGB4444_Premultiplied") << QImage::Format_ARGB4444_Premultiplied; + QTest::newRow("Format_RGBA64") << QImage::Format_RGBA64; + QTest::newRow("Format_RGBA64_Premultiplied") << QImage::Format_RGBA64_Premultiplied; } void tst_QImageReader::saveFormat() diff --git a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp index 58ac025f0c..877306e8b8 100644 --- a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp +++ b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp @@ -1091,6 +1091,7 @@ void tst_QPainter::fillRect_data() QTest::newRow("argb32pm") << QImage::Format_ARGB32_Premultiplied; QTest::newRow("rgba8888pm") << QImage::Format_RGBA8888_Premultiplied; + QTest::newRow("rgba64pm") << QImage::Format_RGBA64_Premultiplied; } void tst_QPainter::fillRect() @@ -2443,6 +2444,24 @@ void tst_QPainter::setOpacity_data() QTest::newRow("A2RGB30P on RGB30") << QImage::Format_RGB30 << QImage::Format_A2RGB30_Premultiplied; + QTest::newRow("RGBA64P on RGBA64P") << QImage::Format_RGBA64_Premultiplied + << QImage::Format_RGBA64_Premultiplied; + + QTest::newRow("RGBA64 on RGBA64") << QImage::Format_RGBA64 + << QImage::Format_RGBA64; + + QTest::newRow("RGBx64 on RGBx64") << QImage::Format_RGBX64 + << QImage::Format_RGBX64; + + QTest::newRow("RGBA64P on ARGB32P") << QImage::Format_ARGB32_Premultiplied + << QImage::Format_RGBA64_Premultiplied; + + QTest::newRow("RGBx64 on ARGB32P") << QImage::Format_ARGB32_Premultiplied + << QImage::Format_RGBX64; + + QTest::newRow("ARGB32P on RGBA64P") << QImage::Format_RGBA64_Premultiplied + << QImage::Format_ARGB32_Premultiplied; + } void tst_QPainter::setOpacity() @@ -3847,6 +3866,8 @@ void tst_QPainter::gradientPixelFormat_data() QTest::newRow("rgbx8888") << QImage::Format_RGBX8888; QTest::newRow("rgba8888") << QImage::Format_RGBA8888; QTest::newRow("rgba8888_pm") << QImage::Format_RGBA8888_Premultiplied; + QTest::newRow("rgbx64") << QImage::Format_RGBX64; + QTest::newRow("rgba64_pm") << QImage::Format_RGBA64_Premultiplied; } void tst_QPainter::gradientPixelFormat() @@ -4784,7 +4805,19 @@ void tst_QPainter::blendARGBonRGB_data() << QPainter::CompositionMode_SourceIn << qRgba(255, 0, 0, 127) << 255 ; QTest::newRow("ARGB_PM source-in ARGB32") << QImage::Format_ARGB32 << QImage::Format_ARGB32_Premultiplied << QPainter::CompositionMode_SourceIn << qRgba(127, 0, 0, 127) << 255; - // Only ARGB does inverse premultiply, on the rest over and source gives similar results: + QTest::newRow("ARGB over RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32 + << QPainter::CompositionMode_SourceOver << qRgba(255, 0, 0, 127) << 127; + QTest::newRow("ARGB_PM over RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32_Premultiplied + << QPainter::CompositionMode_SourceOver << qRgba(127, 0, 0, 127) << 127; + QTest::newRow("ARGB source RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32 + << QPainter::CompositionMode_Source << qRgba(255, 0, 0, 127) << 255; + QTest::newRow("ARGB_PM source RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32_Premultiplied + << QPainter::CompositionMode_Source << qRgba(127, 0, 0, 127) << 255; + QTest::newRow("ARGB source-in RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32 + << QPainter::CompositionMode_SourceIn << qRgba(255, 0, 0, 127) << 255; + QTest::newRow("ARGB_PM source-in RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32_Premultiplied + << QPainter::CompositionMode_SourceIn << qRgba(127, 0, 0, 127) << 255; + // Only ARGB32 and RGBA8888 does inverse premultiply, on the rest over and source gives similar results: QTest::newRow("ARGB over RGB32") << QImage::Format_RGB32 << QImage::Format_ARGB32 << QPainter::CompositionMode_SourceOver << qRgba(255, 0, 0, 127) << 127; QTest::newRow("ARGB_PM over RGB32") << QImage::Format_RGB32 << QImage::Format_ARGB32_Premultiplied @@ -4821,18 +4854,6 @@ void tst_QPainter::blendARGBonRGB_data() << QPainter::CompositionMode_SourceIn << qRgba(255, 0, 0, 127) << 127; QTest::newRow("ARGB_PM source-in RGBx8888") << QImage::Format_RGBX8888 << QImage::Format_ARGB32_Premultiplied << QPainter::CompositionMode_SourceIn << qRgba(127, 0, 0, 127) << 127; - QTest::newRow("ARGB over RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32 - << QPainter::CompositionMode_SourceOver << qRgba(255, 0, 0, 127) << 127; - QTest::newRow("ARGB_PM over RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32_Premultiplied - << QPainter::CompositionMode_SourceOver << qRgba(127, 0, 0, 127) << 127; - QTest::newRow("ARGB source RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32 - << QPainter::CompositionMode_Source << qRgba(255, 0, 0, 127) << 255; - QTest::newRow("ARGB_PM source RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32_Premultiplied - << QPainter::CompositionMode_Source << qRgba(127, 0, 0, 127) << 255; - QTest::newRow("ARGB source-in RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32 - << QPainter::CompositionMode_SourceIn << qRgba(255, 0, 0, 127) << 255; - QTest::newRow("ARGB_PM source-in RGBA8888") << QImage::Format_RGBA8888 << QImage::Format_ARGB32_Premultiplied - << QPainter::CompositionMode_SourceIn << qRgba(127, 0, 0, 127) << 255; QTest::newRow("ARGB over RGB16") << QImage::Format_RGB16 << QImage::Format_ARGB32 << QPainter::CompositionMode_SourceOver << qRgba(255, 0, 0, 127) << 123; QTest::newRow("ARGB_PM over RGB16") << QImage::Format_RGB16 << QImage::Format_ARGB32_Premultiplied diff --git a/tests/auto/gui/qopengl/tst_qopengl.cpp b/tests/auto/gui/qopengl/tst_qopengl.cpp index 5b1af9b6c9..f1360b9efe 100644 --- a/tests/auto/gui/qopengl/tst_qopengl.cpp +++ b/tests/auto/gui/qopengl/tst_qopengl.cpp @@ -82,6 +82,8 @@ private slots: void fboRendering(); void fboRenderingRGB30_data(); void fboRenderingRGB30(); + void fboRenderingRGB64_data(); + void fboRenderingRGB64(); void fboHandleNulledAfterContextDestroyed(); void fboMRT(); void fboMRT_differentFormats(); @@ -614,6 +616,10 @@ void tst_QOpenGL::fboRenderingRGB30_data() #define GL_RGB10_A2 0x8059 #endif +#ifndef GL_RGBA16 +#define GL_RGBA16 0x805B +#endif + #ifndef GL_FRAMEBUFFER_RENDERABLE #define GL_FRAMEBUFFER_RENDERABLE 0x8289 #endif @@ -622,7 +628,7 @@ void tst_QOpenGL::fboRenderingRGB30_data() #define GL_FULL_SUPPORT 0x82B7 #endif -static bool hasRGB10A2(QOpenGLContext *ctx) +static bool supportsInternalFboFormat(QOpenGLContext *ctx, int glFormat) { if (ctx->format().majorVersion() < 3) return false; @@ -631,7 +637,7 @@ static bool hasRGB10A2(QOpenGLContext *ctx) GLint value = -1; QOpenGLFunctions_4_2_Core* vFuncs = ctx->versionFunctions<QOpenGLFunctions_4_2_Core>(); if (vFuncs && vFuncs->initializeOpenGLFunctions()) { - vFuncs->glGetInternalformativ(GL_TEXTURE_2D, GL_RGB10_A2, GL_FRAMEBUFFER_RENDERABLE, 1, &value); + vFuncs->glGetInternalformativ(GL_TEXTURE_2D, glFormat, GL_FRAMEBUFFER_RENDERABLE, 1, &value); if (value != GL_FULL_SUPPORT) return false; } @@ -657,7 +663,7 @@ void tst_QOpenGL::fboRenderingRGB30() if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()) QSKIP("QOpenGLFramebufferObject not supported on this platform"); - if (!hasRGB10A2(&ctx)) + if (!supportsInternalFboFormat(&ctx, GL_RGB10_A2)) QSKIP("An internal RGB30_A2 format is not guaranteed on this platform"); // No multisample with combined depth/stencil attachment: @@ -713,6 +719,71 @@ void tst_QOpenGL::fboRenderingRGB30() QVERIFY(((pixel >> 20) & 0x3f) > 0); } +void tst_QOpenGL::fboRenderingRGB64_data() +{ + common_data(); +} + +void tst_QOpenGL::fboRenderingRGB64() +{ +#if defined(Q_OS_LINUX) && defined(Q_CC_GNU) && !defined(__x86_64__) + QSKIP("QTBUG-22617"); +#endif + + QFETCH(int, surfaceClass); + QScopedPointer<QSurface> surface(createSurface(surfaceClass)); + + QOpenGLContext ctx; + QVERIFY(ctx.create()); + + QVERIFY(ctx.makeCurrent(surface.data())); + + if (!QOpenGLFramebufferObject::hasOpenGLFramebufferObjects()) + QSKIP("QOpenGLFramebufferObject not supported on this platform"); + + if (!supportsInternalFboFormat(&ctx, GL_RGBA16)) + QSKIP("An internal RGBA16 format is not guaranteed on this platform"); + + // No multisample with combined depth/stencil attachment: + QOpenGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + fboFormat.setInternalTextureFormat(GL_RGBA16); + + // Uncomplicate things by using POT: + const QSize size(256, 128); + QOpenGLFramebufferObject fbo(size, fboFormat); + + if (fbo.attachment() != QOpenGLFramebufferObject::CombinedDepthStencil) + QSKIP("FBOs missing combined depth~stencil support"); + + QVERIFY(fbo.bind()); + + QPainter fboPainter; + QOpenGLPaintDevice device(fbo.width(), fbo.height()); + bool painterBegun = fboPainter.begin(&device); + QVERIFY(painterBegun); + + qt_opengl_draw_test_pattern(&fboPainter, fbo.width(), fbo.height()); + + fboPainter.end(); + + QImage fb = fbo.toImage(); + QCOMPARE(fb.format(), QImage::Format_RGBA64_Premultiplied); + QCOMPARE(fb.size(), size); + + qt_opengl_check_test_pattern(fb); + + // Check rendering can handle precise 16 bit color values. + fboPainter.begin(&device); + fboPainter.fillRect(QRect(0, 0, 256, 128), QColor::fromRgba64(5, 1002, 8001, 65535)); + fboPainter.end(); + fb = fbo.toImage(); + QRgba64 pixel = ((QRgba64*)fb.bits())[0]; + QCOMPARE(pixel.red(), 5); + QCOMPARE(pixel.green(), 1002); + QCOMPARE(pixel.blue(), 8001); +} + void tst_QOpenGL::fboHandleNulledAfterContextDestroyed() { QWindow window; @@ -844,7 +915,7 @@ void tst_QOpenGL::fboMRT_differentFormats() if (!f->hasOpenGLFeature(QOpenGLFunctions::MultipleRenderTargets)) QSKIP("Multiple render targets not supported on this platform"); - if (!hasRGB10A2(&ctx)) + if (!supportsInternalFboFormat(&ctx, GL_RGB10_A2)) QSKIP("RGB10_A2 not supported on this platform"); // 3 color attachments, same size, different internal format, depth/stencil. diff --git a/tests/auto/other/lancelot/paintcommands.cpp b/tests/auto/other/lancelot/paintcommands.cpp index 377b98fa41..65a688ec40 100644 --- a/tests/auto/other/lancelot/paintcommands.cpp +++ b/tests/auto/other/lancelot/paintcommands.cpp @@ -177,6 +177,9 @@ const char *PaintCommands::imageFormatTable[] = { "Format_A2RGB30_Premultiplied", "Alpha8", "Grayscale8", + "RGBx64", + "RGBA64", + "RGBA64_Premultiplied", }; int PaintCommands::translateEnum(const char *table[], const QString &pattern, int limit) diff --git a/tests/benchmarks/gui/painting/qpainter/tst_qpainter.cpp b/tests/benchmarks/gui/painting/qpainter/tst_qpainter.cpp index ca41d7f570..485306a8ac 100644 --- a/tests/benchmarks/gui/painting/qpainter/tst_qpainter.cpp +++ b/tests/benchmarks/gui/painting/qpainter/tst_qpainter.cpp @@ -649,6 +649,9 @@ void tst_QPainter::drawPixmapImage_data_helper(bool pixmaps) "A2RGB30_pm", "Alpha8", "Grayscale8", + "RGBx64", + "RGBA64", + "RGBA64_pm", }; const QImage::Format pixmapFormats[] = { |