From ea70c3c9f9abf17a0daddd9a61f41d3c39a4c152 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Mon, 6 Jul 2020 13:27:31 +0200 Subject: Add rgb64 inplace generic conversion Can avoid having direct conversions between all high precision formats. Change-Id: I36dd223d028cfda1b6f4116b38ca79cdb7bc5243 Reviewed-by: Eirik Aavitsland --- src/gui/image/qimage.cpp | 12 ++-- src/gui/image/qimage_conversions.cpp | 103 +++++++++++++++++++++++++++++++++-- src/gui/image/qimage_p.h | 3 +- 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(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); -- cgit v1.2.3