diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-18 17:03:58 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2021-05-26 18:00:01 +0200 |
commit | 93cd9130d6d8d30e901dd3b2f2546dbc63754e2e (patch) | |
tree | 3def7382cd1e4edd4ff380587deee775e48a2e4b /src/gui/image | |
parent | e51831260a759b58cb089cac089c202a795fc584 (diff) |
Introduce float QImage formats and rendering
Useful for some HDR representations and HDR rendering.
Change-Id: If6e8a661faa3d2afdf17b6ed4d8ff5c5b2aeb30e
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/gui/image')
-rw-r--r-- | src/gui/image/qimage.cpp | 277 | ||||
-rw-r--r-- | src/gui/image/qimage.h | 8 | ||||
-rw-r--r-- | src/gui/image/qimage_conversions.cpp | 221 | ||||
-rw-r--r-- | src/gui/image/qimage_p.h | 51 |
4 files changed, 543 insertions, 14 deletions
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) { |