diff options
Diffstat (limited to 'src/gui/image/qimage_conversions.cpp')
-rw-r--r-- | src/gui/image/qimage_conversions.cpp | 474 |
1 files changed, 165 insertions, 309 deletions
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 97a5f89e68..4f570d8684 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -43,7 +43,12 @@ #include <private/qendian_p.h> #include <private/qsimd_p.h> #include <private/qimage_p.h> + #include <qendian.h> +#if QT_CONFIG(thread) +#include <qsemaphore.h> +#include <qthreadpool.h> +#endif QT_BEGIN_NAMESPACE @@ -159,12 +164,8 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio // Cannot be used with indexed formats. Q_ASSERT(dest->format > QImage::Format_Indexed8); Q_ASSERT(src->format > QImage::Format_Indexed8); - uint buf[BufferSize]; - uint *buffer = buf; const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; const QPixelLayout *destLayout = &qPixelLayouts[dest->format]; - const uchar *srcData = src->data; - uchar *destData = dest->data; FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM; ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM; @@ -197,59 +198,110 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio else store = destLayout->storeFromRGB32; } - QDitherInfo dither; - QDitherInfo *ditherPtr = 0; - if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) - ditherPtr = &dither; - - for (int y = 0; y < src->height; ++y) { - dither.y = y; - int x = 0; - while (x < src->width) { - dither.x = x; - int l = src->width - x; - if (destLayout->bpp == QPixelLayout::BPP32) - buffer = reinterpret_cast<uint *>(destData) + x; - else - l = qMin(l, BufferSize); - const uint *ptr = fetch(buffer, srcData, x, l, 0, ditherPtr); - store(destData, ptr, x, l, 0, ditherPtr); - x += l; + + auto convertSegment = [=](int yStart, int yEnd) { + uint buf[BufferSize]; + uint *buffer = buf; + const uchar *srcData = src->data + src->bytes_per_line * yStart; + uchar *destData = dest->data + dest->bytes_per_line * yStart; + QDitherInfo dither; + QDitherInfo *ditherPtr = nullptr; + if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) + ditherPtr = &dither; + for (int y = yStart; y < yEnd; ++y) { + dither.y = y; + int x = 0; + while (x < src->width) { + dither.x = x; + int l = src->width - x; + if (destLayout->bpp == QPixelLayout::BPP32) + buffer = reinterpret_cast<uint *>(destData) + x; + else + l = qMin(l, BufferSize); + const uint *ptr = fetch(buffer, srcData, x, l, 0, ditherPtr); + store(destData, ptr, x, l, 0, ditherPtr); + x += l; + } + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; } - srcData += src->bytes_per_line; - destData += dest->bytes_per_line; - } + }; + +#if QT_CONFIG(thread) + int segments = src->nbytes / (1<<16); + segments = std::min(segments, src->height); + + if (segments <= 1) + return convertSegment(0, src->height); + + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (src->height - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); +#else + convertSegment(0, src->height); +#endif } void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { Q_ASSERT(dest->format > QImage::Format_Indexed8); Q_ASSERT(src->format > QImage::Format_Indexed8); - QRgba64 buf[BufferSize]; - QRgba64 *buffer = buf; const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; const QPixelLayout *destLayout = &qPixelLayouts[dest->format]; - const uchar *srcData = src->data; - uchar *destData = dest->data; const FetchAndConvertPixelsFunc64 fetch = srcLayout->fetchToRGBA64PM; const ConvertAndStorePixelsFunc64 store = qStoreFromRGBA64PM[dest->format]; - for (int y = 0; y < src->height; ++y) { - int x = 0; - while (x < src->width) { - int l = src->width - x; - if (destLayout->bpp == QPixelLayout::BPP64) - buffer = reinterpret_cast<QRgba64 *>(destData) + 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; + auto convertSegment = [=](int yStart, int yEnd) { + QRgba64 buf[BufferSize]; + QRgba64 *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 (destLayout->bpp == QPixelLayout::BPP64) + buffer = reinterpret_cast<QRgba64 *>(destData) + 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 += src->bytes_per_line; + destData += dest->bytes_per_line; } - srcData += src->bytes_per_line; - destData += dest->bytes_per_line; - } + }; +#if QT_CONFIG(thread) + int segments = src->nbytes / (1<<16); + segments = std::min(segments, src->height); + + if (segments <= 1) + return convertSegment(0, src->height); + + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (src->height - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); +#else + convertSegment(0, src->height); +#endif } bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags flags) @@ -257,7 +309,8 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im // Cannot be used with indexed formats or between formats with different pixel depths. Q_ASSERT(dst_format > QImage::Format_Indexed8); Q_ASSERT(data->format > QImage::Format_Indexed8); - if (data->depth != qt_depthForFormat(dst_format)) + const int destDepth = qt_depthForFormat(dst_format); + if (data->depth < destDepth) return false; const QPixelLayout *srcLayout = &qPixelLayouts[data->format]; @@ -269,12 +322,14 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im && qt_highColorPrecision(dst_format, !srcLayout->hasAlphaChannel)) return false; - uint buf[BufferSize]; - uint *buffer = buf; - uchar *srcData = data->data; + 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; + } - Q_ASSERT(srcLayout->bpp == destLayout->bpp); - Q_ASSERT(srcLayout->bpp != QPixelLayout::BPP64); + Q_ASSERT(destLayout->bpp != QPixelLayout::BPP64); FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM; ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM; if (!srcLayout->hasAlphaChannel && destLayout->storeFromRGB32) { @@ -305,26 +360,61 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im else store = destLayout->storeFromRGB32; } - QDitherInfo dither; - QDitherInfo *ditherPtr = 0; - if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) - ditherPtr = &dither; - - for (int y = 0; y < data->height; ++y) { - dither.y = y; - int x = 0; - while (x < data->width) { - dither.x = x; - int l = data->width - x; - if (destLayout->bpp == QPixelLayout::BPP32) - buffer = reinterpret_cast<uint *>(srcData) + x; - else - l = qMin(l, BufferSize); - const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr); - store(srcData, ptr, x, l, nullptr, ditherPtr); - x += l; + + auto convertSegment = [=](int yStart, int yEnd) { + uint buf[BufferSize]; + uint *buffer = buf; + uchar *srcData = data->data + data->bytes_per_line * yStart; + uchar *destData = srcData; + QDitherInfo dither; + QDitherInfo *ditherPtr = nullptr; + if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) + ditherPtr = &dither; + for (int y = yStart; y < yEnd; ++y) { + dither.y = y; + int x = 0; + while (x < data->width) { + dither.x = x; + int l = data->width - x; + if (srcLayout->bpp == QPixelLayout::BPP32) + buffer = reinterpret_cast<uint *>(srcData) + x; + else + l = qMin(l, BufferSize); + const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr); + store(destData, ptr, x, l, nullptr, ditherPtr); + x += l; + } + srcData += data->bytes_per_line; + destData += params.bytesPerLine; } - srcData += data->bytes_per_line; + }; +#if QT_CONFIG(thread) + int segments = data->nbytes / (1<<16); + segments = std::min(segments, data->height); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (data->height - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + } 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; @@ -832,240 +922,6 @@ static bool convert_A2RGB30_PM_to_ARGB_inplace(QImageData *data, Qt::ImageConver return true; } -static bool convert_indexed8_to_ARGB_PM_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - Q_ASSERT(data->own_data); - - const int depth = 32; - auto params = QImageData::calculateImageParameters(data->width, data->height, depth); - if (params.bytesPerLine < 0) - return false; - uchar *const newData = (uchar *)realloc(data->data, params.totalSize); - if (!newData) - return false; - - data->data = newData; - - // start converting from the end because the end image is bigger than the source - uchar *src_data = newData + data->nbytes; // end of src - quint32 *dest_data = (quint32 *) (newData + params.totalSize); // end of dest > end of src - const int width = data->width; - const int src_pad = data->bytes_per_line - width; - const int dest_pad = (params.bytesPerLine >> 2) - width; - if (data->colortable.size() == 0) { - data->colortable.resize(256); - for (int i = 0; i < 256; ++i) - data->colortable[i] = qRgb(i, i, i); - } else { - for (int i = 0; i < data->colortable.size(); ++i) - data->colortable[i] = qPremultiply(data->colortable.at(i)); - - // Fill the rest of the table in case src_data > colortable.size() - const int oldSize = data->colortable.size(); - const QRgb lastColor = data->colortable.at(oldSize - 1); - data->colortable.insert(oldSize, 256 - oldSize, lastColor); - } - - for (int i = 0; i < data->height; ++i) { - src_data -= src_pad; - dest_data -= dest_pad; - for (int pixI = 0; pixI < width; ++pixI) { - --src_data; - --dest_data; - *dest_data = data->colortable.at(*src_data); - } - } - - data->colortable = QVector<QRgb>(); - data->format = QImage::Format_ARGB32_Premultiplied; - data->bytes_per_line = params.bytesPerLine; - data->depth = depth; - data->nbytes = params.totalSize; - - return true; -} - -static bool convert_indexed8_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - Q_ASSERT(data->own_data); - - const int depth = 32; - auto params = QImageData::calculateImageParameters(data->width, data->height, depth); - if (params.bytesPerLine < 0) - return false; - uchar *const newData = (uchar *)realloc(data->data, params.totalSize); - if (!newData) - return false; - - data->data = newData; - - // start converting from the end because the end image is bigger than the source - uchar *src_data = newData + data->nbytes; - quint32 *dest_data = (quint32 *) (newData + params.totalSize); - const int width = data->width; - const int src_pad = data->bytes_per_line - width; - const int dest_pad = (params.bytesPerLine >> 2) - width; - if (data->colortable.size() == 0) { - data->colortable.resize(256); - for (int i = 0; i < 256; ++i) - data->colortable[i] = qRgb(i, i, i); - } else { - // Fill the rest of the table in case src_data > colortable.size() - const int oldSize = data->colortable.size(); - const QRgb lastColor = data->colortable.at(oldSize - 1); - data->colortable.insert(oldSize, 256 - oldSize, lastColor); - } - - for (int i = 0; i < data->height; ++i) { - src_data -= src_pad; - dest_data -= dest_pad; - for (int pixI = 0; pixI < width; ++pixI) { - --src_data; - --dest_data; - *dest_data = (quint32) data->colortable.at(*src_data); - } - } - - data->colortable = QVector<QRgb>(); - data->format = QImage::Format_ARGB32; - data->bytes_per_line = params.bytesPerLine; - data->depth = depth; - data->nbytes = params.totalSize; - - return true; -} - -static bool convert_indexed8_to_RGB_inplace(QImageData *data, Qt::ImageConversionFlags flags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - Q_ASSERT(data->own_data); - - if (data->has_alpha_clut) { - for (int i = 0; i < data->colortable.size(); ++i) - data->colortable[i] |= 0xff000000; - } - - if (!convert_indexed8_to_ARGB_inplace(data, flags)) - return false; - - data->format = QImage::Format_RGB32; - return true; -} - -static bool convert_indexed8_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_Indexed8); - Q_ASSERT(data->own_data); - - const int depth = 16; - auto params = QImageData::calculateImageParameters(data->width, data->height, depth); - if (params.bytesPerLine < 0) - return false; - uchar *const newData = (uchar *)realloc(data->data, params.totalSize); - if (!newData) - return false; - - data->data = newData; - - // start converting from the end because the end image is bigger than the source - uchar *src_data = newData + data->nbytes; - quint16 *dest_data = (quint16 *) (newData + params.totalSize); - const int width = data->width; - const int src_pad = data->bytes_per_line - width; - const int dest_pad = (params.bytesPerLine >> 1) - width; - - quint16 colorTableRGB16[256]; - const int tableSize = data->colortable.size(); - if (tableSize == 0) { - for (int i = 0; i < 256; ++i) - colorTableRGB16[i] = qConvertRgb32To16(qRgb(i, i, i)); - } else { - // 1) convert the existing colors to RGB16 - for (int i = 0; i < tableSize; ++i) - colorTableRGB16[i] = qConvertRgb32To16(data->colortable.at(i)); - data->colortable = QVector<QRgb>(); - - // 2) fill the rest of the table in case src_data > colortable.size() - const quint16 lastColor = colorTableRGB16[tableSize - 1]; - for (int i = tableSize; i < 256; ++i) - colorTableRGB16[i] = lastColor; - } - - for (int i = 0; i < data->height; ++i) { - src_data -= src_pad; - dest_data -= dest_pad; - for (int pixI = 0; pixI < width; ++pixI) { - --src_data; - --dest_data; - *dest_data = colorTableRGB16[*src_data]; - } - } - - data->format = QImage::Format_RGB16; - data->bytes_per_line = params.bytesPerLine; - data->depth = depth; - data->nbytes = params.totalSize; - - return true; -} - -static bool convert_RGB_to_RGB16_inplace(QImageData *data, Qt::ImageConversionFlags) -{ - Q_ASSERT(data->format == QImage::Format_RGB32); - Q_ASSERT(data->own_data); - - const int depth = 16; - - // cannot overflow, since we're shrinking the buffer - const qsizetype dst_bytes_per_line = ((data->width * depth + 31) >> 5) << 2; - const qsizetype src_bytes_per_line = data->bytes_per_line; - quint32 *src_data = (quint32 *) data->data; - quint16 *dst_data = (quint16 *) data->data; - - for (int i = 0; i < data->height; ++i) { - for (int j = 0; j < data->width; ++j) - dst_data[j] = qConvertRgb32To16(src_data[j]); - src_data = (quint32 *) (((char*)src_data) + src_bytes_per_line); - dst_data = (quint16 *) (((char*)dst_data) + dst_bytes_per_line); - } - data->format = QImage::Format_RGB16; - data->bytes_per_line = dst_bytes_per_line; - data->depth = depth; - data->nbytes = dst_bytes_per_line * data->height; - uchar *const newData = (uchar *)realloc(data->data, data->nbytes); - if (newData) - data->data = newData; - - // can't fail, since we're shrinking - return true; -} - -static void convert_ARGB_PM_to_ARGB(QImageData *dest, const QImageData *src) -{ - Q_ASSERT(src->format == QImage::Format_ARGB32_Premultiplied || src->format == QImage::Format_RGBA8888_Premultiplied); - Q_ASSERT(dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_RGBA8888); - Q_ASSERT(src->width == dest->width); - Q_ASSERT(src->height == dest->height); - - const int src_pad = (src->bytes_per_line >> 2) - src->width; - const int dest_pad = (dest->bytes_per_line >> 2) - dest->width; - const QRgb *src_data = (QRgb *) src->data; - QRgb *dest_data = (QRgb *) dest->data; - - for (int i = 0; i < src->height; ++i) { - const QRgb *end = src_data + src->width; - while (src_data < end) { - *dest_data = qUnpremultiply(*src_data); - ++src_data; - ++dest_data; - } - src_data += src_pad; - dest_data += dest_pad; - } -} - static void convert_RGBA_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { Q_ASSERT(src->format == QImage::Format_RGBA8888 || src->format == QImage::Format_RGBX8888); @@ -1732,7 +1588,7 @@ static void convert_X_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageC static void convert_ARGB_PM_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) { QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32)); - convert_ARGB_PM_to_ARGB(tmp.data(), src); + convert_generic(tmp.data(), src, Qt::AutoColor); dither_to_Mono(dst, tmp.data(), flags, false); } @@ -2012,7 +1868,7 @@ static void convert_RGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt:: static void convert_ARGB_PM_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags) { QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32)); - convert_ARGB_PM_to_ARGB(tmp.data(), src); + convert_generic(tmp.data(), src, Qt::AutoColor); convert_RGB_to_Indexed8(dst, tmp.data(), flags); } @@ -2991,10 +2847,10 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, 0, 0, - convert_indexed8_to_RGB_inplace, - convert_indexed8_to_ARGB_inplace, - convert_indexed8_to_ARGB_PM_inplace, - convert_indexed8_to_RGB16_inplace, + 0, + 0, + 0, + 0, 0, 0, 0, @@ -3018,7 +2874,7 @@ InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QIma 0, mask_alpha_converter_inplace<QImage::Format_ARGB32>, mask_alpha_converter_inplace<QImage::Format_ARGB32_Premultiplied>, - convert_RGB_to_RGB16_inplace, + 0, 0, 0, 0, |