summaryrefslogtreecommitdiffstats
path: root/src/gui/image
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-18 17:03:58 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2021-05-26 18:00:01 +0200
commit93cd9130d6d8d30e901dd3b2f2546dbc63754e2e (patch)
tree3def7382cd1e4edd4ff380587deee775e48a2e4b /src/gui/image
parente51831260a759b58cb089cac089c202a795fc584 (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.cpp277
-rw-r--r--src/gui/image/qimage.h8
-rw-r--r--src/gui/image/qimage_conversions.cpp221
-rw-r--r--src/gui/image/qimage_p.h51
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)
{