summaryrefslogtreecommitdiffstats
path: root/src/gui/image/qimage_conversions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/image/qimage_conversions.cpp')
-rw-r--r--src/gui/image/qimage_conversions.cpp474
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,