summaryrefslogtreecommitdiffstats
path: root/src/gui/image
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-06 13:27:31 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-08-28 12:18:03 +0200
commitea70c3c9f9abf17a0daddd9a61f41d3c39a4c152 (patch)
treedc34bf8dec2e6eb4d890abf702e9af7c193aafd1 /src/gui/image
parent28f31002c49b7c20d24528ae0ff0f4e946228c80 (diff)
Add rgb64 inplace generic conversion
Can avoid having direct conversions between all high precision formats. Change-Id: I36dd223d028cfda1b6f4116b38ca79cdb7bc5243 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
Diffstat (limited to 'src/gui/image')
-rw-r--r--src/gui/image/qimage.cpp12
-rw-r--r--src/gui/image/qimage_conversions.cpp103
-rw-r--r--src/gui/image/qimage_p.h3
3 files changed, 108 insertions, 10 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 15233d0989..0866da67f9 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -2052,7 +2052,7 @@ 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_to_rgb64;
+ converter = convert_generic_over_rgb64;
} else
converter = convert_generic;
}
@@ -4829,12 +4829,16 @@ bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFla
InPlace_Image_Converter converter = qimage_inplace_converter_map[format][newFormat];
if (converter)
return converter(this, flags);
- else if (format > QImage::Format_Indexed8 && newFormat > QImage::Format_Indexed8 && !qimage_converter_map[format][newFormat])
+ if (format > QImage::Format_Indexed8 && newFormat > QImage::Format_Indexed8 && !qimage_converter_map[format][newFormat]) {
// Convert inplace generic, but only if there are no direct converters,
// any direct ones are probably better even if not inplace.
+ if (qt_highColorPrecision(newFormat, !qPixelLayouts[newFormat].hasAlphaChannel)
+ && qt_highColorPrecision(format, !qPixelLayouts[format].hasAlphaChannel)) {
+ return convert_generic_inplace_over_rgb64(this, newFormat, flags);
+ }
return convert_generic_inplace(this, newFormat, flags);
- else
- return false;
+ }
+ return false;
}
/*!
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp
index cb34dcf9b8..99a007ccaf 100644
--- a/src/gui/image/qimage_conversions.cpp
+++ b/src/gui/image/qimage_conversions.cpp
@@ -257,7 +257,7 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio
#endif
}
-void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+void convert_generic_over_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
{
Q_ASSERT(dest->format > QImage::Format_Indexed8);
Q_ASSERT(src->format > QImage::Format_Indexed8);
@@ -325,10 +325,9 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im
const QPixelLayout *destLayout = &qPixelLayouts[dst_format];
// The precision here is only ARGB32PM so don't convert between higher accuracy
- // formats (assert instead when we have a convert_generic_over_rgb64_inplace).
- if (qt_highColorPrecision(data->format, !destLayout->hasAlphaChannel)
- && qt_highColorPrecision(dst_format, !srcLayout->hasAlphaChannel))
- return false;
+ // formats.
+ Q_ASSERT(!qt_highColorPrecision(data->format, !destLayout->hasAlphaChannel)
+ || !qt_highColorPrecision(dst_format, !srcLayout->hasAlphaChannel));
QImageData::ImageSizeParameters params = { data->bytes_per_line, data->nbytes };
if (data->depth != destDepth) {
@@ -441,6 +440,100 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im
return true;
}
+bool convert_generic_inplace_over_rgb64(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(data->format > QImage::Format_Indexed8);
+ Q_ASSERT(dst_format > QImage::Format_Indexed8);
+ 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;
+ }
+
+ FetchAndConvertPixelsFunc64 fetch = srcLayout->fetchToRGBA64PM;
+ ConvertAndStorePixelsFunc64 store = qStoreFromRGBA64PM[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 = qPixelLayouts[data->format + 1].fetchToRGBA64PM;
+ store = qStoreFromRGBA64PM[dst_format + 1];
+ }
+
+ auto convertSegment = [=](int yStart, int yEnd) {
+ QRgba64 buf[BufferSize];
+ QRgba64 *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::BPP64)
+ buffer = reinterpret_cast<QRgba64 *>(srcData) + x;
+ else
+ l = qMin(l, BufferSize);
+ const QRgba64 *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 = data->nbytes / (1<<16);
+ segments = std::min(segments, data->height);
+ QThreadPool *threadPool = QThreadPool::globalInstance();
+ if (segments > 1 && !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;
+}
+
static void convert_passthrough(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
{
Q_ASSERT(src->width == dest->width);
diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h
index 4065ac27ce..6ca415d187 100644
--- a/src/gui/image/qimage_p.h
+++ b/src/gui/image/qimage_p.h
@@ -158,8 +158,9 @@ extern Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImag
extern InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats];
void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
-void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
+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);
void dither_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags, bool fromalpha);