diff options
26 files changed, 5173 insertions, 276 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index c46075d3ed..7eb901f778 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -195,6 +195,7 @@ qt_internal_add_module(Gui painting/qregion.cpp painting/qregion.h painting/qrgb.h painting/qrgba64.h painting/qrgba64_p.h + painting/qrgbaf.h painting/qstroker.cpp painting/qstroker_p.h painting/qtextureglyphcache.cpp painting/qtextureglyphcache_p.h painting/qtransform.cpp painting/qtransform.h diff --git a/src/gui/configure.cmake b/src/gui/configure.cmake index 5650126326..49b4753870 100644 --- a/src/gui/configure.cmake +++ b/src/gui/configure.cmake @@ -1154,6 +1154,11 @@ qt_feature("raster-64bit" PRIVATE LABEL "QPainter - 64 bit raster" PURPOSE "Internal painting support for 64 bit (16 bpc) rasterization." ) +qt_feature("raster-fp" PRIVATE + SECTION "Painting" + LABEL "QPainter - floating point raster" + PURPOSE "Internal painting support for floating point rasterization." +) qt_feature("undocommand" PUBLIC SECTION "Utilities" LABEL "QUndoCommand" diff --git a/src/gui/configure.json b/src/gui/configure.json index f8c945fc21..47398c9a4e 100644 --- a/src/gui/configure.json +++ b/src/gui/configure.json @@ -1663,6 +1663,12 @@ "section": "Painting", "output": [ "privateFeature" ] }, + "raster-fp": { + "label": "QPainter - floating point raster", + "purpose": "Internal painting support for floating point rasterization.", + "section": "Painting", + "output": [ "privateFeature" ] + }, "undocommand": { "label": "QUndoCommand", "purpose": "Applies (redo or) undo of a single change in a document.", diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 95be9772c8..4ee781a383 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -42,10 +42,12 @@ #include "qbuffer.h" #include "qdatastream.h" #include "qcolortransform.h" +#include "qfloat16.h" #include "qmap.h" #include "qtransform.h" #include "qimagereader.h" #include "qimagewriter.h" +#include "qrgbaf.h" #include "qstringlist.h" #include "qvariant.h" #include "qimagepixmapcleanuphooks_p.h" @@ -293,6 +295,24 @@ bool QImageData::checkForAlphaPixels() const bits += bytes_per_line; } } break; + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: { + uchar *bits = data; + for (int y = 0; y < height && !has_alpha_pixels; ++y) { + for (int x = 0; x < width; ++x) + has_alpha_pixels |= ((qfloat16 *)bits)[x * 4 + 3] < 1.0f; + bits += bytes_per_line; + } + } break; + case QImage::Format_RGBA32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: { + uchar *bits = data; + for (int y = 0; y < height && !has_alpha_pixels; ++y) { + for (int x = 0; x < width; ++x) + has_alpha_pixels |= ((float *)bits)[x * 4 + 3] < 1.0f; + bits += bytes_per_line; + } + } break; case QImage::Format_RGB32: case QImage::Format_RGB16: @@ -307,6 +327,8 @@ bool QImageData::checkForAlphaPixels() const case QImage::Format_Grayscale8: case QImage::Format_Grayscale16: case QImage::Format_RGBX64: + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBX32FPx4: break; case QImage::Format_Invalid: case QImage::NImageFormats: @@ -735,6 +757,16 @@ bool QImageData::checkForAlphaPixels() const \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) \value Format_BGR888 The image is stored using a 24-bit BGR format. (added in Qt 5.14) + \value Format_RGBX16FPx4 The image is stored using a 4 16-bit halfword floating point RGBx format (16FP-16FP-16FP-16FP). + This is the same as the Format_RGBA16FPx4 except alpha must always be 1.0. (added in Qt 6.2) + \value Format_RGBA16FPx4 The image is stored using a 4 16-bit halfword floating point RGBA format (16FP-16FP-16FP-16FP). (added in Qt 6.2) + \value Format_RGBA16FPx4_Premultiplied The image is stored using a premultiplied 4 16-bit halfword floating point + RGBA format (16FP-16FP-16FP-16FP). (added in Qt 6.2) + \value Format_RGBX32FPx4 The image is stored using a 4 32-bit floating point RGBx format (32FP-32FP-32FP-32FP). + This is the same as the Format_RGBA32FPx4 except alpha must always be 1.0. (added in Qt 6.2) + \value Format_RGBA32FPx4 The image is stored using a 4 32-bit floating point RGBA format (32FP-32FP-32FP-32FP). (added in Qt 6.2) + \value Format_RGBA32FPx4_Premultiplied The image is stored using a premultiplied 4 32-bit floating point + RGBA format (32FP-32FP-32FP-32FP). (added in Qt 6.2) \note Drawing into a QImage with QImage::Format_Indexed8 is not supported. @@ -1741,11 +1773,29 @@ 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) { + } else if (d->format >= QImage::Format_RGBX64 && d->format <= QImage::Format_RGBA64_Premultiplied) { qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), QRgba64::fromArgb32(pixel), 0, 0, d->width, d->height, d->bytes_per_line); return; + } else if (d->format >= QImage::Format_RGBX16FPx4 && d->format <= QImage::Format_RGBA16FPx4_Premultiplied) { + quint64 cu; + QRgba16F cf = QRgba16F::fromArgb32(pixel); + ::memcpy(&cu, &cf, sizeof(quint64)); + qt_rectfill<quint64>(reinterpret_cast<quint64*>(d->data), cu, + 0, 0, d->width, d->height, d->bytes_per_line); + return; + } else if (d->format >= QImage::Format_RGBX32FPx4 && d->format <= QImage::Format_RGBA32FPx4_Premultiplied) { + QRgba32F cf = QRgba32F::fromArgb32(pixel); + uchar *data = d->data; + for (int y = 0; y < d->height; ++y) { + QRgba32F *line = reinterpret_cast<QRgba32F *>(data); + for (int x = 0; x < d->width; ++x) + line[x] = cf; + data += d->bytes_per_line; + } + return; } + Q_ASSERT(d->depth == 32); if (d->format == Format_RGB32) pixel |= 0xff000000; @@ -1907,7 +1957,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 (depth() > 32) { + if (d->format == QImage::Format_RGBA16FPx4_Premultiplied) { + if (!d->convertInPlace(QImage::Format_RGBA16FPx4, { })) + *this = convertToFormat(QImage::Format_RGBA16FPx4); + } else if (d->format == QImage::Format_RGBA32FPx4_Premultiplied) { + if (!d->convertInPlace(QImage::Format_RGBA32FPx4, { })) + *this = convertToFormat(QImage::Format_RGBA32FPx4); + } else if (depth() > 32) { if (!d->convertInPlace(QImage::Format_RGBA64, { })) *this = convertToFormat(QImage::Format_RGBA64); } else { @@ -1926,8 +1982,32 @@ void QImage::invertPixels(InvertMode mode) *sl++ ^= 0xff; sl += pad; } - } - else if (depth() == 64) { + } else if (format() >= QImage::Format_RGBX16FPx4 && format() <= QImage::Format_RGBA16FPx4_Premultiplied) { + qfloat16 *p = reinterpret_cast<qfloat16 *>(d->data); + qfloat16 *end = reinterpret_cast<qfloat16 *>(d->data + d->nbytes); + while (p < end) { + p[0] = 1.0f - p[0]; + p[1] = 1.0f - p[1]; + p[2] = 1.0f - p[2]; + if (mode == InvertRgba) + p[3] = 1.0f - p[3]; + p += 4; + } + } else if (format() >= QImage::Format_RGBX32FPx4 && format() <= QImage::Format_RGBA32FPx4_Premultiplied) { + uchar *data = d->data; + for (int y = 0; y < d->height; ++y) { + float *p = reinterpret_cast<float *>(data); + for (int x = 0; x < d->width; ++x) { + p[0] = 1.0f - p[0]; + p[1] = 1.0f - p[1]; + p[2] = 1.0f - p[2]; + if (mode == InvertRgba) + p[3] = 1.0f - p[3]; + p += 4; + } + data += d->bytes_per_line; + } + } else if (depth() == 64) { quint16 *p = (quint16*)d->data; quint16 *end = (quint16*)(d->data + d->nbytes); quint16 xorbits = 0xffff; @@ -2085,7 +2165,12 @@ QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags fl if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8) { if (qt_highColorPrecision(d->format, !destLayout->hasAlphaChannel) && qt_highColorPrecision(format, !hasAlphaChannel())) { - converter = convert_generic_over_rgb64; +#if QT_CONFIG(raster_fp) + if (qt_fpColorPrecision(d->format) && qt_fpColorPrecision(format)) + converter = convert_generic_over_rgba32f; + else +#endif + converter = convert_generic_over_rgb64; } else converter = convert_generic; } @@ -2418,6 +2503,14 @@ QRgb QImage::pixel(int x, int y) const case Format_RGBA64: // Match ARGB32 behavior. case Format_RGBA64_Premultiplied: return reinterpret_cast<const QRgba64 *>(s)[x].toArgb32(); + case Format_RGBX16FPx4: + case Format_RGBA16FPx4: // Match ARGB32 behavior. + case Format_RGBA16FPx4_Premultiplied: + return reinterpret_cast<const QRgba16F *>(s)[x].toArgb32(); + case Format_RGBX32FPx4: + case Format_RGBA32FPx4: // Match ARGB32 behavior. + case Format_RGBA32FPx4_Premultiplied: + return reinterpret_cast<const QRgba32F *>(s)[x].toArgb32(); default: break; } @@ -2519,6 +2612,20 @@ void QImage::setPixel(int x, int y, uint index_or_rgb) case Format_RGBA64_Premultiplied: ((QRgba64 *)s)[x] = QRgba64::fromArgb32(index_or_rgb); return; + case Format_RGBX16FPx4: + ((QRgba16F *)s)[x] = QRgba16F::fromArgb32(index_or_rgb | 0xff000000); + return; + case Format_RGBA16FPx4: + case Format_RGBA16FPx4_Premultiplied: + ((QRgba16F *)s)[x] = QRgba16F::fromArgb32(index_or_rgb); + return; + case Format_RGBX32FPx4: + ((QRgba32F *)s)[x] = QRgba32F::fromArgb32(index_or_rgb | 0xff000000); + return; + case Format_RGBA32FPx4: + case Format_RGBA32FPx4_Premultiplied: + ((QRgba32F *)s)[x] = QRgba32F::fromArgb32(index_or_rgb); + return; case Format_Invalid: case NImageFormats: Q_ASSERT(false); @@ -2583,6 +2690,26 @@ QColor QImage::pixelColor(int x, int y) const quint16 v = reinterpret_cast<const quint16 *>(s)[x]; return QColor(qRgba64(v, v, v, 0xffff)); } + case Format_RGBX16FPx4: + case Format_RGBA16FPx4: + case Format_RGBA16FPx4_Premultiplied: { + QRgba16F p = reinterpret_cast<const QRgba16F *>(s)[x]; + if (d->format == Format_RGBA16FPx4_Premultiplied) + p = p.unpremultiplied(); + QColor color; + color.setRgbF(p.red(), p.green(), p.blue(), p.alpha()); + return color; + } + case Format_RGBX32FPx4: + case Format_RGBA32FPx4: + case Format_RGBA32FPx4_Premultiplied: { + QRgba32F p = reinterpret_cast<const QRgba32F *>(s)[x]; + if (d->format == Format_RGBA32FPx4_Premultiplied) + p = p.unpremultiplied(); + QColor color; + color.setRgbF(p.red(), p.green(), p.blue(), p.alpha()); + return color; + } default: c = QRgba64::fromArgb32(pixel(x, y)); break; @@ -2658,6 +2785,32 @@ void QImage::setPixelColor(int x, int y, const QColor &color) case Format_RGBA64_Premultiplied: ((QRgba64 *)s)[x] = c; return; + case Format_RGBX16FPx4: + case Format_RGBA16FPx4: + case Format_RGBA16FPx4_Premultiplied: { + float r, g, b, a; + color.getRgbF(&r, &g, &b, &a); + if (d->format == Format_RGBX16FPx4) + a = 1.0f; + QRgba16F c16f{r, g, b, a}; + if (d->format == Format_RGBA16FPx4_Premultiplied) + c16f = c16f.premultiplied(); + ((QRgba16F *)s)[x] = c16f; + return; + } + case Format_RGBX32FPx4: + case Format_RGBA32FPx4: + case Format_RGBA32FPx4_Premultiplied: { + float r, g, b, a; + color.getRgbF(&r, &g, &b, &a); + if (d->format == Format_RGBX32FPx4) + a = 1.0f; + QRgba32F c32f{r, g, b, a}; + if (d->format == Format_RGBA32FPx4_Premultiplied) + c32f = c32f.premultiplied(); + ((QRgba32F *)s)[x] = c32f; + return; + } default: setPixel(x, y, c.toArgb32()); return; @@ -3218,6 +3371,9 @@ inline void do_mirror(QImageData *dst, QImageData *src, bool horizontal, bool ve } switch (depth) { + case 128: + do_mirror_data<QRgba32F>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h); + break; case 64: do_mirror_data<quint64>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h); break; @@ -4409,8 +4565,12 @@ int QImage::bitPlaneCount() const bpc = 12; break; case QImage::Format_RGBX64: + case QImage::Format_RGBX16FPx4: bpc = 48; break; + case QImage::Format_RGBX32FPx4: + bpc = 96; + break; default: bpc = qt_depthForFormat(d->format); break; @@ -4429,7 +4589,8 @@ int QImage::bitPlaneCount() const if necessary. To avoid unnecessary conversion the result is returned in the format internally used, and not in the original format. */ -QImage QImage::smoothScaled(int w, int h) const { +QImage QImage::smoothScaled(int w, int h) const +{ QImage src = *this; switch (src.format()) { case QImage::Format_RGB32: @@ -4443,14 +4604,28 @@ QImage QImage::smoothScaled(int w, int h) const { case QImage::Format_RGBA64_Premultiplied: break; case QImage::Format_RGBA64: - src = src.convertToFormat(QImage::Format_RGBA64_Premultiplied); + case QImage::Format_Grayscale16: + src.convertTo(QImage::Format_RGBA64_Premultiplied); + break; +#endif +#if QT_CONFIG(raster_fp) + case QImage::Format_RGBX32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: + break; + case QImage::Format_RGBX16FPx4: + src.convertTo(QImage::Format_RGBX32FPx4); + break; + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: + case QImage::Format_RGBA32FPx4: + src.convertTo(QImage::Format_RGBA32FPx4_Premultiplied); break; #endif default: if (src.hasAlphaChannel()) - src = src.convertToFormat(QImage::Format_ARGB32_Premultiplied); + src.convertTo(QImage::Format_ARGB32_Premultiplied); else - src = src.convertToFormat(QImage::Format_RGB32); + src.convertTo(QImage::Format_RGB32); } src = qSmoothScaleImage(src, w, h); if (!src.isNull()) @@ -4903,6 +5078,10 @@ bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFla // any direct ones are probably better even if not inplace. if (qt_highColorPrecision(newFormat, !qPixelLayouts[newFormat].hasAlphaChannel) && qt_highColorPrecision(format, !qPixelLayouts[format].hasAlphaChannel)) { +#if QT_CONFIG(raster_fp) + if (qt_fpColorPrecision(format) && qt_fpColorPrecision(newFormat)) + return convert_generic_inplace_over_rgba32f(this, newFormat, flags); +#endif return convert_generic_inplace_over_rgb64(this, newFormat, flags); } return convert_generic_inplace(this, newFormat, flags); @@ -5328,6 +5507,84 @@ static constexpr QPixelFormat pixelformats[] = { /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, /*INTERPRETATION*/ QPixelFormat::UnsignedByte, /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBX16FPx4: + 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::FloatingPoint, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBA16FPx4: + 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::FloatingPoint, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBA16FPx4_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::FloatingPoint, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBX32FPx4: + QPixelFormat(QPixelFormat::RGB, + /*RED*/ 32, + /*GREEN*/ 32, + /*BLUE*/ 32, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 32, + /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtEnd, + /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, + /*INTERPRETATION*/ QPixelFormat::FloatingPoint, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBA32FPx4: + QPixelFormat(QPixelFormat::RGB, + /*RED*/ 32, + /*GREEN*/ 32, + /*BLUE*/ 32, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 32, + /*ALPHA USAGE*/ QPixelFormat::UsesAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtEnd, + /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied, + /*INTERPRETATION*/ QPixelFormat::FloatingPoint, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), + //QImage::Format_RGBA32FPx4_Premultiplied: + QPixelFormat(QPixelFormat::RGB, + /*RED*/ 32, + /*GREEN*/ 32, + /*BLUE*/ 32, + /*FOURTH*/ 0, + /*FIFTH*/ 0, + /*ALPHA*/ 32, + /*ALPHA USAGE*/ QPixelFormat::UsesAlpha, + /*ALPHA POSITION*/ QPixelFormat::AtEnd, + /*PREMULTIPLIED*/ QPixelFormat::Premultiplied, + /*INTERPRETATION*/ QPixelFormat::FloatingPoint, + /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), }; static_assert(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats); diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index ecb38af172..885c804457 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -104,6 +104,12 @@ public: Format_RGBA64_Premultiplied, Format_Grayscale16, Format_BGR888, + Format_RGBX16FPx4, + Format_RGBA16FPx4, + Format_RGBA16FPx4_Premultiplied, + Format_RGBX32FPx4, + Format_RGBA32FPx4, + Format_RGBA32FPx4_Premultiplied, #ifndef Q_QDOC NImageFormats #endif diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 7415c86b9b..5825397a21 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -47,6 +47,7 @@ #include <private/qimage_p.h> #include <qendian.h> +#include <qrgbaf.h> #if QT_CONFIG(thread) #include <qsemaphore.h> #include <qthreadpool.h> @@ -313,6 +314,61 @@ void convert_generic_over_rgb64(QImageData *dest, const QImageData *src, Qt::Ima #endif } +#if QT_CONFIG(raster_fp) +void convert_generic_over_rgba32f(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(dest->format >= QImage::Format_RGBX16FPx4); + Q_ASSERT(src->format >= QImage::Format_RGBX16FPx4); + + const FetchAndConvertPixelsFuncFP fetch = qFetchToRGBA32F[src->format]; + const ConvertAndStorePixelsFuncFP store = qStoreFromRGBA32F[dest->format]; + + auto convertSegment = [=](int yStart, int yEnd) { + QRgba32F buf[BufferSize]; + QRgba32F *buffer = buf; + const uchar *srcData = src->data + yStart * src->bytes_per_line; + uchar *destData = dest->data + yStart * dest->bytes_per_line; + for (int y = yStart; y < yEnd; ++y) { + int x = 0; + while (x < src->width) { + int l = src->width - x; + if (dest->depth == 128) + buffer = reinterpret_cast<QRgba32F *>(destData) + x; + else + l = qMin(l, BufferSize); + const QRgba32F *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr); + store(destData, ptr, x, l, nullptr, nullptr); + x += l; + } + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; + } + }; +#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS + int segments = (qsizetype(src->width) * src->height) >> 16; + segments = std::min(segments, src->height); + + QThreadPool *threadPool = QThreadPool::globalInstance(); + if (segments <= 1 || !threadPool || threadPool->contains(QThread::currentThread())) + return convertSegment(0, src->height); + + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (src->height - y) / (segments - i); + threadPool->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); +#else + convertSegment(0, src->height); +#endif +} +#endif + 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. @@ -337,7 +393,7 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im return false; } - Q_ASSERT(destLayout->bpp != QPixelLayout::BPP64); + Q_ASSERT(destLayout->bpp < QPixelLayout::BPP64); FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM; ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM; if (!srcLayout->hasAlphaChannel && destLayout->storeFromRGB32) { @@ -535,6 +591,102 @@ bool convert_generic_inplace_over_rgb64(QImageData *data, QImage::Format dst_for return true; } +#if QT_CONFIG(raster_fp) +bool convert_generic_inplace_over_rgba32f(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format >= QImage::Format_RGBX16FPx4); + Q_ASSERT(dst_format >= QImage::Format_RGBX16FPx4); + const int destDepth = qt_depthForFormat(dst_format); + if (data->depth < destDepth) + return false; + + const QPixelLayout *srcLayout = &qPixelLayouts[data->format]; + const QPixelLayout *destLayout = &qPixelLayouts[dst_format]; + + QImageData::ImageSizeParameters params = { data->bytes_per_line, data->nbytes }; + if (data->depth != destDepth) { + params = QImageData::calculateImageParameters(data->width, data->height, destDepth); + if (!params.isValid()) + return false; + } + + FetchAndConvertPixelsFuncFP fetch = qFetchToRGBA32F[data->format]; + ConvertAndStorePixelsFuncFP store = qStoreFromRGBA32F[dst_format]; + if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied && + destLayout->hasAlphaChannel && !destLayout->premultiplied) { + // Avoid unnecessary premultiply and unpremultiply when converting between two unpremultiplied formats. + // This abuses the fact unpremultiplied formats are always before their premultiplied counterparts. + fetch = qFetchToRGBA32F[data->format + 1]; + store = qStoreFromRGBA32F[dst_format + 1]; + } + + auto convertSegment = [=](int yStart, int yEnd) { + QRgba32F buf[BufferSize]; + QRgba32F *buffer = buf; + uchar *srcData = data->data + yStart * data->bytes_per_line; + uchar *destData = srcData; + for (int y = yStart; y < yEnd; ++y) { + int x = 0; + while (x < data->width) { + int l = data->width - x; + if (srcLayout->bpp == QPixelLayout::BPP32FPx4) + buffer = reinterpret_cast<QRgba32F *>(srcData) + x; + else + l = qMin(l, BufferSize); + const QRgba32F *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr); + store(destData, ptr, x, l, nullptr, nullptr); + x += l; + } + srcData += data->bytes_per_line; + destData += params.bytesPerLine; + } + }; +#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS + int segments = (qsizetype(data->width) * data->height) >> 16; + segments = std::min(segments, data->height); + QThreadPool *threadPool = QThreadPool::globalInstance(); + if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (data->height - y) / (segments - i); + threadPool->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + if (data->bytes_per_line != params.bytesPerLine) { + // Compress segments to a continuous block + y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (data->height - y) / (segments - i); + uchar *srcData = data->data + data->bytes_per_line * y; + uchar *destData = data->data + params.bytesPerLine * y; + if (srcData != destData) + memmove(destData, srcData, params.bytesPerLine * yn); + y += yn; + } + } + } else +#endif + convertSegment(0, data->height); + if (params.totalSize != data->nbytes) { + Q_ASSERT(params.totalSize < data->nbytes); + void *newData = realloc(data->data, params.totalSize); + if (newData) { + data->data = (uchar *)newData; + data->nbytes = params.totalSize; + } + data->bytes_per_line = params.bytesPerLine; + } + data->depth = destDepth; + data->format = dst_format; + return true; +} +#endif + static void convert_passthrough(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { Q_ASSERT(src->width == dest->width); @@ -1424,6 +1576,55 @@ static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt } } +template<bool MaskAlpha> +static void convert_RGBA16FPM_to_RGBA16F(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) +{ + Q_ASSERT(src->format == QImage::Format_RGBA16FPx4_Premultiplied); + Q_ASSERT(dest->format == QImage::Format_RGBA16FPx4 || dest->format == QImage::Format_RGBX16FPx4); + 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 QRgba16F *src_data = reinterpret_cast<const QRgba16F *>(src->data); + QRgba16F *dest_data = reinterpret_cast<QRgba16F *>(dest->data); + + for (int i = 0; i < src->height; ++i) { + const QRgba16F *end = src_data + src->width; + while (src_data < end) { + *dest_data = src_data->unpremultiplied(); + if (MaskAlpha) + dest_data->setAlpha(1.0f); + ++src_data; + ++dest_data; + } + src_data += src_pad; + dest_data += dest_pad; + } +} + +template<bool MaskAlpha> +static bool convert_RGBA16FPM_to_RGBA16F_inplace(QImageData *data, Qt::ImageConversionFlags) +{ + Q_ASSERT(data->format == QImage::Format_RGBA16FPx4_Premultiplied); + + const int pad = (data->bytes_per_line >> 3) - data->width; + QRgba16F *rgb_data = reinterpret_cast<QRgba16F *>(data->data); + + for (int i = 0; i < data->height; ++i) { + const QRgba16F *end = rgb_data + data->width; + while (rgb_data < end) { + *rgb_data = rgb_data->unpremultiplied(); + if (MaskAlpha) + rgb_data->setAlpha(1.0f); + ++rgb_data; + } + rgb_data += pad; + } + data->format = MaskAlpha ? QImage::Format_RGBX16FPx4 : QImage::Format_RGBA16FPx4; + return true; +} + static QList<QRgb> fix_color_table(const QList<QRgb> &ctbl, QImage::Format format) { QList<QRgb> colorTable = ctbl; @@ -2421,6 +2622,12 @@ static void qInitImageConversions() qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888_Premultiplied] = convert_RGB888_to_RGB<false>; #endif + qimage_converter_map[QImage::Format_RGBX16FPx4][QImage::Format_RGBA16FPx4] = convert_passthrough; + qimage_converter_map[QImage::Format_RGBX16FPx4][QImage::Format_RGBA16FPx4_Premultiplied] = convert_passthrough; + + qimage_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4] = convert_passthrough; + qimage_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4_Premultiplied] = convert_passthrough; + // Inline converters: qimage_inplace_converter_map[QImage::Format_Indexed8][QImage::Format_Grayscale8] = convert_Indexed8_to_Grayscale8_inplace; @@ -2526,6 +2733,16 @@ static void qInitImageConversions() qimage_inplace_converter_map[QImage::Format_BGR888][QImage::Format_RGB888] = convert_rgbswap_generic_inplace; + qimage_inplace_converter_map[QImage::Format_RGBX16FPx4][QImage::Format_RGBA16FPx4] = + convert_passthrough_inplace<QImage::Format_RGBA16FPx4>; + qimage_inplace_converter_map[QImage::Format_RGBX16FPx4][QImage::Format_RGBA16FPx4_Premultiplied] = + convert_passthrough_inplace<QImage::Format_RGBA16FPx4_Premultiplied>; + + qimage_inplace_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4] = + convert_passthrough_inplace<QImage::Format_RGBA32FPx4>; + qimage_inplace_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4_Premultiplied] = + convert_passthrough_inplace<QImage::Format_RGBA32FPx4_Premultiplied>; + // Now architecture specific conversions: #if defined(__SSE2__) && defined(QT_COMPILER_SUPPORTS_SSSE3) if (qCpuHasFeature(SSSE3)) { diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index 64f5be9e76..a2c50adc43 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -161,6 +161,10 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio void convert_generic_over_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags); bool convert_generic_inplace_over_rgb64(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags); +#if QT_CONFIG(raster_fp) +void convert_generic_over_rgba32f(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags); +bool convert_generic_inplace_over_rgba32f(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags); +#endif void dither_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags, bool fromalpha); @@ -216,8 +220,16 @@ inline int qt_depthForFormat(QImage::Format format) case QImage::Format_RGBX64: case QImage::Format_RGBA64: case QImage::Format_RGBA64_Premultiplied: + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: depth = 64; break; + case QImage::Format_RGBX32FPx4: + case QImage::Format_RGBA32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: + depth = 128; + break; } return depth; } @@ -247,6 +259,12 @@ inline QImage::Format qt_opaqueVersion(QImage::Format format) case QImage::Format_RGBA64: case QImage::Format_RGBA64_Premultiplied: return QImage::Format_RGBX64; + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: + return QImage::Format_RGBX16FPx4; + case QImage::Format_RGBA32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: + return QImage::Format_RGBX32FPx4; case QImage::Format_ARGB32_Premultiplied: case QImage::Format_ARGB32: return QImage::Format_RGB32; @@ -261,6 +279,8 @@ inline QImage::Format qt_opaqueVersion(QImage::Format format) case QImage::Format_BGR30: case QImage::Format_RGB30: case QImage::Format_RGBX64: + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBX32FPx4: case QImage::Format_Grayscale8: case QImage::Format_Grayscale16: return format; @@ -300,6 +320,12 @@ inline QImage::Format qt_alphaVersion(QImage::Format format) case QImage::Format_RGBA64: case QImage::Format_Grayscale16: return QImage::Format_RGBA64_Premultiplied; + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBA16FPx4: + return QImage::Format_RGBA16FPx4_Premultiplied; + case QImage::Format_RGBX32FPx4: + case QImage::Format_RGBA32FPx4: + return QImage::Format_RGBA32FPx4_Premultiplied; case QImage::Format_ARGB32_Premultiplied: case QImage::Format_ARGB8565_Premultiplied: case QImage::Format_ARGB8555_Premultiplied: @@ -309,6 +335,8 @@ inline QImage::Format qt_alphaVersion(QImage::Format format) case QImage::Format_A2BGR30_Premultiplied: case QImage::Format_A2RGB30_Premultiplied: case QImage::Format_RGBA64_Premultiplied: + case QImage::Format_RGBA16FPx4_Premultiplied: + case QImage::Format_RGBA32FPx4_Premultiplied: return format; case QImage::Format_Mono: case QImage::Format_MonoLSB: @@ -339,6 +367,12 @@ inline bool qt_highColorPrecision(QImage::Format format, bool opaque = false) case QImage::Format_RGBA64: case QImage::Format_RGBA64_Premultiplied: case QImage::Format_Grayscale16: + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: + case QImage::Format_RGBX32FPx4: + case QImage::Format_RGBA32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: return true; default: break; @@ -346,6 +380,21 @@ inline bool qt_highColorPrecision(QImage::Format format, bool opaque = false) return false; } +inline bool qt_fpColorPrecision(QImage::Format format) +{ + switch (format) { + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: + case QImage::Format_RGBX32FPx4: + case QImage::Format_RGBA32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: + return true; + default: + break; + } + return false; +} inline QImage::Format qt_maybeAlphaVersionWithSameDepth(QImage::Format format) { diff --git a/src/gui/painting/qcompositionfunctions.cpp b/src/gui/painting/qcompositionfunctions.cpp index 65b03a1e12..3fc9aa3e4e 100644 --- a/src/gui/painting/qcompositionfunctions.cpp +++ b/src/gui/painting/qcompositionfunctions.cpp @@ -38,8 +38,10 @@ ****************************************************************************/ #include <qglobal.h> + #include "qdrawhelper_p.h" #include "qrgba64_p.h" +#include "qrgbaf.h" QT_BEGIN_NAMESPACE @@ -117,6 +119,8 @@ struct Argb32OperationsC const Argb32OperationsC::Type Argb32OperationsC::clear = 0; +typedef Argb32OperationsC Argb32Operations; + struct Rgba64OperationsBase { typedef QRgba64 Type; @@ -319,9 +323,188 @@ typedef Rgba64OperationsNEON Rgba64Operations; #else typedef Rgba64OperationsC Rgba64Operations; #endif + #endif // QT_CONFIG(raster_64bit) -typedef Argb32OperationsC Argb32Operations; +#if QT_CONFIG(raster_fp) + +static inline QRgba32F qRgba32f(float r, float g, float b, float a) +{ + return QRgba32F{r, g, b, a}; +} + +struct RgbaFPOperationsBase +{ + typedef QRgba32F Type; + typedef float Scalar; + + static inline constexpr Type clear = { 0, 0, 0, 0 }; + + static bool isOpaque(Type val) + { return val.a >= 1.0f; } + static bool isTransparent(Type val) + { return val.a <= 0.0f; } + static Scalar scalarFrom8bit(uint8_t a) + { return a * (1.0f / 255.0f); } + + static void memfill(Type *ptr, Type value, qsizetype len) + { + for (qsizetype i = 0; i < len; ++i) + ptr[i] = value; + } + static void memcpy(Type *Q_DECL_RESTRICT dest, const Type *Q_DECL_RESTRICT src, qsizetype len) + { ::memcpy(dest, src, len * sizeof(Type)); } +}; + +struct RgbaFPOperationsC : RgbaFPOperationsBase +{ + typedef QRgba32F OptimalType; + typedef float OptimalScalar; + + static OptimalType load(const Type *ptr) + { + return QRgba32F { ptr->r, ptr->g, ptr->b, ptr->a }; + } + static OptimalType convert(const Type &val) + { + return QRgba32F { val.r, val.g, val.b, val.a }; + } + static void store(Type *ptr, OptimalType value) + { + ptr->r = value.r; + ptr->g = value.g; + ptr->b = value.b; + ptr->a = value.a; + } + static OptimalType add(OptimalType a, OptimalType b) + { + a.r += b.r; + a.g += b.g; + a.b += b.b; + a.a += b.a; + return a; + } + static OptimalScalar add(OptimalScalar a, OptimalScalar b) + { return a + b; } + static OptimalType plus(OptimalType a, OptimalType b) + { + a = add(a, b); // no saturation on color values + if (a.a < 0.0f) a.a = 0.0f; + else if (a.a > 1.0f) a.a = 1.0f; + return a; + } + static OptimalScalar alpha(OptimalType val) + { return val.a; } + static OptimalScalar invAlpha(OptimalScalar c) + { return 1.0f - c; } + static OptimalScalar invAlpha(OptimalType val) + { return 1.0f - val.a; } + static OptimalScalar scalar(Scalar v) + { return v; } + static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a) + { + val.r *= a; + val.g *= a; + val.b *= a; + val.a *= a; + return val; + } + static OptimalScalar multiplyAlpha8bit(OptimalScalar val, uint8_t a) + { + return val * a * (1.0f / 255.0f); + } + static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, OptimalScalar a2) + { + return add(multiplyAlpha(x, a1), multiplyAlpha(y, a2)); + } + static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a) + { + return multiplyAlpha(val, a * (1.0f / 255.0f)); + } + static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2) + { + return add(multiplyAlpha8bit(x, a1), multiplyAlpha8bit(y, a2)); + } +}; + +#if defined(__SSE2__) +struct RgbaFPOperationsSSE2 : public RgbaFPOperationsBase +{ + typedef __m128 OptimalType; + typedef __m128 OptimalScalar; + + static OptimalType load(const Type *ptr) + { + return _mm_load_ps(reinterpret_cast<const float *>(ptr)); + } + static OptimalType convert(const Type &value) + { + return load(&value); + } + static void store(Type *ptr, OptimalType value) + { + _mm_store_ps(reinterpret_cast<float *>(ptr), value); + } + static OptimalType add(OptimalType a, OptimalType b) + { + return _mm_add_ps(a, b); + } +// same as above: +// static OptimalScalar add(OptimalScalar a, OptimalScalar b) + static OptimalType plus(OptimalType a, OptimalType b) + { + a = _mm_add_ps(a, b); + __m128 aa = _mm_min_ps(a, _mm_set1_ps(1.0f)); + aa = _mm_max_ps(aa, _mm_set1_ps(0.0f)); + // An indirect insert using only SSE2: + aa = _mm_shuffle_ps(aa, a, _MM_SHUFFLE(2, 2, 3, 3)); + a = _mm_shuffle_ps(a, aa, _MM_SHUFFLE(0, 2, 1, 0)); + return a; + } + static OptimalScalar alpha(OptimalType c) + { + return _mm_shuffle_ps(c, c, _MM_SHUFFLE(3, 3, 3, 3)); + } + static OptimalScalar invAlpha(Scalar c) + { + return _mm_set1_ps(1.0f - float(c)); + } + static OptimalScalar invAlpha(OptimalType c) + { + return _mm_sub_ps(_mm_set1_ps(1.0f), alpha(c)); + } + static OptimalScalar scalar(Scalar n) + { + return _mm_set1_ps(float(n)); + } + static OptimalType multiplyAlpha(OptimalType val, OptimalScalar a) + { + return _mm_mul_ps(val, a); + } + static OptimalType interpolate(OptimalType x, OptimalScalar a1, OptimalType y, OptimalScalar a2) + { + return add(multiplyAlpha(x, a1), multiplyAlpha(y, a2)); + } + static OptimalType multiplyAlpha8bit(OptimalType val, uint8_t a) + { + return multiplyAlpha(val, _mm_set1_ps(a * (1.0f / 255.0f))); + } +// same as above: +// static OptimalScalar multiplyAlpha8bit(OptimalScalar a, uint8_t a) + static OptimalType interpolate8bit(OptimalType x, uint8_t a1, OptimalType y, uint8_t a2) + { + return add(multiplyAlpha8bit(x, a1), multiplyAlpha8bit(y, a2)); + } +}; +#endif + +#if defined(__SSE2__) +typedef RgbaFPOperationsSSE2 RgbaFPOperations; +#else +typedef RgbaFPOperationsC RgbaFPOperations; +#endif + +#endif // QT_CONFIG(raster_fp) /* result = 0 @@ -362,6 +545,17 @@ void QT_FASTCALL comp_func_Clear_rgb64(QRgba64 *dest, const QRgba64 *, int lengt } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_Clear_rgbafp(QRgba32F *dest, int length, QRgba32F, uint const_alpha) +{ + comp_func_Clear_template<RgbaFPOperations>(dest, length, const_alpha); +} + +void QT_FASTCALL comp_func_Clear_rgbafp(QRgba32F *dest, const QRgba32F *, int length, uint const_alpha) +{ + comp_func_Clear_template<RgbaFPOperations>(dest, length, const_alpha); +} +#endif /* result = s @@ -421,6 +615,18 @@ void QT_FASTCALL comp_func_Source_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRg } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_Source_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_Source_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_Source_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_Source_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + void QT_FASTCALL comp_func_solid_Destination(uint *, int, uint, uint) { } @@ -439,6 +645,16 @@ void QT_FASTCALL comp_func_Destination_rgb64(QRgba64 *, const QRgba64 *, int, ui } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_Destination_rgbafp(QRgba32F *, int, QRgba32F, uint) +{ +} + +void QT_FASTCALL comp_func_Destination_rgbafp(QRgba32F *, const QRgba32F *, int, uint) +{ +} +#endif + /* result = s + d * sia dest = (s + d * sia) * ca + d * cia @@ -509,6 +725,19 @@ void QT_FASTCALL comp_func_SourceOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_SourceOver_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_SourceOver_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + + +void QT_FASTCALL comp_func_SourceOver_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_SourceOver_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + /* result = d + s * dia dest = (d + s * dia) * ca + d * cia @@ -570,6 +799,18 @@ void QT_FASTCALL comp_func_DestinationOver_rgb64(QRgba64 *Q_DECL_RESTRICT dest, } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_DestinationOver_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_DestinationOver_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_DestinationOver_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_DestinationOver_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + /* result = s * da dest = s * da * ca + d * cia @@ -636,6 +877,18 @@ void QT_FASTCALL comp_func_SourceIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const Q } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_SourceIn_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_SourceIn_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_SourceIn_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_SourceIn_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + /* result = d * sa dest = d * sa * ca + d * cia @@ -697,6 +950,18 @@ void QT_FASTCALL comp_func_DestinationIn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, co } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_DestinationIn_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_DestinationIn_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_DestinationIn_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_DestinationIn_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + /* result = s * dia dest = s * dia * ca + d * cia @@ -761,6 +1026,18 @@ void QT_FASTCALL comp_func_SourceOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_SourceOut_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_SourceOut_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_SourceOut_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_SourceOut_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + /* result = d * sia dest = d * sia * ca + d * cia @@ -822,6 +1099,18 @@ void QT_FASTCALL comp_func_DestinationOut_rgb64(QRgba64 *Q_DECL_RESTRICT dest, c } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_DestinationOut_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_DestinationOut_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_DestinationOut_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_DestinationOut_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + /* result = s*da + d*sia dest = s*da*ca + d*sia*ca + d *cia @@ -883,6 +1172,18 @@ void QT_FASTCALL comp_func_SourceAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_SourceAtop_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_SourceAtop_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_SourceAtop_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_SourceAtop_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + /* result = d*sa + s*dia dest = d*sa*ca + s*dia*ca + d *cia @@ -949,6 +1250,18 @@ void QT_FASTCALL comp_func_DestinationAtop_rgb64(QRgba64 *Q_DECL_RESTRICT dest, } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_DestinationAtop_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_DestinationAtop_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_DestinationAtop_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_DestinationAtop_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + /* result = d*sia + s*dia dest = d*sia*ca + s*dia*ca + d *cia @@ -1011,15 +1324,35 @@ void QT_FASTCALL comp_func_XOR_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba6 } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_XOR_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_XOR_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_XOR_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_XOR_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + struct QFullCoverage { inline void store(uint *dest, const uint src) const { *dest = src; } +#if QT_CONFIG(raster_64bit) inline void store(QRgba64 *dest, const QRgba64 src) const { *dest = src; } +#endif +#if QT_CONFIG(raster_fp) + inline void store(QRgba32F *dest, const QRgba32F src) const + { + *dest = src; + } +#endif }; struct QPartialCoverage { @@ -1029,14 +1362,27 @@ struct QPartialCoverage { { } + template<typename Op> + inline void store_template(typename Op::Type *dest, const typename Op::Type src) const + { + Op::store(dest, Op::interpolate8bit(Op::convert(src), ca, Op::load(dest), ica)); + } inline void store(uint *dest, const uint src) const { - *dest = INTERPOLATE_PIXEL_255(src, ca, *dest, ica); + store_template<Argb32Operations>(dest, src); } +#if QT_CONFIG(raster_64bit) inline void store(QRgba64 *dest, const QRgba64 src) const { - *dest = interpolate255(src, ca, *dest, ica); + store_template<Rgba64Operations>(dest, src); } +#endif +#if QT_CONFIG(raster_fp) + inline void store(QRgba32F *dest, const QRgba32F src) const + { + store_template<RgbaFPOperations>(dest, src); + } +#endif private: const uint ca; @@ -1048,10 +1394,19 @@ static inline int mix_alpha(int da, int sa) return 255 - ((255 - sa) * (255 - da) >> 8); } +#if QT_CONFIG(raster_64bit) static inline uint mix_alpha_rgb64(uint da, uint sa) { return 65535U - ((65535U - sa) * (65535U - da) >> 16); } +#endif + +#if QT_CONFIG(raster_fp) +static inline float mix_alpha_rgbafp(float da, float sa) +{ + return 1.0f - (1.0f - sa) * (1.0f - da); +} +#endif /* Dca' = Sca.Da + Dca.Sa + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1122,6 +1477,18 @@ void QT_FASTCALL comp_func_Plus_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRgba } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_Plus_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + comp_func_solid_Plus_template<RgbaFPOperations>(dest, length, color, const_alpha); +} + +void QT_FASTCALL comp_func_Plus_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + comp_func_Plus_template<RgbaFPOperations>(dest, src, length, const_alpha); +} +#endif + /* Dca' = Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) */ @@ -1199,6 +1566,45 @@ void QT_FASTCALL comp_func_solid_Multiply_rgb64(QRgba64 *dest, int length, QRgba } #endif +#if QT_CONFIG(raster_fp) +static inline float multiply_op_rgbafp(float dst, float src, float da, float sa) +{ + return src * dst + src * (1.0f - da) + dst * (1.0f - sa); +} + +template <typename T> +static inline void comp_func_solid_Multiply_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a, b) multiply_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Multiply_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Multiply_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Multiply_impl(dest, length, color, QPartialCoverage(const_alpha)); +} +#endif + + template <typename T> static inline void comp_func_Multiply_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1259,6 +1665,37 @@ void QT_FASTCALL comp_func_Multiply_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const Q } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_Multiply_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) multiply_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Multiply_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Multiply_impl(dest, src, length, QFullCoverage()); + else + comp_func_Multiply_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + /* Dca' = (Sca.Da + Dca.Sa - Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) = Sca + Dca - Sca.Dca @@ -1327,6 +1764,39 @@ void QT_FASTCALL comp_func_solid_Screen_rgb64(QRgba64 *dest, int length, QRgba64 } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_solid_Screen_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a, b) (1.0f - ((1.0f - a) * (1.0f - b))) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Screen_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Screen_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Screen_impl(dest, length, color, QPartialCoverage(const_alpha)); +} +#endif + template <typename T> static inline void comp_func_Screen_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1387,6 +1857,37 @@ void QT_FASTCALL comp_func_Screen_rgb64(QRgba64 *dest, const QRgba64 *src, int l } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_Screen_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) (1.0f - ((1.0f - a) * (1.0f - b))) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Screen_rgbafp(QRgba32F *dest, const QRgba32F *src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Screen_impl(dest, src, length, QFullCoverage()); + else + comp_func_Screen_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + /* if 2.Dca < Da Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1475,6 +1976,48 @@ void QT_FASTCALL comp_func_solid_Overlay_rgb64(QRgba64 *dest, int length, QRgba6 } #endif +#if QT_CONFIG(raster_fp) +static inline float overlay_op_rgbafp(float dst, float src, float da, float sa) +{ + const float temp = src * (1.0f - da) + dst * (1.0f - sa); + if (2 * dst < da) + return 2 * src * dst + temp; + else + return sa * da - 2 * (da - dst) * (sa - src) + temp; +} + +template <typename T> +static inline void comp_func_solid_Overlay_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a, b) overlay_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Overlay_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Overlay_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Overlay_impl(dest, length, color, QPartialCoverage(const_alpha)); +} +#endif + template <typename T> static inline void comp_func_Overlay_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1535,6 +2078,37 @@ void QT_FASTCALL comp_func_Overlay_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QR } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_Overlay_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) overlay_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Overlay_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Overlay_impl(dest, src, length, QFullCoverage()); + else + comp_func_Overlay_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + /* Dca' = min(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) Da' = Sa + Da - Sa.Da @@ -1613,6 +2187,44 @@ void QT_FASTCALL comp_func_solid_Darken_rgb64(QRgba64 *dest, int length, QRgba64 } #endif +#if QT_CONFIG(raster_fp) +static inline float darken_op_rgbafp(float dst, float src, float da, float sa) +{ + return qMin(src * da, dst * sa) + src * (1.0f - da) + dst * (1.0f - sa); +} + +template <typename T> +static inline void comp_func_solid_Darken_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a, b) darken_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Darken_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Darken_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Darken_impl(dest, length, color, QPartialCoverage(const_alpha)); +} +#endif + template <typename T> static inline void comp_func_Darken_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1673,6 +2285,37 @@ void QT_FASTCALL comp_func_Darken_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QRg } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_Darken_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) darken_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Darken_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Darken_impl(dest, src, length, QFullCoverage()); + else + comp_func_Darken_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + /* Dca' = max(Sca.Da, Dca.Sa) + Sca.(1 - Da) + Dca.(1 - Sa) Da' = Sa + Da - Sa.Da @@ -1752,6 +2395,44 @@ void QT_FASTCALL comp_func_solid_Lighten_rgb64(QRgba64 *dest, int length, QRgba6 } #endif +#if QT_CONFIG(raster_fp) +static inline float lighten_op_rgbafp(float dst, float src, float da, float sa) +{ + return qMax(src * da, dst * sa) + src * (1.0f - da) + dst * (1.0f - sa); +} + +template <typename T> +static inline void comp_func_solid_Lighten_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a, b) lighten_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Lighten_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Lighten_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Lighten_impl(dest, length, color, QPartialCoverage(const_alpha)); +} +#endif + template <typename T> static inline void comp_func_Lighten_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1812,6 +2493,37 @@ void QT_FASTCALL comp_func_Lighten_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const QR } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_Lighten_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) lighten_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Lighten_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Lighten_impl(dest, src, length, QFullCoverage()); + else + comp_func_Lighten_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + /* if Sca.Da + Dca.Sa > Sa.Da Dca' = Sa.Da + Sca.(1 - Da) + Dca.(1 - Sa) @@ -1914,6 +2626,54 @@ void QT_FASTCALL comp_func_solid_ColorDodge_rgb64(QRgba64 *dest, int length, QRg } #endif +#if QT_CONFIG(raster_fp) +static inline float color_dodge_op_rgbafp(float dst, float src, float da, float sa) +{ + const float sa_da = sa * da; + const float dst_sa = dst * sa; + const float src_da = src * da; + + const float temp = src * (1.0f - da) + dst * (1.0f - sa); + if (src_da + dst_sa > sa_da) + return sa_da + temp; + else if (src == sa || sa == 0.0f) + return temp; + else + return dst_sa / (1.0f - src / sa) + temp; +} + +template <typename T> +static inline void comp_func_solid_ColorDodge_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a,b) color_dodge_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_ColorDodge_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_ColorDodge_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_ColorDodge_impl(dest, length, color, QPartialCoverage(const_alpha)); +} +#endif + template <typename T> static inline void comp_func_ColorDodge_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -1974,6 +2734,37 @@ void QT_FASTCALL comp_func_ColorDodge_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_ColorDodge_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) color_dodge_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_ColorDodge_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_ColorDodge_impl(dest, src, length, QFullCoverage()); + else + comp_func_ColorDodge_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + /* if Sca.Da + Dca.Sa < Sa.Da Dca' = Sca.(1 - Da) + Dca.(1 - Sa) @@ -2076,6 +2867,54 @@ void QT_FASTCALL comp_func_solid_ColorBurn_rgb64(QRgba64 *dest, int length, QRgb } #endif +#if QT_CONFIG(raster_fp) +static inline float color_burn_op_rgbafp(float dst, float src, float da, float sa) +{ + const float src_da = src * da; + const float dst_sa = dst * sa; + const float sa_da = sa * da; + + const float temp = src * (1.0f - da) + dst * (1.0f - sa); + + if (src_da + dst_sa < sa_da) + return temp; + else if (src == 0) + return dst_sa + temp; + return sa * (src_da + dst_sa - sa_da) / src + temp; +} + +template <typename T> +static inline void comp_func_solid_ColorBurn_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a, b) color_burn_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_ColorBurn_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_ColorBurn_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_ColorBurn_impl(dest, length, color, QPartialCoverage(const_alpha)); +} +#endif + template <typename T> static inline void comp_func_ColorBurn_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2136,6 +2975,37 @@ void QT_FASTCALL comp_func_ColorBurn_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_ColorBurn_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) color_burn_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_ColorBurn_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_ColorBurn_impl(dest, src, length, QFullCoverage()); + else + comp_func_ColorBurn_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + /* if 2.Sca < Sa Dca' = 2.Sca.Dca + Sca.(1 - Da) + Dca.(1 - Sa) @@ -2226,6 +3096,49 @@ void QT_FASTCALL comp_func_solid_HardLight_rgb64(QRgba64 *dest, int length, QRgb } #endif +#if QT_CONFIG(raster_fp) +static inline float hardlight_op_rgbafp(float dst, float src, float da, float sa) +{ + const float temp = src * (1.0f - da) + dst * (1.0f - sa); + + if (2 * src < sa) + return 2 * src * dst + temp; + else + return sa * da - 2 * (da - dst) * (sa - src) + temp; +} + +template <typename T> +static inline void comp_func_solid_HardLight_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a, b) hardlight_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_HardLight_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_HardLight_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_HardLight_impl(dest, length, color, QPartialCoverage(const_alpha)); +} +#endif + template <typename T> static inline void comp_func_HardLight_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2286,6 +3199,37 @@ void QT_FASTCALL comp_func_HardLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_HardLight_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) hardlight_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_HardLight_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_HardLight_impl(dest, src, length, QFullCoverage()); + else + comp_func_HardLight_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + /* if 2.Sca <= Sa Dca' = Dca.(Sa + (2.Sca - Sa).(1 - Dca/Da)) + Sca.(1 - Da) + Dca.(1 - Sa) @@ -2332,6 +3276,14 @@ static inline void comp_func_solid_SoftLight_impl(uint *dest, int length, uint c } } +void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + #if QT_CONFIG(raster_64bit) static inline uint soft_light_op_rgb64(qint64 dst, qint64 src, qint64 da, qint64 sa) { @@ -2371,15 +3323,64 @@ static inline void comp_func_solid_SoftLight_impl(QRgba64 *dest, int length, QRg coverage.store(&dest[i], qRgba64(r, g, b, a)); } } + +void QT_FASTCALL comp_func_solid_SoftLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha)); +} + #endif -void QT_FASTCALL comp_func_solid_SoftLight(uint *dest, int length, uint color, uint const_alpha) +#if QT_CONFIG(raster_fp) +static inline float soft_light_op_rgbafp(float dst, float src, float da, float sa) +{ + const float src2 = src * 2; + const float dst_np = da != 0.0f ? (dst / da) : 0.0f; + const float temp = src * (1.0f - da) + dst * (1.0f - sa); + + if (src2 < sa) + return dst * (sa + (src2 - sa) * (1.0f - dst_np)) + temp; + else if (4 * dst <= da) + return dst * sa + da * (src2 - sa) * (((16 * dst_np - 12) * dst_np + 3) * dst_np) + temp; + else { + return dst * sa + da * (src2 - sa) * (qSqrt(qreal(dst_np)) - dst_np) + temp; + } +} + +template <typename T> +static inline void comp_func_solid_SoftLight_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a, b) soft_light_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_SoftLight_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) { if (const_alpha == 255) comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage()); else comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha)); } +#endif template <typename T> static inline void comp_func_SoftLight_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) @@ -2411,14 +3412,6 @@ void QT_FASTCALL comp_func_SoftLight(uint *Q_DECL_RESTRICT dest, const uint *Q_D } #if QT_CONFIG(raster_64bit) -void QT_FASTCALL comp_func_solid_SoftLight_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) -{ - if (const_alpha == 255) - comp_func_solid_SoftLight_impl(dest, length, color, QFullCoverage()); - else - comp_func_solid_SoftLight_impl(dest, length, color, QPartialCoverage(const_alpha)); -} - template <typename T> static inline void comp_func_SoftLight_impl(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2449,6 +3442,37 @@ void QT_FASTCALL comp_func_SoftLight_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_SoftLight_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) soft_light_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_SoftLight_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_SoftLight_impl(dest, src, length, QFullCoverage()); + else + comp_func_SoftLight_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + /* Dca' = abs(Dca.Sa - Sca.Da) + Sca.(1 - Da) + Dca.(1 - Sa) = Sca + Dca - 2.min(Sca.Da, Dca.Sa) @@ -2527,6 +3551,44 @@ void QT_FASTCALL comp_func_solid_Difference_rgb64(QRgba64 *dest, int length, QRg } #endif +#if QT_CONFIG(raster_fp) +static inline float difference_op_rgbafp(float dst, float src, float da, float sa) +{ + return src + dst - (2 * qMin(src * da, dst * sa)); +} + +template <typename T> +static inline void comp_func_solid_Difference_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a, b) difference_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Difference_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Difference_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Difference_impl(dest, length, color, QPartialCoverage(const_alpha)); +} +#endif + template <typename T> static inline void comp_func_Difference_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2587,6 +3649,37 @@ void QT_FASTCALL comp_func_Difference_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_Difference_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) difference_op_rgbafp(a, b, da, sa) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Difference_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Difference_impl(dest, src, length, QFullCoverage()); + else + comp_func_Difference_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + /* Dca' = (Sca.Da + Dca.Sa - 2.Sca.Dca) + Sca.(1 - Da) + Dca.(1 - Sa) */ @@ -2645,7 +3738,6 @@ static inline void QT_FASTCALL comp_func_solid_Exclusion_impl(QRgba64 *dest, int } } - void QT_FASTCALL comp_func_solid_Exclusion_rgb64(QRgba64 *dest, int length, QRgba64 color, uint const_alpha) { if (const_alpha == 255) @@ -2655,6 +3747,39 @@ void QT_FASTCALL comp_func_solid_Exclusion_rgb64(QRgba64 *dest, int length, QRgb } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void QT_FASTCALL comp_func_solid_Exclusion_impl(QRgba32F *dest, int length, QRgba32F color, const T &coverage) +{ + float sa = color.alpha(); + float sr = color.red(); + float sg = color.green(); + float sb = color.blue(); + + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + float da = d.alpha(); + +#define OP(a, b) (a + b - (2.0f * a * b)) + float r = OP( d.red(), sr); + float b = OP( d.blue(), sb); + float g = OP(d.green(), sg); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_solid_Exclusion_rgbafp(QRgba32F *dest, int length, QRgba32F color, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_solid_Exclusion_impl(dest, length, color, QFullCoverage()); + else + comp_func_solid_Exclusion_impl(dest, length, color, QPartialCoverage(const_alpha)); +} +#endif + template <typename T> static inline void comp_func_Exclusion_impl(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, const T &coverage) { @@ -2715,6 +3840,37 @@ void QT_FASTCALL comp_func_Exclusion_rgb64(QRgba64 *Q_DECL_RESTRICT dest, const } #endif +#if QT_CONFIG(raster_fp) +template <typename T> +static inline void comp_func_Exclusion_impl(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, const T &coverage) +{ + for (int i = 0; i < length; ++i) { + QRgba32F d = dest[i]; + QRgba32F s = src[i]; + + float da = d.alpha(); + float sa = s.alpha(); + +#define OP(a, b) (a + b - (2.0f * a * b)) + float r = OP( d.red(), s.red()); + float b = OP( d.blue(), s.blue()); + float g = OP(d.green(), s.green()); + float a = mix_alpha_rgbafp(da, sa); +#undef OP + + coverage.store(&dest[i], qRgba32f(r, g, b, a)); + } +} + +void QT_FASTCALL comp_func_Exclusion_rgbafp(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha) +{ + if (const_alpha == 255) + comp_func_Exclusion_impl(dest, src, length, QFullCoverage()); + else + comp_func_Exclusion_impl(dest, src, length, QPartialCoverage(const_alpha)); +} +#endif + void QT_FASTCALL rasterop_solid_SourceOrDestination(uint *dest, int length, uint color, @@ -3097,6 +4253,40 @@ CompositionFunctionSolid64 qt_functionForModeSolid64_C[] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; +CompositionFunctionSolidFP qt_functionForModeSolidFP_C[] = { +#if QT_CONFIG(raster_fp) + comp_func_solid_SourceOver_rgbafp, + comp_func_solid_DestinationOver_rgbafp, + comp_func_solid_Clear_rgbafp, + comp_func_solid_Source_rgbafp, + comp_func_solid_Destination_rgbafp, + comp_func_solid_SourceIn_rgbafp, + comp_func_solid_DestinationIn_rgbafp, + comp_func_solid_SourceOut_rgbafp, + comp_func_solid_DestinationOut_rgbafp, + comp_func_solid_SourceAtop_rgbafp, + comp_func_solid_DestinationAtop_rgbafp, + comp_func_solid_XOR_rgbafp, + comp_func_solid_Plus_rgbafp, + comp_func_solid_Multiply_rgbafp, + comp_func_solid_Screen_rgbafp, + comp_func_solid_Overlay_rgbafp, + comp_func_solid_Darken_rgbafp, + comp_func_solid_Lighten_rgbafp, + comp_func_solid_ColorDodge_rgbafp, + comp_func_solid_ColorBurn_rgbafp, + comp_func_solid_HardLight_rgbafp, + comp_func_solid_SoftLight_rgbafp, + comp_func_solid_Difference_rgbafp, + comp_func_solid_Exclusion_rgbafp, +#else + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + CompositionFunction qt_functionForMode_C[] = { comp_func_SourceOver, comp_func_DestinationOver, @@ -3173,4 +4363,38 @@ CompositionFunction64 qt_functionForMode64_C[] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; +CompositionFunctionFP qt_functionForModeFP_C[] = { +#if QT_CONFIG(raster_fp) + comp_func_SourceOver_rgbafp, + comp_func_DestinationOver_rgbafp, + comp_func_Clear_rgbafp, + comp_func_Source_rgbafp, + comp_func_Destination_rgbafp, + comp_func_SourceIn_rgbafp, + comp_func_DestinationIn_rgbafp, + comp_func_SourceOut_rgbafp, + comp_func_DestinationOut_rgbafp, + comp_func_SourceAtop_rgbafp, + comp_func_DestinationAtop_rgbafp, + comp_func_XOR_rgbafp, + comp_func_Plus_rgbafp, + comp_func_Multiply_rgbafp, + comp_func_Screen_rgbafp, + comp_func_Overlay_rgbafp, + comp_func_Darken_rgbafp, + comp_func_Lighten_rgbafp, + comp_func_ColorDodge_rgbafp, + comp_func_ColorBurn_rgbafp, + comp_func_HardLight_rgbafp, + comp_func_SoftLight_rgbafp, + comp_func_Difference_rgbafp, + comp_func_Exclusion_rgbafp, +#else + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +#endif + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + QT_END_NAMESPACE diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 161eebb300..eb01487743 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -38,7 +38,7 @@ ** ****************************************************************************/ -#include <qglobal.h> +#include "qdrawhelper_p.h" #include <qstylehints.h> #include <qguiapplication.h> @@ -76,16 +76,255 @@ enum { half_point = 1 << 15 }; +template <QPixelLayout::BPP bpp> static +inline uint QT_FASTCALL fetch1Pixel(const uchar *, int) +{ + Q_UNREACHABLE(); + return 0; +} + +template <> +inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP1LSB>(const uchar *src, int index) +{ + return (src[index >> 3] >> (index & 7)) & 1; +} + +template <> +inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP1MSB>(const uchar *src, int index) +{ + return (src[index >> 3] >> (~index & 7)) & 1; +} + +template <> +inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP8>(const uchar *src, int index) +{ + return src[index]; +} + +template <> +inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP16>(const uchar *src, int index) +{ + return reinterpret_cast<const quint16 *>(src)[index]; +} + +template <> +inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP24>(const uchar *src, int index) +{ + return reinterpret_cast<const quint24 *>(src)[index]; +} + +template <> +inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP32>(const uchar *src, int index) +{ + return reinterpret_cast<const uint *>(src)[index]; +} + +template <> +inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP64>(const uchar *src, int index) +{ + // 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 uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP16FPx4>(const uchar *src, int index) +{ + // We have to do the conversion in fetch to fit into a 32bit uint + QRgba16F c = reinterpret_cast<const QRgba16F *>(src)[index]; + return c.toArgb32(); +} + +template <> +inline uint QT_FASTCALL fetch1Pixel<QPixelLayout::BPP32FPx4>(const uchar *src, int index) +{ + // We have to do the conversion in fetch to fit into a 32bit uint + QRgba32F c = reinterpret_cast<const QRgba32F *>(src)[index]; + return c.toArgb32(); +} + +typedef uint (QT_FASTCALL *Fetch1PixelFunc)(const uchar *src, int index); + +constexpr Fetch1PixelFunc fetch1PixelTable[QPixelLayout::BPPCount] = { + nullptr, // BPPNone + fetch1Pixel<QPixelLayout::BPP1MSB>, + fetch1Pixel<QPixelLayout::BPP1LSB>, + fetch1Pixel<QPixelLayout::BPP8>, + fetch1Pixel<QPixelLayout::BPP16>, + fetch1Pixel<QPixelLayout::BPP24>, + fetch1Pixel<QPixelLayout::BPP32>, + fetch1Pixel<QPixelLayout::BPP64>, + fetch1Pixel<QPixelLayout::BPP16FPx4>, + fetch1Pixel<QPixelLayout::BPP32FPx4>, +}; + #if QT_CONFIG(raster_64bit) -static void convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count) +static void QT_FASTCALL convertRGBA64ToRGBA64PM(QRgba64 *buffer, int count) +{ + for (int i = 0; i < count; ++i) + buffer[i] = buffer[i].premultiplied(); +} + +static void QT_FASTCALL convertRGBA64PMToRGBA64PM(QRgba64 *, int) +{ +} + +static void QT_FASTCALL convertRGBA16FToRGBA64PM(QRgba64 *buffer, int count) +{ + const QRgba16F *in = reinterpret_cast<const QRgba16F *>(buffer); + for (int i = 0; i < count; ++i) { + QRgba16F c = in[i]; + buffer[i] = QRgba64::fromRgba64(c.red16(), c.green16(), c.blue16(), c.alpha16()).premultiplied(); + } +} + +static void QT_FASTCALL convertRGBA16FPMToRGBA64PM(QRgba64 *buffer, int count) +{ + const QRgba16F *in = reinterpret_cast<const QRgba16F *>(buffer); + for (int i = 0; i < count; ++i) { + QRgba16F c = in[i]; + buffer[i] = QRgba64::fromRgba64(c.red16(), c.green16(), c.blue16(), c.alpha16()); + } +} + +static void QT_FASTCALL convertRGBA32FToRGBA64PM(QRgba64 *buffer, int count) +{ + const QRgba32F *in = reinterpret_cast<const QRgba32F *>(buffer); + for (int i = 0; i < count; ++i) { + QRgba32F c = in[i]; + buffer[i] = QRgba64::fromRgba64(c.red16(), c.green16(), c.blue16(), c.alpha16()).premultiplied(); + } +} + +static void QT_FASTCALL convertRGBA32FPMToRGBA64PM(QRgba64 *buffer, int count) +{ + const QRgba32F *in = reinterpret_cast<const QRgba32F *>(buffer); + for (int i = 0; i < count; ++i) { + QRgba32F c = in[i]; + buffer[i] = QRgba64::fromRgba64(c.red16(), c.green16(), c.blue16(), c.alpha16()); + } +} + +static Convert64Func convert64ToRGBA64PM[QImage::NImageFormats] = { + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + convertRGBA64PMToRGBA64PM, + convertRGBA64ToRGBA64PM, + convertRGBA64PMToRGBA64PM, + nullptr, + nullptr, + convertRGBA16FPMToRGBA64PM, + convertRGBA16FToRGBA64PM, + convertRGBA16FPMToRGBA64PM, + convertRGBA32FPMToRGBA64PM, + convertRGBA32FToRGBA64PM, + convertRGBA32FPMToRGBA64PM, +}; +#endif + +#if QT_CONFIG(raster_fp) +static void QT_FASTCALL convertRGBA64PMToRGBA32F(QRgba32F *buffer, const quint64 *src, int count) +{ + const auto *in = reinterpret_cast<const QRgba64 *>(src); + for (int i = 0; i < count; ++i) { + auto c = in[i]; + buffer[i] = QRgba32F::fromRgba64(c.red(), c.green(), c.blue(), c.alpha()).premultiplied(); + } +} + +static void QT_FASTCALL convertRGBA64ToRGBA32F(QRgba32F *buffer, const quint64 *src, int count) +{ + const auto *in = reinterpret_cast<const QRgba64 *>(src); + for (int i = 0; i < count; ++i) { + auto c = in[i]; + buffer[i] = QRgba32F::fromRgba64(c.red(), c.green(), c.blue(), c.alpha()); + } +} + +static void QT_FASTCALL convertRGBA16FPMToRGBA32F(QRgba32F *buffer, const quint64 *src, int count) +{ + qFloatFromFloat16((float *)buffer, (const qfloat16 *)src, count * 4); + for (int i = 0; i < count; ++i) + buffer[i] = buffer[i].premultiplied(); +} + +static void QT_FASTCALL convertRGBA16FToRGBA32F(QRgba32F *buffer, const quint64 *src, int count) +{ + qFloatFromFloat16((float *)buffer, (const qfloat16 *)src, count * 4); +} + +static Convert64ToFPFunc convert64ToRGBA32F[QImage::NImageFormats] = { + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + convertRGBA64ToRGBA32F, + convertRGBA64PMToRGBA32F, + convertRGBA64ToRGBA32F, + nullptr, + nullptr, + convertRGBA16FToRGBA32F, + convertRGBA16FPMToRGBA32F, + convertRGBA16FToRGBA32F, + nullptr, + nullptr, + nullptr, +}; + +static void convertRGBA32FToRGBA32FPM(QRgba32F *buffer, int count) { for (int i = 0; i < count; ++i) buffer[i] = buffer[i].premultiplied(); } -static void convertRGBA64PMToRGBA64PM(QRgba64 *, int) +static void convertRGBA32FToRGBA32F(QRgba32F *, int) { } + #endif /* @@ -175,6 +414,12 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] = destFetch, // Format_RGBA64_Premultiplied destFetch, // Format_Grayscale16 destFetch, // Format_BGR888 + destFetch, // Format_RGBX16FPx4 + destFetch, // Format_RGBA16FPx4 + destFetch, // Format_RGBA16FPx4_Premultiplied + destFetch, // Format_RGBX32FPx4 + destFetch, // Format_RGBA32FPx4 + destFetch, // Format_RGBA32FPx4_Premultiplied }; #if QT_CONFIG(raster_64bit) @@ -226,9 +471,27 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] = destFetchRGB64, // Format_RGBA64_Premultiplied destFetch64, // Format_Grayscale16 destFetch64, // Format_BGR888 + destFetch64, // Format_RGBX16FPx4 + destFetch64, // Format_RGBA16FPx4 + destFetch64, // Format_RGBA16FPx4_Premultiplied + destFetch64, // Format_RGBX32FPx4 + destFetch64, // Format_RGBA32FPx4 + destFetch64, // Format_RGBA32FPx4_Premultiplied }; #endif +#if QT_CONFIG(raster_fp) +static QRgba32F *QT_FASTCALL destFetchFP(QRgba32F *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) +{ + return const_cast<QRgba32F *>(qFetchToRGBA32F[rasterBuffer->format](buffer, rasterBuffer->scanLine(y), x, length, nullptr, nullptr)); +} + +static QRgba32F *QT_FASTCALL destFetchFPUndefined(QRgba32F *buffer, QRasterBuffer *, int, int, int) +{ + return buffer; +} +#endif + /* Returns the color in the mono destination color table that is the "nearest" to /color/. @@ -412,6 +675,12 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] = destStore, // Format_RGBA64_Premultiplied destStoreGray16, // Format_Grayscale16 destStore, // Format_BGR888 + destStore, // Format_RGBX16FPx4 + destStore, // Format_RGBA16FPx4 + destStore, // Format_RGBA16FPx4_Premultiplied + destStore, // Format_RGBX32FPx4 + destStore, // Format_RGBA32FPx4 + destStore, // Format_RGBA32FPx4_Premultiplied }; #if QT_CONFIG(raster_64bit) @@ -506,9 +775,24 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] = nullptr, // Format_RGBA64_Premultiplied destStore64Gray16, // Format_Grayscale16 destStore64, // Format_BGR888 + destStore64, // Format_RGBX16FPx4 + destStore64, // Format_RGBA16FPx4 + destStore64, // Format_RGBA16FPx4_Premultiplied + destStore64, // Format_RGBX32FPx4 + destStore64, // Format_RGBA32FPx4 + destStore64, // Format_RGBA32FPx4_Premultiplied }; #endif +#if QT_CONFIG(raster_fp) +static void QT_FASTCALL destStoreFP(QRasterBuffer *rasterBuffer, int x, int y, const QRgba32F *buffer, int length) +{ + auto store = qStoreFromRGBA32F[rasterBuffer->format]; + uchar *dest = rasterBuffer->scanLine(y); + store(dest, buffer, x, length, nullptr, nullptr); +} +#endif + /* Source fetches @@ -574,6 +858,15 @@ static const QRgba64 *QT_FASTCALL fetchUntransformedRGBA64PM(QRgba64 *, const Op } #endif +#if QT_CONFIG(raster_fp) +static const QRgba32F *QT_FASTCALL fetchUntransformedFP(QRgba32F *buffer, const Operator *, + const QSpanData *data, int y, int x, int length) +{ + const auto fetch = qFetchToRGBA32F[data->texture.format]; + return fetch(buffer, data->texture.scanLine(y), x, length, data->texture.colorTable, nullptr); +} +#endif + template<TextureBlendType blendType> inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v) { @@ -618,9 +911,9 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat constexpr bool useFetch = (bpp < QPixelLayout::BPP32) && sizeof(T) == sizeof(uint); const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; if (!useFetch) - Q_ASSERT(layout->bpp == bpp); + Q_ASSERT(layout->bpp == bpp || (layout->bpp == QPixelLayout::BPP16FPx4 && bpp == QPixelLayout::BPP64)); // When templated 'fetch' should be inlined at compile time: - const FetchPixelFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixelTable[layout->bpp] : FetchPixelFunc(qFetchPixel<bpp>); + const Fetch1PixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? fetch1PixelTable[layout->bpp] : Fetch1PixelFunc(fetch1Pixel<bpp>); if (canUseFastMatrixPath(cx, cy, length, data)) { // The increment pr x in the scanline @@ -651,8 +944,8 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1); if (x1 == x2) break; - if (useFetch) - buffer[i] = fetch(src, x1); + if constexpr (useFetch) + buffer[i] = fetch1(src, x1); else buffer[i] = reinterpret_cast<const T*>(src)[x1]; fx += fdx; @@ -660,8 +953,8 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat for (; i < fastLen; ++i) { int px = (fx >> 16); - if (useFetch) - buffer[i] = fetch(src, px); + if constexpr (useFetch) + buffer[i] = fetch1(src, px); else buffer[i] = reinterpret_cast<const T*>(src)[px]; fx += fdx; @@ -671,8 +964,8 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat for (; i < length; ++i) { int px = (fx >> 16); fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px); - if (useFetch) - buffer[i] = fetch(src, px); + if constexpr (useFetch) + buffer[i] = fetch1(src, px); else buffer[i] = reinterpret_cast<const T*>(src)[px]; fx += fdx; @@ -699,8 +992,8 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, y1); if (x1 == x2 && y1 == y2) break; - if (useFetch) - buffer[i] = fetch(image.scanLine(y1), x1); + if constexpr (useFetch) + buffer[i] = fetch1(image.scanLine(y1), x1); else buffer[i] = reinterpret_cast<const T*>(image.scanLine(y1))[x1]; fx += fdx; @@ -710,8 +1003,8 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat for (; i < fastLen; ++i) { int px = (fx >> 16); int py = (fy >> 16); - if (useFetch) - buffer[i] = fetch(image.scanLine(py), px); + if constexpr (useFetch) + buffer[i] = fetch1(image.scanLine(py), px); else buffer[i] = reinterpret_cast<const T*>(image.scanLine(py))[px]; fx += fdx; @@ -724,8 +1017,8 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat 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); - if (useFetch) - buffer[i] = fetch(image.scanLine(py), px); + if constexpr (useFetch) + buffer[i] = fetch1(image.scanLine(py), px); else buffer[i] = reinterpret_cast<const T*>(image.scanLine(py))[px]; fx += fdx; @@ -752,8 +1045,8 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat fetchTransformed_pixelBounds<blendType>(image.height, image.y1, image.y2 - 1, py); fetchTransformed_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, px); - if (useFetch) - *b = fetch(image.scanLine(py), px); + if constexpr (useFetch) + *b = fetch1(image.scanLine(py), px); else *b = reinterpret_cast<const T*>(image.scanLine(py))[px]; @@ -786,7 +1079,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper int y, int x, int length) { const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; - if (layout->bpp != QPixelLayout::BPP64) { + if (layout->bpp < QPixelLayout::BPP64) { uint buffer32[BufferSize]; Q_ASSERT(length <= BufferSize); if (layout->bpp == QPixelLayout::BPP32) @@ -796,9 +1089,37 @@ static const QRgba64 *QT_FASTCALL fetchTransformed64(QRgba64 *buffer, const Oper 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); + fetchTransformed_fetcher<blendType, QPixelLayout::BPP64, quint64>(reinterpret_cast<quint64*>(buffer), data, y, x, length); + if (auto convert = convert64ToRGBA64PM[data->texture.format]) + convert(buffer, length); + return buffer; +} +#endif + +#if QT_CONFIG(raster_fp) +template<TextureBlendType blendType> /* either BlendTransformed or BlendTransformedTiled */ +static const QRgba32F *QT_FASTCALL fetchTransformedFP(QRgba32F *buffer, const Operator *, const QSpanData *data, + int y, int x, int length) +{ + const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; + 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); + qConvertToRGBA32F[data->texture.format](buffer, buffer32, length, data->texture.colorTable, nullptr); + } else if (layout->bpp < QPixelLayout::BPP32FPx4) { + quint64 buffer64[BufferSize]; + fetchTransformed_fetcher<blendType, QPixelLayout::BPP64, quint64>(buffer64, data, y, x, length); + convert64ToRGBA32F[data->texture.format](buffer, buffer64, length); + } else { + fetchTransformed_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgba32F>(buffer, data, y, x, length); + if (data->texture.format == QImage::Format_RGBA32FPx4) + convertRGBA32FToRGBA32FPM(buffer, length); + return buffer; + } return buffer; } #endif @@ -1771,8 +2092,8 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const if (useFetch) Q_ASSERT(sizeof(T) == sizeof(uint)); else - Q_ASSERT(layout.bpp == bpp); - const FetchPixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? qFetchPixelTable[layout.bpp] : qFetchPixel<bpp>; + Q_ASSERT(layout.bpp == bpp || (layout.bpp == QPixelLayout::BPP16FPx4 && bpp == QPixelLayout::BPP64)); + const Fetch1PixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? fetch1PixelTable[layout.bpp] : fetch1Pixel<bpp>; if (fdy == 0) { int y1 = (fy >> 16); int y2; @@ -1788,7 +2109,7 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); if (x1 != x2) break; - if (useFetch) { + if constexpr (useFetch) { buf1[i * 2 + 0] = buf1[i * 2 + 1] = fetch1(s1, x1); buf2[i * 2 + 0] = buf2[i * 2 + 1] = fetch1(s2, x1); } else { @@ -1805,7 +2126,7 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const for (; i < fastLen; ++i) { int x = (fx >> 16); - if (useFetch) { + if constexpr (useFetch) { buf1[i * 2 + 0] = fetch1(s1, x); buf1[i * 2 + 1] = fetch1(s1, x + 1); buf2[i * 2 + 0] = fetch1(s2, x); @@ -1824,7 +2145,7 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const int x1 = (fx >> 16); int x2; fetchTransformedBilinear_pixelBounds<blendType>(image.width, image.x1, image.x2 - 1, x1, x2); - if (useFetch) { + if constexpr (useFetch) { buf1[i * 2 + 0] = fetch1(s1, x1); buf1[i * 2 + 1] = fetch1(s1, x2); buf2[i * 2 + 0] = fetch1(s2, x1); @@ -1851,7 +2172,7 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const break; const uchar *s1 = image.scanLine(y1); const uchar *s2 = image.scanLine(y2); - if (useFetch) { + if constexpr (useFetch) { buf1[i * 2 + 0] = fetch1(s1, x1); buf1[i * 2 + 1] = fetch1(s1, x2); buf2[i * 2 + 0] = fetch1(s2, x1); @@ -1880,7 +2201,7 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const int y = (fy >> 16); const uchar *s1 = image.scanLine(y); const uchar *s2 = s1 + image.bytesPerLine; - if (useFetch) { + if constexpr (useFetch) { buf1[i * 2 + 0] = fetch1(s1, x); buf1[i * 2 + 1] = fetch1(s1, x + 1); buf2[i * 2 + 0] = fetch1(s2, x); @@ -1906,7 +2227,7 @@ static void QT_FASTCALL fetchTransformedBilinear_fetcher(T *buf1, T *buf2, const const uchar *s1 = image.scanLine(y1); const uchar *s2 = image.scanLine(y2); - if (useFetch) { + if constexpr (useFetch) { buf1[i * 2 + 0] = fetch1(s1, x1); buf1[i * 2 + 1] = fetch1(s1, x2); buf2[i * 2 + 0] = fetch1(s2, x1); @@ -1936,7 +2257,7 @@ static void QT_FASTCALL fetchTransformedBilinear_slow_fetcher(T *buf1, T *buf2, else Q_ASSERT(layout.bpp == bpp); - const FetchPixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? qFetchPixelTable[layout.bpp] : qFetchPixel<bpp>; + const Fetch1PixelFunc fetch1 = (bpp == QPixelLayout::BPPNone) ? fetch1PixelTable[layout.bpp] : fetch1Pixel<bpp>; for (int i = 0; i < len; ++i) { const qreal iw = fw == 0 ? 16384 : 1 / fw; @@ -1956,7 +2277,7 @@ static void QT_FASTCALL fetchTransformedBilinear_slow_fetcher(T *buf1, T *buf2, const uchar *s1 = image.scanLine(y1); const uchar *s2 = image.scanLine(y2); - if (useFetch) { + if constexpr (useFetch) { buf1[i * 2 + 0] = fetch1(s1, x1); buf1[i * 2 + 1] = fetch1(s1, x2); buf2[i * 2 + 0] = fetch1(s2, x1); @@ -2128,7 +2449,8 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buf int y, int x, int length) { const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; - const QList<QRgb> *clut = data->texture.colorTable; + const auto *clut = data->texture.colorTable; + const auto convert = layout->convertToRGBA64PM; const qreal cx = x + qreal(0.5); const qreal cy = y + qreal(0.5); @@ -2137,7 +2459,6 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buf uint sbuf2[BufferSize]; alignas(8) QRgba64 buf1[BufferSize]; alignas(8) QRgba64 buf2[BufferSize]; - QRgba64 *end = buffer + length; QRgba64 *b = buffer; if (canUseFastMatrixPath(cx, cy, length, data)) { @@ -2158,20 +2479,20 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buf if (fdy == 0) { //simple scale, no rotation while (length) { - int len = qMin(length, BufferSize / 2); - int disty = (fy & 0x0000ffff); + const int len = qMin(length, BufferSize / 2); + const int disty = (fy & 0x0000ffff); #if defined(__SSE2__) const __m128i vdy = _mm_set1_epi16(disty); const __m128i vidy = _mm_set1_epi16(0x10000 - disty); #endif fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy); - layout->convertToRGBA64PM(buf1, sbuf1, len * 2, clut, nullptr); + convert(buf1, sbuf1, len * 2, clut, nullptr); if (disty) - layout->convertToRGBA64PM(buf2, sbuf2, len * 2, clut, nullptr); + convert(buf2, sbuf2, len * 2, clut, nullptr); for (int i = 0; i < len; ++i) { - int distx = (fx & 0x0000ffff); + const int distx = (fx & 0x0000ffff); #if defined(__SSE2__) __m128i vt = _mm_loadu_si128((const __m128i*)(buf1 + i*2)); if (disty) { @@ -2196,17 +2517,17 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buf b += len; } } else { // rotation or shear - while (b < end) { - int len = qMin(length, BufferSize / 2); + while (length) { + const int len = qMin(length, BufferSize / 2); fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy); - layout->convertToRGBA64PM(buf1, sbuf1, len * 2, clut, nullptr); - layout->convertToRGBA64PM(buf2, sbuf2, len * 2, clut, nullptr); + convert(buf1, sbuf1, len * 2, clut, nullptr); + convert(buf2, sbuf2, len * 2, clut, nullptr); for (int i = 0; i < len; ++i) { - int distx = (fx & 0x0000ffff); - int disty = (fy & 0x0000ffff); + const int distx = (fx & 0x0000ffff); + const int disty = (fy & 0x0000ffff); b[i] = interpolate_4_pixels_rgb64(buf1 + i*2, buf2 + i*2, distx, disty); fx += fdx; fy += fdy; @@ -2237,8 +2558,8 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buf const int len = qMin(length, BufferSize / 2); fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw); - layout->convertToRGBA64PM(buf1, sbuf1, len * 2, clut, nullptr); - layout->convertToRGBA64PM(buf2, sbuf2, len * 2, clut, nullptr); + convert(buf1, sbuf1, len * 2, clut, nullptr); + convert(buf2, sbuf2, len * 2, clut, nullptr); for (int i = 0; i < len; ++i) { const int distx = distxs[i]; @@ -2257,8 +2578,7 @@ template<TextureBlendType blendType> static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buffer, const QSpanData *data, int y, int x, int length) { - Q_ASSERT(qPixelLayouts[data->texture.format].bpp == QPixelLayout::BPP64); - const auto convert = (data->texture.format == QImage::Format_RGBA64) ? convertRGBA64ToRGBA64PM : convertRGBA64PMToRGBA64PM; + const auto convert = convert64ToRGBA64PM[data->texture.format]; const qreal cx = x + qreal(0.5); const qreal cy = y + qreal(0.5); @@ -2375,15 +2695,366 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buf } template<TextureBlendType blendType> +static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_f32x4(QRgba64 *buffer, const QSpanData *data, + int y, int x, int length) +{ + const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; + const auto *clut = data->texture.colorTable; + const auto convert = layout->fetchToRGBA64PM; + + const qreal cx = x + qreal(0.5); + const qreal cy = y + qreal(0.5); + + QRgba32F sbuf1[BufferSize]; + QRgba32F sbuf2[BufferSize]; + alignas(8) QRgba64 buf1[BufferSize]; + alignas(8) QRgba64 buf2[BufferSize]; + QRgba64 *b = buffer; + + if (canUseFastMatrixPath(cx, cy, length, data)) { + // 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::BPP32FPx4, QRgba32F>; + + const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0); + while (length) { + const int len = qMin(length, BufferSize / 2); + + fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy); + + convert(buf1, (const uchar *)sbuf1, 0, len * 2, clut, nullptr); + if (!skipsecond) + convert(buf2, (const uchar *)sbuf2, 0, len * 2, clut, nullptr); + + for (int i = 0; i < len; ++i) { + const int distx = (fx & 0x0000ffff); + const 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 auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgba32F>; + + 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; + + ushort distxs[BufferSize / 2]; + ushort distys[BufferSize / 2]; + + while (length) { + const int len = qMin(length, BufferSize / 2); + fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw); + + convert(buf1, (const uchar *)sbuf1, 0, len * 2, clut, nullptr); + convert(buf2, (const uchar *)sbuf2, 0, len * 2, clut, nullptr); + + for (int i = 0; i < len; ++i) { + const int distx = distxs[i]; + const 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(QRgba64 *buffer, const Operator *, const QSpanData *data, int y, int x, int length) { - if (qPixelLayouts[data->texture.format].bpp == QPixelLayout::BPP64) + switch (qPixelLayouts[data->texture.format].bpp) { + case QPixelLayout::BPP64: + case QPixelLayout::BPP16FPx4: return fetchTransformedBilinear64_uint64<blendType>(buffer, data, y, x, length); - return fetchTransformedBilinear64_uint32<blendType>(buffer, data, y, x, length); + case QPixelLayout::BPP32FPx4: + return fetchTransformedBilinear64_f32x4<blendType>(buffer, data, y, x, length); + default: + return fetchTransformedBilinear64_uint32<blendType>(buffer, data, y, x, length); + } } #endif +#if QT_CONFIG(raster_fp) +static void interpolate_simple_rgba32f(QRgba32F *b, const QRgba32F *buf1, const QRgba32F *buf2, int len, + int &fx, int fdx, + int &fy, int fdy) +{ + for (int i = 0; i < len; ++i) { + const int distx = (fx & 0x0000ffff); + const int disty = (fy & 0x0000ffff); + b[i] = interpolate_4_pixels_rgba32f(buf1 + i*2, buf2 + i*2, distx, disty); + fx += fdx; + fy += fdy; + } +} + +static void interpolate_perspective_rgba32f(QRgba32F *b, const QRgba32F *buf1, const QRgba32F *buf2, int len, + unsigned short *distxs, + unsigned short *distys) +{ + for (int i = 0; i < len; ++i) { + const int dx = distxs[i]; + const int dy = distys[i]; + b[i] = interpolate_4_pixels_rgba32f(buf1 + i*2, buf2 + i*2, dx, dy); + } +} + +template<TextureBlendType blendType> +static const QRgba32F *QT_FASTCALL fetchTransformedBilinearFP_uint32(QRgba32F *buffer, const QSpanData *data, + int y, int x, int length) +{ + const QPixelLayout *layout = &qPixelLayouts[data->texture.format]; + const auto *clut = data->texture.colorTable; + const auto convert = qConvertToRGBA32F[data->texture.format]; + + const qreal cx = x + qreal(0.5); + const qreal cy = y + qreal(0.5); + + uint sbuf1[BufferSize]; + uint sbuf2[BufferSize]; + QRgba32F buf1[BufferSize]; + QRgba32F buf2[BufferSize]; + QRgba32F *b = buffer; + + if (canUseFastMatrixPath(cx, cy, length, data)) { + // 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 = + (layout->bpp == QPixelLayout::BPP32) + ? fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPP32, uint> + : fetchTransformedBilinear_fetcher<blendType, QPixelLayout::BPPNone, uint>; + + const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0); + while (length) { + const int len = qMin(length, BufferSize / 2); + fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy); + + convert(buf1, sbuf1, len * 2, clut, nullptr); + if (!skipsecond) + convert(buf2, sbuf2, len * 2, clut, nullptr); + + interpolate_simple_rgba32f(b, buf1, buf2, len, fx, fdx, fy, fdy); + + length -= len; + b += len; + } + } else { // !(data->fast_matrix) + const auto fetcher = + (layout->bpp == QPixelLayout::BPP32) + ? fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32, uint> + : fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPPNone, uint>; + + 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; + ushort distxs[BufferSize / 2]; + ushort distys[BufferSize / 2]; + + while (length) { + const int len = qMin(length, BufferSize / 2); + fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw); + + convert(buf1, sbuf1, len * 2, clut, nullptr); + convert(buf2, sbuf2, len * 2, clut, nullptr); + + interpolate_perspective_rgba32f(b, buf1, buf2, len, distxs, distys); + + length -= len; + b += len; + } + } + return buffer; +} + +template<TextureBlendType blendType> +static const QRgba32F *QT_FASTCALL fetchTransformedBilinearFP_uint64(QRgba32F *buffer, const QSpanData *data, + int y, int x, int length) +{ + const auto convert = convert64ToRGBA32F[data->texture.format]; + + const qreal cx = x + qreal(0.5); + const qreal cy = y + qreal(0.5); + + quint64 sbuf1[BufferSize]; + quint64 sbuf2[BufferSize]; + QRgba32F buf1[BufferSize]; + QRgba32F buf2[BufferSize]; + QRgba32F *b = buffer; + + if (canUseFastMatrixPath(cx, cy, length, data)) { + // 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, quint64>; + + const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0); + while (length) { + const int len = qMin(length, BufferSize / 2); + fetcher(sbuf1, sbuf2, len, data->texture, fx, fy, fdx, fdy); + + convert(buf1, sbuf1, len * 2); + if (!skipsecond) + convert(buf2, sbuf2, len * 2); + + interpolate_simple_rgba32f(b, buf1, buf2, len, fx, fdx, fy, fdy); + + length -= len; + b += len; + } + } else { // !(data->fast_matrix) + const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP64, quint64>; + + 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; + + ushort distxs[BufferSize / 2]; + ushort distys[BufferSize / 2]; + + while (length) { + const int len = qMin(length, BufferSize / 2); + fetcher(sbuf1, sbuf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw); + + convert(buf1, sbuf1, len * 2); + convert(buf2, sbuf2, len * 2); + + interpolate_perspective_rgba32f(b, buf1, buf2, len, distxs, distys); + + length -= len; + b += len; + } + } + return buffer; +} + +template<TextureBlendType blendType> +static const QRgba32F *QT_FASTCALL fetchTransformedBilinearFP(QRgba32F *buffer, const QSpanData *data, + int y, int x, int length) +{ + const auto convert = data->rasterBuffer->format == QImage::Format_RGBA32FPx4 ? convertRGBA32FToRGBA32FPM + : convertRGBA32FToRGBA32F; + + const qreal cx = x + qreal(0.5); + const qreal cy = y + qreal(0.5); + + QRgba32F buf1[BufferSize]; + QRgba32F buf2[BufferSize]; + QRgba32F *b = buffer; + + if (canUseFastMatrixPath(cx, cy, length, data)) { + // 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::BPP32FPx4, QRgba32F>; + + const bool skipsecond = (fdy == 0) && ((fy & 0x0000ffff) == 0); + while (length) { + const int len = qMin(length, BufferSize / 2); + fetcher(buf1, buf2, len, data->texture, fx, fy, fdx, fdy); + + convert(buf1, len * 2); + if (!skipsecond) + convert(buf2, len * 2); + + interpolate_simple_rgba32f(b, buf1, buf2, len, fx, fdx, fy, fdy); + + length -= len; + b += len; + } + } else { // !(data->fast_matrix) + const auto fetcher = fetchTransformedBilinear_slow_fetcher<blendType, QPixelLayout::BPP32FPx4, QRgba32F>; + + 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; + + ushort distxs[BufferSize / 2]; + ushort distys[BufferSize / 2]; + + while (length) { + const int len = qMin(length, BufferSize / 2); + fetcher(buf1, buf2, distxs, distys, len, data->texture, fx, fy, fw, fdx, fdy, fdw); + + convert(buf1, len * 2); + convert(buf2, len * 2); + + interpolate_perspective_rgba32f(b, buf1, buf2, len, distxs, distys); + + length -= len; + b += len; + } + } + return buffer; +} + +template<TextureBlendType blendType> +static const QRgba32F *QT_FASTCALL fetchTransformedBilinearFP(QRgba32F *buffer, const Operator *, + const QSpanData *data, int y, int x, int length) +{ + switch (qPixelLayouts[data->texture.format].bpp) { + case QPixelLayout::BPP64: + case QPixelLayout::BPP16FPx4: + return fetchTransformedBilinearFP_uint64<blendType>(buffer, data, y, x, length); + case QPixelLayout::BPP32FPx4: + return fetchTransformedBilinearFP<blendType>(buffer, data, y, x, length); + default: + return fetchTransformedBilinearFP_uint32<blendType>(buffer, data, y, x, length); + } +} +#endif // QT_CONFIG(raster_fp) + // FetchUntransformed can have more specialized methods added depending on SIMD features. static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = { nullptr, // Invalid @@ -2416,6 +3087,12 @@ static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = { fetchUntransformed, // RGBA64_Premultiplied fetchUntransformed, // Grayscale16 fetchUntransformed, // BGR888 + fetchUntransformed, // RGBX16FPx4 + fetchUntransformed, // RGBA16FPx4 + fetchUntransformed, // RGBA16FPx4_Premultiplied + fetchUntransformed, // RGBX32Px4 + fetchUntransformed, // RGBA32FPx4 + fetchUntransformed, // RGBA32FPx4_Premultiplied }; static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = { @@ -2494,6 +3171,21 @@ static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QIm } #endif +#if QT_CONFIG(raster_fp) +static const SourceFetchProcFP sourceFetchGenericFP[NBlendTypes] = { + fetchUntransformedFP, // Untransformed + fetchUntransformedFP, // Tiled + fetchTransformedFP<BlendTransformed>, // Transformed + fetchTransformedFP<BlendTransformedTiled>, // TransformedTiled + fetchTransformedBilinearFP<BlendTransformedBilinear>, // Bilinear + fetchTransformedBilinearFP<BlendTransformedBilinearTiled> // BilinearTiled +}; + +static inline SourceFetchProcFP getSourceFetchFP(TextureBlendType blendType, QImage::Format /*format*/) +{ + return sourceFetchGenericFP[blendType]; +} +#endif #define FIXPT_BITS 8 #define FIXPT_SIZE (1<<FIXPT_BITS) @@ -2513,6 +3205,22 @@ static const QRgba64& qt_gradient_pixel64_fixed(const QGradientData *data, int f } #endif +#if QT_CONFIG(raster_fp) +static inline QRgba32F qt_gradient_pixelFP(const QGradientData *data, qreal pos) +{ + int ipos = int(pos * (GRADIENT_STOPTABLE_SIZE - 1) + qreal(0.5)); + QRgba64 rgb64 = data->colorTable64[qt_gradient_clamp(data, ipos)]; + return QRgba32F::fromRgba64(rgb64.red(),rgb64.green(), rgb64.blue(), rgb64.alpha()); +} + +static inline QRgba32F qt_gradient_pixelFP_fixed(const QGradientData *data, int fixed_pos) +{ + int ipos = (fixed_pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS; + QRgba64 rgb64 = data->colorTable64[qt_gradient_clamp(data, ipos)]; + return QRgba32F::fromRgba64(rgb64.red(), rgb64.green(), rgb64.blue(), rgb64.alpha()); +} +#endif + static void QT_FASTCALL getLinearGradientValues(LinearGradientValues *v, const QSpanData *data) { v->dx = data->gradient.linear.end.x - data->gradient.linear.origin.x; @@ -2566,6 +3274,29 @@ public: }; #endif +#if QT_CONFIG(raster_fp) +class GradientBaseFP +{ +public: + typedef QRgba32F Type; + static Type null() { return QRgba32F::fromRgba64(0,0,0,0); } + static Type fetchSingle(const QGradientData& gradient, qreal v) + { + return qt_gradient_pixelFP(&gradient, v); + } + static Type fetchSingle(const QGradientData& gradient, int v) + { + return qt_gradient_pixelFP_fixed(&gradient, v); + } + static void memfill(Type *buffer, Type fill, int length) + { + quint64 fillCopy; + memcpy(&fillCopy, &fill, sizeof(quint64)); + qt_memfill64((quint64*)buffer, fillCopy, length); + } +}; +#endif + template<class GradientBase, typename BlendType> static inline const BlendType * QT_FASTCALL qt_fetch_linear_gradient_template( BlendType *buffer, const Operator *op, const QSpanData *data, @@ -2651,6 +3382,13 @@ static const QRgba64 * QT_FASTCALL qt_fetch_linear_gradient_rgb64(QRgba64 *buffe return qt_fetch_linear_gradient_template<GradientBase64, QRgba64>(buffer, op, data, y, x, length); } #endif +#if QT_CONFIG(raster_fp) +static const QRgba32F * QT_FASTCALL qt_fetch_linear_gradient_rgbfp(QRgba32F *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) +{ + return qt_fetch_linear_gradient_template<GradientBaseFP, QRgba32F>(buffer, op, data, y, x, length); +} +#endif static void QT_FASTCALL getRadialGradientValues(RadialGradientValues *v, const QSpanData *data) { @@ -2720,6 +3458,14 @@ const QRgba64 * QT_FASTCALL qt_fetch_radial_gradient_rgb64(QRgba64 *buffer, cons } #endif +#if QT_CONFIG(raster_fp) +static const QRgba32F * QT_FASTCALL qt_fetch_radial_gradient_rgbfp(QRgba32F *buffer, const Operator *op, const QSpanData *data, + int y, int x, int length) +{ + return qt_fetch_radial_gradient_template<RadialFetchPlain<GradientBaseFP>, QRgba32F>(buffer, op, data, y, x, length); +} +#endif + template <class GradientBase, typename BlendType> static inline const BlendType * QT_FASTCALL qt_fetch_conical_gradient_template( BlendType *buffer, const QSpanData *data, @@ -2785,21 +3531,37 @@ static const QRgba64 * QT_FASTCALL qt_fetch_conical_gradient_rgb64(QRgba64 *buff } #endif +#if QT_CONFIG(raster_fp) +static const QRgba32F * QT_FASTCALL qt_fetch_conical_gradient_rgbfp(QRgba32F *buffer, const Operator *, const QSpanData *data, + int y, int x, int length) +{ + return qt_fetch_conical_gradient_template<GradientBaseFP, QRgba32F>(buffer, data, y, x, length); +} +#endif + extern CompositionFunctionSolid qt_functionForModeSolid_C[]; extern CompositionFunctionSolid64 qt_functionForModeSolid64_C[]; +extern CompositionFunctionSolidFP qt_functionForModeSolidFP_C[]; static const CompositionFunctionSolid *functionForModeSolid = qt_functionForModeSolid_C; #if QT_CONFIG(raster_64bit) static const CompositionFunctionSolid64 *functionForModeSolid64 = qt_functionForModeSolid64_C; #endif +#if QT_CONFIG(raster_fp) +static const CompositionFunctionSolidFP *functionForModeSolidFP = qt_functionForModeSolidFP_C; +#endif extern CompositionFunction qt_functionForMode_C[]; extern CompositionFunction64 qt_functionForMode64_C[]; +extern CompositionFunctionFP qt_functionForModeFP_C[]; static const CompositionFunction *functionForMode = qt_functionForMode_C; #if QT_CONFIG(raster_64bit) static const CompositionFunction64 *functionForMode64 = qt_functionForMode64_C; #endif +#if QT_CONFIG(raster_fp) +static const CompositionFunctionFP *functionForModeFP = qt_functionForModeFP_C; +#endif static TextureBlendType getBlendType(const QSpanData *data) { @@ -2833,9 +3595,8 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in case QSpanData::Solid: solidSource = data->solidColor.isOpaque(); op.srcFetch = nullptr; -#if QT_CONFIG(raster_64bit) op.srcFetch64 = nullptr; -#endif + op.srcFetchFP = nullptr; break; case QSpanData::LinearGradient: solidSource = !data->gradient.alphaColor; @@ -2844,6 +3605,9 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in #if QT_CONFIG(raster_64bit) op.srcFetch64 = qt_fetch_linear_gradient_rgb64; #endif +#if QT_CONFIG(raster_fp) + op.srcFetchFP = qt_fetch_linear_gradient_rgbfp; +#endif break; case QSpanData::RadialGradient: solidSource = !data->gradient.alphaColor; @@ -2852,6 +3616,9 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in #if QT_CONFIG(raster_64bit) op.srcFetch64 = qt_fetch_radial_gradient_rgb64; #endif +#if QT_CONFIG(raster_fp) + op.srcFetchFP = qt_fetch_radial_gradient_rgbfp; +#endif break; case QSpanData::ConicalGradient: solidSource = !data->gradient.alphaColor; @@ -2859,6 +3626,9 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in #if QT_CONFIG(raster_64bit) op.srcFetch64 = qt_fetch_conical_gradient_rgb64; #endif +#if QT_CONFIG(raster_fp) + op.srcFetchFP = qt_fetch_conical_gradient_rgbfp; +#endif break; case QSpanData::Texture: solidSource = !data->texture.hasAlpha; @@ -2866,13 +3636,19 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in #if QT_CONFIG(raster_64bit) op.srcFetch64 = getSourceFetch64(getBlendType(data), data->texture.format);; #endif +#if QT_CONFIG(raster_fp) + op.srcFetchFP = getSourceFetchFP(getBlendType(data), data->texture.format); +#endif break; default: Q_UNREACHABLE(); break; } #if !QT_CONFIG(raster_64bit) - op.srcFetch64 = 0; + op.srcFetch64 = nullptr; +#endif +#if !QT_CONFIG(raster_fp) + op.srcFetchFP = nullptr; #endif op.mode = data->rasterBuffer->compositionMode; @@ -2883,8 +3659,14 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in #if QT_CONFIG(raster_64bit) op.destFetch64 = destFetchProc64[data->rasterBuffer->format]; #else - op.destFetch64 = 0; + op.destFetch64 = nullptr; #endif +#if QT_CONFIG(raster_fp) + if (data->rasterBuffer->format > QImage::Format_Indexed8) + op.destFetchFP = destFetchFP; + else +#endif + op.destFetchFP = nullptr; if (op.mode == QPainter::CompositionMode_Source && (data->type != QSpanData::Texture || data->texture.const_alpha == 256)) { const QSpan *lastSpan = spans + spanCount; @@ -2905,6 +3687,9 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in if (op.destFetch64 != destFetchRGB64) op.destFetch64 = destFetch64Undefined; #endif +#if QT_CONFIG(raster_fp) + op.destFetchFP = destFetchFPUndefined; +#endif } } @@ -2916,9 +3701,18 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in op.funcSolid64 = functionForModeSolid64[op.mode]; op.func64 = functionForMode64[op.mode]; #else - op.destStore64 = 0; - op.funcSolid64 = 0; - op.func64 = 0; + op.destStore64 = nullptr; + op.funcSolid64 = nullptr; + op.func64 = nullptr; +#endif +#if QT_CONFIG(raster_fp) + op.destStoreFP = destStoreFP; + op.funcSolidFP = functionForModeSolidFP[op.mode]; + op.funcFP = functionForModeFP[op.mode]; +#else + op.destStoreFP = nullptr; + op.funcSolidFP = nullptr; + op.funcFP = nullptr; #endif return op; @@ -2927,6 +3721,12 @@ static inline Operator getOperator(const QSpanData *data, const QSpan *spans, in static void spanfill_from_first(QRasterBuffer *rasterBuffer, QPixelLayout::BPP bpp, int x, int y, int length) { switch (bpp) { + case QPixelLayout::BPP32FPx4: { + QRgba32F *dest = reinterpret_cast<QRgba32F *>(rasterBuffer->scanLine(y)) + x; + qt_memfill_template(dest + 1, dest[0], length - 1); + break; + } + case QPixelLayout::BPP16FPx4: case QPixelLayout::BPP64: { quint64 *dest = reinterpret_cast<quint64 *>(rasterBuffer->scanLine(y)) + x; qt_memfill_template(dest + 1, dest[0], length - 1); @@ -3027,7 +3827,7 @@ static void blend_color_argb(int count, const QSpan *spans, void *userData) } } -void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) +static void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) { #if QT_CONFIG(raster_64bit) QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -3068,6 +3868,47 @@ void blend_color_generic_rgb64(int count, const QSpan *spans, void *userData) #endif } +static void blend_color_generic_fp(int count, const QSpan *spans, void *userData) +{ +#if QT_CONFIG(raster_fp) + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + Operator op = getOperator(data, nullptr, 0); + if (!op.funcSolidFP || !op.destFetchFP) { + qCDebug(lcQtGuiDrawHelper, "blend_color_generic_fp: unsupported 4xF16 blend attempted, falling back to 32-bit"); + return blend_color_generic(count, spans, userData); + } + + QRgba32F buffer[BufferSize]; + const QRgba32F color = qConvertRgb64ToRgbaF32(data->solidColor); + const bool solidFill = op.mode == QPainter::CompositionMode_Source; + QPixelLayout::BPP bpp = qPixelLayouts[data->rasterBuffer->format].bpp; + + while (count--) { + int x = spans->x; + int length = spans->len; + if (solidFill && bpp >= QPixelLayout::BPP8 && spans->coverage == 255 && length && op.destStoreFP) { + // If dest doesn't matter we don't need to bother with blending or converting all the identical pixels + op.destStoreFP(data->rasterBuffer, x, spans->y, &color, 1); + spanfill_from_first(data->rasterBuffer, bpp, x, spans->y, length); + length = 0; + } + + while (length) { + int l = qMin(BufferSize, length); + QRgba32F *dest = op.destFetchFP(buffer, data->rasterBuffer, x, spans->y, l); + op.funcSolidFP(dest, l, color, spans->coverage); + if (op.destStoreFP) + op.destStoreFP(data->rasterBuffer, x, spans->y, dest, l); + length -= l; + x += l; + } + ++spans; + } +#else + blend_color_generic(count, spans, userData); +#endif +} + template <typename T> void handleSpans(int count, const QSpan *spans, const QSpanData *data, T &handler) { @@ -3098,7 +3939,7 @@ void handleSpans(int count, const QSpan *spans, const QSpanData *data, T &handle int process_length = l; int process_x = x; - const typename T::BlendType *src = handler.fetch(process_x, y, process_length); + const auto *src = handler.fetch(process_x, y, process_length); int offset = 0; while (l > 0) { if (x == spans->x) // new span? @@ -3123,31 +3964,20 @@ void handleSpans(int count, const QSpan *spans, const QSpanData *data, T &handle } } -template<typename T> struct QBlendBase { - typedef T BlendType; - QBlendBase(QSpanData *d, const Operator &o) - : data(d) - , op(o) - , dest(nullptr) - { - } - QSpanData *data; Operator op; - - BlendType *dest; - - alignas(8) BlendType buffer[BufferSize]; - alignas(8) BlendType src_buffer[BufferSize]; }; -class BlendSrcGeneric : public QBlendBase<uint> +class BlendSrcGeneric : public QBlendBase { public: + uint *dest = nullptr; + alignas(16) uint buffer[BufferSize]; + alignas(16) uint src_buffer[BufferSize]; BlendSrcGeneric(QSpanData *d, const Operator &o) - : QBlendBase<uint>(d, o) + : QBlendBase{d, o} { } @@ -3170,11 +4000,14 @@ public: }; #if QT_CONFIG(raster_64bit) -class BlendSrcGenericRGB64 : public QBlendBase<QRgba64> +class BlendSrcGenericRGB64 : public QBlendBase { public: + QRgba64 *dest = nullptr; + alignas(16) QRgba64 buffer[BufferSize]; + alignas(16) QRgba64 src_buffer[BufferSize]; BlendSrcGenericRGB64(QSpanData *d, const Operator &o) - : QBlendBase<QRgba64>(d, o) + : QBlendBase{d, o} { } @@ -3202,6 +4035,42 @@ public: }; #endif +#if QT_CONFIG(raster_fp) +class BlendSrcGenericRGBFP : public QBlendBase +{ +public: + QRgba32F *dest = nullptr; + alignas(16) QRgba32F buffer[BufferSize]; + alignas(16) QRgba32F src_buffer[BufferSize]; + BlendSrcGenericRGBFP(QSpanData *d, const Operator &o) + : QBlendBase{d, o} + { + } + + bool isSupported() const + { + return op.funcFP && op.destFetchFP && op.srcFetchFP; + } + + const QRgba32F *fetch(int x, int y, int len) + { + dest = op.destFetchFP(buffer, data->rasterBuffer, x, y, len); + return op.srcFetchFP(src_buffer, &op, data, y, x, len); + } + + void process(int, int, int len, int coverage, const QRgba32F *src, int offset) + { + op.funcFP(dest + offset, src + offset, len, coverage); + } + + void store(int x, int y, int len) + { + if (op.destStoreFP) + op.destStoreFP(data->rasterBuffer, x, y, dest, len); + } +}; +#endif + static void blend_src_generic(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -3225,6 +4094,22 @@ static void blend_src_generic_rgb64(int count, const QSpan *spans, void *userDat } #endif +#if QT_CONFIG(raster_fp) +static void blend_src_generic_fp(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + Operator op = getOperator(data, spans, count); + BlendSrcGenericRGBFP blendFP(data, op); + if (blendFP.isSupported()) + handleSpans(count, spans, data, blendFP); + else { + qCDebug(lcQtGuiDrawHelper, "blend_src_generic_fp: unsupported 4xFP blend attempted, falling back to 32-bit"); + BlendSrcGeneric blend32(data, op); + handleSpans(count, spans, data, blend32); + } +} +#endif + static void blend_untransformed_generic(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -3323,6 +4208,58 @@ static void blend_untransformed_generic_rgb64(int count, const QSpan *spans, voi } #endif +#if QT_CONFIG(raster_fp) +static void blend_untransformed_generic_fp(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + Operator op = getOperator(data, spans, count); + if (!op.funcFP) { + qCDebug(lcQtGuiDrawHelper, "blend_untransformed_generic_rgbaf16: unsupported 4xFP16 blend attempted, falling back to 32-bit"); + return blend_untransformed_generic(count, spans, userData); + } + QRgba32F buffer[BufferSize]; + QRgba32F src_buffer[BufferSize]; + + const int image_width = data->texture.width; + const int image_height = data->texture.height; + int xoff = -qRound(-data->dx); + int yoff = -qRound(-data->dy); + + for (; count--; spans++) { + if (!spans->len) + continue; + int x = spans->x; + int length = spans->len; + int sx = xoff + x; + int sy = yoff + spans->y; + if (sy >= 0 && sy < image_height && sx < image_width) { + if (sx < 0) { + x -= sx; + length += sx; + sx = 0; + } + if (sx + length > image_width) + length = image_width - sx; + if (length > 0) { + const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; + while (length) { + int l = qMin(BufferSize, length); + const QRgba32F *src = op.srcFetchFP(src_buffer, &op, data, sy, sx, l); + QRgba32F *dest = op.destFetchFP(buffer, data->rasterBuffer, x, spans->y, l); + op.funcFP(dest, src, l, coverage); + if (op.destStoreFP) + op.destStoreFP(data->rasterBuffer, x, spans->y, dest, l); + x += l; + sx += l; + length -= l; + } + } + } + } +} +#endif + static void blend_untransformed_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -3626,6 +4563,62 @@ static void blend_tiled_generic_rgb64(int count, const QSpan *spans, void *userD } #endif +#if QT_CONFIG(raster_fp) +static void blend_tiled_generic_fp(int count, const QSpan *spans, void *userData) +{ + QSpanData *data = reinterpret_cast<QSpanData *>(userData); + + Operator op = getOperator(data, spans, count); + if (!op.funcFP) { + qCDebug(lcQtGuiDrawHelper, "blend_tiled_generic_fp: unsupported 4xFP blend attempted, falling back to 32-bit"); + return blend_tiled_generic(count, spans, userData); + } + QRgba32F buffer[BufferSize]; + QRgba32F src_buffer[BufferSize]; + + const int image_width = data->texture.width; + const int image_height = data->texture.height; + int xoff = -qRound(-data->dx) % image_width; + int yoff = -qRound(-data->dy) % image_height; + + if (xoff < 0) + xoff += image_width; + if (yoff < 0) + yoff += image_height; + + // Consider tiling optimizing like the other versions. + + while (count--) { + int x = spans->x; + int length = spans->len; + int sx = (xoff + spans->x) % image_width; + int sy = (spans->y + yoff) % image_height; + if (sx < 0) + sx += image_width; + if (sy < 0) + sy += image_height; + + const int coverage = (spans->coverage * data->texture.const_alpha) >> 8; + while (length) { + int l = qMin(image_width - sx, length); + if (BufferSize < l) + l = BufferSize; + const QRgba32F *src = op.srcFetchFP(src_buffer, &op, data, sy, sx, l); + QRgba32F *dest = op.destFetchFP(buffer, data->rasterBuffer, x, spans->y, l); + op.funcFP(dest, src, l, coverage); + if (op.destStoreFP) + op.destStoreFP(data->rasterBuffer, x, spans->y, dest, l); + x += l; + sx += l; + length -= l; + if (sx >= image_width) + sx = 0; + } + ++spans; + } +} +#endif + static void blend_tiled_argb(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); @@ -3812,19 +4805,31 @@ static const ProcessSpans processTextureSpansGeneric64[NBlendTypes] = { }; #endif +#if QT_CONFIG(raster_fp) +static const ProcessSpans processTextureSpansGenericFP[NBlendTypes] = { + blend_untransformed_generic_fp, // Untransformed + blend_tiled_generic_fp, // Tiled + blend_src_generic_fp, // Transformed + blend_src_generic_fp, // TransformedTiled + blend_src_generic_fp, // TransformedBilinear + blend_src_generic_fp // TransformedBilinearTiled +}; +#endif void qBlendTexture(int count, const QSpan *spans, void *userData) { QSpanData *data = reinterpret_cast<QSpanData *>(userData); TextureBlendType blendType = getBlendType(data); ProcessSpans proc; switch (data->rasterBuffer->format) { + case QImage::Format_Invalid: + Q_UNREACHABLE(); + return; case QImage::Format_ARGB32_Premultiplied: proc = processTextureSpansARGB32PM[blendType]; break; case QImage::Format_RGB16: proc = processTextureSpansRGB16[blendType]; break; -#if QT_CONFIG(raster_64bit) #if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8) case QImage::Format_ARGB32: case QImage::Format_RGBA8888: @@ -3837,12 +4842,28 @@ void qBlendTexture(int count, const QSpan *spans, void *userData) case QImage::Format_RGBA64: case QImage::Format_RGBA64_Premultiplied: case QImage::Format_Grayscale16: +#if !QT_CONFIG(raster_fp) + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: + case QImage::Format_RGBX32FPx4: + case QImage::Format_RGBA32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: +#endif +#if QT_CONFIG(raster_64bit) proc = processTextureSpansGeneric64[blendType]; break; #endif // QT_CONFIG(raster_64bit) - case QImage::Format_Invalid: - Q_UNREACHABLE(); - return; +#if QT_CONFIG(raster_fp) + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: + case QImage::Format_RGBX32FPx4: + case QImage::Format_RGBA32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: + proc = processTextureSpansGenericFP[blendType]; + break; +#endif default: proc = processTextureSpansGeneric[blendType]; break; @@ -3925,12 +4946,13 @@ void qBlendGradient(int count, const QSpan *spans, void *userData) data->type == QSpanData::LinearGradient && data->gradient.linear.end.x == data->gradient.linear.origin.x; switch (data->rasterBuffer->format) { + case QImage::Format_Invalid: + break; case QImage::Format_RGB32: case QImage::Format_ARGB32_Premultiplied: if (isVerticalGradient) return blend_vertical_gradient_argb(count, spans, userData); return blend_src_generic(count, spans, userData); -#if QT_CONFIG(raster_64bit) #if defined(__SSE2__) || defined(__ARM_NEON__) || (Q_PROCESSOR_WORDSIZE == 8) case QImage::Format_ARGB32: case QImage::Format_RGBA8888: @@ -3942,12 +4964,30 @@ void qBlendGradient(int count, const QSpan *spans, void *userData) case QImage::Format_RGBX64: case QImage::Format_RGBA64: case QImage::Format_RGBA64_Premultiplied: +#if !QT_CONFIG(raster_fp) + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: + case QImage::Format_RGBX32FPx4: + case QImage::Format_RGBA32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: +#endif +#if QT_CONFIG(raster_64bit) if (isVerticalGradient) return blend_vertical_gradient<blend_color_generic_rgb64>(count, spans, userData); return blend_src_generic_rgb64(count, spans, userData); #endif // QT_CONFIG(raster_64bit) - case QImage::Format_Invalid: - break; +#if QT_CONFIG(raster_fp) + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: + case QImage::Format_RGBX32FPx4: + case QImage::Format_RGBA32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: + if (isVerticalGradient) + return blend_vertical_gradient<blend_color_generic_fp>(count, spans, userData); + return blend_src_generic_fp(count, spans, userData); +#endif default: if (isVerticalGradient) return blend_vertical_gradient<blend_color_generic>(count, spans, userData); @@ -4806,6 +5846,17 @@ static void qt_rectfill_quint64(QRasterBuffer *rasterBuffer, c64, x, y, width, height, rasterBuffer->bytesPerLine()); } +static void qt_rectfill_fp32x4(QRasterBuffer *rasterBuffer, + int x, int y, int width, int height, + const QRgba64 &color) +{ + const auto store = qStoreFromRGBA64PM[rasterBuffer->format]; + QRgba32F c; + store(reinterpret_cast<uchar *>(&c), &color, 0, 1, nullptr, nullptr); + qt_rectfill<QRgba32F>(reinterpret_cast<QRgba32F *>(rasterBuffer->buffer()), + c, x, y, width, height, rasterBuffer->bytesPerLine()); +} + // Map table for destination image format. Contains function pointers // for blends of various types unto the destination @@ -5036,6 +6087,54 @@ DrawHelper qDrawHelper[QImage::NImageFormats] = qt_alphargbblit_generic, qt_rectfill_quint24 }, + // Format_RGBX16FPx4 + { + blend_color_generic_fp, + nullptr, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_quint64 + }, + // Format_RGBA16FPx4 + { + blend_color_generic_fp, + nullptr, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_quint64 + }, + // Format_RGBA16FPx4_Premultiplied + { + blend_color_generic_fp, + nullptr, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_quint64 + }, + // Format_RGBX32FPx4 + { + blend_color_generic_fp, + nullptr, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_fp32x4 + }, + // Format_RGBA32FPx4 + { + blend_color_generic_fp, + nullptr, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_fp32x4 + }, + // Format_RGBA32FPx4_Premultiplied + { + blend_color_generic_fp, + nullptr, + qt_alphamapblit_generic, + qt_alphargbblit_generic, + qt_rectfill_fp32x4 + }, }; #if !defined(__SSE2__) @@ -5260,6 +6359,14 @@ static void qInitDrawhelperFunctions() destStoreProc64[QImage::Format_ARGB32] = destStore64ARGB32_sse4; destStoreProc64[QImage::Format_RGBA8888] = destStore64RGBA8888_sse4; #endif +#if QT_CONFIG(raster_fp) + extern const QRgba32F *QT_FASTCALL fetchRGBA32FToRGBA32F_sse4(QRgba32F *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + extern void QT_FASTCALL storeRGBX32FFromRGBA32F_sse4(uchar *dest, const QRgba32F *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + extern void QT_FASTCALL storeRGBA32FFromRGBA32F_sse4(uchar *dest, const QRgba32F *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + qFetchToRGBA32F[QImage::Format_RGBA32FPx4] = fetchRGBA32FToRGBA32F_sse4; + qStoreFromRGBA32F[QImage::Format_RGBX32FPx4] = storeRGBX32FFromRGBA32F_sse4; + qStoreFromRGBA32F[QImage::Format_RGBA32FPx4] = storeRGBA32FFromRGBA32F_sse4; +#endif // QT_CONFIG(raster_fp) } #endif @@ -5296,6 +6403,16 @@ static void qInitDrawhelperFunctions() qt_functionForMode64_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgb64_avx2; qt_functionForModeSolid64_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgb64_avx2; #endif +#if QT_CONFIG(raster_fp) + extern void QT_FASTCALL comp_func_Source_rgbafp_avx2(QRgba32F *destPixels, const QRgba32F *srcPixels, int length, uint const_alpha); + extern void QT_FASTCALL comp_func_SourceOver_rgbafp_avx2(QRgba32F *destPixels, const QRgba32F *srcPixels, int length, uint const_alpha); + extern void QT_FASTCALL comp_func_solid_Source_rgbafp_avx2(QRgba32F *destPixels, int length, QRgba32F color, uint const_alpha); + extern void QT_FASTCALL comp_func_solid_SourceOver_rgbafp_avx2(QRgba32F *destPixels, int length, QRgba32F color, uint const_alpha); + qt_functionForModeFP_C[QPainter::CompositionMode_Source] = comp_func_Source_rgbafp_avx2; + qt_functionForModeFP_C[QPainter::CompositionMode_SourceOver] = comp_func_SourceOver_rgbafp_avx2; + qt_functionForModeSolidFP_C[QPainter::CompositionMode_Source] = comp_func_solid_Source_rgbafp_avx2; + qt_functionForModeSolidFP_C[QPainter::CompositionMode_SourceOver] = comp_func_solid_SourceOver_rgbafp_avx2; +#endif extern void QT_FASTCALL fetchTransformedBilinearARGB32PM_simple_scale_helper_avx2(uint *b, uint *end, const QTextureData &image, int &fx, int &fy, int fdx, int /*fdy*/); @@ -5329,7 +6446,35 @@ static void qInitDrawhelperFunctions() qPixelLayouts[QImage::Format_ARGB32].fetchToRGBA64PM = fetchARGB32ToRGBA64PM_avx2; qPixelLayouts[QImage::Format_RGBX8888].fetchToRGBA64PM = fetchRGBA8888ToRGBA64PM_avx2; qPixelLayouts[QImage::Format_RGBA64].fetchToRGBA64PM = fetchRGBA64ToRGBA64PM_avx2; + + extern const uint *QT_FASTCALL fetchRGB16FToRGB32_avx2(uint *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + extern const uint *QT_FASTCALL fetchRGBA16FToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchRGBA16FPMToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + extern const QRgba64 *QT_FASTCALL fetchRGBA16FToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + extern void QT_FASTCALL storeRGB16FFromRGB32_avx2(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + extern void QT_FASTCALL storeRGBA16FFromARGB32PM_avx2(uchar *dest, const uint *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + qPixelLayouts[QImage::Format_RGBX16FPx4].fetchToARGB32PM = fetchRGB16FToRGB32_avx2; + qPixelLayouts[QImage::Format_RGBX16FPx4].fetchToRGBA64PM = fetchRGBA16FPMToRGBA64PM_avx2; + qPixelLayouts[QImage::Format_RGBX16FPx4].storeFromARGB32PM = storeRGB16FFromRGB32_avx2; + qPixelLayouts[QImage::Format_RGBX16FPx4].storeFromRGB32 = storeRGB16FFromRGB32_avx2; + qPixelLayouts[QImage::Format_RGBA16FPx4].fetchToARGB32PM = fetchRGBA16FToARGB32PM_avx2; + qPixelLayouts[QImage::Format_RGBA16FPx4].fetchToRGBA64PM = fetchRGBA16FToRGBA64PM_avx2; + qPixelLayouts[QImage::Format_RGBA16FPx4].storeFromARGB32PM = storeRGBA16FFromARGB32PM_avx2; + qPixelLayouts[QImage::Format_RGBA16FPx4].storeFromRGB32 = storeRGB16FFromRGB32_avx2; + qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].fetchToARGB32PM = fetchRGB16FToRGB32_avx2; + qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].fetchToRGBA64PM = fetchRGBA16FPMToRGBA64PM_avx2; + qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].storeFromARGB32PM = storeRGB16FFromRGB32_avx2; + qPixelLayouts[QImage::Format_RGBA16FPx4_Premultiplied].storeFromRGB32 = storeRGB16FFromRGB32_avx2; +#if QT_CONFIG(raster_fp) + extern const QRgba32F *QT_FASTCALL fetchRGBA16FToRGBA32F_avx2(QRgba32F *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + extern void QT_FASTCALL storeRGBX16FFromRGBA32F_avx2(uchar *dest, const QRgba32F *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + extern void QT_FASTCALL storeRGBA16FFromRGBA32F_avx2(uchar *dest, const QRgba32F *src, int index, int count, const QList<QRgb> *, QDitherInfo *); + qFetchToRGBA32F[QImage::Format_RGBA16FPx4] = fetchRGBA16FToRGBA32F_avx2; + qStoreFromRGBA32F[QImage::Format_RGBX16FPx4] = storeRGBX16FFromRGBA32F_avx2; + qStoreFromRGBA32F[QImage::Format_RGBA16FPx4] = storeRGBA16FFromRGBA32F_avx2; +#endif // QT_CONFIG(raster_fp) } + #endif #endif // SSE2 diff --git a/src/gui/painting/qdrawhelper_avx2.cpp b/src/gui/painting/qdrawhelper_avx2.cpp index e401a1463f..3e7743e980 100644 --- a/src/gui/painting/qdrawhelper_avx2.cpp +++ b/src/gui/painting/qdrawhelper_avx2.cpp @@ -143,7 +143,6 @@ INTERPOLATE_PIXEL_RGB64_AVX2(__m256i srcVector, __m256i &dstVector, __m256i alph dstVector = _mm256_or_si256(finalAG, finalRB); } - // See BLEND_SOURCE_OVER_ARGB32_SSE2 for details. inline static void Q_DECL_VECTORCALL BLEND_SOURCE_OVER_ARGB32_AVX2(quint32 *dst, const quint32 *src, const int length) { @@ -457,6 +456,40 @@ void QT_FASTCALL comp_func_SourceOver_rgb64_avx2(QRgba64 *dst, const QRgba64 *sr } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_SourceOver_rgbafp_avx2(QRgba32F *dst, const QRgba32F *src, int length, uint const_alpha) +{ + Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255] + + const float a = const_alpha / 255.0f; + const __m128 one = _mm_set1_ps(1.0f); + const __m128 constAlphaVector = _mm_set1_ps(a); + const __m256 one256 = _mm256_set1_ps(1.0f); + const __m256 constAlphaVector256 = _mm256_set1_ps(a); + int x = 0; + for (; x < length - 1; x += 2) { + __m256 srcVector = _mm256_loadu_ps((const float *)&src[x]); + __m256 dstVector = _mm256_loadu_ps((const float *)&dst[x]); + srcVector = _mm256_mul_ps(srcVector, constAlphaVector256); + __m256 alphaChannel = _mm256_permute_ps(srcVector, _MM_SHUFFLE(3, 3, 3, 3)); + alphaChannel = _mm256_sub_ps(one256, alphaChannel); + dstVector = _mm256_mul_ps(dstVector, alphaChannel); + dstVector = _mm256_add_ps(dstVector, srcVector); + _mm256_storeu_ps((float *)(dst + x), dstVector); + } + if (x < length) { + __m128 srcVector = _mm_load_ps((float *)(src + x)); + __m128 dstVector = _mm_load_ps((const float *)(dst + x)); + srcVector = _mm_mul_ps(srcVector, constAlphaVector); + __m128 alphaChannel = _mm_permute_ps(srcVector, _MM_SHUFFLE(3, 3, 3, 3)); + alphaChannel = _mm_sub_ps(one, alphaChannel); + dstVector = _mm_mul_ps(dstVector, alphaChannel); + dstVector = _mm_add_ps(dstVector, srcVector); + _mm_store_ps((float *)(dst + x), dstVector); + } +} +#endif + void QT_FASTCALL comp_func_Source_avx2(uint *dst, const uint *src, int length, uint const_alpha) { if (const_alpha == 255) { @@ -523,6 +556,41 @@ void QT_FASTCALL comp_func_Source_rgb64_avx2(QRgba64 *dst, const QRgba64 *src, i } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_Source_rgbafp_avx2(QRgba32F *dst, const QRgba32F *src, int length, uint const_alpha) +{ + Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255] + if (const_alpha == 255) { + ::memcpy(dst, src, length * sizeof(QRgba32F)); + } else { + const float ca = const_alpha / 255.f; + const float cia = 1.0f - ca; + + const __m128 constAlphaVector = _mm_set1_ps(ca); + const __m128 oneMinusConstAlpha = _mm_set1_ps(cia); + const __m256 constAlphaVector256 = _mm256_set1_ps(ca); + const __m256 oneMinusConstAlpha256 = _mm256_set1_ps(cia); + int x = 0; + for (; x < length - 1; x += 2) { + __m256 srcVector = _mm256_loadu_ps((const float *)&src[x]); + __m256 dstVector = _mm256_loadu_ps((const float *)&dst[x]); + srcVector = _mm256_mul_ps(srcVector, constAlphaVector256); + dstVector = _mm256_mul_ps(dstVector, oneMinusConstAlpha256); + dstVector = _mm256_add_ps(dstVector, srcVector); + _mm256_storeu_ps((float *)&dst[x], dstVector); + } + if (x < length) { + __m128 srcVector = _mm_load_ps((const float *)&src[x]); + __m128 dstVector = _mm_load_ps((const float *)&dst[x]); + srcVector = _mm_mul_ps(srcVector, constAlphaVector); + dstVector = _mm_mul_ps(dstVector, oneMinusConstAlpha); + dstVector = _mm_add_ps(dstVector, srcVector); + _mm_store_ps((float *)&dst[x], dstVector); + } + } +} +#endif + void QT_FASTCALL comp_func_solid_SourceOver_avx2(uint *destPixels, int length, uint color, uint const_alpha) { if ((const_alpha & qAlpha(color)) == 255) { @@ -587,6 +655,69 @@ void QT_FASTCALL comp_func_solid_SourceOver_rgb64_avx2(QRgba64 *destPixels, int } #endif +#if QT_CONFIG(raster_fp) +void QT_FASTCALL comp_func_solid_Source_rgbafp_avx2(QRgba32F *dst, int length, QRgba32F color, uint const_alpha) +{ + Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255] + if (const_alpha == 255) { + for (int i = 0; i < length; ++i) + dst[i] = color; + } else { + const float a = const_alpha / 255.0f; + const __m128 alphaVector = _mm_set1_ps(a); + const __m128 minusAlphaVector = _mm_set1_ps(1.0f - a); + __m128 colorVector = _mm_load_ps((const float *)&color); + colorVector = _mm_mul_ps(colorVector, alphaVector); + const __m256 colorVector256 = _mm256_insertf128_ps(_mm256_castps128_ps256(colorVector), colorVector, 1); + const __m256 minusAlphaVector256 = _mm256_set1_ps(1.0f - a); + int x = 0; + for (; x < length - 1; x += 2) { + __m256 dstVector = _mm256_loadu_ps((const float *)&dst[x]); + dstVector = _mm256_mul_ps(dstVector, minusAlphaVector256); + dstVector = _mm256_add_ps(dstVector, colorVector256); + _mm256_storeu_ps((float *)&dst[x], dstVector); + } + if (x < length) { + __m128 dstVector = _mm_load_ps((const float *)&dst[x]); + dstVector = _mm_mul_ps(dstVector, minusAlphaVector); + dstVector = _mm_add_ps(dstVector, colorVector); + _mm_store_ps((float *)&dst[x], dstVector); + } + } +} + +void QT_FASTCALL comp_func_solid_SourceOver_rgbafp_avx2(QRgba32F *dst, int length, QRgba32F color, uint const_alpha) +{ + Q_ASSERT(const_alpha < 256); // const_alpha is in [0-255] + if (const_alpha == 255 && color.a >= 1.0f) { + for (int i = 0; i < length; ++i) + dst[i] = color; + } else { + __m128 colorVector = _mm_load_ps((const float *)&color); + if (const_alpha != 255) + colorVector = _mm_mul_ps(colorVector, _mm_set1_ps(const_alpha / 255.f)); + __m128 minusAlphaOfColorVector = + _mm_sub_ps(_mm_set1_ps(1.0f), _mm_permute_ps(colorVector, _MM_SHUFFLE(3, 3, 3, 3))); + const __m256 colorVector256 = _mm256_insertf128_ps(_mm256_castps128_ps256(colorVector), colorVector, 1); + const __m256 minusAlphaVector256 = _mm256_insertf128_ps(_mm256_castps128_ps256(minusAlphaOfColorVector), + minusAlphaOfColorVector, 1); + int x = 0; + for (; x < length - 1; x += 2) { + __m256 dstVector = _mm256_loadu_ps((const float *)&dst[x]); + dstVector = _mm256_mul_ps(dstVector, minusAlphaVector256); + dstVector = _mm256_add_ps(dstVector, colorVector256); + _mm256_storeu_ps((float *)&dst[x], dstVector); + } + if (x < length) { + __m128 dstVector = _mm_load_ps((const float *)&dst[x]); + dstVector = _mm_mul_ps(dstVector, minusAlphaOfColorVector); + dstVector = _mm_add_ps(dstVector, colorVector); + _mm_store_ps((float *)&dst[x], dstVector); + } + } +} +#endif + #define interpolate_4_pixels_16_avx2(tlr1, tlr2, blr1, blr2, distx, disty, colorMask, v_256, b) \ { \ /* Correct for later unpack */ \ @@ -1261,6 +1392,258 @@ const QRgba64 *QT_FASTCALL fetchRGBA64ToRGBA64PM_avx2(QRgba64 *buffer, const uch return buffer; } +const uint *QT_FASTCALL fetchRGB16FToRGB32_avx2(uint *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const quint64 *s = reinterpret_cast<const quint64 *>(src) + index; + const __m256 vf = _mm256_set1_ps(255.0f); + const __m256 vh = _mm256_set1_ps(0.5f); + int i = 0; + for (; i + 1 < count; i += 2) { + __m256 vsf = _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(s + i))); + vsf = _mm256_mul_ps(vsf, vf); + vsf = _mm256_add_ps(vsf, vh); + __m256i vsi = _mm256_cvttps_epi32(vsf); + vsi = _mm256_packs_epi32(vsi, vsi); + vsi = _mm256_shufflelo_epi16(vsi, _MM_SHUFFLE(3, 0, 1, 2)); + vsi = _mm256_permute4x64_epi64(vsi, _MM_SHUFFLE(3, 1, 2, 0)); + __m128i vsi128 = _mm256_castsi256_si128(vsi); + vsi128 = _mm_packus_epi16(vsi128, vsi128); + _mm_storel_epi64((__m128i *)(buffer + i), vsi128); + } + if (i < count) { + __m128 vsf = _mm_cvtph_ps(_mm_loadl_epi64((const __m128i *)(s + i))); + vsf = _mm_mul_ps(vsf, _mm_set1_ps(255.0f)); + vsf = _mm_add_ps(vsf, _mm_set1_ps(0.5f)); + __m128i vsi = _mm_cvttps_epi32(vsf); + vsi = _mm_packs_epi32(vsi, vsi); + vsi = _mm_shufflelo_epi16(vsi, _MM_SHUFFLE(3, 0, 1, 2)); + vsi = _mm_packus_epi16(vsi, vsi); + buffer[i] = _mm_cvtsi128_si32(vsi); + } + return buffer; +} + +const uint *QT_FASTCALL fetchRGBA16FToARGB32PM_avx2(uint *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const quint64 *s = reinterpret_cast<const quint64 *>(src) + index; + const __m256 vf = _mm256_set1_ps(255.0f); + const __m256 vh = _mm256_set1_ps(0.5f); + int i = 0; + for (; i + 1 < count; i += 2) { + __m256 vsf = _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(s + i))); + __m256 vsa = _mm256_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3)); + vsf = _mm256_mul_ps(vsf, vsa); + vsf = _mm256_blend_ps(vsf, vsa, 0x88); + vsf = _mm256_mul_ps(vsf, vf); + vsf = _mm256_add_ps(vsf, vh); + __m256i vsi = _mm256_cvttps_epi32(vsf); + vsi = _mm256_packus_epi32(vsi, vsi); + vsi = _mm256_shufflelo_epi16(vsi, _MM_SHUFFLE(3, 0, 1, 2)); + vsi = _mm256_permute4x64_epi64(vsi, _MM_SHUFFLE(3, 1, 2, 0)); + __m128i vsi128 = _mm256_castsi256_si128(vsi); + vsi128 = _mm_packus_epi16(vsi128, vsi128); + _mm_storel_epi64((__m128i *)(buffer + i), vsi128); + } + if (i < count) { + __m128 vsf = _mm_cvtph_ps(_mm_loadl_epi64((const __m128i *)(s + i))); + __m128 vsa = _mm_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3)); + vsf = _mm_mul_ps(vsf, vsa); + vsf = _mm_insert_ps(vsf, vsa, 0x30); + vsf = _mm_mul_ps(vsf, _mm_set1_ps(255.0f)); + vsf = _mm_add_ps(vsf, _mm_set1_ps(0.5f)); + __m128i vsi = _mm_cvttps_epi32(vsf); + vsi = _mm_packus_epi32(vsi, vsi); + vsi = _mm_shufflelo_epi16(vsi, _MM_SHUFFLE(3, 0, 1, 2)); + vsi = _mm_packus_epi16(vsi, vsi); + buffer[i] = _mm_cvtsi128_si32(vsi); + } + return buffer; +} + +const QRgba64 *QT_FASTCALL fetchRGBA16FPMToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const quint64 *s = reinterpret_cast<const quint64 *>(src) + index; + const __m256 vf = _mm256_set1_ps(65535.0f); + const __m256 vh = _mm256_set1_ps(0.5f); + int i = 0; + for (; i + 1 < count; i += 2) { + __m256 vsf = _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(s + i))); + vsf = _mm256_mul_ps(vsf, vf); + vsf = _mm256_add_ps(vsf, vh); + __m256i vsi = _mm256_cvttps_epi32(vsf); + vsi = _mm256_packus_epi32(vsi, vsi); + vsi = _mm256_permute4x64_epi64(vsi, _MM_SHUFFLE(3, 1, 2, 0)); + _mm_storeu_si128((__m128i *)(buffer + i), _mm256_castsi256_si128(vsi)); + } + if (i < count) { + __m128 vsf = _mm_cvtph_ps(_mm_loadl_epi64((const __m128i *)(s + i))); + vsf = _mm_mul_ps(vsf, _mm_set1_ps(65535.0f)); + vsf = _mm_add_ps(vsf, _mm_set1_ps(0.5f)); + __m128i vsi = _mm_cvttps_epi32(vsf); + vsi = _mm_packus_epi32(vsi, vsi); + _mm_storel_epi64((__m128i *)(buffer + i), vsi); + } + return buffer; +} + +const QRgba64 *QT_FASTCALL fetchRGBA16FToRGBA64PM_avx2(QRgba64 *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const quint64 *s = reinterpret_cast<const quint64 *>(src) + index; + const __m256 vf = _mm256_set1_ps(65535.0f); + const __m256 vh = _mm256_set1_ps(0.5f); + int i = 0; + for (; i + 1 < count; i += 2) { + __m256 vsf = _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(s + i))); + __m256 vsa = _mm256_shuffle_ps(vsf, vsf, _MM_SHUFFLE(3, 3, 3, 3)); + vsf = _mm256_mul_ps(vsf, vsa); + vsf = _mm256_blend_ps(vsf, vsa, 0x88); + vsf = _mm256_mul_ps(vsf, vf); + vsf = _mm256_add_ps(vsf, vh); + __m256i vsi = _mm256_cvttps_epi32(vsf); + vsi = _mm256_packus_epi32(vsi, vsi); + vsi = _mm256_permute4x64_epi64(vsi, _MM_SHUFFLE(3, 1, 2, 0)); + _mm_storeu_si128((__m128i *)(buffer + i), _mm256_castsi256_si128(vsi)); + } + if (i < count) { + __m128 vsf = _mm_cvtph_ps(_mm_loadl_epi64((const __m128i *)(s + i))); + __m128 vsa = _mm_shuffle_ps(vsf, vsf, _MM_SHUFFLE(3, 3, 3, 3)); + vsf = _mm_mul_ps(vsf, vsa); + vsf = _mm_insert_ps(vsf, vsa, 0x30); + vsf = _mm_mul_ps(vsf, _mm_set1_ps(65535.0f)); + vsf = _mm_add_ps(vsf, _mm_set1_ps(0.5f)); + __m128i vsi = _mm_cvttps_epi32(vsf); + vsi = _mm_packus_epi32(vsi, vsi); + _mm_storel_epi64((__m128i *)(buffer + i), vsi); + } + return buffer; +} + +void QT_FASTCALL storeRGB16FFromRGB32_avx2(uchar *dest, const uint *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + quint64 *d = reinterpret_cast<quint64 *>(dest) + index; + const __m256 vf = _mm256_set1_ps(1.0f / 255.0f); + int i = 0; + for (; i + 1 < count; i += 2) { + __m256i vsi = _mm256_cvtepu8_epi32(_mm_loadl_epi64((const __m128i *)(src + i))); + vsi = _mm256_shuffle_epi32(vsi, _MM_SHUFFLE(3, 0, 1, 2)); + __m256 vsf = _mm256_cvtepi32_ps(vsi); + vsf = _mm256_mul_ps(vsf, vf); + _mm_storeu_si128((__m128i *)(d + i), _mm256_cvtps_ph(vsf, 0)); + } + if (i < count) { + __m128i vsi = _mm_cvtsi32_si128(src[i]); + vsi = _mm_cvtepu8_epi32(vsi); + vsi = _mm_shuffle_epi32(vsi, _MM_SHUFFLE(3, 0, 1, 2)); + __m128 vsf = _mm_cvtepi32_ps(vsi); + vsf = _mm_mul_ps(vsf, _mm_set1_ps(1.0f / 255.0f)); + _mm_storel_epi64((__m128i *)(d + i), _mm_cvtps_ph(vsf, 0)); + } +} + +void QT_FASTCALL storeRGBA16FFromARGB32PM_avx2(uchar *dest, const uint *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + quint64 *d = reinterpret_cast<quint64 *>(dest) + index; + const __m128 vf = _mm_set1_ps(1.0f / 255.0f); + for (int i = 0; i < count; ++i) { + const uint s = src[i]; + __m128i vsi = _mm_cvtsi32_si128(s); + vsi = _mm_cvtepu8_epi32(vsi); + vsi = _mm_shuffle_epi32(vsi, _MM_SHUFFLE(3, 0, 1, 2)); + __m128 vsf = _mm_cvtepi32_ps(vsi); + const uint8_t a = (s >> 24); + if (a == 255) + vsf = _mm_mul_ps(vsf, vf); + else if (a == 0) + vsf = _mm_set1_ps(0.0f); + else { + const __m128 vsa = _mm_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3)); + __m128 vsr = _mm_rcp_ps(vsa); + vsr = _mm_sub_ps(_mm_add_ps(vsr, vsr), _mm_mul_ps(vsr, _mm_mul_ps(vsr, vsa))); + vsr = _mm_insert_ps(vsr, _mm_set_ss(1.0f), 0x30); + vsf = _mm_mul_ps(vsf, vsr); + } + _mm_storel_epi64((__m128i *)(d + i), _mm_cvtps_ph(vsf, 0)); + } +} + +#if QT_CONFIG(raster_fp) +const QRgba32F *QT_FASTCALL fetchRGBA16FToRGBA32F_avx2(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const quint64 *s = reinterpret_cast<const quint64 *>(src) + index; + int i = 0; + for (; i + 1 < count; i += 2) { + __m256 vsf = _mm256_cvtph_ps(_mm_loadu_si128((const __m128i *)(s + i))); + __m256 vsa = _mm256_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3)); + vsf = _mm256_mul_ps(vsf, vsa); + vsf = _mm256_blend_ps(vsf, vsa, 0x88); + _mm256_storeu_ps((float *)(buffer + i), vsf); + } + if (i < count) { + __m128 vsf = _mm_cvtph_ps(_mm_loadl_epi64((const __m128i *)(s + i))); + __m128 vsa = _mm_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3)); + vsf = _mm_mul_ps(vsf, vsa); + vsf = _mm_insert_ps(vsf, vsa, 0x30); + _mm_store_ps((float *)(buffer + i), vsf); + } + return buffer; +} + +void QT_FASTCALL storeRGBX16FFromRGBA32F_avx2(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + quint64 *d = reinterpret_cast<quint64 *>(dest) + index; + const __m128 *s = reinterpret_cast<const __m128 *>(src); + const __m128 zero = _mm_set_ps(1.0f, 0.0f, 0.0f, 0.0f); + for (int i = 0; i < count; ++i) { + __m128 vsf = _mm_load_ps(reinterpret_cast<const float *>(s + i)); + const __m128 vsa = _mm_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3)); + const float a = _mm_cvtss_f32(vsa); + if (a == 1.0f) + { } + else if (a == 0.0f) + vsf = zero; + else { + __m128 vsr = _mm_rcp_ps(vsa); + vsr = _mm_sub_ps(_mm_add_ps(vsr, vsr), _mm_mul_ps(vsr, _mm_mul_ps(vsr, vsa))); + vsf = _mm_mul_ps(vsf, vsr); + vsf = _mm_insert_ps(vsf, _mm_set_ss(1.0f), 0x30); + } + _mm_storel_epi64((__m128i *)(d + i), _mm_cvtps_ph(vsf, 0)); + } +} + +void QT_FASTCALL storeRGBA16FFromRGBA32F_avx2(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + quint64 *d = reinterpret_cast<quint64 *>(dest) + index; + const __m128 *s = reinterpret_cast<const __m128 *>(src); + const __m128 zero = _mm_set1_ps(0.0f); + for (int i = 0; i < count; ++i) { + __m128 vsf = _mm_load_ps(reinterpret_cast<const float *>(s + i)); + const __m128 vsa = _mm_permute_ps(vsf, _MM_SHUFFLE(3, 3, 3, 3)); + const float a = _mm_cvtss_f32(vsa); + if (a == 1.0f) + { } + else if (a == 0.0f) + vsf = zero; + else { + __m128 vsr = _mm_rcp_ps(vsa); + vsr = _mm_sub_ps(_mm_add_ps(vsr, vsr), _mm_mul_ps(vsr, _mm_mul_ps(vsr, vsa))); + vsr = _mm_insert_ps(vsr, _mm_set_ss(1.0f), 0x30); + vsf = _mm_mul_ps(vsf, vsr); + } + _mm_storel_epi64((__m128i *)(d + i), _mm_cvtps_ph(vsf, 0)); + } +} +#endif + QT_END_NAMESPACE #endif diff --git a/src/gui/painting/qdrawhelper_p.h b/src/gui/painting/qdrawhelper_p.h index 08826bca2f..89db63725f 100644 --- a/src/gui/painting/qdrawhelper_p.h +++ b/src/gui/painting/qdrawhelper_p.h @@ -108,6 +108,9 @@ class QRasterBuffer; class QClipData; class QRasterPaintEngineState; +template<typename F> class QRgbaF; +typedef QRgbaF<float> QRgba32F; + typedef QT_FT_SpanFunc ProcessSpans; typedef void (*BitmapBlitFunc)(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 &color, @@ -194,8 +197,10 @@ extern void qt_memfill16(quint16 *dest, quint16 value, qsizetype count); typedef void (QT_FASTCALL *CompositionFunction)(uint *Q_DECL_RESTRICT dest, const uint *Q_DECL_RESTRICT src, int length, uint const_alpha); typedef void (QT_FASTCALL *CompositionFunction64)(QRgba64 *Q_DECL_RESTRICT dest, const QRgba64 *Q_DECL_RESTRICT src, int length, uint const_alpha); +typedef void (QT_FASTCALL *CompositionFunctionFP)(QRgba32F *Q_DECL_RESTRICT dest, const QRgba32F *Q_DECL_RESTRICT src, int length, uint const_alpha); typedef void (QT_FASTCALL *CompositionFunctionSolid)(uint *dest, int length, uint color, uint const_alpha); typedef void (QT_FASTCALL *CompositionFunctionSolid64)(QRgba64 *dest, int length, QRgba64 color, uint const_alpha); +typedef void (QT_FASTCALL *CompositionFunctionSolidFP)(QRgba32F *dest, int length, QRgba32F color, uint const_alpha); struct LinearGradientValues { @@ -219,10 +224,13 @@ struct RadialGradientValues struct Operator; typedef uint* (QT_FASTCALL *DestFetchProc)(uint *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length); typedef QRgba64* (QT_FASTCALL *DestFetchProc64)(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length); +typedef QRgba32F* (QT_FASTCALL *DestFetchProcFP)(QRgba32F *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length); typedef void (QT_FASTCALL *DestStoreProc)(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length); typedef void (QT_FASTCALL *DestStoreProc64)(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length); +typedef void (QT_FASTCALL *DestStoreProcFP)(QRasterBuffer *rasterBuffer, int x, int y, const QRgba32F *buffer, int length); typedef const uint* (QT_FASTCALL *SourceFetchProc)(uint *buffer, const Operator *o, const QSpanData *data, int y, int x, int length); typedef const QRgba64* (QT_FASTCALL *SourceFetchProc64)(QRgba64 *buffer, const Operator *o, const QSpanData *data, int y, int x, int length); +typedef const QRgba32F* (QT_FASTCALL *SourceFetchProcFP)(QRgba32F *buffer, const Operator *o, const QSpanData *data, int y, int x, int length); struct Operator { @@ -239,6 +247,12 @@ struct Operator CompositionFunctionSolid64 funcSolid64; CompositionFunction64 func64; + DestFetchProcFP destFetchFP; + DestStoreProcFP destStoreFP; + SourceFetchProcFP srcFetchFP; + CompositionFunctionSolidFP funcSolidFP; + CompositionFunctionFP funcFP; + union { LinearGradientValues linear; RadialGradientValues radial; @@ -295,7 +309,7 @@ struct QGradientData #define GRADIENT_STOPTABLE_SIZE 1024 #define GRADIENT_STOPTABLE_SIZE_SHIFT 10 -#if QT_CONFIG(raster_64bit) +#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp) const QRgba64 *colorTable64; //[GRADIENT_STOPTABLE_SIZE]; #endif const QRgb *colorTable32; //[GRADIENT_STOPTABLE_SIZE]; @@ -473,7 +487,7 @@ const BlendType * QT_FASTCALL qt_fetch_radial_gradient_template(BlendType *buffe while (buffer < end) { if (rw == 0) { - *buffer = 0; + *buffer = RadialFetchFunc::null(); } else { qreal invRw = 1 / rw; qreal gx = rx * invRw - data->gradient.radial.focal.x; @@ -845,6 +859,57 @@ static inline QRgba64 interpolate_4_pixels_rgb64(const QRgba64 t[], const QRgba6 } #endif // __SSE2__ +#if QT_CONFIG(raster_fp) +static inline QRgba32F multiplyAlpha_rgba32f(QRgba32F c, float a) +{ + return QRgba32F { c.r * a, c.g * a, c.b * a, c.a * a }; +} + +static inline QRgba32F interpolate_rgba32f(QRgba32F x, float alpha1, QRgba32F y, float alpha2) +{ + x = multiplyAlpha_rgba32f(x, alpha1); + y = multiplyAlpha_rgba32f(y, alpha2); + return QRgba32F { x.r + y.r, x.g + y.g, x.b + y.b, x.a + y.a }; +} +#ifdef __SSE2__ +static inline __m128 Q_DECL_VECTORCALL interpolate_rgba32f(__m128 x, __m128 alpha1, __m128 y, __m128 alpha2) +{ + return _mm_add_ps(_mm_mul_ps(x, alpha1), _mm_mul_ps(y, alpha2)); +} +#endif + +static inline QRgba32F interpolate_4_pixels_rgba32f(const QRgba32F t[], const QRgba32F b[], uint distx, uint disty) +{ + constexpr float f = 1.0f / 65536.0f; + const float dx = distx * f; + const float dy = disty * f; + const float idx = 1.0f - dx; + const float idy = 1.0f - dy; +#ifdef __SSE2__ + const __m128 vtl = _mm_load_ps((const float *)&t[0]); + const __m128 vtr = _mm_load_ps((const float *)&t[1]); + const __m128 vbl = _mm_load_ps((const float *)&b[0]); + const __m128 vbr = _mm_load_ps((const float *)&b[1]); + + const __m128 vdx = _mm_set1_ps(dx); + const __m128 vidx = _mm_set1_ps(idx); + __m128 vt = interpolate_rgba32f(vtl, vidx, vtr, vdx); + __m128 vb = interpolate_rgba32f(vbl, vidx, vbr, vdx); + const __m128 vdy = _mm_set1_ps(dy); + const __m128 vidy = _mm_set1_ps(idy); + vt = interpolate_rgba32f(vt, vidy, vb, vdy); + QRgba32F res; + _mm_store_ps((float*)&res, vt); + return res; +#else + QRgba32F xtop = interpolate_rgba32f(t[0], idx, t[1], dx); + QRgba32F xbot = interpolate_rgba32f(b[0], idx, b[1], dx); + xtop = interpolate_rgba32f(xtop, idy, xbot, dy); + return xtop; +#endif +} +#endif // QT_CONFIG(raster_fp) + static inline uint BYTE_MUL_RGB16(uint x, uint a) { a += 1; uint t = (((x & 0x07e0)*a) >> 8) & 0x07e0; @@ -1024,70 +1089,6 @@ struct IntermediateBuffer quint32 buffer_ag[BufferSize+2]; }; -template <QPixelLayout::BPP bpp> -inline uint QT_FASTCALL qFetchPixel(const uchar *, int) -{ - Q_UNREACHABLE(); - return 0; -} - -template <> -inline uint QT_FASTCALL qFetchPixel<QPixelLayout::BPP1LSB>(const uchar *src, int index) -{ - return (src[index >> 3] >> (index & 7)) & 1; -} - -template <> -inline uint QT_FASTCALL qFetchPixel<QPixelLayout::BPP1MSB>(const uchar *src, int index) -{ - return (src[index >> 3] >> (~index & 7)) & 1; -} - -template <> -inline uint QT_FASTCALL qFetchPixel<QPixelLayout::BPP8>(const uchar *src, int index) -{ - return src[index]; -} - -template <> -inline uint QT_FASTCALL qFetchPixel<QPixelLayout::BPP16>(const uchar *src, int index) -{ - return reinterpret_cast<const quint16 *>(src)[index]; -} - -template <> -inline uint QT_FASTCALL qFetchPixel<QPixelLayout::BPP24>(const uchar *src, int index) -{ - return reinterpret_cast<const quint24 *>(src)[index]; -} - -template <> -inline uint QT_FASTCALL qFetchPixel<QPixelLayout::BPP32>(const uchar *src, int index) -{ - return reinterpret_cast<const uint *>(src)[index]; -} - -template <> -inline uint QT_FASTCALL qFetchPixel<QPixelLayout::BPP64>(const uchar *src, int index) -{ - // 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(); -} - -typedef uint (QT_FASTCALL *FetchPixelFunc)(const uchar *src, int index); - -constexpr FetchPixelFunc qFetchPixelTable[QPixelLayout::BPPCount] = { - nullptr, // BPPNone - qFetchPixel<QPixelLayout::BPP1MSB>, - qFetchPixel<QPixelLayout::BPP1LSB>, - qFetchPixel<QPixelLayout::BPP8>, - qFetchPixel<QPixelLayout::BPP16>, - qFetchPixel<QPixelLayout::BPP24>, - qFetchPixel<QPixelLayout::BPP32>, - qFetchPixel<QPixelLayout::BPP64>, -}; - QT_END_NAMESPACE #endif // QDRAWHELPER_P_H diff --git a/src/gui/painting/qdrawhelper_sse4.cpp b/src/gui/painting/qdrawhelper_sse4.cpp index dc9755e414..f6ec19c718 100644 --- a/src/gui/painting/qdrawhelper_sse4.cpp +++ b/src/gui/painting/qdrawhelper_sse4.cpp @@ -502,6 +502,13 @@ void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4(uchar *dest, const uint *src, i d[i] = qConvertArgb32ToA2rgb30_sse4<PixelOrder>(src[i]); } +template +void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>(uchar *dest, const uint *src, int index, int count, + const QList<QRgb> *, QDitherInfo *); +template +void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>(uchar *dest, const uint *src, int index, int count, + const QList<QRgb> *, QDitherInfo *); + #if QT_CONFIG(raster_64bit) void QT_FASTCALL destStore64ARGB32_sse4(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) { @@ -544,12 +551,68 @@ void QT_FASTCALL storeRGBx64FromRGBA64PM_sse4(uchar *dest, const QRgba64 *src, i convertRGBA64FromRGBA64PM_sse4<true>(d, src, count); } -template -void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4<PixelOrderBGR>(uchar *dest, const uint *src, int index, int count, - const QList<QRgb> *, QDitherInfo *); -template -void QT_FASTCALL storeA2RGB30PMFromARGB32PM_sse4<PixelOrderRGB>(uchar *dest, const uint *src, int index, int count, - const QList<QRgb> *, QDitherInfo *); +#if QT_CONFIG(raster_fp) +const QRgba32F *QT_FASTCALL fetchRGBA32FToRGBA32F_sse4(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba32F *s = reinterpret_cast<const QRgba32F *>(src) + index; + for (int i = 0; i < count; ++i) { + __m128 vsf = _mm_load_ps(reinterpret_cast<const float *>(s + i)); + __m128 vsa = _mm_shuffle_ps(vsf, vsf, _MM_SHUFFLE(3, 3, 3, 3)); + vsf = _mm_mul_ps(vsf, vsa); + vsf = _mm_insert_ps(vsf, vsa, 0x30); + _mm_store_ps(reinterpret_cast<float *>(buffer + i), vsf); + } + return buffer; +} + +void QT_FASTCALL storeRGBX32FFromRGBA32F_sse4(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba32F *d = reinterpret_cast<QRgba32F *>(dest) + index; + const __m128 zero = _mm_set_ps(1.0f, 0.0f, 0.0f, 0.0f); + for (int i = 0; i < count; ++i) { + __m128 vsf = _mm_load_ps(reinterpret_cast<const float *>(src + i)); + const __m128 vsa = _mm_shuffle_ps(vsf, vsf, _MM_SHUFFLE(3, 3, 3, 3)); + const float a = _mm_cvtss_f32(vsa); + if (a == 1.0f) + { } + else if (a == 0.0f) + vsf = zero; + else { + __m128 vsr = _mm_rcp_ps(vsa); + vsr = _mm_sub_ps(_mm_add_ps(vsr, vsr), _mm_mul_ps(vsr, _mm_mul_ps(vsr, vsa))); + vsf = _mm_mul_ps(vsf, vsr); + vsf = _mm_insert_ps(vsf, _mm_set_ss(1.0f), 0x30); + } + _mm_store_ps(reinterpret_cast<float *>(d + i), vsf); + } +} + +void QT_FASTCALL storeRGBA32FFromRGBA32F_sse4(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba32F *d = reinterpret_cast<QRgba32F *>(dest) + index; + const __m128 zero = _mm_set1_ps(0.0f); + for (int i = 0; i < count; ++i) { + __m128 vsf = _mm_load_ps(reinterpret_cast<const float *>(src + i)); + const __m128 vsa = _mm_shuffle_ps(vsf, vsf, _MM_SHUFFLE(3, 3, 3, 3)); + const float a = _mm_cvtss_f32(vsa); + if (a == 1.0f) + { } + else if (a == 0.0f) + vsf = zero; + else { + __m128 vsr = _mm_rcp_ps(vsa); + vsr = _mm_sub_ps(_mm_add_ps(vsr, vsr), _mm_mul_ps(vsr, _mm_mul_ps(vsr, vsa))); + vsr = _mm_insert_ps(vsr, _mm_set_ss(1.0f), 0x30); + vsf = _mm_mul_ps(vsf, vsr); + } + _mm_store_ps(reinterpret_cast<float *>(d + i), vsf); + } +} +#endif + QT_END_NAMESPACE diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp index 28691fa89b..ae44ca6297 100644 --- a/src/gui/painting/qimagescale.cpp +++ b/src/gui/painting/qimagescale.cpp @@ -38,10 +38,12 @@ ****************************************************************************/ #include <private/qimagescale_p.h> #include <private/qdrawhelper_p.h> +#include <private/qimage_p.h> #include "qimage.h" #include "qcolor.h" #include "qrgba64_p.h" +#include "qrgbaf.h" #if QT_CONFIG(thread) && !defined(Q_OS_WASM) #include "qsemaphore.h" @@ -789,6 +791,221 @@ static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest, } #endif +#if QT_CONFIG(raster_fp) +static void qt_qimageScaleRgbaFP_up_x_down_y(QImageScaleInfo *isi, QRgba32F *dest, + int dw, int dh, int dow, int sow); + +static void qt_qimageScaleRgbaFP_down_x_up_y(QImageScaleInfo *isi, QRgba32F *dest, + int dw, int dh, int dow, int sow); + +static void qt_qimageScaleRgbaFP_down_xy(QImageScaleInfo *isi, QRgba32F *dest, + int dw, int dh, int dow, int sow); + +static void qt_qimageScaleRgbaFP_up_xy(QImageScaleInfo *isi, QRgba32F *dest, + int dw, int dh, int dow, int sow) +{ + const QRgba32F **ypoints = (const QRgba32F **)isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + const QRgba32F *sptr = ypoints[y]; + QRgba32F *dptr = dest + (y * dow); + const int yap = yapoints[y]; + if (yap > 0) { + for (int x = 0; x < dw; x++) { + const QRgba32F *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = interpolate_4_pixels_rgba32f(pix, pix + sow, xap * 256, yap * 256); + else + *dptr = interpolate_rgba32f(pix[0], 256 - yap, pix[sow], yap); + dptr++; + } + } else { + for (int x = 0; x < dw; x++) { + const QRgba32F *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = interpolate_rgba32f(pix[0], 256 - xap, pix[1], xap); + else + *dptr = pix[0]; + dptr++; + } + } + } + }; + multithread_pixels_function(isi, dh, scaleSection); +} + +void qt_qimageScaleRgbaFP(QImageScaleInfo *isi, QRgba32F *dest, + int dw, int dh, int dow, int sow) +{ + if (isi->xup_yup == 3) + qt_qimageScaleRgbaFP_up_xy(isi, dest, dw, dh, dow, sow); + else if (isi->xup_yup == 1) + qt_qimageScaleRgbaFP_up_x_down_y(isi, dest, dw, dh, dow, sow); + else if (isi->xup_yup == 2) + qt_qimageScaleRgbaFP_down_x_up_y(isi, dest, dw, dh, dow, sow); + else + qt_qimageScaleRgbaFP_down_xy(isi, dest, dw, dh, dow, sow); +} + +inline static void qt_qimageScaleRgbaFP_helper(const QRgba32F *pix, int xyap, int Cxy, int step, float &r, float &g, float &b, float &a) +{ + constexpr float f = (1.0f / float(1<<14)); + const float xyapf = xyap * f; + const float Cxyf = Cxy * f; + r = pix->red() * xyapf; + g = pix->green() * xyapf; + b = pix->blue() * xyapf; + a = pix->alpha() * xyapf; + int j; + for (j = (1 << 14) - xyap; j > Cxy; j -= Cxy ){ + pix += step; + r += pix->red() * Cxyf; + g += pix->green() * Cxyf; + b += pix->blue() * Cxyf; + a += pix->alpha() * Cxyf; + } + pix += step; + const float jf = j * f; + r += pix->red() * jf; + g += pix->green() * jf; + b += pix->blue() * jf; + a += pix->alpha() * jf; +} + +static void qt_qimageScaleRgbaFP_up_x_down_y(QImageScaleInfo *isi, QRgba32F *dest, + int dw, int dh, int dow, int sow) +{ + const QRgba32F **ypoints = (const QRgba32F **)isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = (yapoints[y]) >> 16; + int yap = (yapoints[y]) & 0xffff; + + QRgba32F *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const QRgba32F *sptr = ypoints[y] + xpoints[x]; + float r, g, b, a; + qt_qimageScaleRgbaFP_helper(sptr, yap, Cy, sow, r, g, b, a); + + int xap = xapoints[x]; + float xapf = xap * (1.f / 256.f); + if (xap > 0) { + float rr, gg, bb, aa; + qt_qimageScaleRgbaFP_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa); + + r = (r * (1.0f - xapf) + (rr * xapf)); + g = (g * (1.0f - xapf) + (gg * xapf)); + b = (b * (1.0f - xapf) + (bb * xapf)); + a = (a * (1.0f - xapf) + (aa * xapf)); + } + *dptr++ = QRgba32F{r, g, b, a}; + } + } + }; + multithread_pixels_function(isi, dh, scaleSection); +} + +static void qt_qimageScaleRgbaFP_down_x_up_y(QImageScaleInfo *isi, QRgba32F *dest, + int dw, int dh, int dow, int sow) +{ + const QRgba32F **ypoints = (const QRgba32F **)isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + QRgba32F *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const QRgba32F *sptr = ypoints[y] + xpoints[x]; + float r, g, b, a; + qt_qimageScaleRgbaFP_helper(sptr, xap, Cx, 1, r, g, b, a); + + int yap = yapoints[y]; + float yapf = yap * (1.f / 256.f); + if (yap > 0) { + float rr, gg, bb, aa; + qt_qimageScaleRgbaFP_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa); + + r = (r * (1.0f - yapf) + (rr * yapf)); + g = (g * (1.0f - yapf) + (gg * yapf)); + b = (b * (1.0f - yapf) + (bb * yapf)); + a = (a * (1.0f - yapf) + (aa * yapf)); + } + *dptr++ = QRgba32F{r, g, b, a}; + } + } + }; + multithread_pixels_function(isi, dh, scaleSection); +} + +static void qt_qimageScaleRgbaFP_down_xy(QImageScaleInfo *isi, QRgba32F *dest, + int dw, int dh, int dow, int sow) +{ + const QRgba32F **ypoints = (const QRgba32F **)isi->ypoints; + int *xpoints = isi->xpoints; + int *xapoints = isi->xapoints; + int *yapoints = isi->yapoints; + + auto scaleSection = [&] (int yStart, int yEnd) { + constexpr float f = 1.f / float(1 << 14); + for (int y = yStart; y < yEnd; ++y) { + int Cy = (yapoints[y]) >> 16; + int yap = (yapoints[y]) & 0xffff; + + QRgba32F *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const QRgba32F *sptr = ypoints[y] + xpoints[x]; + float rx, gx, bx, ax; + qt_qimageScaleRgbaFP_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + + const float yapf = yap * f; + const float Cyf = Cy * f; + float r = rx * yapf; + float g = gx * yapf; + float b = bx * yapf; + float a = ax * yapf; + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + qt_qimageScaleRgbaFP_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + r += rx * Cyf; + g += gx * Cyf; + b += bx * Cyf; + a += ax * Cyf; + } + sptr += sow; + qt_qimageScaleRgbaFP_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + const float jf = j * f; + r += rx * jf; + g += gx * jf; + b += bx * jf; + a += ax * jf; + + *dptr++ = QRgba32F{r, g, b, a}; + } + } + }; + multithread_pixels_function(isi, dh, scaleSection); +} +#endif + static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int *dest, int dw, int dh, int dow, int sow); @@ -1014,6 +1231,12 @@ QImage qSmoothScaleImage(const QImage &src, int dw, int dh) return QImage(); } +#if QT_CONFIG(raster_fp) + if (qt_fpColorPrecision(src.format())) + qt_qimageScaleRgbaFP(scaleinfo, (QRgba32F *)buffer.scanLine(0), + dw, dh, dw, src.bytesPerLine() / 16); + else +#endif #if QT_CONFIG(raster_64bit) if (src.depth() > 32) qt_qimageScaleRgba64(scaleinfo, (QRgba64 *)buffer.scanLine(0), diff --git a/src/gui/painting/qmemrotate.cpp b/src/gui/painting/qmemrotate.cpp index b3bee9e5e7..c58ead6b84 100644 --- a/src/gui/painting/qmemrotate.cpp +++ b/src/gui/painting/qmemrotate.cpp @@ -224,24 +224,11 @@ inline void qt_memrotate90_template(const T *src, int srcWidth, int srcHeight, i { #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN // packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer - if (sizeof(quint32) % sizeof(T) == 0) - qt_memrotate90_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); - else -#endif + static_assert(sizeof(quint32) % sizeof(T) == 0); + qt_memrotate90_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); +#else qt_memrotate90_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -} - -template <> -inline void qt_memrotate90_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride) -{ - // packed algorithm doesn't have any benefit for quint32 - 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); +#endif } template<class T> @@ -268,24 +255,11 @@ inline void qt_memrotate270_template(const T *src, int srcWidth, int srcHeight, { #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN // packed algorithm assumes little endian and that sizeof(quint32)/sizeof(T) is an integer - if (sizeof(quint32) % sizeof(T) == 0) - qt_memrotate270_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); - else -#endif + static_assert(sizeof(quint32) % sizeof(T) == 0); + qt_memrotate270_tiled<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); +#else qt_memrotate270_tiled_unpacked<T>(src, srcWidth, srcHeight, srcStride, dest, dstStride); -} - -template <> -inline void qt_memrotate270_template<quint32>(const quint32 *src, int w, int h, int sstride, quint32 *dest, int dstride) -{ - // packed algorithm doesn't have any benefit for quint32 - 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); +#endif } #define QT_IMPL_MEMROTATE(type) \ @@ -322,10 +296,11 @@ 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_SIMPLE_MEMROTATE(QRgba32F) +QT_IMPL_SIMPLE_MEMROTATE(quint64) +QT_IMPL_SIMPLE_MEMROTATE(quint32) +QT_IMPL_SIMPLE_MEMROTATE(quint24) QT_IMPL_MEMROTATE(quint16) -QT_IMPL_MEMROTATE(quint24) QT_IMPL_MEMROTATE(quint8) void qt_memrotate90_8(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) @@ -404,6 +379,21 @@ void qt_memrotate270_64(const uchar *srcPixels, int w, int h, int sbpl, uchar *d qt_memrotate270((const quint64 *)srcPixels, w, h, sbpl, (quint64 *)destPixels, dbpl); } +void qt_memrotate90_128(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate90((const QRgba32F *)srcPixels, w, h, sbpl, (QRgba32F *)destPixels, dbpl); +} + +void qt_memrotate180_128(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate180((const QRgba32F *)srcPixels, w, h, sbpl, (QRgba32F *)destPixels, dbpl); +} + +void qt_memrotate270_128(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl) +{ + qt_memrotate270((const QRgba32F *)srcPixels, w, h, sbpl, (QRgba32F *)destPixels, dbpl); +} + MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3] = // 90, 180, 270 { @@ -415,6 +405,8 @@ MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3] = { 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_memrotate90_64, qt_memrotate180_64, qt_memrotate270_64 }, // BPP16FPx4 + { qt_memrotate90_128, qt_memrotate180_128, qt_memrotate270_128 }, // BPP32FPx4 }; QT_END_NAMESPACE diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index bae8695e33..68ea3239f9 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -4517,7 +4517,7 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha); gradient.colorTable32 = cacheInfo->buffer32; -#if QT_CONFIG(raster_64bit) +#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp) gradient.colorTable64 = cacheInfo->buffer64; #endif cachedGradient = std::move(cacheInfo); @@ -4541,7 +4541,7 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha); gradient.colorTable32 = cacheInfo->buffer32; -#if QT_CONFIG(raster_64bit) +#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp) gradient.colorTable64 = cacheInfo->buffer64; #endif cachedGradient = std::move(cacheInfo); @@ -4569,7 +4569,7 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode auto cacheInfo = qt_gradient_cache()->getBuffer(*g, alpha); gradient.colorTable32 = cacheInfo->buffer32; -#if QT_CONFIG(raster_64bit) +#if QT_CONFIG(raster_64bit) || QT_CONFIG(raster_fp) gradient.colorTable64 = cacheInfo->buffer64; #endif cachedGradient = std::move(cacheInfo); diff --git a/src/gui/painting/qpixellayout.cpp b/src/gui/painting/qpixellayout.cpp index ca6bca344f..e32d4e124a 100644 --- a/src/gui/painting/qpixellayout.cpp +++ b/src/gui/painting/qpixellayout.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -55,6 +55,9 @@ template<QImage::Format> constexpr uint blueShift(); template<QImage::Format> constexpr uint alphaWidth(); template<QImage::Format> constexpr uint alphaShift(); +template<> constexpr uint redWidth<QImage::Format_RGB32>() { return 8; } +template<> constexpr uint redWidth<QImage::Format_ARGB32>() { return 8; } +template<> constexpr uint redWidth<QImage::Format_ARGB32_Premultiplied>() { return 8; } template<> constexpr uint redWidth<QImage::Format_RGB16>() { return 5; } template<> constexpr uint redWidth<QImage::Format_RGB444>() { return 4; } template<> constexpr uint redWidth<QImage::Format_RGB555>() { return 5; } @@ -69,6 +72,9 @@ template<> constexpr uint redWidth<QImage::Format_RGBX8888>() { return 8; } template<> constexpr uint redWidth<QImage::Format_RGBA8888>() { return 8; } template<> constexpr uint redWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; } +template<> constexpr uint redShift<QImage::Format_RGB32>() { return 16; } +template<> constexpr uint redShift<QImage::Format_ARGB32>() { return 16; } +template<> constexpr uint redShift<QImage::Format_ARGB32_Premultiplied>() { return 16; } template<> constexpr uint redShift<QImage::Format_RGB16>() { return 11; } template<> constexpr uint redShift<QImage::Format_RGB444>() { return 8; } template<> constexpr uint redShift<QImage::Format_RGB555>() { return 10; } @@ -88,6 +94,9 @@ template<> constexpr uint redShift<QImage::Format_RGBX8888>() { return 0; } template<> constexpr uint redShift<QImage::Format_RGBA8888>() { return 0; } template<> constexpr uint redShift<QImage::Format_RGBA8888_Premultiplied>() { return 0; } #endif +template<> constexpr uint greenWidth<QImage::Format_RGB32>() { return 8; } +template<> constexpr uint greenWidth<QImage::Format_ARGB32>() { return 8; } +template<> constexpr uint greenWidth<QImage::Format_ARGB32_Premultiplied>() { return 8; } template<> constexpr uint greenWidth<QImage::Format_RGB16>() { return 6; } template<> constexpr uint greenWidth<QImage::Format_RGB444>() { return 4; } template<> constexpr uint greenWidth<QImage::Format_RGB555>() { return 5; } @@ -102,6 +111,9 @@ template<> constexpr uint greenWidth<QImage::Format_RGBX8888>() { return 8; } template<> constexpr uint greenWidth<QImage::Format_RGBA8888>() { return 8; } template<> constexpr uint greenWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; } +template<> constexpr uint greenShift<QImage::Format_RGB32>() { return 8; } +template<> constexpr uint greenShift<QImage::Format_ARGB32>() { return 8; } +template<> constexpr uint greenShift<QImage::Format_ARGB32_Premultiplied>() { return 8; } template<> constexpr uint greenShift<QImage::Format_RGB16>() { return 5; } template<> constexpr uint greenShift<QImage::Format_RGB444>() { return 4; } template<> constexpr uint greenShift<QImage::Format_RGB555>() { return 5; } @@ -121,6 +133,9 @@ template<> constexpr uint greenShift<QImage::Format_RGBX8888>() { return 8; } template<> constexpr uint greenShift<QImage::Format_RGBA8888>() { return 8; } template<> constexpr uint greenShift<QImage::Format_RGBA8888_Premultiplied>() { return 8; } #endif +template<> constexpr uint blueWidth<QImage::Format_RGB32>() { return 8; } +template<> constexpr uint blueWidth<QImage::Format_ARGB32>() { return 8; } +template<> constexpr uint blueWidth<QImage::Format_ARGB32_Premultiplied>() { return 8; } template<> constexpr uint blueWidth<QImage::Format_RGB16>() { return 5; } template<> constexpr uint blueWidth<QImage::Format_RGB444>() { return 4; } template<> constexpr uint blueWidth<QImage::Format_RGB555>() { return 5; } @@ -135,6 +150,9 @@ template<> constexpr uint blueWidth<QImage::Format_RGBX8888>() { return 8; } template<> constexpr uint blueWidth<QImage::Format_RGBA8888>() { return 8; } template<> constexpr uint blueWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; } +template<> constexpr uint blueShift<QImage::Format_RGB32>() { return 0; } +template<> constexpr uint blueShift<QImage::Format_ARGB32>() { return 0; } +template<> constexpr uint blueShift<QImage::Format_ARGB32_Premultiplied>() { return 0; } template<> constexpr uint blueShift<QImage::Format_RGB16>() { return 0; } template<> constexpr uint blueShift<QImage::Format_RGB444>() { return 0; } template<> constexpr uint blueShift<QImage::Format_RGB555>() { return 0; } @@ -154,6 +172,9 @@ template<> constexpr uint blueShift<QImage::Format_RGBX8888>() { return 16; } template<> constexpr uint blueShift<QImage::Format_RGBA8888>() { return 16; } template<> constexpr uint blueShift<QImage::Format_RGBA8888_Premultiplied>() { return 16; } #endif +template<> constexpr uint alphaWidth<QImage::Format_RGB32>() { return 0; } +template<> constexpr uint alphaWidth<QImage::Format_ARGB32>() { return 8; } +template<> constexpr uint alphaWidth<QImage::Format_ARGB32_Premultiplied>() { return 8; } template<> constexpr uint alphaWidth<QImage::Format_RGB16>() { return 0; } template<> constexpr uint alphaWidth<QImage::Format_RGB444>() { return 0; } template<> constexpr uint alphaWidth<QImage::Format_RGB555>() { return 0; } @@ -168,6 +189,9 @@ template<> constexpr uint alphaWidth<QImage::Format_RGBX8888>() { return 0; } template<> constexpr uint alphaWidth<QImage::Format_RGBA8888>() { return 8; } template<> constexpr uint alphaWidth<QImage::Format_RGBA8888_Premultiplied>() { return 8; } +template<> constexpr uint alphaShift<QImage::Format_RGB32>() { return 24; } +template<> constexpr uint alphaShift<QImage::Format_ARGB32>() { return 24; } +template<> constexpr uint alphaShift<QImage::Format_ARGB32_Premultiplied>() { return 24; } template<> constexpr uint alphaShift<QImage::Format_RGB16>() { return 0; } template<> constexpr uint alphaShift<QImage::Format_RGB444>() { return 0; } template<> constexpr uint alphaShift<QImage::Format_RGB555>() { return 0; } @@ -189,6 +213,9 @@ template<> constexpr uint alphaShift<QImage::Format_RGBA8888_Premultiplied>() { #endif template<QImage::Format> constexpr QPixelLayout::BPP bitsPerPixel(); +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB32>() { return QPixelLayout::BPP32; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB32>() { return QPixelLayout::BPP32; } +template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_ARGB32_Premultiplied>() { return QPixelLayout::BPP32; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB16>() { return QPixelLayout::BPP16; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB444>() { return QPixelLayout::BPP16; } template<> constexpr QPixelLayout::BPP bitsPerPixel<QImage::Format_RGB555>() { return QPixelLayout::BPP16; } @@ -218,6 +245,73 @@ inline void QT_FASTCALL storePixel<QPixelLayout::BPP24>(uchar *dest, int index, reinterpret_cast<quint24 *>(dest)[index] = quint24(pixel); } +template <QPixelLayout::BPP bpp> static +inline uint QT_FASTCALL fetchPixel(const uchar *, int) +{ + Q_UNREACHABLE(); + return 0; +} + +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1LSB>(const uchar *src, int index) +{ + return (src[index >> 3] >> (index & 7)) & 1; +} + +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP1MSB>(const uchar *src, int index) +{ + return (src[index >> 3] >> (~index & 7)) & 1; +} + +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP8>(const uchar *src, int index) +{ + return src[index]; +} + +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP16>(const uchar *src, int index) +{ + return reinterpret_cast<const quint16 *>(src)[index]; +} + +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP24>(const uchar *src, int index) +{ + return reinterpret_cast<const quint24 *>(src)[index]; +} + +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP32>(const uchar *src, int index) +{ + return reinterpret_cast<const uint *>(src)[index]; +} + +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP64>(const uchar *src, int index) +{ + // 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 uint QT_FASTCALL fetchPixel<QPixelLayout::BPP16FPx4>(const uchar *src, int index) +{ + // We have to do the conversion in fetch to fit into a 32bit uint + QRgba16F c = reinterpret_cast<const QRgba16F *>(src)[index]; + return c.toArgb32(); +} + +template <> +inline uint QT_FASTCALL fetchPixel<QPixelLayout::BPP32FPx4>(const uchar *src, int index) +{ + // We have to do the conversion in fetch to fit into a 32bit uint + QRgba32F c = reinterpret_cast<const QRgba32F *>(src)[index]; + return c.toArgb32(); +} + template<QImage::Format Format> static inline uint convertPixelToRGB32(uint s) { @@ -269,7 +363,7 @@ static const uint *QT_FASTCALL fetchRGBToRGB32(uint *buffer, const uchar *src, i } #endif for (int i = 0; i < count; ++i) - buffer[i] = convertPixelToRGB32<Format>(qFetchPixel<BPP>(src, index + i)); + buffer[i] = convertPixelToRGB32<Format>(fetchPixel<BPP>(src, index + i)); return buffer; } @@ -293,7 +387,22 @@ static const QRgba64 *QT_FASTCALL fetchRGBToRGB64(QRgba64 *buffer, const uchar * const QList<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) - buffer[i] = convertPixelToRGB64<Format>(qFetchPixel<bitsPerPixel<Format>()>(src, index + i)); + buffer[i] = convertPixelToRGB64<Format>(fetchPixel<bitsPerPixel<Format>()>(src, index + i)); + return buffer; +} + +template<QImage::Format Format> +static Q_ALWAYS_INLINE QRgba32F convertPixelToRGB32F(uint s) +{ + return QRgba32F::fromArgb32(convertPixelToRGB32<Format>(s)); +} + +template<QImage::Format Format> +static const QRgba32F *QT_FASTCALL fetchRGBToRGB32F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = convertPixelToRGB32F<Format>(fetchPixel<bitsPerPixel<Format>()>(src, index + i)); return buffer; } @@ -360,7 +469,7 @@ static const uint *QT_FASTCALL fetchARGBPMToARGB32PM(uint *buffer, const uchar * } #endif for (int i = 0; i < count; ++i) - buffer[i] = convertPixelToARGB32PM<Format>(qFetchPixel<BPP>(src, index + i)); + buffer[i] = convertPixelToARGB32PM<Format>(fetchPixel<BPP>(src, index + i)); return buffer; } @@ -385,7 +494,33 @@ static const QRgba64 *QT_FASTCALL fetchARGBPMToRGBA64PM(QRgba64 *buffer, const u { constexpr QPixelLayout::BPP bpp = bitsPerPixel<Format>(); for (int i = 0; i < count; ++i) - buffer[i] = convertPixelToRGBA64PM<Format>(qFetchPixel<bpp>(src, index + i)); + buffer[i] = convertPixelToRGBA64PM<Format>(fetchPixel<bpp>(src, index + i)); + return buffer; +} + +template<QImage::Format Format> +static Q_ALWAYS_INLINE QRgba32F convertPixelToRGBA32F(uint s) +{ + return QRgba32F::fromArgb32(convertPixelToARGB32PM<Format>(s)); +} + +template<QImage::Format Format> +static const QRgba32F *QT_FASTCALL fetchARGBPMToRGBA32F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + constexpr QPixelLayout::BPP bpp = bitsPerPixel<Format>(); + for (int i = 0; i < count; ++i) + buffer[i] = convertPixelToRGBA32F<Format>(fetchPixel<bpp>(src, index + i)); + return buffer; +} + +template<QImage::Format Format> +static const QRgba32F *QT_FASTCALL fetchARGBToRGBA32F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + constexpr QPixelLayout::BPP bpp = bitsPerPixel<Format>(); + for (int i = 0; i < count; ++i) + buffer[i] = convertPixelToRGBA32F<Format>(fetchPixel<bpp>(src, index + i)).premultiplied(); return buffer; } @@ -518,7 +653,7 @@ static void QT_FASTCALL rbSwap(uchar *dst, const uchar *src, int count) constexpr QPixelLayout::BPP bpp = bitsPerPixel<Format>(); for (int i = 0; i < count; ++i) { - const uint c = qFetchPixel<bpp>(src, i); + const uint c = fetchPixel<bpp>(src, i); const uint r = (c >> rShift) & redBlueMask; const uint b = (c >> bShift) & redBlueMask; const uint t = (c & alphaGreenMask) @@ -589,6 +724,27 @@ static void QT_FASTCALL rbSwap_4x16(uchar *d, const uchar *s, int count) } } +static void QT_FASTCALL rbSwap_4x32(uchar *d, const uchar *s, int count) +{ + const uint *src = reinterpret_cast<const uint *>(s); + uint *dest = reinterpret_cast<uint *>(d); + if (src != dest) { + for (int i = 0; i < count; ++i) { + dest[i * 4 + 0] = src[i * 4 + 2]; + dest[i * 4 + 1] = src[i * 4 + 1]; + dest[i * 4 + 2] = src[i * 4 + 0]; + dest[i * 4 + 3] = src[i * 4 + 3]; + } + } else { + for (int i = 0; i < count; ++i) { + const uint r = src[i * 4 + 0]; + const uint b = src[i * 4 + 2]; + dest[i * 4 + 0] = b; + dest[i * 4 + 2] = r; + } + } +} + template<QImage::Format Format> constexpr static inline QPixelLayout pixelLayoutRGB() { return QPixelLayout{ @@ -632,7 +788,7 @@ static const uint *QT_FASTCALL fetchIndexedToARGB32PM(uint *buffer, const uchar const QList<QRgb> *clut, QDitherInfo *) { for (int i = 0; i < count; ++i) { - const uint s = qFetchPixel<BPP>(src, index + i); + const uint s = fetchPixel<BPP>(src, index + i); buffer[i] = qPremultiply(clut->at(s)); } return buffer; @@ -643,17 +799,29 @@ static const QRgba64 *QT_FASTCALL fetchIndexedToRGBA64PM(QRgba64 *buffer, const const QList<QRgb> *clut, QDitherInfo *) { for (int i = 0; i < count; ++i) { - const uint s = qFetchPixel<BPP>(src, index + 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 QList<QRgb> *clut, QDitherInfo *) +template<QPixelLayout::BPP BPP> +static const QRgba32F *QT_FASTCALL fetchIndexedToRGBA32F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *clut, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) { + const uint s = fetchPixel<BPP>(src, index + i); + buffer[i] = QRgba32F::fromArgb32(clut->at(s)).premultiplied(); + } + return buffer; +} + +template<typename QRgba> +static const QRgba *QT_FASTCALL convertIndexedTo(QRgba *buffer, const uint *src, int count, + const QList<QRgb> *clut, QDitherInfo *) { for (int i = 0; i < count; ++i) - buffer[i] = QRgba64::fromArgb32(clut->at(src[i])).premultiplied(); + buffer[i] = QRgba::fromArgb32(clut->at(src[i])).premultiplied(); return buffer; } @@ -731,18 +899,21 @@ static const uint *QT_FASTCALL fetchAlpha8ToRGB32(uint *buffer, const uchar *src return buffer; } -static const QRgba64 *QT_FASTCALL convertAlpha8ToRGB64(QRgba64 *buffer, const uint *src, int count, - const QList<QRgb> *, QDitherInfo *) +template<typename QRgba> +static const QRgba *QT_FASTCALL convertAlpha8To(QRgba *buffer, const uint *src, int count, + const QList<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) - buffer[i] = QRgba64::fromRgba(0, 0, 0, src[i]); + buffer[i] = QRgba::fromRgba(0, 0, 0, src[i]); return buffer; } -static const QRgba64 *QT_FASTCALL fetchAlpha8ToRGB64(QRgba64 *buffer, const uchar *src, int index, int count, - const QList<QRgb> *, QDitherInfo *) + +template<typename QRgba> +static const QRgba *QT_FASTCALL fetchAlpha8To(QRgba *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) - buffer[i] = QRgba64::fromRgba(0, 0, 0, src[index + i]); + buffer[i] = QRgba::fromRgba(0, 0, 0, src[index + i]); return buffer; } @@ -764,20 +935,22 @@ static const uint *QT_FASTCALL fetchGrayscale8ToRGB32(uint *buffer, const uchar return buffer; } -static const QRgba64 *QT_FASTCALL convertGrayscale8ToRGB64(QRgba64 *buffer, const uint *src, int count, - const QList<QRgb> *, QDitherInfo *) +template<typename QRgba> +static const QRgba *QT_FASTCALL convertGrayscale8To(QRgba *buffer, const uint *src, int count, + const QList<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) - buffer[i] = QRgba64::fromRgba(src[i], src[i], src[i], 255); + buffer[i] = QRgba::fromRgba(src[i], src[i], src[i], 255); return buffer; } -static const QRgba64 *QT_FASTCALL fetchGrayscale8ToRGB64(QRgba64 *buffer, const uchar *src, int index, int count, - const QList<QRgb> *, QDitherInfo *) +template<typename QRgba> +static const QRgba *QT_FASTCALL fetchGrayscale8To(QRgba *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) { const uint s = src[index + i]; - buffer[i] = QRgba64::fromRgba(s, s, s, 255); + buffer[i] = QRgba::fromRgba(s, s, s, 255); } return buffer; } @@ -789,7 +962,6 @@ static void QT_FASTCALL convertGrayscale16ToRGB32(uint *buffer, int count, const buffer[i] = qRgb(x, x, x); } } - static const uint *QT_FASTCALL fetchGrayscale16ToRGB32(uint *buffer, const uchar *src, int index, int count, const QList<QRgb> *, QDitherInfo *) { @@ -801,20 +973,22 @@ static const uint *QT_FASTCALL fetchGrayscale16ToRGB32(uint *buffer, const uchar return buffer; } -static const QRgba64 *QT_FASTCALL convertGrayscale16ToRGBA64(QRgba64 *buffer, const uint *src, int count, - const QList<QRgb> *, QDitherInfo *) +template<typename QRgba> +static const QRgba *QT_FASTCALL convertGrayscale16To(QRgba *buffer, const uint *src, int count, + const QList<QRgb> *, QDitherInfo *) { for (int i = 0; i < count; ++i) - buffer[i] = QRgba64::fromRgba64(src[i], src[i], src[i], 65535); + buffer[i] = QRgba::fromRgba64(src[i], src[i], src[i], 65535); return buffer; } -static const QRgba64 *QT_FASTCALL fetchGrayscale16ToRGBA64(QRgba64 *buffer, const uchar *src, int index, int count, - const QList<QRgb> *, QDitherInfo *) +template<typename QRgba> +static const QRgba *QT_FASTCALL fetchGrayscale16To(QRgba *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) { const unsigned short *s = reinterpret_cast<const unsigned short *>(src) + index; for (int i = 0; i < count; ++i) { - buffer[i] = QRgba64::fromRgba64(s[i], s[i], s[i], 65535); + buffer[i] = QRgba::fromRgba64(s[i], s[i], s[i], 65535); } return buffer; } @@ -1212,6 +1386,44 @@ static const QRgba64 *QT_FASTCALL fetchA2RGB30PMToRGBA64PM(QRgba64 *buffer, cons return convertA2RGB30PMToRGBA64PM<PixelOrder>(buffer, reinterpret_cast<const uint *>(src) + index, count, nullptr, nullptr); } +template<enum QtPixelOrder> inline QRgba32F qConvertA2rgb30ToRgbaFP(uint rgb); + +template<> +inline QRgba32F qConvertA2rgb30ToRgbaFP<PixelOrderBGR>(uint rgb) +{ + float alpha = (rgb >> 30) * (1.f/3.f); + float blue = ((rgb >> 20) & 0x3ff) * (1.f/1023.f); + float green = ((rgb >> 10) & 0x3ff) * (1.f/1023.f); + float red = (rgb & 0x3ff) * (1.f/1023.f); + return QRgba32F{ red, green, blue, alpha }; +} + +template<> +inline QRgba32F qConvertA2rgb30ToRgbaFP<PixelOrderRGB>(uint rgb) +{ + float alpha = (rgb >> 30) * (1.f/3.f); + float red = ((rgb >> 20) & 0x3ff) * (1.f/1023.f); + float green = ((rgb >> 10) & 0x3ff) * (1.f/1023.f); + float blue = (rgb & 0x3ff) * (1.f/1023.f); + return QRgba32F{ red, green, blue, alpha }; +} + +template<QtPixelOrder PixelOrder> +static const QRgba32F *QT_FASTCALL convertA2RGB30PMToRGBA32F(QRgba32F *buffer, const uint *src, int count, + const QList<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = qConvertA2rgb30ToRgbaFP<PixelOrder>(src[i]); + return buffer; +} + +template<QtPixelOrder PixelOrder> +static const QRgba32F *QT_FASTCALL fetchRGB30ToRGBA32F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + return convertA2RGB30PMToRGBA32F<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 QList<QRgb> *, QDitherInfo *) @@ -1365,6 +1577,118 @@ static void QT_FASTCALL storeRGBA64FromARGB32(uchar *dest, const uint *src, int d[i] = QRgba64::fromArgb32(src[i]); } +static const uint *QT_FASTCALL fetchRGB16FToRGB32(uint *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba16F *s = reinterpret_cast<const QRgba16F *>(src) + index; + for (int i = 0; i < count; ++i) + buffer[i] = s[i].toArgb32(); + return buffer; +} + +static void QT_FASTCALL storeRGB16FFromRGB32(uchar *dest, const uint *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba16F *d = reinterpret_cast<QRgba16F *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = QRgba16F::fromArgb32(src[i]); +} + +static const uint *QT_FASTCALL fetchRGBA16FToARGB32PM(uint *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba16F *s = reinterpret_cast<const QRgba16F *>(src) + index; + for (int i = 0; i < count; ++i) + buffer[i] = s[i].premultiplied().toArgb32(); + return buffer; +} + +static const QRgba64 *QT_FASTCALL fetchRGBA16FToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba16F *s = reinterpret_cast<const QRgba16F *>(src) + index; + for (int i = 0; i < count; ++i) { + QRgba16F c = s[i].premultiplied(); + buffer[i] = QRgba64::fromRgba64(c.red16(), c.green16(), c.blue16(), c.alpha16()); + } + return buffer; +} + +static void QT_FASTCALL storeRGBA16FFromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba16F *d = reinterpret_cast<QRgba16F *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = QRgba16F::fromArgb32(src[i]).unpremultiplied(); +} + +static const QRgba64 *QT_FASTCALL fetchRGBA16FPMToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba16F *s = reinterpret_cast<const QRgba16F *>(src) + index; + for (int i = 0; i < count; ++i) { + QRgba16F c = s[i]; + buffer[i] = QRgba64::fromRgba64(c.red16(), c.green16(), c.blue16(), c.alpha16()); + } + return buffer; +} + +static const uint *QT_FASTCALL fetchRGB32FToRGB32(uint *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba32F *s = reinterpret_cast<const QRgba32F *>(src) + index; + for (int i = 0; i < count; ++i) + buffer[i] = s[i].toArgb32(); + return buffer; +} + +static void QT_FASTCALL storeRGB32FFromRGB32(uchar *dest, const uint *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba32F *d = reinterpret_cast<QRgba32F *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = QRgba32F::fromArgb32(src[i]); +} + +static const uint *QT_FASTCALL fetchRGBA32FToARGB32PM(uint *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba32F *s = reinterpret_cast<const QRgba32F *>(src) + index; + for (int i = 0; i < count; ++i) + buffer[i] = s[i].premultiplied().toArgb32(); + return buffer; +} + +static const QRgba64 *QT_FASTCALL fetchRGBA32FToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba32F *s = reinterpret_cast<const QRgba32F *>(src) + index; + for (int i = 0; i < count; ++i) { + QRgba32F c = s[i].premultiplied(); + buffer[i] = QRgba64::fromRgba64(c.red16(), c.green16(), c.blue16(), c.alpha16()); + } + return buffer; +} + +static void QT_FASTCALL storeRGBA32FFromARGB32PM(uchar *dest, const uint *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba32F *d = reinterpret_cast<QRgba32F *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = QRgba32F::fromArgb32(src[i]).unpremultiplied(); +} + +static const QRgba64 *QT_FASTCALL fetchRGBA32FPMToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba32F *s = reinterpret_cast<const QRgba32F *>(src) + index; + for (int i = 0; i < count; ++i) { + QRgba32F c = s[i]; + buffer[i] = QRgba64::fromRgba64(c.red16(), c.green16(), c.blue16(), c.alpha16()); + } + return buffer; +} + // Note: // convertToArgb32() assumes that no color channel is less than 4 bits. // storeRGBFromARGB32PM() assumes that no color channel is more than 8 bits. @@ -1372,15 +1696,15 @@ static void QT_FASTCALL storeRGBA64FromARGB32(uchar *dest, const uint *src, int QPixelLayout qPixelLayouts[QImage::NImageFormats] = { { false, false, QPixelLayout::BPPNone, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }, // Format_Invalid { false, false, QPixelLayout::BPP1MSB, nullptr, - convertIndexedToARGB32PM, convertIndexedToRGBA64PM, + convertIndexedToARGB32PM, convertIndexedTo<QRgba64>, fetchIndexedToARGB32PM<QPixelLayout::BPP1MSB>, fetchIndexedToRGBA64PM<QPixelLayout::BPP1MSB>, nullptr, nullptr }, // Format_Mono { false, false, QPixelLayout::BPP1LSB, nullptr, - convertIndexedToARGB32PM, convertIndexedToRGBA64PM, + convertIndexedToARGB32PM, convertIndexedTo<QRgba64>, fetchIndexedToARGB32PM<QPixelLayout::BPP1LSB>, fetchIndexedToRGBA64PM<QPixelLayout::BPP1LSB>, nullptr, nullptr }, // Format_MonoLSB { false, false, QPixelLayout::BPP8, nullptr, - convertIndexedToARGB32PM, convertIndexedToRGBA64PM, + convertIndexedToARGB32PM, convertIndexedTo<QRgba64>, fetchIndexedToARGB32PM<QPixelLayout::BPP8>, fetchIndexedToRGBA64PM<QPixelLayout::BPP8>, nullptr, nullptr }, // Format_Indexed8 // Technically using convertPassThrough to convert from ARGB32PM to RGB32 is wrong, @@ -1439,12 +1763,12 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = { storeRGB30FromRGB32<PixelOrderRGB> }, // Format_A2RGB30_Premultiplied { true, true, QPixelLayout::BPP8, nullptr, - convertAlpha8ToRGB32, convertAlpha8ToRGB64, - fetchAlpha8ToRGB32, fetchAlpha8ToRGB64, + convertAlpha8ToRGB32, convertAlpha8To<QRgba64>, + fetchAlpha8ToRGB32, fetchAlpha8To<QRgba64>, storeAlpha8FromARGB32PM, nullptr }, // Format_Alpha8 { false, false, QPixelLayout::BPP8, nullptr, - convertGrayscale8ToRGB32, convertGrayscale8ToRGB64, - fetchGrayscale8ToRGB32, fetchGrayscale8ToRGB64, + convertGrayscale8ToRGB32, convertGrayscale8To<QRgba64>, + fetchGrayscale8ToRGB32, fetchGrayscale8To<QRgba64>, storeGrayscale8FromARGB32PM, storeGrayscale8FromRGB32 }, // Format_Grayscale8 { false, false, QPixelLayout::BPP64, rbSwap_4x16, convertPassThrough, nullptr, @@ -1459,10 +1783,34 @@ QPixelLayout qPixelLayouts[QImage::NImageFormats] = { fetchRGB64ToRGB32, fetchPassThrough64, storeRGBA64FromARGB32, storeRGB64FromRGB32 }, // Format_RGBA64_Premultiplied { false, false, QPixelLayout::BPP16, nullptr, - convertGrayscale16ToRGB32, convertGrayscale16ToRGBA64, - fetchGrayscale16ToRGB32, fetchGrayscale16ToRGBA64, + convertGrayscale16ToRGB32, convertGrayscale16To<QRgba64>, + fetchGrayscale16ToRGB32, fetchGrayscale16To<QRgba64>, storeGrayscale16FromARGB32PM, storeGrayscale16FromRGB32 }, // Format_Grayscale16 pixelLayoutRGB<QImage::Format_BGR888>(), + { false, false, QPixelLayout::BPP16FPx4, rbSwap_4x16, + convertPassThrough, nullptr, + fetchRGB16FToRGB32, fetchRGBA16FPMToRGBA64PM, + storeRGB16FFromRGB32, storeRGB16FFromRGB32 }, // Format_RGBX16FPx4 + { true, false, QPixelLayout::BPP16FPx4, rbSwap_4x16, + convertARGB32ToARGB32PM, nullptr, + fetchRGBA16FToARGB32PM, fetchRGBA16FToRGBA64PM, + storeRGBA16FFromARGB32PM, storeRGB16FFromRGB32 }, // Format_RGBA16FPx4 + { true, true, QPixelLayout::BPP16FPx4, rbSwap_4x16, + convertPassThrough, nullptr, + fetchRGB16FToRGB32, fetchRGBA16FPMToRGBA64PM, + storeRGB16FFromRGB32, storeRGB16FFromRGB32 }, // Format_RGBA16FPx4_Premultiplied + { false, false, QPixelLayout::BPP32FPx4, rbSwap_4x32, + convertPassThrough, nullptr, + fetchRGB32FToRGB32, fetchRGBA32FPMToRGBA64PM, + storeRGB32FFromRGB32, storeRGB32FFromRGB32 }, // Format_RGBX32FPx4 + { true, false, QPixelLayout::BPP32FPx4, rbSwap_4x32, + convertARGB32ToARGB32PM, nullptr, + fetchRGBA32FToARGB32PM, fetchRGBA32FToRGBA64PM, + storeRGBA32FFromARGB32PM, storeRGB32FFromRGB32 }, // Format_RGBA32FPx4 + { true, true, QPixelLayout::BPP32FPx4, rbSwap_4x32, + convertPassThrough, nullptr, + fetchRGB32FToRGB32, fetchRGBA32FPMToRGBA64PM, + storeRGB32FFromRGB32, storeRGB32FFromRGB32 }, // Format_RGBA32FPx4_Premultiplied }; static_assert(sizeof(qPixelLayouts) / sizeof(*qPixelLayouts) == QImage::NImageFormats); @@ -1548,6 +1896,58 @@ static void QT_FASTCALL storeGray16FromRGBA64PM(uchar *dest, const QRgba64 *src, } } +static void QT_FASTCALL storeRGBX16FFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba16F *d = reinterpret_cast<QRgba16F *>(dest) + index; + for (int i = 0; i < count; ++i) { + d[i] = qConvertRgb64ToRgbaF16(src[i]).unpremultiplied(); + d[i].setAlpha(1.0); + } +} + +static void QT_FASTCALL storeRGBA16FFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba16F *d = reinterpret_cast<QRgba16F *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = qConvertRgb64ToRgbaF16(src[i]).unpremultiplied(); +} + +static void QT_FASTCALL storeRGBA16FPMFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba16F *d = reinterpret_cast<QRgba16F *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = qConvertRgb64ToRgbaF16(src[i]); +} + +static void QT_FASTCALL storeRGBX32FFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba32F *d = reinterpret_cast<QRgba32F *>(dest) + index; + for (int i = 0; i < count; ++i) { + d[i] = qConvertRgb64ToRgbaF32(src[i]).unpremultiplied(); + d[i].setAlpha(1.0); + } +} + +static void QT_FASTCALL storeRGBA32FFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba32F *d = reinterpret_cast<QRgba32F *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = qConvertRgb64ToRgbaF32(src[i]).unpremultiplied(); +} + +static void QT_FASTCALL storeRGBA32FPMFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba32F *d = reinterpret_cast<QRgba32F *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = qConvertRgb64ToRgbaF32(src[i]); +} + ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = { nullptr, nullptr, @@ -1579,6 +1979,375 @@ ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats] = { storeRGBA64PMFromRGBA64PM, storeGray16FromRGBA64PM, storeGenericFromRGBA64PM<QImage::Format_BGR888>, + storeRGBX16FFromRGBA64PM, + storeRGBA16FFromRGBA64PM, + storeRGBA16FPMFromRGBA64PM, + storeRGBX32FFromRGBA64PM, + storeRGBA32FFromRGBA64PM, + storeRGBA32FPMFromRGBA64PM, +}; + +#if QT_CONFIG(raster_fp) +static void QT_FASTCALL convertToRgbaF32(QRgba32F *dest, const uint *src, int length) +{ + for (int i = 0; i < length; ++i) + dest[i] = QRgba32F::fromArgb32(src[i]); +} + +template<QImage::Format format> +static const QRgba32F * QT_FASTCALL convertGenericToRGBA32F(QRgba32F *buffer, const uint *src, int count, + const QList<QRgb> *clut, QDitherInfo *) +{ + uint buffer32[BufferSize]; + memcpy(buffer32, src, count * sizeof(uint)); + qPixelLayouts[format].convertToARGB32PM(buffer32, count, clut); + convertToRgbaF32(buffer, buffer32, count); + return buffer; +} + +static const QRgba32F * QT_FASTCALL convertARGB32ToRGBA32F(QRgba32F *buffer, const uint *src, int count, + const QList<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = QRgba32F::fromArgb32(src[i]).premultiplied(); + return buffer; +} + +static const QRgba32F * QT_FASTCALL convertRGBA8888ToRGBA32F(QRgba32F *buffer, const uint *src, int count, + const QList<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) + buffer[i] = QRgba32F::fromArgb32(RGBA2ARGB(src[i])).premultiplied(); + return buffer; +} + +template<QtPixelOrder PixelOrder> +static const QRgba32F * QT_FASTCALL convertRGB30ToRGBA32F(QRgba32F *buffer, const uint *src, int count, + const QList<QRgb> *, QDitherInfo *) +{ + for (int i = 0; i < count; ++i) { + QRgba64 s = qConvertA2rgb30ToRgb64<PixelOrder>(src[i]); + buffer[i] = QRgba32F::fromRgba64(s.red(), s.green(), s.blue(), s.alpha()); + } + return buffer; +} + +ConvertToFPFunc qConvertToRGBA32F[QImage::NImageFormats] = { + nullptr, + convertIndexedTo<QRgba32F>, + convertIndexedTo<QRgba32F>, + convertIndexedTo<QRgba32F>, + convertGenericToRGBA32F<QImage::Format_RGB32>, + convertARGB32ToRGBA32F, + convertGenericToRGBA32F<QImage::Format_ARGB32_Premultiplied>, + convertGenericToRGBA32F<QImage::Format_RGB16>, + convertGenericToRGBA32F<QImage::Format_ARGB8565_Premultiplied>, + convertGenericToRGBA32F<QImage::Format_RGB666>, + convertGenericToRGBA32F<QImage::Format_ARGB6666_Premultiplied>, + convertGenericToRGBA32F<QImage::Format_RGB555>, + convertGenericToRGBA32F<QImage::Format_ARGB8555_Premultiplied>, + convertGenericToRGBA32F<QImage::Format_RGB888>, + convertGenericToRGBA32F<QImage::Format_RGB444>, + convertGenericToRGBA32F<QImage::Format_ARGB4444_Premultiplied>, + convertGenericToRGBA32F<QImage::Format_RGBX8888>, + convertRGBA8888ToRGBA32F, + convertGenericToRGBA32F<QImage::Format_RGBA8888_Premultiplied>, + convertRGB30ToRGBA32F<PixelOrderBGR>, + convertRGB30ToRGBA32F<PixelOrderBGR>, + convertRGB30ToRGBA32F<PixelOrderRGB>, + convertRGB30ToRGBA32F<PixelOrderRGB>, + convertAlpha8To<QRgba32F>, + convertGrayscale8To<QRgba32F>, + nullptr, + nullptr, + nullptr, + convertGrayscale16To<QRgba32F>, + convertGenericToRGBA32F<QImage::Format_BGR888>, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, +}; + +static const QRgba32F *QT_FASTCALL fetchRGBX64ToRGBA32F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba64 *s = reinterpret_cast<const QRgba64 *>(src) + index; + for (int i = 0; i < count; ++i) { + QRgba64 c = s[i]; + buffer[i] = QRgba32F::fromRgba64(c.red(), c.green(), c.blue(), 65535); + } + return buffer; +} + +static const QRgba32F *QT_FASTCALL fetchRGBA64ToRGBA32F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba64 *s = reinterpret_cast<const QRgba64 *>(src) + index; + for (int i = 0; i < count; ++i) + buffer[i] = qConvertRgb64ToRgbaF32(s[i]).premultiplied(); + return buffer; +} + +static const QRgba32F *QT_FASTCALL fetchRGBA64PMToRGBA32F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba64 *s = reinterpret_cast<const QRgba64 *>(src) + index; + for (int i = 0; i < count; ++i) + buffer[i] = qConvertRgb64ToRgbaF32(s[i]); + return buffer; +} + +static const QRgba32F *QT_FASTCALL fetchRGBA16FToRGBA32F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba16F *s = reinterpret_cast<const QRgba16F *>(src) + index; + for (int i = 0; i < count; ++i) { + auto c = s[i].premultiplied(); + buffer[i] = QRgba32F { c.r, c.g, c.b, c.a}; + } + return buffer; +} + +static const QRgba32F *QT_FASTCALL fetchRGBA16F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba16F *s = reinterpret_cast<const QRgba16F *>(src) + index; + qFloatFromFloat16((float *)buffer, (const qfloat16 *)s, count * 4); + return buffer; +} + +static const QRgba32F *QT_FASTCALL fetchRGBA32FToRGBA32F(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba32F *s = reinterpret_cast<const QRgba32F *>(src) + index; + for (int i = 0; i < count; ++i) + buffer[i] = s[i].premultiplied(); + return buffer; +} + +static const QRgba32F *QT_FASTCALL fetchRGBA32F(QRgba32F *, const uchar *src, int index, int, + const QList<QRgb> *, QDitherInfo *) +{ + const QRgba32F *s = reinterpret_cast<const QRgba32F *>(src) + index; + return s; +} + +FetchAndConvertPixelsFuncFP qFetchToRGBA32F[QImage::NImageFormats] = { + nullptr, + fetchIndexedToRGBA32F<QPixelLayout::BPP1MSB>, + fetchIndexedToRGBA32F<QPixelLayout::BPP1LSB>, + fetchIndexedToRGBA32F<QPixelLayout::BPP8>, + fetchRGBToRGB32F<QImage::Format_RGB32>, + fetchARGBToRGBA32F<QImage::Format_ARGB32>, + fetchARGBPMToRGBA32F<QImage::Format_ARGB32_Premultiplied>, + fetchRGBToRGB32F<QImage::Format_RGB16>, + fetchARGBToRGBA32F<QImage::Format_ARGB8565_Premultiplied>, + fetchRGBToRGB32F<QImage::Format_RGB666>, + fetchARGBToRGBA32F<QImage::Format_ARGB6666_Premultiplied>, + fetchRGBToRGB32F<QImage::Format_RGB555>, + fetchARGBToRGBA32F<QImage::Format_ARGB8555_Premultiplied>, + fetchRGBToRGB32F<QImage::Format_RGB888>, + fetchRGBToRGB32F<QImage::Format_RGB444>, + fetchARGBToRGBA32F<QImage::Format_ARGB4444_Premultiplied>, + fetchRGBToRGB32F<QImage::Format_RGBX8888>, + fetchARGBToRGBA32F<QImage::Format_RGBA8888>, + fetchARGBPMToRGBA32F<QImage::Format_RGBA8888_Premultiplied>, + fetchRGB30ToRGBA32F<PixelOrderBGR>, + fetchRGB30ToRGBA32F<PixelOrderBGR>, + fetchRGB30ToRGBA32F<PixelOrderRGB>, + fetchRGB30ToRGBA32F<PixelOrderRGB>, + fetchAlpha8To<QRgba32F>, + fetchGrayscale8To<QRgba32F>, + fetchRGBX64ToRGBA32F, + fetchRGBA64ToRGBA32F, + fetchRGBA64PMToRGBA32F, + fetchGrayscale16To<QRgba32F>, + fetchRGBToRGB32F<QImage::Format_BGR888>, + fetchRGBA16F, + fetchRGBA16FToRGBA32F, + fetchRGBA16F, + fetchRGBA32F, + fetchRGBA32FToRGBA32F, + fetchRGBA32F, +}; + +static void QT_FASTCALL convertFromRgba32f(uint *dest, const QRgba32F *src, int length) +{ + for (int i = 0; i < length; ++i) + dest[i] = src[i].toArgb32(); +} + +template<QImage::Format format> +static void QT_FASTCALL storeGenericFromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *clut, QDitherInfo *dither) +{ + uint buffer[BufferSize]; + convertFromRgba32f(buffer, src, count); + qPixelLayouts[format].storeFromARGB32PM(dest, buffer, index, count, clut, dither); +} + +static void QT_FASTCALL storeARGB32FromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + uint *d = (uint*)dest + index; + for (int i = 0; i < count; ++i) + d[i] = src[i].unpremultiplied().toArgb32(); +} + +static void QT_FASTCALL storeRGBA8888FromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + uint *d = (uint*)dest + index; + for (int i = 0; i < count; ++i) + d[i] = ARGB2RGBA(src[i].unpremultiplied().toArgb32()); +} + +template<QtPixelOrder PixelOrder> +static void QT_FASTCALL storeRGB30FromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + uint *d = (uint*)dest + index; + for (int i = 0; i < count; ++i) { + const auto s = src[i]; + d[i] = qConvertRgb64ToRgb30<PixelOrder>(QRgba64::fromRgba64(s.red16(), s.green16(), s.blue16(), s.alpha16())); + } +} + +static void QT_FASTCALL storeRGBX64FromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba64 *d = reinterpret_cast<QRgba64 *>(dest) + index; + for (int i = 0; i < count; ++i) { + const auto s = src[i].unpremultiplied(); + d[i] = QRgba64::fromRgba64(s.red16(), s.green16(), s.blue16(), 65535); + } +} + +static void QT_FASTCALL storeRGBA64FromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba64 *d = reinterpret_cast<QRgba64 *>(dest) + index; + for (int i = 0; i < count; ++i) { + const auto s = src[i].unpremultiplied(); + d[i] = QRgba64::fromRgba64(s.red16(), s.green16(), s.blue16(), s.alpha16()); + } +} + +static void QT_FASTCALL storeRGBA64PMFromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba64 *d = reinterpret_cast<QRgba64 *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = QRgba64::fromRgba64(src[i].red16(), src[i].green16(), src[i].blue16(), src[i].alpha16()); +} + +static void QT_FASTCALL storeGray16FromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + quint16 *d = reinterpret_cast<quint16 *>(dest) + index; + for (int i = 0; i < count; ++i) { + auto s = src[i].unpremultiplied(); + d[i] = qGray(s.red16(), s.green16(), s.blue16()); + } +} + +static void QT_FASTCALL storeRGBX16FFromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba16F *d = reinterpret_cast<QRgba16F *>(dest) + index; + for (int i = 0; i < count; ++i) { + auto s = src[i].unpremultiplied(); + d[i] = QRgba16F{ s.r, s.g, s.b, 1.0f }; + } +} + +static void QT_FASTCALL storeRGBA16FFromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba16F *d = reinterpret_cast<QRgba16F *>(dest) + index; + for (int i = 0; i < count; ++i) { + auto s = src[i].unpremultiplied(); + d[i] = QRgba16F{ s.r, s.g, s.b, s.a }; + } +} + +static void QT_FASTCALL storeRGBA16FPMFromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba16F *d = reinterpret_cast<QRgba16F *>(dest) + index; + qFloatToFloat16((qfloat16 *)d, (const float *)src, count * 4); +} + +static void QT_FASTCALL storeRGBX32FFromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba32F *d = reinterpret_cast<QRgba32F *>(dest) + index; + for (int i = 0; i < count; ++i) { + auto s = src[i].unpremultiplied(); + s.a = 1.0f; + d[i] = s; + } +} + +static void QT_FASTCALL storeRGBA32FFromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba32F *d = reinterpret_cast<QRgba32F *>(dest) + index; + for (int i = 0; i < count; ++i) + d[i] = src[i].unpremultiplied(); +} + +static void QT_FASTCALL storeRGBA32FPMFromRGBA32F(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *, QDitherInfo *) +{ + QRgba32F *d = reinterpret_cast<QRgba32F *>(dest) + index; + if (d != src) { + for (int i = 0; i < count; ++i) + d[i] = src[i]; + } +} + +ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[QImage::NImageFormats] = { + nullptr, + nullptr, + nullptr, + nullptr, + storeGenericFromRGBA32F<QImage::Format_RGB32>, + storeARGB32FromRGBA32F, + storeGenericFromRGBA32F<QImage::Format_ARGB32_Premultiplied>, + storeGenericFromRGBA32F<QImage::Format_RGB16>, + storeGenericFromRGBA32F<QImage::Format_ARGB8565_Premultiplied>, + storeGenericFromRGBA32F<QImage::Format_RGB666>, + storeGenericFromRGBA32F<QImage::Format_ARGB6666_Premultiplied>, + storeGenericFromRGBA32F<QImage::Format_RGB555>, + storeGenericFromRGBA32F<QImage::Format_ARGB8555_Premultiplied>, + storeGenericFromRGBA32F<QImage::Format_RGB888>, + storeGenericFromRGBA32F<QImage::Format_RGB444>, + storeGenericFromRGBA32F<QImage::Format_ARGB4444_Premultiplied>, + storeGenericFromRGBA32F<QImage::Format_RGBX8888>, + storeRGBA8888FromRGBA32F, + storeGenericFromRGBA32F<QImage::Format_RGBA8888_Premultiplied>, + storeRGB30FromRGBA32F<PixelOrderBGR>, + storeRGB30FromRGBA32F<PixelOrderBGR>, + storeRGB30FromRGBA32F<PixelOrderRGB>, + storeRGB30FromRGBA32F<PixelOrderRGB>, + storeGenericFromRGBA32F<QImage::Format_Alpha8>, + storeGenericFromRGBA32F<QImage::Format_Grayscale8>, + storeRGBX64FromRGBA32F, + storeRGBA64FromRGBA32F, + storeRGBA64PMFromRGBA32F, + storeGray16FromRGBA32F, + storeGenericFromRGBA32F<QImage::Format_BGR888>, + storeRGBX16FFromRGBA32F, + storeRGBA16FFromRGBA32F, + storeRGBA16FPMFromRGBA32F, + storeRGBX32FFromRGBA32F, + storeRGBA32FFromRGBA32F, + storeRGBA32FPMFromRGBA32F, }; +#endif // QT_CONFIG(raster_fp) QT_END_NAMESPACE diff --git a/src/gui/painting/qpixellayout_p.h b/src/gui/painting/qpixellayout_p.h index 6d20131efe..bf0bac3b09 100644 --- a/src/gui/painting/qpixellayout_p.h +++ b/src/gui/painting/qpixellayout_p.h @@ -54,6 +54,7 @@ #include <QtCore/qlist.h> #include <QtGui/qimage.h> #include <QtGui/qrgba64.h> +#include <QtGui/qrgbaf.h> QT_BEGIN_NAMESPACE @@ -217,6 +218,16 @@ inline unsigned int qConvertRgb64ToRgb30<PixelOrderRGB>(QRgba64 c) return (a << 30) | (r << 20) | (g << 10) | b; } +inline constexpr QRgba16F qConvertRgb64ToRgbaF16(QRgba64 c) +{ + return QRgba16F::fromRgba64(c.red(), c.green(), c.blue(), c.alpha()); +} + +inline constexpr QRgba32F qConvertRgb64ToRgbaF32(QRgba64 c) +{ + return QRgba32F::fromRgba64(c.red(), c.green(), c.blue(), c.alpha()); +} + inline uint qRgbSwapRgb30(uint c) { const uint ag = c & 0xc00ffc00; @@ -296,10 +307,19 @@ typedef void(QT_FASTCALL *ConvertAndStorePixelsFunc64)(uchar *dest, const QRgba6 int count, const QList<QRgb> *clut, QDitherInfo *dither); -typedef void(QT_FASTCALL *ConvertFunc)(uint *buffer, int count, const QList<QRgb> *clut); -typedef void(QT_FASTCALL *Convert64Func)(quint64 *buffer, int count, const QList<QRgb> *clut); +typedef const QRgba32F *(QT_FASTCALL *FetchAndConvertPixelsFuncFP)(QRgba32F *buffer, const uchar *src, int index, int count, + const QList<QRgb> *clut, QDitherInfo *dither); +typedef void (QT_FASTCALL *ConvertAndStorePixelsFuncFP)(uchar *dest, const QRgba32F *src, int index, int count, + const QList<QRgb> *clut, QDitherInfo *dither); +typedef void (QT_FASTCALL *ConvertFunc)(uint *buffer, int count, const QList<QRgb> *clut); +typedef void (QT_FASTCALL *Convert64Func)(QRgba64 *buffer, int count); +typedef void (QT_FASTCALL *ConvertFPFunc)(QRgba32F *buffer, int count); +typedef void (QT_FASTCALL *Convert64ToFPFunc)(QRgba32F *buffer, const quint64 *src, int count); + typedef const QRgba64 *(QT_FASTCALL *ConvertTo64Func)(QRgba64 *buffer, const uint *src, int count, const QList<QRgb> *clut, QDitherInfo *dither); +typedef const QRgba32F *(QT_FASTCALL *ConvertToFPFunc)(QRgba32F *buffer, const uint *src, int count, + const QList<QRgb> *clut, QDitherInfo *dither); typedef void (QT_FASTCALL *RbSwapFunc)(uchar *dst, const uchar *src, int count); typedef void (*MemRotateFunc)(const uchar *srcPixels, int w, int h, int sbpl, uchar *destPixels, int dbpl); @@ -316,6 +336,8 @@ struct QPixelLayout BPP24, BPP32, BPP64, + BPP16FPx4, + BPP32FPx4, BPPCount }; @@ -333,6 +355,12 @@ struct QPixelLayout extern ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[QImage::NImageFormats]; +#if QT_CONFIG(raster_fp) +extern ConvertToFPFunc qConvertToRGBA32F[QImage::NImageFormats]; +extern FetchAndConvertPixelsFuncFP qFetchToRGBA32F[QImage::NImageFormats]; +extern ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[QImage::NImageFormats]; +#endif + extern QPixelLayout qPixelLayouts[QImage::NImageFormats]; extern MemRotateFunc qMemRotateFunctions[QPixelLayout::BPPCount][3]; diff --git a/src/gui/painting/qrgbaf.h b/src/gui/painting/qrgbaf.h new file mode 100644 index 0000000000..da6d0a3646 --- /dev/null +++ b/src/gui/painting/qrgbaf.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QRGBAF_H +#define QRGBAF_H + +#include <QtGui/qtguiglobal.h> +#include <QtCore/qfloat16.h> + +#include <algorithm> +#include <cmath> + +QT_BEGIN_NAMESPACE + +template<typename F> +class alignas(sizeof(F) * 4) QRgbaF { +public: + F r; + F g; + F b; + F a; + + static constexpr + QRgbaF fromRgba64(quint16 red, quint16 green, quint16 blue, quint16 alpha) + { + return QRgbaF{ + red * (1.0f / 65535.0f), + green * (1.0f / 65535.0f), + blue * (1.0f / 65535.0f), + alpha * (1.0f / 65535.0f) }; + } + + static constexpr + QRgbaF fromRgba(quint8 red, quint8 green, quint8 blue, quint8 alpha) + { + return QRgbaF{ + red * (1.0f / 255.0f), + green * (1.0f / 255.0f), + blue * (1.0f / 255.0f), + alpha * (1.0f / 255.0f) }; + } + static constexpr + QRgbaF fromArgb32(uint rgb) + { + return fromRgba(quint8(rgb >> 16), quint8(rgb >> 8), quint8(rgb), quint8(rgb >> 24)); + } + + constexpr bool isOpaque() const { return a >= 1.0f; } + constexpr bool isTransparent() const { return a <= 0.0f; } + + constexpr float red() const { return r; } + constexpr float green() const { return g; } + constexpr float blue() const { return b; } + constexpr float alpha() const { return a; } + void setRed(float _red) { r = _red; } + void setGreen(float _green) { g = _green; } + void setBlue(float _blue) { b = _blue; } + void setAlpha(float _alpha) { a = _alpha; } + + constexpr float redNormalized() const { return std::clamp(static_cast<float>(r), 0.0f, 1.0f); } + constexpr float greenNormalized() const { return std::clamp(static_cast<float>(g), 0.0f, 1.0f); } + constexpr float blueNormalized() const { return std::clamp(static_cast<float>(b), 0.0f, 1.0f); } + constexpr float alphaNormalized() const { return std::clamp(static_cast<float>(a), 0.0f, 1.0f); } + + constexpr quint8 red8() const { return std::lround(redNormalized() * 255.0f); } + constexpr quint8 green8() const { return std::lround(greenNormalized() * 255.0f); } + constexpr quint8 blue8() const { return std::lround(blueNormalized() * 255.0f); } + constexpr quint8 alpha8() const { return std::lround(alphaNormalized() * 255.0f); } + constexpr uint toArgb32() const + { + return uint((alpha8() << 24) | (red8() << 16) | (green8() << 8) | blue8()); + } + + constexpr quint16 red16() const { return std::lround(redNormalized() * 65535.0f); } + constexpr quint16 green16() const { return std::lround(greenNormalized() * 65535.0f); } + constexpr quint16 blue16() const { return std::lround(blueNormalized() * 65535.0f); } + constexpr quint16 alpha16() const { return std::lround(alphaNormalized() * 65535.0f); } + + constexpr Q_ALWAYS_INLINE QRgbaF premultiplied() const + { + return QRgbaF{r * a, g * a, b * a, a}; + } + constexpr Q_ALWAYS_INLINE QRgbaF unpremultiplied() const + { + if (a <= 0.0f) + return QRgbaF{0.0f, 0.0f, 0.0f, 0.0f}; + if (a >= 1.0f) + return *this; + const float ia = 1.0f / a; + return QRgbaF{r * ia, g * ia, b * ia, a}; + } +}; + +typedef QRgbaF<float> QRgba32F; +typedef QRgbaF<qfloat16> QRgba16F; + +QT_END_NAMESPACE + +#endif // QRGBAF_H diff --git a/src/gui/painting/qrgbaf.qdoc b/src/gui/painting/qrgbaf.qdoc new file mode 100644 index 0000000000..f503e597ee --- /dev/null +++ b/src/gui/painting/qrgbaf.qdoc @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \class QRgbaF + \brief The QRgbaF struct contains a four part RGBA floating-point color. + \since 6.2 + + \ingroup painting + \inmodule QtGui + + QRgba16F is a 64-bit data-structure containing four 16-bit floating point color channels: Red, green, blue and alpha. + QRgba32F is a 128-bit data-structure containing four 32-bit floating point color channels: Red, green, blue and alpha. + + \sa QRgb, QRgba64, QColor +*/ + +/*! + \fn static QRgbaF QRgbaF::fromRgba64(quint16 r, quint16 g, quint16 b, quint16 a) + + Constructs a QRgbaF value from the four 16-bit integer color channels \a red, \a green, \a blue and \a alpha. + + \sa fromRgba() +*/ + +/*! + \fn static QRgbaF QRgbaF::fromRgba(quint8 red, quint8 green, quint8 blue, quint8 alpha) + + Constructs a QRgbaF value from the four 8-bit color channels \a red, \a green, \a blue and \a alpha. + + \sa fromArgb32() +*/ + +/*! + \fn static QRgbaF QRgbaF::fromArgb32(uint rgb) + + Constructs a QRgbaF value from the 32bit ARGB value \a rgb. + + \sa fromRgba(), toArgb32() +*/ + +/*! + \fn bool QRgbaF::isOpaque() const + + Returns whether the color is fully opaque. + + \sa isTransparent(), alpha() +*/ + +/*! + \fn bool QRgbaF::isTransparent() const + + Returns whether the color is fully transparent. + + \sa isOpaque(), alpha() +*/ + +/*! + \fn float QRgbaF::red() const + + Returns the red color component. + + \sa setRed() +*/ + +/*! + \fn void QRgbaF::setRed(float red) + + Sets the red color component of this color to \a red. + + \sa red() +*/ + +/*! + \fn float QRgbaF::green() const + + Returns the green color component. + + \sa setGreen() +*/ + +/*! + \fn void QRgbaF::setGreen(float green) + + Sets the green color component of this color to \a green. + + \sa green() +*/ + +/*! + \fn float QRgbaF::blue() const + + Returns the blue color component. + + \sa setBlue() +*/ + +/*! + \fn void QRgbaF::setBlue(float blue) + + Sets the blue color component of this color to \a blue. + + \sa blue() +*/ + +/*! + \fn float QRgbaF::alpha() const + + Returns the alpha channel. + + \sa setAlpha() +*/ + +/*! + \fn void QRgbaF::setAlpha(float alpha) + + Sets the alpha of this color to \a alpha. + + \sa alpha() +*/ + +/*! + \fn float QRgbaF::redNormalized() const + + Returns the red color component normalized to values between \c 0.0f and \c 1.0f. + + \sa setRed() +*/ + +/*! + \fn float QRgbaF::greenNormalized() const + + Returns the green color component normalized to values between \c 0.0f and \c 1.0f. + + \sa setGreen() +*/ + +/*! + \fn float QRgbaF::blueNormalized() const + + Returns the blue color component normalized to values between \c 0.0f and \c 1.0f. + + \sa setBlue() +*/ + +/*! + \fn float QRgbaF::alphaNormalized() const + + Returns the alpha channel normalized to values between \c 0.0f and \c 1.0f. + + \sa alpha() +*/ + +/*! + \fn quint8 QRgbaF::red8() const + + Returns the red color component as an 8-bit. +*/ + +/*! + \fn quint8 QRgbaF::green8() const + + Returns the green color component as an 8-bit. +*/ + +/*! + \fn quint8 QRgbaF::blue8() const + + Returns the blue color component as an 8-bit. +*/ + +/*! + \fn quint8 QRgbaF::alpha8() const + + Returns the alpha channel as an 8-bit. +*/ + +/*! + \fn uint QRgbaF::toArgb32() const + + Returns the color as a 32-bit ARGB value. + + \sa fromArgb32() +*/ + +/*! + \fn quint16 QRgbaF::red16() const + + Returns the red color component as a 16-bit integer. +*/ + +/*! + \fn quint16 QRgbaF::green16() const + + Returns the green color component as a 16-bit integer. +*/ + +/*! + \fn quint16 QRgbaF::blue16() const + + Returns the blue color component as a 16-bit integer. +*/ + +/*! + \fn quint16 QRgbaF::alpha16() const + + Returns the alpha channel as a 16-bit integer. +*/ + +/*! + \fn QRgbaF QRgbaF::premultiplied() const + + Returns the color with the alpha premultiplied. + + \sa unpremultiplied() +*/ + +/*! + \fn QRgbaF QRgbaF::unpremultiplied() const + + Returns the color with the alpha unpremultiplied. + + \sa premultiplied() +*/ diff --git a/src/opengl/qopenglframebufferobject.cpp b/src/opengl/qopenglframebufferobject.cpp index 78b192b630..aa1c396892 100644 --- a/src/opengl/qopenglframebufferobject.cpp +++ b/src/opengl/qopenglframebufferobject.cpp @@ -152,6 +152,25 @@ QT_BEGIN_NAMESPACE #define GL_DEPTH_STENCIL 0x84F9 #endif +#ifndef GL_HALF_FLOAT +#define GL_HALF_FLOAT 0x140B +#endif + +#ifndef GL_RGBA32F +#define GL_RGBA32F 0x8814 +#endif + +#ifndef GL_RGB32F +#define GL_RGB32F 0x8815 +#endif + +#ifndef GL_RGBA16F +#define GL_RGBA16F 0x881A +#endif + +#ifndef GL_RGB16F +#define GL_RGB16F 0x881B +#endif /*! @@ -554,6 +573,8 @@ void QOpenGLFramebufferObjectPrivate::initTexture(int idx) pixelType = GL_UNSIGNED_INT_2_10_10_10_REV; else if (color.internalFormat == GL_RGB16 || color.internalFormat == GL_RGBA16) pixelType = GL_UNSIGNED_SHORT; + else if (color.internalFormat == GL_RGB16F || color.internalFormat == GL_RGBA16F) + pixelType = GL_HALF_FLOAT; funcs.glTexImage2D(target, 0, color.internalFormat, color.size.width(), color.size.height(), 0, GL_RGBA, pixelType, nullptr); @@ -1379,6 +1400,21 @@ static inline QImage qt_gl_read_framebuffer_rgba16(const QSize &size, bool inclu return img; } +static inline QImage qt_gl_read_framebuffer_rgba16f(const QSize &size, bool include_alpha, QOpenGLContext *context) +{ + // We assume OpenGL (ES) 3.0+ here. + QImage img(size, include_alpha ? QImage::Format_RGBA16FPx4_Premultiplied : QImage::Format_RGBX16FPx4); + context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_HALF_FLOAT, img.bits()); + return img; +} + +static inline QImage qt_gl_read_framebuffer_rgba32f(const QSize &size, bool include_alpha, QOpenGLContext *context) +{ + QImage img(size, include_alpha ? QImage::Format_RGBA32FPx4_Premultiplied : QImage::Format_RGBX32FPx4); + context->functions()->glReadPixels(0, 0, size.width(), size.height(), GL_RGBA, GL_FLOAT, 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(); @@ -1400,6 +1436,14 @@ static QImage qt_gl_read_framebuffer(const QSize &size, GLenum internal_format, 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_RGB16F: + return qt_gl_read_framebuffer_rgba16f(size, false, ctx).mirrored(false, flip); + case GL_RGBA16F: + return qt_gl_read_framebuffer_rgba16f(size, include_alpha, ctx).mirrored(false, flip); + case GL_RGB32F: + return qt_gl_read_framebuffer_rgba32f(size, false, ctx).mirrored(false, flip); + case GL_RGBA32F: + return qt_gl_read_framebuffer_rgba32f(size, include_alpha, ctx).mirrored(false, flip); case GL_RGBA: case GL_RGBA8: default: diff --git a/src/opengl/qopenglpaintengine.cpp b/src/opengl/qopenglpaintengine.cpp index 7da9bf143d..8606a2e7ef 100644 --- a/src/opengl/qopenglpaintengine.cpp +++ b/src/opengl/qopenglpaintengine.cpp @@ -1572,6 +1572,8 @@ void QOpenGL2PaintEngineEx::drawImage(const QRectF& dest, const QImage& image, c case QImage::Format_RGBA8888: case QImage::Format_ARGB32: case QImage::Format_RGBA64: + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA32FPx4: d->shaderManager->setSrcPixelType(QOpenGLEngineShaderManager::NonPremultipliedImageSrc); bindOption = { }; break; diff --git a/src/opengl/qopengltextureuploader.cpp b/src/opengl/qopengltextureuploader.cpp index 469ddc56c1..488baf32ad 100644 --- a/src/opengl/qopengltextureuploader.cpp +++ b/src/opengl/qopengltextureuploader.cpp @@ -45,6 +45,10 @@ #include <private/qopenglcontext_p.h> #include <private/qopenglextensions_p.h> +#ifndef GL_HALF_FLOAT +#define GL_HALF_FLOAT 0x140B +#endif + #ifndef GL_RED #define GL_RED 0x1903 #endif @@ -81,6 +85,14 @@ #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 #endif +#ifndef GL_RGBA16F +#define GL_RGBA16F 0x881A +#endif + +#ifndef GL_RGBA32F +#define GL_RGBA32F 0x8814 +#endif + #ifndef GL_TEXTURE_SWIZZLE_R #define GL_TEXTURE_SWIZZLE_R 0x8E42 #endif @@ -236,6 +248,25 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag pixelType = GL_UNSIGNED_SHORT; targetFormat = image.format(); break; + case QImage::Format_RGBX16FPx4: + case QImage::Format_RGBA16FPx4: + case QImage::Format_RGBA16FPx4_Premultiplied: + if (context->format().majorVersion() >= 3) { + externalFormat = GL_RGBA; + internalFormat = GL_RGBA16F; + pixelType = GL_HALF_FLOAT; + targetFormat = image.format(); + } + break; + case QImage::Format_RGBX32FPx4: + case QImage::Format_RGBA32FPx4: + case QImage::Format_RGBA32FPx4_Premultiplied: + externalFormat = internalFormat = GL_RGBA; + if (context->format().majorVersion() >= 3) + internalFormat = GL_RGBA32F; + pixelType = GL_FLOAT; + targetFormat = image.format(); + break; case QImage::Format_Indexed8: if (sRgbBinding) { // Always needs conversion @@ -333,6 +364,10 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag targetFormat = QImage::Format_RGBA8888_Premultiplied; else if (targetFormat == QImage::Format_RGBA64) targetFormat = QImage::Format_RGBA64_Premultiplied; + else if (targetFormat == QImage::Format_RGBA16FPx4) + targetFormat = QImage::Format_RGBA16FPx4_Premultiplied; + else if (targetFormat == QImage::Format_RGBA32FPx4) + targetFormat = QImage::Format_RGBA32FPx4_Premultiplied; } else { if (targetFormat == QImage::Format_ARGB32_Premultiplied) targetFormat = QImage::Format_ARGB32; @@ -340,6 +375,10 @@ qsizetype QOpenGLTextureUploader::textureImage(GLenum target, const QImage &imag targetFormat = QImage::Format_RGBA8888; else if (targetFormat == QImage::Format_RGBA64_Premultiplied) targetFormat = QImage::Format_RGBA64; + else if (targetFormat == QImage::Format_RGBA16FPx4_Premultiplied) + targetFormat = QImage::Format_RGBA16FPx4; + else if (targetFormat == QImage::Format_RGBA32FPx4_Premultiplied) + targetFormat = QImage::Format_RGBA32FPx4; } if (sRgbBinding) { diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp index c3096bd47f..57fee0a547 100644 --- a/tests/auto/gui/image/qimage/tst_qimage.cpp +++ b/tests/auto/gui/image/qimage/tst_qimage.cpp @@ -323,6 +323,18 @@ static QLatin1String formatToString(QImage::Format format) return QLatin1String("Grayscale16"); case QImage::Format_BGR888: return QLatin1String("BGR888"); + case QImage::Format_RGBX16FPx4: + return QLatin1String("RGBx16FPx4"); + case QImage::Format_RGBA16FPx4: + return QLatin1String("RGBA16FPx4"); + case QImage::Format_RGBA16FPx4_Premultiplied: + return QLatin1String("RGBA16FPx4pm"); + case QImage::Format_RGBX32FPx4: + return QLatin1String("RGBx32FPx4"); + case QImage::Format_RGBA32FPx4: + return QLatin1String("RGBA32FPx4"); + case QImage::Format_RGBA32FPx4_Premultiplied: + return QLatin1String("RGBA32FPx4pm"); default: break; }; @@ -1765,7 +1777,17 @@ void tst_QImage::smoothScale2_data() QTest::addColumn<int>("size"); int sizes[] = { 2, 3, 4, 6, 7, 8, 10, 16, 20, 32, 40, 64, 100, 101, 128, 0 }; - QImage::Format formats[] = { QImage::Format_RGB32, QImage::Format_ARGB32_Premultiplied, QImage::Format_RGBX64, QImage::Format_RGBA64_Premultiplied, QImage::Format_Invalid }; + QImage::Format formats[] = { QImage::Format_RGB32, + QImage::Format_ARGB32_Premultiplied, +#if QT_CONFIG(raster_64bit) + QImage::Format_RGBX64, + QImage::Format_RGBA64_Premultiplied, +#endif +#if QT_CONFIG(raster_fp) + QImage::Format_RGBX32FPx4, + QImage::Format_RGBA32FPx4_Premultiplied, +#endif + QImage::Format_Invalid }; for (int j = 0; formats[j] != QImage::Format_Invalid; ++j) { QString formatstr = formatToString(formats[j]); for (int i = 0; sizes[i] != 0; ++i) { @@ -1780,11 +1802,9 @@ void tst_QImage::smoothScale2() QFETCH(QImage::Format, format); QFETCH(int, size); - bool opaque = (format == QImage::Format_RGB32 || format == QImage::Format_RGBX64); - - QRgb expected = opaque ? qRgb(63, 127, 255) : qRgba(31, 63, 127, 127); - QImage img(size, size, format); + bool opaque = !img.hasAlphaChannel(); + QRgb expected = opaque ? qRgb(63, 127, 255) : qRgba(31, 63, 127, 127); img.fill(expected); // scale x down, y down @@ -1929,6 +1949,9 @@ void tst_QImage::smoothScale4_data() #if QT_CONFIG(raster_64bit) QTest::newRow("RGBx64") << QImage::Format_RGBX64; #endif +#if QT_CONFIG(raster_fp) + QTest::newRow("RGBx32FP") << QImage::Format_RGBX32FPx4; +#endif } void tst_QImage::smoothScale4() @@ -2356,6 +2379,8 @@ void tst_QImage::fillColor_data() QImage::Format_RGBA8888_Premultiplied, QImage::Format_BGR30, QImage::Format_A2RGB30_Premultiplied, + QImage::Format_RGBX16FPx4, + QImage::Format_RGBA32FPx4_Premultiplied, }; for (int i=0; names[i] != 0; ++i) { @@ -2678,7 +2703,6 @@ void tst_QImage::inplaceRgbSwapped_data() void tst_QImage::inplaceRgbSwapped() { -#if defined(Q_COMPILER_REF_QUALIFIERS) QFETCH(QImage::Format, format); QImage image(64, 1, format); @@ -2686,7 +2710,7 @@ void tst_QImage::inplaceRgbSwapped() QList<QRgb> testColor(image.width()); for (int i = 0; i < image.width(); ++i) - testColor[i] = qRgb(i * 2, i * 3, 255 - i * 4); + testColor[i] = qRgb(i * 2, i * 3, std::min(255 - i * 4, 0)); if (format == QImage::Format_Indexed8) { for (int i = 0; i < image.width(); ++i) { @@ -2739,7 +2763,6 @@ void tst_QImage::inplaceRgbSwapped() QCOMPARE(dataSwapped, orig.rgbSwapped()); } -#endif } @@ -3006,6 +3029,7 @@ void tst_QImage::inplaceRgbConversion_data() void tst_QImage::inplaceRgbConversion() { + // Test that conversions between RGB formats of the same bitwidth can be done inplace. QFETCH(QImage::Format, format); QFETCH(QImage::Format, dest_format); diff --git a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp index 30ce4fcfa8..a028990a16 100644 --- a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp +++ b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp @@ -1060,6 +1060,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; + QTest::newRow("rgbaFP16pm") << QImage::Format_RGBA16FPx4_Premultiplied; } void tst_QPainter::fillRect() @@ -1558,6 +1559,8 @@ void tst_QPainter::qimageFormats_data() QTest::newRow("Qimage::Format_BGR888") << QImage::Format_BGR888; QTest::newRow("Qimage::Format_A2RGB30_Premultiplied") << QImage::Format_A2RGB30_Premultiplied; QTest::newRow("Qimage::Format_RGB30") << QImage::Format_RGB30; + QTest::newRow("QImage::Format_RGBX16FPx4") << QImage::Format_RGBX16FPx4; + QTest::newRow("QImage::Format_RGBA32FPx4_Premultiplied") << QImage::Format_RGBA32FPx4_Premultiplied; } /* diff --git a/tests/auto/other/lancelot/paintcommands.cpp b/tests/auto/other/lancelot/paintcommands.cpp index d8157f047f..9344c9a67d 100644 --- a/tests/auto/other/lancelot/paintcommands.cpp +++ b/tests/auto/other/lancelot/paintcommands.cpp @@ -182,6 +182,14 @@ const char *PaintCommands::imageFormatTable[] = { "RGBx64", "RGBA64", "RGBA64_Premultiplied", + "Grayscale16", + "BGR888", + "RGBx16FPx4", + "RGBA16FPx4", + "RGBA16FPx4_Premultiplied", + "RGBx32FPx4", + "RGBA32FPx4", + "RGBA32FPx4_Premultiplied", }; int PaintCommands::translateEnum(const char *table[], const QString &pattern, int limit) diff --git a/tests/auto/other/lancelot/tst_lancelot.cpp b/tests/auto/other/lancelot/tst_lancelot.cpp index 7a85e9c370..407b14be94 100644 --- a/tests/auto/other/lancelot/tst_lancelot.cpp +++ b/tests/auto/other/lancelot/tst_lancelot.cpp @@ -90,6 +90,10 @@ private slots: void testRasterGrayscale8(); void testRasterRGBA64PM_data(); void testRasterRGBA64PM(); + void testRasterRGBA16F_data(); + void testRasterRGBA16F(); + void testRasterRGBA32FPM_data(); + void testRasterRGBA32FPM(); #ifndef QT_NO_OPENGL void testOpenGL_data(); @@ -242,6 +246,27 @@ void tst_Lancelot::testRasterRGBA64PM() } +void tst_Lancelot::testRasterRGBA16F_data() +{ + setupTestSuite(); +} + +void tst_Lancelot::testRasterRGBA16F() +{ + runTestSuite(Raster, QImage::Format_RGBA16FPx4); +} + +void tst_Lancelot::testRasterRGBA32FPM_data() +{ + setupTestSuite(); +} + +void tst_Lancelot::testRasterRGBA32FPM() +{ + runTestSuite(Raster, QImage::Format_RGBA32FPx4_Premultiplied); +} + + #ifndef QT_NO_OPENGL bool tst_Lancelot::checkSystemGLSupport() { |