1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <private/qguiapplication_p.h>
5#include <private/qcolortransform_p.h>
6#include <private/qcolortrclut_p.h>
7#include <private/qcmyk_p.h>
8#include <private/qdrawhelper_p.h>
9#include <private/qendian_p.h>
10#include <private/qpixellayout_p.h>
11#include <private/qsimd_p.h>
12#include <private/qimage_p.h>
13
14#include <qendian.h>
15#include <qrgbafloat.h>
16#if QT_CONFIG(thread)
17#include <qsemaphore.h>
18#include <qthreadpool.h>
19#include <private/qthreadpool_p.h>
20#ifdef Q_OS_WASM
21// WebAssembly has threads; however we can't block the main thread.
22#else
23#define QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
24#endif
25#endif
26
27QT_BEGIN_NAMESPACE
28
29struct QDefaultColorTables
30{
31 QDefaultColorTables()
32 : gray(256), alpha(256)
33 {
34 for (int i = 0; i < 256; ++i) {
35 gray[i] = qRgb(r: i, g: i, b: i);
36 alpha[i] = qRgba(r: 0, g: 0, b: 0, a: i);
37 }
38 }
39
40 QList<QRgb> gray, alpha;
41};
42
43Q_GLOBAL_STATIC(QDefaultColorTables, defaultColorTables);
44
45// table to flip bits
46static const uchar bitflip[256] = {
47 /*
48 open OUT, "| fmt";
49 for $i (0..255) {
50 print OUT (($i >> 7) & 0x01) | (($i >> 5) & 0x02) |
51 (($i >> 3) & 0x04) | (($i >> 1) & 0x08) |
52 (($i << 7) & 0x80) | (($i << 5) & 0x40) |
53 (($i << 3) & 0x20) | (($i << 1) & 0x10), ", ";
54 }
55 close OUT;
56 */
57 0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240,
58 8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248,
59 4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244,
60 12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252,
61 2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
62 10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250,
63 6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246,
64 14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254,
65 1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241,
66 9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
67 5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245,
68 13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253,
69 3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243,
70 11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251,
71 7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
72 15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255
73};
74
75const uchar *qt_get_bitflip_array()
76{
77 return bitflip;
78}
79
80void qGamma_correct_back_to_linear_cs(QImage *image)
81{
82 const QColorTrcLut *cp = QGuiApplicationPrivate::instance()->colorProfileForA32Text();
83 if (!cp)
84 return;
85 // gamma correct the pixels back to linear color space...
86 int h = image->height();
87 int w = image->width();
88
89 for (int y=0; y<h; ++y) {
90 QRgb *pixels = reinterpret_cast<QRgb *>(image->scanLine(y));
91 for (int x=0; x<w; ++x)
92 pixels[x] = cp->toLinear(rgb32: pixels[x]);
93 }
94}
95
96/*****************************************************************************
97 Internal routines for converting image depth.
98 *****************************************************************************/
99
100// The drawhelper conversions from/to RGB32 are passthroughs which is not always correct for general image conversion
101#if !defined(__ARM_NEON__) || !(Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
102static void QT_FASTCALL storeRGB32FromARGB32PM(uchar *dest, const uint *src, int index, int count,
103 const QList<QRgb> *, QDitherInfo *)
104{
105 uint *d = reinterpret_cast<uint *>(dest) + index;
106 for (int i = 0; i < count; ++i)
107 d[i] = 0xff000000 | qUnpremultiply(p: src[i]);
108}
109#endif
110
111static void QT_FASTCALL storeRGB32FromARGB32(uchar *dest, const uint *src, int index, int count,
112 const QList<QRgb> *, QDitherInfo *)
113{
114 uint *d = reinterpret_cast<uint *>(dest) + index;
115 for (int i = 0; i < count; ++i)
116 d[i] = 0xff000000 | src[i];
117}
118
119static const uint *QT_FASTCALL fetchRGB32ToARGB32PM(uint *buffer, const uchar *src, int index, int count,
120 const QList<QRgb> *, QDitherInfo *)
121{
122 const uint *s = reinterpret_cast<const uint *>(src) + index;
123 for (int i = 0; i < count; ++i)
124 buffer[i] = 0xff000000 | s[i];
125 return buffer;
126}
127
128#ifdef QT_COMPILER_SUPPORTS_SSE4_1
129extern void QT_FASTCALL storeRGB32FromARGB32PM_sse4(uchar *dest, const uint *src, int index, int count,
130 const QList<QRgb> *, QDitherInfo *);
131#elif defined(__ARM_NEON__) && (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
132extern void QT_FASTCALL storeRGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
133 const QList<QRgb> *, QDitherInfo *);
134#endif
135
136void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags flags)
137{
138 // Cannot be used with indexed formats.
139 Q_ASSERT(dest->format > QImage::Format_Indexed8);
140 Q_ASSERT(src->format > QImage::Format_Indexed8);
141 const QPixelLayout *srcLayout = &qPixelLayouts[src->format];
142 const QPixelLayout *destLayout = &qPixelLayouts[dest->format];
143
144 FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM;
145 ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM;
146 if (!srcLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
147 // If the source doesn't have an alpha channel, we can use the faster storeFromRGB32 method.
148 store = destLayout->storeFromRGB32;
149 } else {
150 // The drawhelpers do not mask the alpha value in RGB32, we want to here.
151 if (src->format == QImage::Format_RGB32)
152 fetch = fetchRGB32ToARGB32PM;
153 if (dest->format == QImage::Format_RGB32) {
154#ifdef QT_COMPILER_SUPPORTS_SSE4_1
155 if (qCpuHasFeature(SSE4_1))
156 store = storeRGB32FromARGB32PM_sse4;
157 else
158 store = storeRGB32FromARGB32PM;
159#elif defined(__ARM_NEON__) && (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
160 store = storeRGB32FromARGB32PM_neon;
161#else
162 store = storeRGB32FromARGB32PM;
163#endif
164 }
165 }
166 if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
167 !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
168 // Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format.
169 fetch = qPixelLayouts[qt_toPremultipliedFormat(format: src->format)].fetchToARGB32PM;
170 if (dest->format == QImage::Format_RGB32)
171 store = storeRGB32FromARGB32;
172 else
173 store = destLayout->storeFromRGB32;
174 }
175
176 auto convertSegment = [=](int yStart, int yEnd) {
177 uint buf[BufferSize];
178 uint *buffer = buf;
179 const uchar *srcData = src->data + src->bytes_per_line * yStart;
180 uchar *destData = dest->data + dest->bytes_per_line * yStart;
181 QDitherInfo dither;
182 QDitherInfo *ditherPtr = nullptr;
183 if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither)
184 ditherPtr = &dither;
185 for (int y = yStart; y < yEnd; ++y) {
186 dither.y = y;
187 int x = 0;
188 while (x < src->width) {
189 dither.x = x;
190 int l = src->width - x;
191 if (destLayout->bpp == QPixelLayout::BPP32)
192 buffer = reinterpret_cast<uint *>(destData) + x;
193 else
194 l = qMin(a: l, b: BufferSize);
195 const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr);
196 store(destData, ptr, x, l, nullptr, ditherPtr);
197 x += l;
198 }
199 srcData += src->bytes_per_line;
200 destData += dest->bytes_per_line;
201 }
202 };
203
204#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
205 int segments = (qsizetype(src->width) * src->height) >> 16;
206 segments = std::min(a: segments, b: src->height);
207
208 QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
209 if (segments <= 1 || !threadPool || threadPool->contains(thread: QThread::currentThread()))
210 return convertSegment(0, src->height);
211
212 QSemaphore semaphore;
213 int y = 0;
214 for (int i = 0; i < segments; ++i) {
215 int yn = (src->height - y) / (segments - i);
216 threadPool->start(functionToRun: [&, y, yn]() {
217 convertSegment(y, y + yn);
218 semaphore.release(n: 1);
219 });
220 y += yn;
221 }
222 semaphore.acquire(n: segments);
223#else
224 convertSegment(0, src->height);
225#endif
226}
227
228void convert_generic_over_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
229{
230 Q_ASSERT(dest->format > QImage::Format_Indexed8);
231 Q_ASSERT(src->format > QImage::Format_Indexed8);
232 const QPixelLayout *srcLayout = &qPixelLayouts[src->format];
233 const QPixelLayout *destLayout = &qPixelLayouts[dest->format];
234
235 const FetchAndConvertPixelsFunc64 fetch = srcLayout->fetchToRGBA64PM;
236 const ConvertAndStorePixelsFunc64 store = qStoreFromRGBA64PM[dest->format];
237
238 auto convertSegment = [=](int yStart, int yEnd) {
239 QRgba64 buf[BufferSize];
240 QRgba64 *buffer = buf;
241 const uchar *srcData = src->data + yStart * src->bytes_per_line;
242 uchar *destData = dest->data + yStart * dest->bytes_per_line;
243 for (int y = yStart; y < yEnd; ++y) {
244 int x = 0;
245 while (x < src->width) {
246 int l = src->width - x;
247 if (destLayout->bpp == QPixelLayout::BPP64)
248 buffer = reinterpret_cast<QRgba64 *>(destData) + x;
249 else
250 l = qMin(a: l, b: BufferSize);
251 const QRgba64 *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr);
252 store(destData, ptr, x, l, nullptr, nullptr);
253 x += l;
254 }
255 srcData += src->bytes_per_line;
256 destData += dest->bytes_per_line;
257 }
258 };
259#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
260 int segments = (qsizetype(src->width) * src->height) >> 16;
261 segments = std::min(a: segments, b: src->height);
262
263 QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
264 if (segments <= 1 || !threadPool || threadPool->contains(thread: QThread::currentThread()))
265 return convertSegment(0, src->height);
266
267 QSemaphore semaphore;
268 int y = 0;
269 for (int i = 0; i < segments; ++i) {
270 int yn = (src->height - y) / (segments - i);
271 threadPool->start(functionToRun: [&, y, yn]() {
272 convertSegment(y, y + yn);
273 semaphore.release(n: 1);
274 });
275 y += yn;
276 }
277 semaphore.acquire(n: segments);
278#else
279 convertSegment(0, src->height);
280#endif
281}
282
283#if QT_CONFIG(raster_fp)
284void convert_generic_over_rgba32f(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
285{
286 Q_ASSERT(dest->format >= QImage::Format_RGBX16FPx4);
287 Q_ASSERT(src->format >= QImage::Format_RGBX16FPx4);
288
289 const FetchAndConvertPixelsFuncFP fetch = qFetchToRGBA32F[src->format];
290 const ConvertAndStorePixelsFuncFP store = qStoreFromRGBA32F[dest->format];
291
292 auto convertSegment = [=](int yStart, int yEnd) {
293 QRgbaFloat32 buf[BufferSize];
294 QRgbaFloat32 *buffer = buf;
295 const uchar *srcData = src->data + yStart * src->bytes_per_line;
296 uchar *destData = dest->data + yStart * dest->bytes_per_line;
297 for (int y = yStart; y < yEnd; ++y) {
298 int x = 0;
299 while (x < src->width) {
300 int l = src->width - x;
301 if (dest->depth == 128)
302 buffer = reinterpret_cast<QRgbaFloat32 *>(destData) + x;
303 else
304 l = qMin(a: l, b: BufferSize);
305 const QRgbaFloat32 *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr);
306 store(destData, ptr, x, l, nullptr, nullptr);
307 x += l;
308 }
309 srcData += src->bytes_per_line;
310 destData += dest->bytes_per_line;
311 }
312 };
313#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
314 int segments = (qsizetype(src->width) * src->height) >> 16;
315 segments = std::min(a: segments, b: src->height);
316
317 QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
318 if (segments <= 1 || !threadPool || threadPool->contains(thread: QThread::currentThread()))
319 return convertSegment(0, src->height);
320
321 QSemaphore semaphore;
322 int y = 0;
323 for (int i = 0; i < segments; ++i) {
324 int yn = (src->height - y) / (segments - i);
325 threadPool->start(functionToRun: [&, y, yn]() {
326 convertSegment(y, y + yn);
327 semaphore.release(n: 1);
328 });
329 y += yn;
330 }
331 semaphore.acquire(n: segments);
332#else
333 convertSegment(0, src->height);
334#endif
335}
336#endif
337
338bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags flags)
339{
340 // Cannot be used with indexed formats or between formats with different pixel depths.
341 Q_ASSERT(dst_format > QImage::Format_Indexed8);
342 Q_ASSERT(data->format > QImage::Format_Indexed8);
343 const int destDepth = qt_depthForFormat(format: dst_format);
344 if (data->depth < destDepth)
345 return false;
346
347 const QPixelLayout *srcLayout = &qPixelLayouts[data->format];
348 const QPixelLayout *destLayout = &qPixelLayouts[dst_format];
349
350 // The precision here is only ARGB32PM so don't convert between higher accuracy
351 // formats.
352 Q_ASSERT(!qt_highColorPrecision(data->format, !destLayout->hasAlphaChannel)
353 || !qt_highColorPrecision(dst_format, !srcLayout->hasAlphaChannel));
354
355 QImageData::ImageSizeParameters params = { .bytesPerLine: data->bytes_per_line, .totalSize: data->nbytes };
356 if (data->depth != destDepth) {
357 params = QImageData::calculateImageParameters(width: data->width, height: data->height, depth: destDepth);
358 if (!params.isValid())
359 return false;
360 }
361
362 Q_ASSERT(destLayout->bpp < QPixelLayout::BPP64);
363 FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM;
364 ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM;
365 if (!srcLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
366 // If the source doesn't have an alpha channel, we can use the faster storeFromRGB32 method.
367 store = destLayout->storeFromRGB32;
368 } else {
369 if (data->format == QImage::Format_RGB32)
370 fetch = fetchRGB32ToARGB32PM;
371 if (dst_format == QImage::Format_RGB32) {
372#ifdef QT_COMPILER_SUPPORTS_SSE4_1
373 if (qCpuHasFeature(SSE4_1))
374 store = storeRGB32FromARGB32PM_sse4;
375 else
376 store = storeRGB32FromARGB32PM;
377#elif defined(__ARM_NEON__) && (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
378 store = storeRGB32FromARGB32PM_neon;
379#else
380 store = storeRGB32FromARGB32PM;
381#endif
382 }
383 }
384 if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
385 !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
386 // Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format.
387 fetch = qPixelLayouts[qt_toPremultipliedFormat(format: data->format)].fetchToARGB32PM;
388 if (data->format == QImage::Format_RGB32)
389 store = storeRGB32FromARGB32;
390 else
391 store = destLayout->storeFromRGB32;
392 }
393
394 auto convertSegment = [=](int yStart, int yEnd) {
395 uint buf[BufferSize];
396 uint *buffer = buf;
397 uchar *srcData = data->data + data->bytes_per_line * yStart;
398 uchar *destData = srcData; // This can be temporarily wrong if we doing a shrinking conversion
399 QDitherInfo dither;
400 QDitherInfo *ditherPtr = nullptr;
401 if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither)
402 ditherPtr = &dither;
403 for (int y = yStart; y < yEnd; ++y) {
404 dither.y = y;
405 int x = 0;
406 while (x < data->width) {
407 dither.x = x;
408 int l = data->width - x;
409 if (srcLayout->bpp == QPixelLayout::BPP32)
410 buffer = reinterpret_cast<uint *>(srcData) + x;
411 else
412 l = qMin(a: l, b: BufferSize);
413 const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr);
414 store(destData, ptr, x, l, nullptr, ditherPtr);
415 x += l;
416 }
417 srcData += data->bytes_per_line;
418 destData += params.bytesPerLine;
419 }
420 };
421#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
422 int segments = (qsizetype(data->width) * data->height) >> 16;
423 segments = std::min(a: segments, b: data->height);
424 QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
425 if (segments > 1 && threadPool && !threadPool->contains(thread: QThread::currentThread())) {
426 QSemaphore semaphore;
427 int y = 0;
428 for (int i = 0; i < segments; ++i) {
429 int yn = (data->height - y) / (segments - i);
430 threadPool->start(functionToRun: [&, y, yn]() {
431 convertSegment(y, y + yn);
432 semaphore.release(n: 1);
433 });
434 y += yn;
435 }
436 semaphore.acquire(n: segments);
437 if (data->bytes_per_line != params.bytesPerLine) {
438 // Compress segments to a continuous block
439 y = 0;
440 for (int i = 0; i < segments; ++i) {
441 int yn = (data->height - y) / (segments - i);
442 uchar *srcData = data->data + data->bytes_per_line * y;
443 uchar *destData = data->data + params.bytesPerLine * y;
444 if (srcData != destData)
445 memmove(dest: destData, src: srcData, n: params.bytesPerLine * yn);
446 y += yn;
447 }
448 }
449 } else
450#endif
451 convertSegment(0, data->height);
452 if (params.totalSize != data->nbytes) {
453 Q_ASSERT(params.totalSize < data->nbytes);
454 void *newData = realloc(ptr: data->data, size: params.totalSize);
455 if (newData) {
456 data->data = (uchar *)newData;
457 data->nbytes = params.totalSize;
458 }
459 data->bytes_per_line = params.bytesPerLine;
460 }
461 data->depth = destDepth;
462 data->format = dst_format;
463 return true;
464}
465
466bool convert_generic_inplace_over_rgb64(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags)
467{
468 Q_ASSERT(data->format > QImage::Format_Indexed8);
469 Q_ASSERT(dst_format > QImage::Format_Indexed8);
470 const int destDepth = qt_depthForFormat(format: dst_format);
471 if (data->depth < destDepth)
472 return false;
473
474 const QPixelLayout *srcLayout = &qPixelLayouts[data->format];
475 const QPixelLayout *destLayout = &qPixelLayouts[dst_format];
476
477 QImageData::ImageSizeParameters params = { .bytesPerLine: data->bytes_per_line, .totalSize: data->nbytes };
478 if (data->depth != destDepth) {
479 params = QImageData::calculateImageParameters(width: data->width, height: data->height, depth: destDepth);
480 if (!params.isValid())
481 return false;
482 }
483
484 FetchAndConvertPixelsFunc64 fetch = srcLayout->fetchToRGBA64PM;
485 ConvertAndStorePixelsFunc64 store = qStoreFromRGBA64PM[dst_format];
486 if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
487 destLayout->hasAlphaChannel && !destLayout->premultiplied) {
488 // Avoid unnecessary premultiply and unpremultiply when converting between two unpremultiplied formats.
489 fetch = qPixelLayouts[qt_toPremultipliedFormat(format: data->format)].fetchToRGBA64PM;
490 store = qStoreFromRGBA64PM[qt_toPremultipliedFormat(format: dst_format)];
491 }
492
493 auto convertSegment = [=](int yStart, int yEnd) {
494 QRgba64 buf[BufferSize];
495 QRgba64 *buffer = buf;
496 uchar *srcData = data->data + yStart * data->bytes_per_line;
497 uchar *destData = srcData;
498 for (int y = yStart; y < yEnd; ++y) {
499 int x = 0;
500 while (x < data->width) {
501 int l = data->width - x;
502 if (srcLayout->bpp == QPixelLayout::BPP64)
503 buffer = reinterpret_cast<QRgba64 *>(srcData) + x;
504 else
505 l = qMin(a: l, b: BufferSize);
506 const QRgba64 *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr);
507 store(destData, ptr, x, l, nullptr, nullptr);
508 x += l;
509 }
510 srcData += data->bytes_per_line;
511 destData += params.bytesPerLine;
512 }
513 };
514#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
515 int segments = (qsizetype(data->width) * data->height) >> 16;
516 segments = std::min(a: segments, b: data->height);
517 QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
518 if (segments > 1 && threadPool && !threadPool->contains(thread: QThread::currentThread())) {
519 QSemaphore semaphore;
520 int y = 0;
521 for (int i = 0; i < segments; ++i) {
522 int yn = (data->height - y) / (segments - i);
523 threadPool->start(functionToRun: [&, y, yn]() {
524 convertSegment(y, y + yn);
525 semaphore.release(n: 1);
526 });
527 y += yn;
528 }
529 semaphore.acquire(n: segments);
530 if (data->bytes_per_line != params.bytesPerLine) {
531 // Compress segments to a continuous block
532 y = 0;
533 for (int i = 0; i < segments; ++i) {
534 int yn = (data->height - y) / (segments - i);
535 uchar *srcData = data->data + data->bytes_per_line * y;
536 uchar *destData = data->data + params.bytesPerLine * y;
537 if (srcData != destData)
538 memmove(dest: destData, src: srcData, n: params.bytesPerLine * yn);
539 y += yn;
540 }
541 }
542 } else
543#endif
544 convertSegment(0, data->height);
545 if (params.totalSize != data->nbytes) {
546 Q_ASSERT(params.totalSize < data->nbytes);
547 void *newData = realloc(ptr: data->data, size: params.totalSize);
548 if (newData) {
549 data->data = (uchar *)newData;
550 data->nbytes = params.totalSize;
551 }
552 data->bytes_per_line = params.bytesPerLine;
553 }
554 data->depth = destDepth;
555 data->format = dst_format;
556 return true;
557}
558
559#if QT_CONFIG(raster_fp)
560bool convert_generic_inplace_over_rgba32f(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags)
561{
562 Q_ASSERT(data->format >= QImage::Format_RGBX16FPx4);
563 Q_ASSERT(dst_format >= QImage::Format_RGBX16FPx4);
564 const int destDepth = qt_depthForFormat(format: dst_format);
565 if (data->depth < destDepth)
566 return false;
567
568 const QPixelLayout *srcLayout = &qPixelLayouts[data->format];
569 const QPixelLayout *destLayout = &qPixelLayouts[dst_format];
570
571 QImageData::ImageSizeParameters params = { .bytesPerLine: data->bytes_per_line, .totalSize: data->nbytes };
572 if (data->depth != destDepth) {
573 params = QImageData::calculateImageParameters(width: data->width, height: data->height, depth: destDepth);
574 if (!params.isValid())
575 return false;
576 }
577
578 FetchAndConvertPixelsFuncFP fetch = qFetchToRGBA32F[data->format];
579 ConvertAndStorePixelsFuncFP store = qStoreFromRGBA32F[dst_format];
580 if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
581 destLayout->hasAlphaChannel && !destLayout->premultiplied) {
582 // Avoid unnecessary premultiply and unpremultiply when converting between two unpremultiplied formats.
583 fetch = qFetchToRGBA32F[qt_toPremultipliedFormat(format: data->format)];
584 store = qStoreFromRGBA32F[qt_toPremultipliedFormat(format: dst_format)];
585 }
586
587 auto convertSegment = [=](int yStart, int yEnd) {
588 QRgbaFloat32 buf[BufferSize];
589 QRgbaFloat32 *buffer = buf;
590 uchar *srcData = data->data + yStart * data->bytes_per_line;
591 uchar *destData = srcData;
592 for (int y = yStart; y < yEnd; ++y) {
593 int x = 0;
594 while (x < data->width) {
595 int l = data->width - x;
596 if (srcLayout->bpp == QPixelLayout::BPP32FPx4)
597 buffer = reinterpret_cast<QRgbaFloat32 *>(srcData) + x;
598 else
599 l = qMin(a: l, b: BufferSize);
600 const QRgbaFloat32 *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr);
601 store(destData, ptr, x, l, nullptr, nullptr);
602 x += l;
603 }
604 srcData += data->bytes_per_line;
605 destData += params.bytesPerLine;
606 }
607 };
608#ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
609 int segments = (qsizetype(data->width) * data->height) >> 16;
610 segments = std::min(a: segments, b: data->height);
611 QThreadPool *threadPool = QThreadPoolPrivate::qtGuiInstance();
612 if (segments > 1 && threadPool && !threadPool->contains(thread: QThread::currentThread())) {
613 QSemaphore semaphore;
614 int y = 0;
615 for (int i = 0; i < segments; ++i) {
616 int yn = (data->height - y) / (segments - i);
617 threadPool->start(functionToRun: [&, y, yn]() {
618 convertSegment(y, y + yn);
619 semaphore.release(n: 1);
620 });
621 y += yn;
622 }
623 semaphore.acquire(n: segments);
624 if (data->bytes_per_line != params.bytesPerLine) {
625 // Compress segments to a continuous block
626 y = 0;
627 for (int i = 0; i < segments; ++i) {
628 int yn = (data->height - y) / (segments - i);
629 uchar *srcData = data->data + data->bytes_per_line * y;
630 uchar *destData = data->data + params.bytesPerLine * y;
631 if (srcData != destData)
632 memmove(dest: destData, src: srcData, n: params.bytesPerLine * yn);
633 y += yn;
634 }
635 }
636 } else
637#endif
638 convertSegment(0, data->height);
639 if (params.totalSize != data->nbytes) {
640 Q_ASSERT(params.totalSize < data->nbytes);
641 void *newData = realloc(ptr: data->data, size: params.totalSize);
642 if (newData) {
643 data->data = (uchar *)newData;
644 data->nbytes = params.totalSize;
645 }
646 data->bytes_per_line = params.bytesPerLine;
647 }
648 data->depth = destDepth;
649 data->format = dst_format;
650 return true;
651}
652#endif
653
654static void convert_passthrough(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
655{
656 Q_ASSERT(src->width == dest->width);
657 Q_ASSERT(src->height == dest->height);
658
659 const int src_bpl = src->bytes_per_line;
660 const int dest_bpl = dest->bytes_per_line;
661 const uchar *src_data = src->data;
662 uchar *dest_data = dest->data;
663
664 for (int i = 0; i < src->height; ++i) {
665 memcpy(dest: dest_data, src: src_data, n: src_bpl);
666 src_data += src_bpl;
667 dest_data += dest_bpl;
668 }
669}
670
671template<QImage::Format Format>
672static bool convert_passthrough_inplace(QImageData *data, Qt::ImageConversionFlags)
673{
674 data->format = Format;
675 return true;
676}
677
678Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32(quint32 *dest_data, const uchar *src_data, int len)
679{
680 int pixel = 0;
681 // prolog: align input to 32bit
682 while ((quintptr(src_data) & 0x3) && pixel < len) {
683 *dest_data = 0xff000000 | (src_data[0] << 16) | (src_data[1] << 8) | (src_data[2]);
684 src_data += 3;
685 ++dest_data;
686 ++pixel;
687 }
688
689 // Handle 4 pixels at a time 12 bytes input to 16 bytes output.
690 for (; pixel + 3 < len; pixel += 4) {
691 const quint32_be *src_packed = reinterpret_cast<const quint32_be *>(src_data);
692 const quint32 src1 = src_packed[0];
693 const quint32 src2 = src_packed[1];
694 const quint32 src3 = src_packed[2];
695
696 dest_data[0] = 0xff000000 | (src1 >> 8);
697 dest_data[1] = 0xff000000 | (src1 << 16) | (src2 >> 16);
698 dest_data[2] = 0xff000000 | (src2 << 8) | (src3 >> 24);
699 dest_data[3] = 0xff000000 | src3;
700
701 src_data += 12;
702 dest_data += 4;
703 }
704
705 // epilog: handle left over pixels
706 for (; pixel < len; ++pixel) {
707 *dest_data = 0xff000000 | (src_data[0] << 16) | (src_data[1] << 8) | (src_data[2]);
708 src_data += 3;
709 ++dest_data;
710 }
711}
712
713Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgbx8888(quint32 *dest_data, const uchar *src_data, int len)
714{
715 int pixel = 0;
716 // prolog: align input to 32bit
717 while ((quintptr(src_data) & 0x3) && pixel < len) {
718 *dest_data = ARGB2RGBA(x: 0xff000000 | (src_data[0] << 16) | (src_data[1] << 8) | (src_data[2]));
719 src_data += 3;
720 ++dest_data;
721 ++pixel;
722 }
723
724 // Handle 4 pixels at a time 12 bytes input to 16 bytes output.
725 for (; pixel + 3 < len; pixel += 4) {
726 const quint32 *src_packed = (const quint32 *) src_data;
727 const quint32 src1 = src_packed[0];
728 const quint32 src2 = src_packed[1];
729 const quint32 src3 = src_packed[2];
730
731#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
732 dest_data[0] = 0xff000000 | src1;
733 dest_data[1] = 0xff000000 | (src1 >> 24) | (src2 << 8);
734 dest_data[2] = 0xff000000 | (src2 >> 16) | (src3 << 16);
735 dest_data[3] = 0xff000000 | (src3 >> 8);
736#else
737 dest_data[0] = 0xff | src1;
738 dest_data[1] = 0xff | (src1 << 24) | (src2 >> 8);
739 dest_data[2] = 0xff | (src2 << 16) | (src3 >> 16);
740 dest_data[3] = 0xff | (src3 << 8);
741#endif
742
743 src_data += 12;
744 dest_data += 4;
745 }
746
747 // epilog: handle left over pixels
748 for (; pixel < len; ++pixel) {
749 *dest_data = ARGB2RGBA(x: 0xff000000 | (src_data[0] << 16) | (src_data[1] << 8) | (src_data[2]));
750 src_data += 3;
751 ++dest_data;
752 }
753}
754
755typedef void (QT_FASTCALL *Rgb888ToRgbConverter)(quint32 *dst, const uchar *src, int len);
756
757template <bool rgbx>
758static void convert_RGB888_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
759{
760 Q_ASSERT(src->format == QImage::Format_RGB888 || src->format == QImage::Format_BGR888);
761 if (rgbx ^ (src->format == QImage::Format_BGR888))
762 Q_ASSERT(dest->format == QImage::Format_RGBX8888 || dest->format == QImage::Format_RGBA8888 || dest->format == QImage::Format_RGBA8888_Premultiplied);
763 else
764 Q_ASSERT(dest->format == QImage::Format_RGB32 || dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied);
765 Q_ASSERT(src->width == dest->width);
766 Q_ASSERT(src->height == dest->height);
767
768 const uchar *src_data = (uchar *) src->data;
769 quint32 *dest_data = (quint32 *) dest->data;
770
771 Rgb888ToRgbConverter line_converter= rgbx ? qt_convert_rgb888_to_rgbx8888 : qt_convert_rgb888_to_rgb32;
772
773 for (int i = 0; i < src->height; ++i) {
774 line_converter(dest_data, src_data, src->width);
775 src_data += src->bytes_per_line;
776 dest_data = (quint32 *)((uchar*)dest_data + dest->bytes_per_line);
777 }
778}
779
780static void convert_ARGB_to_RGBx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
781{
782 Q_ASSERT(src->format == QImage::Format_ARGB32);
783 Q_ASSERT(dest->format == QImage::Format_RGBX8888);
784 Q_ASSERT(src->width == dest->width);
785 Q_ASSERT(src->height == dest->height);
786
787 const int src_pad = (src->bytes_per_line >> 2) - src->width;
788 const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
789 const quint32 *src_data = (quint32 *) src->data;
790 quint32 *dest_data = (quint32 *) dest->data;
791
792 for (int i = 0; i < src->height; ++i) {
793 const quint32 *end = src_data + src->width;
794 while (src_data < end) {
795 *dest_data = ARGB2RGBA(x: 0xff000000 | *src_data);
796 ++src_data;
797 ++dest_data;
798 }
799 src_data += src_pad;
800 dest_data += dest_pad;
801 }
802}
803
804static void convert_ARGB_to_RGBA(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
805{
806 Q_ASSERT(src->format == QImage::Format_ARGB32 || src->format == QImage::Format_ARGB32_Premultiplied);
807 Q_ASSERT(dest->format == QImage::Format_RGBA8888 || dest->format == QImage::Format_RGBA8888_Premultiplied);
808 Q_ASSERT(src->width == dest->width);
809 Q_ASSERT(src->height == dest->height);
810
811 const int src_pad = (src->bytes_per_line >> 2) - src->width;
812 const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
813 const quint32 *src_data = (quint32 *) src->data;
814 quint32 *dest_data = (quint32 *) dest->data;
815
816 for (int i = 0; i < src->height; ++i) {
817 const quint32 *end = src_data + src->width;
818 while (src_data < end) {
819 *dest_data = ARGB2RGBA(x: *src_data);
820 ++src_data;
821 ++dest_data;
822 }
823 src_data += src_pad;
824 dest_data += dest_pad;
825 }
826}
827
828template<QImage::Format DestFormat>
829static bool convert_ARGB_to_RGBA_inplace(QImageData *data, Qt::ImageConversionFlags)
830{
831 Q_ASSERT(data->format == QImage::Format_ARGB32 || data->format == QImage::Format_ARGB32_Premultiplied);
832
833 const int pad = (data->bytes_per_line >> 2) - data->width;
834 quint32 *rgb_data = (quint32 *) data->data;
835 constexpr uint mask = (DestFormat == QImage::Format_RGBX8888) ? 0xff000000 : 0;
836
837 for (int i = 0; i < data->height; ++i) {
838 const quint32 *end = rgb_data + data->width;
839 while (rgb_data < end) {
840 *rgb_data = ARGB2RGBA(x: *rgb_data | mask);
841 ++rgb_data;
842 }
843 rgb_data += pad;
844 }
845
846 data->format = DestFormat;
847 return true;
848}
849
850static void convert_RGBA_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
851{
852 Q_ASSERT(src->format == QImage::Format_RGBX8888 || src->format == QImage::Format_RGBA8888 || src->format == QImage::Format_RGBA8888_Premultiplied);
853 Q_ASSERT(dest->format == QImage::Format_ARGB32 || dest->format == QImage::Format_ARGB32_Premultiplied);
854 Q_ASSERT(src->width == dest->width);
855 Q_ASSERT(src->height == dest->height);
856
857 const int src_pad = (src->bytes_per_line >> 2) - src->width;
858 const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
859 const quint32 *src_data = (quint32 *) src->data;
860 quint32 *dest_data = (quint32 *) dest->data;
861
862 for (int i = 0; i < src->height; ++i) {
863 const quint32 *end = src_data + src->width;
864 while (src_data < end) {
865 *dest_data = RGBA2ARGB(x: *src_data);
866 ++src_data;
867 ++dest_data;
868 }
869 src_data += src_pad;
870 dest_data += dest_pad;
871 }
872}
873
874template<QImage::Format DestFormat>
875static bool convert_RGBA_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFlags)
876{
877 Q_ASSERT(data->format == QImage::Format_RGBX8888 || data->format == QImage::Format_RGBA8888 || data->format == QImage::Format_RGBA8888_Premultiplied);
878
879 const int pad = (data->bytes_per_line >> 2) - data->width;
880 QRgb *rgb_data = (QRgb *) data->data;
881 constexpr uint mask = (DestFormat == QImage::Format_RGB32) ? 0xff000000 : 0;
882
883 for (int i = 0; i < data->height; ++i) {
884 const QRgb *end = rgb_data + data->width;
885 while (rgb_data < end) {
886 *rgb_data = mask | RGBA2ARGB(x: *rgb_data);
887 ++rgb_data;
888 }
889 rgb_data += pad;
890 }
891 data->format = DestFormat;
892 return true;
893}
894
895static void convert_rgbswap_generic(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
896{
897 Q_ASSERT(src->width == dest->width);
898 Q_ASSERT(src->height == dest->height);
899
900 const RbSwapFunc func = qPixelLayouts[src->format].rbSwap;
901 Q_ASSERT(func);
902
903 const qsizetype sbpl = src->bytes_per_line;
904 const qsizetype dbpl = dest->bytes_per_line;
905 const uchar *src_data = src->data;
906 uchar *dest_data = dest->data;
907
908 for (int i = 0; i < src->height; ++i) {
909 func(dest_data, src_data, src->width);
910
911 src_data += sbpl;
912 dest_data += dbpl;
913 }
914}
915
916static bool convert_rgbswap_generic_inplace(QImageData *data, Qt::ImageConversionFlags)
917{
918 const RbSwapFunc func = qPixelLayouts[data->format].rbSwap;
919 Q_ASSERT(func);
920
921 const qsizetype bpl = data->bytes_per_line;
922 uchar *line_data = data->data;
923
924 for (int i = 0; i < data->height; ++i) {
925 func(line_data, line_data, data->width);
926 line_data += bpl;
927 }
928
929 switch (data->format) {
930 case QImage::Format_RGB888:
931 data->format = QImage::Format_BGR888;
932 break;
933 case QImage::Format_BGR888:
934 data->format = QImage::Format_RGB888;
935 break;
936 case QImage::Format_BGR30:
937 data->format = QImage::Format_RGB30;
938 break;
939 case QImage::Format_A2BGR30_Premultiplied:
940 data->format = QImage::Format_A2RGB30_Premultiplied;
941 break;
942 case QImage::Format_RGB30:
943 data->format = QImage::Format_BGR30;
944 break;
945 case QImage::Format_A2RGB30_Premultiplied:
946 data->format = QImage::Format_A2BGR30_Premultiplied;
947 break;
948 default:
949 Q_UNREACHABLE();
950 data->format = QImage::Format_Invalid;
951 return false;
952 }
953 return true;
954}
955
956template<QtPixelOrder PixelOrder, bool RGBA>
957static void convert_ARGB_to_A2RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
958{
959
960 Q_ASSERT(RGBA || src->format == QImage::Format_ARGB32);
961 Q_ASSERT(!RGBA || src->format == QImage::Format_RGBA8888);
962 Q_ASSERT(dest->format == QImage::Format_A2BGR30_Premultiplied
963 || dest->format == QImage::Format_A2RGB30_Premultiplied);
964 Q_ASSERT(src->width == dest->width);
965 Q_ASSERT(src->height == dest->height);
966
967 const int src_pad = (src->bytes_per_line >> 2) - src->width;
968 const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
969 const quint32 *src_data = (quint32 *) src->data;
970 quint32 *dest_data = (quint32 *) dest->data;
971
972 for (int i = 0; i < src->height; ++i) {
973 const quint32 *end = src_data + src->width;
974 while (src_data < end) {
975 QRgb c = *src_data;
976 if (RGBA)
977 c = RGBA2ARGB(x: c);
978 const uint alpha = (qAlpha(rgb: c) >> 6) * 85;
979 c = BYTE_MUL(x: c, a: alpha);
980 *dest_data = (qConvertRgb32ToRgb30<PixelOrder>(c) & 0x3fffffff) | (alpha << 30);
981 ++src_data;
982 ++dest_data;
983 }
984 src_data += src_pad;
985 dest_data += dest_pad;
986 }
987}
988
989template<QtPixelOrder PixelOrder, bool RGBA>
990static bool convert_ARGB_to_A2RGB30_inplace(QImageData *data, Qt::ImageConversionFlags)
991{
992 Q_ASSERT(RGBA || data->format == QImage::Format_ARGB32);
993 Q_ASSERT(!RGBA || data->format == QImage::Format_RGBA8888);
994
995 const int pad = (data->bytes_per_line >> 2) - data->width;
996 QRgb *rgb_data = (QRgb *) data->data;
997
998 for (int i = 0; i < data->height; ++i) {
999 const QRgb *end = rgb_data + data->width;
1000 while (rgb_data < end) {
1001 QRgb c = *rgb_data;
1002 if (RGBA)
1003 c = RGBA2ARGB(x: c);
1004 const uint alpha = (qAlpha(rgb: c) >> 6) * 85;
1005 c = BYTE_MUL(x: c, a: alpha);
1006 *rgb_data = (qConvertRgb32ToRgb30<PixelOrder>(c) & 0x3fffffff) | (alpha << 30);
1007 ++rgb_data;
1008 }
1009 rgb_data += pad;
1010 }
1011
1012 data->format = (PixelOrder == PixelOrderRGB) ? QImage::Format_A2RGB30_Premultiplied
1013 : QImage::Format_A2BGR30_Premultiplied;
1014 return true;
1015}
1016
1017static inline uint qUnpremultiplyRgb30(uint rgb30)
1018{
1019 const uint a = rgb30 >> 30;
1020 switch (a) {
1021 case 0:
1022 return 0;
1023 case 1: {
1024 uint rgb = rgb30 & 0x3fffffff;
1025 rgb *= 3;
1026 return (a << 30) | rgb;
1027 }
1028 case 2: {
1029 uint rgb = rgb30 & 0x3fffffff;
1030 rgb += (rgb >> 1) & 0x5ff7fdff;
1031 return (a << 30) | rgb;
1032 }
1033 case 3:
1034 return rgb30;
1035 }
1036 Q_UNREACHABLE_RETURN(0);
1037}
1038
1039template<bool rgbswap>
1040static void convert_A2RGB30_PM_to_RGB30(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1041{
1042 Q_ASSERT(src->format == QImage::Format_A2RGB30_Premultiplied || src->format == QImage::Format_A2BGR30_Premultiplied);
1043 Q_ASSERT(dest->format == QImage::Format_RGB30 || dest->format == QImage::Format_BGR30);
1044 Q_ASSERT(src->width == dest->width);
1045 Q_ASSERT(src->height == dest->height);
1046
1047 const int src_pad = (src->bytes_per_line >> 2) - src->width;
1048 const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
1049 const quint32 *src_data = (quint32 *) src->data;
1050 quint32 *dest_data = (quint32 *) dest->data;
1051
1052 for (int i = 0; i < src->height; ++i) {
1053 const quint32 *end = src_data + src->width;
1054 while (src_data < end) {
1055 const uint p = 0xc0000000 | qUnpremultiplyRgb30(rgb30: *src_data);
1056 *dest_data = (rgbswap) ? qRgbSwapRgb30(c: p) : p;
1057 ++src_data;
1058 ++dest_data;
1059 }
1060 src_data += src_pad;
1061 dest_data += dest_pad;
1062 }
1063}
1064
1065template<bool rgbswap>
1066static bool convert_A2RGB30_PM_to_RGB30_inplace(QImageData *data, Qt::ImageConversionFlags)
1067{
1068 Q_ASSERT(data->format == QImage::Format_A2RGB30_Premultiplied || data->format == QImage::Format_A2BGR30_Premultiplied);
1069
1070 const int pad = (data->bytes_per_line >> 2) - data->width;
1071 uint *rgb_data = (uint *) data->data;
1072
1073 for (int i = 0; i < data->height; ++i) {
1074 const uint *end = rgb_data + data->width;
1075 while (rgb_data < end) {
1076 const uint p = 0xc0000000 | qUnpremultiplyRgb30(rgb30: *rgb_data);
1077 *rgb_data = (rgbswap) ? qRgbSwapRgb30(c: p) : p;
1078 ++rgb_data;
1079 }
1080 rgb_data += pad;
1081 }
1082
1083 if (data->format == QImage::Format_A2RGB30_Premultiplied)
1084 data->format = (rgbswap) ? QImage::Format_BGR30 : QImage::Format_RGB30;
1085 else
1086 data->format = (rgbswap) ? QImage::Format_RGB30 : QImage::Format_BGR30;
1087 return true;
1088}
1089
1090static bool convert_BGR30_to_A2RGB30_inplace(QImageData *data, Qt::ImageConversionFlags flags)
1091{
1092 Q_ASSERT(data->format == QImage::Format_RGB30 || data->format == QImage::Format_BGR30);
1093 if (!convert_rgbswap_generic_inplace(data, flags))
1094 return false;
1095
1096 if (data->format == QImage::Format_RGB30)
1097 data->format = QImage::Format_A2RGB30_Premultiplied;
1098 else
1099 data->format = QImage::Format_A2BGR30_Premultiplied;
1100 return true;
1101}
1102
1103template<QtPixelOrder PixelOrder, bool RGBA>
1104static void convert_A2RGB30_PM_to_ARGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1105{
1106 Q_ASSERT(src->format == QImage::Format_A2RGB30_Premultiplied || src->format == QImage::Format_A2BGR30_Premultiplied);
1107 Q_ASSERT(RGBA ? dest->format == QImage::Format_RGBA8888 : dest->format == QImage::Format_ARGB32);
1108 Q_ASSERT(src->width == dest->width);
1109 Q_ASSERT(src->height == dest->height);
1110
1111 const int src_pad = (src->bytes_per_line >> 2) - src->width;
1112 const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
1113 const quint32 *src_data = (quint32 *) src->data;
1114 quint32 *dest_data = (quint32 *) dest->data;
1115
1116 for (int i = 0; i < src->height; ++i) {
1117 const quint32 *end = src_data + src->width;
1118 while (src_data < end) {
1119 *dest_data = qConvertA2rgb30ToArgb32<PixelOrder>(qUnpremultiplyRgb30(rgb30: *src_data));
1120 if (RGBA)
1121 *dest_data = ARGB2RGBA(x: *dest_data);
1122 ++src_data;
1123 ++dest_data;
1124 }
1125 src_data += src_pad;
1126 dest_data += dest_pad;
1127 }
1128}
1129
1130template<QtPixelOrder PixelOrder, bool RGBA>
1131static bool convert_A2RGB30_PM_to_ARGB_inplace(QImageData *data, Qt::ImageConversionFlags)
1132{
1133 Q_ASSERT(data->format == QImage::Format_A2RGB30_Premultiplied || data->format == QImage::Format_A2BGR30_Premultiplied);
1134
1135 const int pad = (data->bytes_per_line >> 2) - data->width;
1136 uint *rgb_data = (uint *) data->data;
1137
1138 for (int i = 0; i < data->height; ++i) {
1139 const uint *end = rgb_data + data->width;
1140 while (rgb_data < end) {
1141 *rgb_data = qConvertA2rgb30ToArgb32<PixelOrder>(qUnpremultiplyRgb30(rgb30: *rgb_data));
1142 if (RGBA)
1143 *rgb_data = ARGB2RGBA(x: *rgb_data);
1144 ++rgb_data;
1145 }
1146 rgb_data += pad;
1147 }
1148 if (RGBA)
1149 data->format = QImage::Format_RGBA8888;
1150 else
1151 data->format = QImage::Format_ARGB32;
1152 return true;
1153}
1154
1155static void convert_RGBA_to_RGB(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1156{
1157 Q_ASSERT(src->format == QImage::Format_RGBA8888 || src->format == QImage::Format_RGBX8888);
1158 Q_ASSERT(dest->format == QImage::Format_RGB32);
1159 Q_ASSERT(src->width == dest->width);
1160 Q_ASSERT(src->height == dest->height);
1161
1162 const int src_pad = (src->bytes_per_line >> 2) - src->width;
1163 const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
1164 const uint *src_data = (const uint *)src->data;
1165 uint *dest_data = (uint *)dest->data;
1166
1167 for (int i = 0; i < src->height; ++i) {
1168 const uint *end = src_data + src->width;
1169 while (src_data < end) {
1170 *dest_data = RGBA2ARGB(x: *src_data) | 0xff000000;
1171 ++src_data;
1172 ++dest_data;
1173 }
1174 src_data += src_pad;
1175 dest_data += dest_pad;
1176 }
1177}
1178
1179static void swap_bit_order(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1180{
1181 Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
1182 Q_ASSERT(dest->format == QImage::Format_Mono || dest->format == QImage::Format_MonoLSB);
1183 Q_ASSERT(src->width == dest->width);
1184 Q_ASSERT(src->height == dest->height);
1185 Q_ASSERT(src->nbytes == dest->nbytes);
1186 Q_ASSERT(src->bytes_per_line == dest->bytes_per_line);
1187
1188 dest->colortable = src->colortable;
1189
1190 const uchar *src_data = src->data;
1191 const uchar *end = src->data + src->nbytes;
1192 uchar *dest_data = dest->data;
1193 while (src_data < end) {
1194 *dest_data = bitflip[*src_data];
1195 ++src_data;
1196 ++dest_data;
1197 }
1198}
1199
1200static void mask_alpha_converter(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1201{
1202 Q_ASSERT(src->width == dest->width);
1203 Q_ASSERT(src->height == dest->height);
1204
1205 const int src_pad = (src->bytes_per_line >> 2) - src->width;
1206 const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
1207 const uint *src_data = (const uint *)src->data;
1208 uint *dest_data = (uint *)dest->data;
1209
1210 for (int i = 0; i < src->height; ++i) {
1211 const uint *end = src_data + src->width;
1212 while (src_data < end) {
1213 *dest_data = *src_data | 0xff000000;
1214 ++src_data;
1215 ++dest_data;
1216 }
1217 src_data += src_pad;
1218 dest_data += dest_pad;
1219 }
1220}
1221
1222template<QImage::Format DestFormat>
1223static bool mask_alpha_converter_inplace(QImageData *data, Qt::ImageConversionFlags)
1224{
1225 Q_ASSERT(data->format == QImage::Format_RGB32
1226 || DestFormat == QImage::Format_RGB32
1227 || DestFormat == QImage::Format_RGBX8888);
1228 const int pad = (data->bytes_per_line >> 2) - data->width;
1229 QRgb *rgb_data = (QRgb *) data->data;
1230
1231 for (int i = 0; i < data->height; ++i) {
1232 const QRgb *end = rgb_data + data->width;
1233 while (rgb_data < end) {
1234 *rgb_data = *rgb_data | 0xff000000;
1235 ++rgb_data;
1236 }
1237 rgb_data += pad;
1238 }
1239 data->format = DestFormat;
1240 return true;
1241}
1242
1243static void mask_alpha_converter_RGBx(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags flags)
1244{
1245#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
1246 return mask_alpha_converter(dest, src, flags);
1247#else
1248 Q_UNUSED(flags);
1249 Q_ASSERT(src->width == dest->width);
1250 Q_ASSERT(src->height == dest->height);
1251
1252 const int src_pad = (src->bytes_per_line >> 2) - src->width;
1253 const int dest_pad = (dest->bytes_per_line >> 2) - dest->width;
1254 const uint *src_data = (const uint *)src->data;
1255 uint *dest_data = (uint *)dest->data;
1256
1257 for (int i = 0; i < src->height; ++i) {
1258 const uint *end = src_data + src->width;
1259 while (src_data < end) {
1260 *dest_data = *src_data | 0x000000ff;
1261 ++src_data;
1262 ++dest_data;
1263 }
1264 src_data += src_pad;
1265 dest_data += dest_pad;
1266 }
1267#endif
1268}
1269
1270static bool mask_alpha_converter_rgbx_inplace(QImageData *data, Qt::ImageConversionFlags flags)
1271{
1272#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
1273 return mask_alpha_converter_inplace<QImage::Format_RGBX8888>(data, flags);
1274#else
1275 Q_UNUSED(flags);
1276
1277 const int pad = (data->bytes_per_line >> 2) - data->width;
1278 QRgb *rgb_data = (QRgb *) data->data;
1279
1280 for (int i = 0; i < data->height; ++i) {
1281 const QRgb *end = rgb_data + data->width;
1282 while (rgb_data < end) {
1283 *rgb_data = *rgb_data | 0x000000fff;
1284 ++rgb_data;
1285 }
1286 rgb_data += pad;
1287 }
1288 data->format = QImage::Format_RGBX8888;
1289 return true;
1290#endif
1291}
1292
1293template<bool RGBA>
1294static void convert_RGBA64_to_ARGB32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1295{
1296 Q_ASSERT(src->format == QImage::Format_RGBA64);
1297 Q_ASSERT(RGBA || dest->format == QImage::Format_ARGB32);
1298 Q_ASSERT(!RGBA || dest->format == QImage::Format_RGBA8888);
1299 Q_ASSERT(src->width == dest->width);
1300 Q_ASSERT(src->height == dest->height);
1301
1302 const uchar *srcData = src->data;
1303 uchar *destData = dest->data;
1304
1305 for (int i = 0; i < src->height; ++i) {
1306 uint *d = reinterpret_cast<uint *>(destData);
1307 const QRgba64 *s = reinterpret_cast<const QRgba64 *>(srcData);
1308 qt_convertRGBA64ToARGB32<RGBA>(d, s, src->width);
1309 srcData += src->bytes_per_line;
1310 destData += dest->bytes_per_line;
1311 }
1312}
1313
1314template<bool RGBA>
1315static void convert_ARGB32_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1316{
1317 Q_ASSERT(RGBA || src->format == QImage::Format_ARGB32);
1318 Q_ASSERT(!RGBA || src->format == QImage::Format_RGBA8888);
1319 Q_ASSERT(dest->format == QImage::Format_RGBA64);
1320 Q_ASSERT(src->width == dest->width);
1321 Q_ASSERT(src->height == dest->height);
1322
1323 const uchar *src_data = src->data;
1324 uchar *dest_data = dest->data;
1325 const FetchAndConvertPixelsFunc64 fetch = qPixelLayouts[qt_toPremultipliedFormat(format: src->format)].fetchToRGBA64PM;
1326
1327 for (int i = 0; i < src->height; ++i) {
1328 fetch(reinterpret_cast<QRgba64 *>(dest_data), src_data, 0, src->width, nullptr, nullptr);
1329 src_data += src->bytes_per_line;
1330 dest_data += dest->bytes_per_line;
1331 }
1332}
1333
1334static void convert_RGBA64_to_RGBx64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1335{
1336 Q_ASSERT(src->format == QImage::Format_RGBA64);
1337 Q_ASSERT(dest->format == QImage::Format_RGBX64);
1338 Q_ASSERT(src->width == dest->width);
1339 Q_ASSERT(src->height == dest->height);
1340
1341 const int src_pad = (src->bytes_per_line >> 3) - src->width;
1342 const int dest_pad = (dest->bytes_per_line >> 3) - dest->width;
1343 const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data);
1344 QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data);
1345
1346 for (int i = 0; i < src->height; ++i) {
1347 const QRgba64 *end = src_data + src->width;
1348 while (src_data < end) {
1349 *dest_data = *src_data;
1350 dest_data->setAlpha(65535);
1351 ++src_data;
1352 ++dest_data;
1353 }
1354 src_data += src_pad;
1355 dest_data += dest_pad;
1356 }
1357}
1358
1359static bool convert_RGBA64_to_RGBx64_inplace(QImageData *data, Qt::ImageConversionFlags)
1360{
1361 Q_ASSERT(data->format == QImage::Format_RGBA64);
1362
1363 const int pad = (data->bytes_per_line >> 3) - data->width;
1364 QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data);
1365
1366 for (int i = 0; i < data->height; ++i) {
1367 const QRgba64 *end = rgb_data + data->width;
1368 while (rgb_data < end) {
1369 rgb_data->setAlpha(65535);
1370 ++rgb_data;
1371 }
1372 rgb_data += pad;
1373 }
1374 data->format = QImage::Format_RGBX64;
1375 return true;
1376}
1377
1378static void convert_gray16_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1379{
1380 Q_ASSERT(src->format == QImage::Format_Grayscale16);
1381 Q_ASSERT(dest->format == QImage::Format_RGBA64 || dest->format == QImage::Format_RGBX64 ||
1382 dest->format == QImage::Format_RGBA64_Premultiplied);
1383 Q_ASSERT(src->width == dest->width);
1384 Q_ASSERT(src->height == dest->height);
1385
1386 const qsizetype sbpl = src->bytes_per_line;
1387 const qsizetype dbpl = dest->bytes_per_line;
1388 const uchar *src_data = src->data;
1389 uchar *dest_data = dest->data;
1390
1391 for (int i = 0; i < src->height; ++i) {
1392 const quint16 *src_line = reinterpret_cast<const quint16 *>(src_data);
1393 QRgba64 *dest_line = reinterpret_cast<QRgba64 *>(dest_data);
1394 for (int j = 0; j < src->width; ++j) {
1395 quint16 s = src_line[j];
1396 dest_line[j] = qRgba64(r: s, g: s, b: s, a: 0xFFFF);
1397 }
1398 src_data += sbpl;
1399 dest_data += dbpl;
1400 }
1401}
1402
1403template<bool Premultiplied>
1404static void convert_ARGB_to_gray8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1405{
1406 Q_ASSERT(dest->format == QImage::Format_Grayscale8);
1407 Q_ASSERT(src->format == QImage::Format_RGB32 ||
1408 src->format == QImage::Format_ARGB32 ||
1409 src->format == QImage::Format_ARGB32_Premultiplied);
1410 Q_ASSERT(src->width == dest->width);
1411 Q_ASSERT(src->height == dest->height);
1412
1413 const qsizetype sbpl = src->bytes_per_line;
1414 const qsizetype dbpl = dest->bytes_per_line;
1415 const uchar *src_data = src->data;
1416 uchar *dest_data = dest->data;
1417
1418 QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
1419 QColorTransform tf = QColorSpacePrivate::get(colorSpace&: fromCS)->transformationToXYZ();
1420 const QColorTransformPrivate *tfd = QColorTransformPrivate::get(q: tf);
1421 QColorTransformPrivate::TransformFlags flags = Premultiplied
1422 ? QColorTransformPrivate::InputPremultiplied
1423 : QColorTransformPrivate::Unpremultiplied;
1424
1425 for (int i = 0; i < src->height; ++i) {
1426 const QRgb *src_line = reinterpret_cast<const QRgb *>(src_data);
1427 tfd->apply(dst: dest_data, src: src_line, count: src->width, flags);
1428 src_data += sbpl;
1429 dest_data += dbpl;
1430 }
1431}
1432
1433template<bool Premultiplied>
1434static void convert_ARGB_to_gray16(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1435{
1436 Q_ASSERT(dest->format == QImage::Format_Grayscale16);
1437 Q_ASSERT(src->format == QImage::Format_RGB32 ||
1438 src->format == QImage::Format_ARGB32 ||
1439 src->format == QImage::Format_ARGB32_Premultiplied);
1440 Q_ASSERT(src->width == dest->width);
1441 Q_ASSERT(src->height == dest->height);
1442
1443 const qsizetype sbpl = src->bytes_per_line;
1444 const qsizetype dbpl = dest->bytes_per_line;
1445 const uchar *src_data = src->data;
1446 uchar *dest_data = dest->data;
1447
1448 QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
1449 QColorTransform tf = QColorSpacePrivate::get(colorSpace&: fromCS)->transformationToXYZ();
1450 const QColorTransformPrivate *tfd = QColorTransformPrivate::get(q: tf);
1451 QColorTransformPrivate::TransformFlags flags = Premultiplied
1452 ? QColorTransformPrivate::InputPremultiplied
1453 : QColorTransformPrivate::Unpremultiplied;
1454
1455 QRgba64 tmp_line[BufferSize];
1456 for (int i = 0; i < src->height; ++i) {
1457 const QRgb *src_line = reinterpret_cast<const QRgb *>(src_data);
1458 quint16 *dest_line = reinterpret_cast<quint16 *>(dest_data);
1459 int j = 0;
1460 while (j < src->width) {
1461 const int len = std::min(a: src->width - j, b: BufferSize);
1462 for (int k = 0; k < len; ++k)
1463 tmp_line[k] = QRgba64::fromArgb32(rgb: src_line[j + k]);
1464 tfd->apply(dst: dest_line + j, src: tmp_line, count: len, flags);
1465 j += len;
1466 }
1467 src_data += sbpl;
1468 dest_data += dbpl;
1469 }
1470}
1471
1472template<bool Premultiplied>
1473static void convert_RGBA64_to_gray8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1474{
1475 Q_ASSERT(dest->format == QImage::Format_Grayscale8);
1476 Q_ASSERT(src->format == QImage::Format_RGBX64 ||
1477 src->format == QImage::Format_RGBA64 ||
1478 src->format == QImage::Format_RGBA64_Premultiplied);
1479 Q_ASSERT(src->width == dest->width);
1480 Q_ASSERT(src->height == dest->height);
1481
1482 const qsizetype sbpl = src->bytes_per_line;
1483 const qsizetype dbpl = dest->bytes_per_line;
1484 const uchar *src_data = src->data;
1485 uchar *dest_data = dest->data;
1486
1487 QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
1488 QColorTransform tf = QColorSpacePrivate::get(colorSpace&: fromCS)->transformationToXYZ();
1489 const QColorTransformPrivate *tfd = QColorTransformPrivate::get(q: tf);
1490 QColorTransformPrivate::TransformFlags flags = Premultiplied
1491 ? QColorTransformPrivate::InputPremultiplied
1492 : QColorTransformPrivate::Unpremultiplied;
1493
1494 quint16 gray_line[BufferSize];
1495 for (int i = 0; i < src->height; ++i) {
1496 const QRgba64 *src_line = reinterpret_cast<const QRgba64 *>(src_data);
1497 uchar *dest_line = dest_data;
1498 int j = 0;
1499 while (j < src->width) {
1500 const int len = std::min(a: src->width - j, b: BufferSize);
1501 tfd->apply(dst: gray_line, src: src_line + j, count: len, flags);
1502 for (int k = 0; k < len; ++k)
1503 dest_line[j + k] = qt_div_257(x: gray_line[k]);
1504 j += len;
1505 }
1506 src_data += sbpl;
1507 dest_data += dbpl;
1508 }
1509}
1510
1511template<bool Premultiplied>
1512static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1513{
1514 Q_ASSERT(dest->format == QImage::Format_Grayscale16);
1515 Q_ASSERT(src->format == QImage::Format_RGBX64 ||
1516 src->format == QImage::Format_RGBA64 ||
1517 src->format == QImage::Format_RGBA64_Premultiplied);
1518 Q_ASSERT(src->width == dest->width);
1519 Q_ASSERT(src->height == dest->height);
1520
1521 const qsizetype sbpl = src->bytes_per_line;
1522 const qsizetype dbpl = dest->bytes_per_line;
1523 const uchar *src_data = src->data;
1524 uchar *dest_data = dest->data;
1525
1526 QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
1527 QColorTransform tf = QColorSpacePrivate::get(colorSpace&: fromCS)->transformationToXYZ();
1528 const QColorTransformPrivate *tfd = QColorTransformPrivate::get(q: tf);
1529 QColorTransformPrivate::TransformFlags flags = Premultiplied
1530 ? QColorTransformPrivate::InputPremultiplied
1531 : QColorTransformPrivate::Unpremultiplied;
1532
1533 for (int i = 0; i < src->height; ++i) {
1534 const QRgba64 *src_line = reinterpret_cast<const QRgba64 *>(src_data);
1535 quint16 *dest_line = reinterpret_cast<quint16 *>(dest_data);
1536 tfd->apply(dst: dest_line, src: src_line, count: src->width, flags);
1537 src_data += sbpl;
1538 dest_data += dbpl;
1539 }
1540}
1541
1542template<bool MaskAlpha>
1543static void convert_RGBA16FPM_to_RGBA16F(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1544{
1545 Q_ASSERT(src->format == QImage::Format_RGBA16FPx4_Premultiplied);
1546 Q_ASSERT(dest->format == QImage::Format_RGBA16FPx4 || dest->format == QImage::Format_RGBX16FPx4);
1547 Q_ASSERT(src->width == dest->width);
1548 Q_ASSERT(src->height == dest->height);
1549
1550 const int src_pad = (src->bytes_per_line >> 3) - src->width;
1551 const int dest_pad = (dest->bytes_per_line >> 3) - dest->width;
1552 const QRgbaFloat16 *src_data = reinterpret_cast<const QRgbaFloat16 *>(src->data);
1553 QRgbaFloat16 *dest_data = reinterpret_cast<QRgbaFloat16 *>(dest->data);
1554
1555 for (int i = 0; i < src->height; ++i) {
1556 const QRgbaFloat16 *end = src_data + src->width;
1557 while (src_data < end) {
1558 *dest_data = src_data->unpremultiplied();
1559 if (MaskAlpha)
1560 dest_data->setAlpha(1.0f);
1561 ++src_data;
1562 ++dest_data;
1563 }
1564 src_data += src_pad;
1565 dest_data += dest_pad;
1566 }
1567}
1568
1569template<bool MaskAlpha>
1570static bool convert_RGBA16FPM_to_RGBA16F_inplace(QImageData *data, Qt::ImageConversionFlags)
1571{
1572 Q_ASSERT(data->format == QImage::Format_RGBA16FPx4_Premultiplied);
1573
1574 const int pad = (data->bytes_per_line >> 3) - data->width;
1575 QRgbaFloat16 *rgb_data = reinterpret_cast<QRgbaFloat16 *>(data->data);
1576
1577 for (int i = 0; i < data->height; ++i) {
1578 const QRgbaFloat16 *end = rgb_data + data->width;
1579 while (rgb_data < end) {
1580 *rgb_data = rgb_data->unpremultiplied();
1581 if (MaskAlpha)
1582 rgb_data->setAlpha(1.0f);
1583 ++rgb_data;
1584 }
1585 rgb_data += pad;
1586 }
1587 data->format = MaskAlpha ? QImage::Format_RGBX16FPx4 : QImage::Format_RGBA16FPx4;
1588 return true;
1589}
1590
1591static QList<QRgb> fix_color_table(const QList<QRgb> &ctbl, QImage::Format format)
1592{
1593 QList<QRgb> colorTable = ctbl;
1594 if (format == QImage::Format_RGB32) {
1595 // check if the color table has alpha
1596 for (int i = 0; i < colorTable.size(); ++i)
1597 if (qAlpha(rgb: colorTable.at(i)) != 0xff)
1598 colorTable[i] = colorTable.at(i) | 0xff000000;
1599 } else if (format == QImage::Format_ARGB32_Premultiplied) {
1600 // check if the color table has alpha
1601 for (int i = 0; i < colorTable.size(); ++i)
1602 colorTable[i] = qPremultiply(x: colorTable.at(i));
1603 }
1604 return colorTable;
1605}
1606
1607//
1608// dither_to_1: Uses selected dithering algorithm.
1609//
1610
1611void dither_to_Mono(QImageData *dst, const QImageData *src,
1612 Qt::ImageConversionFlags flags, bool fromalpha)
1613{
1614 Q_ASSERT(src->width == dst->width);
1615 Q_ASSERT(src->height == dst->height);
1616 Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB);
1617
1618 dst->colortable.clear();
1619 dst->colortable.append(t: 0xffffffff);
1620 dst->colortable.append(t: 0xff000000);
1621
1622 enum { Threshold, Ordered, Diffuse } dithermode;
1623
1624 if (fromalpha) {
1625 if ((flags & Qt::AlphaDither_Mask) == Qt::DiffuseAlphaDither)
1626 dithermode = Diffuse;
1627 else if ((flags & Qt::AlphaDither_Mask) == Qt::OrderedAlphaDither)
1628 dithermode = Ordered;
1629 else
1630 dithermode = Threshold;
1631 } else {
1632 if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither)
1633 dithermode = Threshold;
1634 else if ((flags & Qt::Dither_Mask) == Qt::OrderedDither)
1635 dithermode = Ordered;
1636 else
1637 dithermode = Diffuse;
1638 }
1639
1640 int w = src->width;
1641 int h = src->height;
1642 int d = src->depth;
1643 uchar gray[256]; // gray map for 8 bit images
1644 bool use_gray = (d == 8);
1645 if (use_gray) { // make gray map
1646 if (fromalpha) {
1647 // Alpha 0x00 -> 0 pixels (white)
1648 // Alpha 0xFF -> 1 pixels (black)
1649 for (int i = 0; i < src->colortable.size(); i++)
1650 gray[i] = (255 - (src->colortable.at(i) >> 24));
1651 } else {
1652 // Pixel 0x00 -> 1 pixels (black)
1653 // Pixel 0xFF -> 0 pixels (white)
1654 for (int i = 0; i < src->colortable.size(); i++)
1655 gray[i] = qGray(rgb: src->colortable.at(i));
1656 }
1657 }
1658
1659 uchar *dst_data = dst->data;
1660 qsizetype dst_bpl = dst->bytes_per_line;
1661 const uchar *src_data = src->data;
1662 qsizetype src_bpl = src->bytes_per_line;
1663
1664 switch (dithermode) {
1665 case Diffuse: {
1666 QScopedArrayPointer<int> lineBuffer(new int[w * 2]);
1667 int *line1 = lineBuffer.data();
1668 int *line2 = lineBuffer.data() + w;
1669 int bmwidth = (w+7)/8;
1670
1671 int *b1, *b2;
1672 int wbytes = w * (d/8);
1673 const uchar *p = src->data;
1674 const uchar *end = p + wbytes;
1675 b2 = line2;
1676 if (use_gray) { // 8 bit image
1677 while (p < end)
1678 *b2++ = gray[*p++];
1679 } else { // 32 bit image
1680 if (fromalpha) {
1681 while (p < end) {
1682 *b2++ = 255 - (*(const uint*)p >> 24);
1683 p += 4;
1684 }
1685 } else {
1686 while (p < end) {
1687 *b2++ = qGray(rgb: *(const uint*)p);
1688 p += 4;
1689 }
1690 }
1691 }
1692 for (int y=0; y<h; y++) { // for each scan line...
1693 int *tmp = line1; line1 = line2; line2 = tmp;
1694 bool not_last_line = y < h - 1;
1695 if (not_last_line) { // calc. grayvals for next line
1696 p = src->data + (y+1)*src->bytes_per_line;
1697 end = p + wbytes;
1698 b2 = line2;
1699 if (use_gray) { // 8 bit image
1700 while (p < end)
1701 *b2++ = gray[*p++];
1702 } else { // 24 bit image
1703 if (fromalpha) {
1704 while (p < end) {
1705 *b2++ = 255 - (*(const uint*)p >> 24);
1706 p += 4;
1707 }
1708 } else {
1709 while (p < end) {
1710 *b2++ = qGray(rgb: *(const uint*)p);
1711 p += 4;
1712 }
1713 }
1714 }
1715 }
1716
1717 int err;
1718 uchar *p = dst->data + y*dst->bytes_per_line;
1719 memset(s: p, c: 0, n: bmwidth);
1720 b1 = line1;
1721 b2 = line2;
1722 int bit = 7;
1723 for (int x=1; x<=w; x++) {
1724 if (*b1 < 128) { // black pixel
1725 err = *b1++;
1726 *p |= 1 << bit;
1727 } else { // white pixel
1728 err = *b1++ - 255;
1729 }
1730 if (bit == 0) {
1731 p++;
1732 bit = 7;
1733 } else {
1734 bit--;
1735 }
1736 const int e7 = ((err * 7) + 8) >> 4;
1737 const int e5 = ((err * 5) + 8) >> 4;
1738 const int e3 = ((err * 3) + 8) >> 4;
1739 const int e1 = err - (e7 + e5 + e3);
1740 if (x < w)
1741 *b1 += e7; // spread error to right pixel
1742 if (not_last_line) {
1743 b2[0] += e5; // pixel below
1744 if (x > 1)
1745 b2[-1] += e3; // pixel below left
1746 if (x < w)
1747 b2[1] += e1; // pixel below right
1748 }
1749 b2++;
1750 }
1751 }
1752 } break;
1753 case Ordered: {
1754
1755 memset(s: dst->data, c: 0, n: dst->nbytes);
1756 if (d == 32) {
1757 for (int i=0; i<h; i++) {
1758 const uint *p = (const uint *)src_data;
1759 const uint *end = p + w;
1760 uchar *m = dst_data;
1761 int bit = 7;
1762 int j = 0;
1763 if (fromalpha) {
1764 while (p < end) {
1765 if ((*p++ >> 24) >= qt_bayer_matrix[j++&15][i&15])
1766 *m |= 1 << bit;
1767 if (bit == 0) {
1768 m++;
1769 bit = 7;
1770 } else {
1771 bit--;
1772 }
1773 }
1774 } else {
1775 while (p < end) {
1776 if ((uint)qGray(rgb: *p++) < qt_bayer_matrix[j++&15][i&15])
1777 *m |= 1 << bit;
1778 if (bit == 0) {
1779 m++;
1780 bit = 7;
1781 } else {
1782 bit--;
1783 }
1784 }
1785 }
1786 dst_data += dst_bpl;
1787 src_data += src_bpl;
1788 }
1789 } else if (d == 8) {
1790 for (int i=0; i<h; i++) {
1791 const uchar *p = src_data;
1792 const uchar *end = p + w;
1793 uchar *m = dst_data;
1794 int bit = 7;
1795 int j = 0;
1796 while (p < end) {
1797 if ((uint)gray[*p++] < qt_bayer_matrix[j++&15][i&15])
1798 *m |= 1 << bit;
1799 if (bit == 0) {
1800 m++;
1801 bit = 7;
1802 } else {
1803 bit--;
1804 }
1805 }
1806 dst_data += dst_bpl;
1807 src_data += src_bpl;
1808 }
1809 }
1810 } break;
1811 default: { // Threshold:
1812 memset(s: dst->data, c: 0, n: dst->nbytes);
1813 if (d == 32) {
1814 for (int i=0; i<h; i++) {
1815 const uint *p = (const uint *)src_data;
1816 const uint *end = p + w;
1817 uchar *m = dst_data;
1818 int bit = 7;
1819 if (fromalpha) {
1820 while (p < end) {
1821 if ((*p++ >> 24) >= 128)
1822 *m |= 1 << bit; // Set mask "on"
1823 if (bit == 0) {
1824 m++;
1825 bit = 7;
1826 } else {
1827 bit--;
1828 }
1829 }
1830 } else {
1831 while (p < end) {
1832 if (qGray(rgb: *p++) < 128)
1833 *m |= 1 << bit; // Set pixel "black"
1834 if (bit == 0) {
1835 m++;
1836 bit = 7;
1837 } else {
1838 bit--;
1839 }
1840 }
1841 }
1842 dst_data += dst_bpl;
1843 src_data += src_bpl;
1844 }
1845 } else
1846 if (d == 8) {
1847 for (int i=0; i<h; i++) {
1848 const uchar *p = src_data;
1849 const uchar *end = p + w;
1850 uchar *m = dst_data;
1851 int bit = 7;
1852 while (p < end) {
1853 if (gray[*p++] < 128)
1854 *m |= 1 << bit; // Set mask "on"/ pixel "black"
1855 if (bit == 0) {
1856 m++;
1857 bit = 7;
1858 } else {
1859 bit--;
1860 }
1861 }
1862 dst_data += dst_bpl;
1863 src_data += src_bpl;
1864 }
1865 }
1866 }
1867 }
1868
1869 if (dst->format == QImage::Format_MonoLSB) {
1870 // need to swap bit order
1871 uchar *sl = dst->data;
1872 int bpl = (dst->width + 7) * dst->depth / 8;
1873 int pad = dst->bytes_per_line - bpl;
1874 for (int y=0; y<dst->height; ++y) {
1875 for (int x=0; x<bpl; ++x) {
1876 *sl = bitflip[*sl];
1877 ++sl;
1878 }
1879 sl += pad;
1880 }
1881 }
1882}
1883
1884static void convert_X_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1885{
1886 dither_to_Mono(dst, src, flags, fromalpha: false);
1887}
1888
1889static void convert_ARGB_PM_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1890{
1891 QScopedPointer<QImageData> tmp(QImageData::create(size: QSize(src->width, src->height), format: QImage::Format_ARGB32));
1892 convert_generic(dest: tmp.data(), src, flags: Qt::AutoColor);
1893 dither_to_Mono(dst, src: tmp.data(), flags, fromalpha: false);
1894}
1895
1896//
1897// convert_32_to_8: Converts a 32 bits depth (true color) to an 8 bit
1898// image with a colormap. If the 32 bit image has more than 256 colors,
1899// we convert the red,green and blue bytes into a single byte encoded
1900// as 6 shades of each of red, green and blue.
1901//
1902// if dithering is needed, only 1 color at most is available for alpha.
1903//
1904struct QRgbMap {
1905 inline QRgbMap() : used(0) { }
1906 uchar pix;
1907 uchar used;
1908 QRgb rgb;
1909};
1910
1911static void convert_RGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1912{
1913 Q_ASSERT(src->format == QImage::Format_RGB32 || src->format == QImage::Format_ARGB32);
1914 Q_ASSERT(dst->format == QImage::Format_Indexed8);
1915 Q_ASSERT(src->width == dst->width);
1916 Q_ASSERT(src->height == dst->height);
1917
1918 bool do_quant = (flags & Qt::DitherMode_Mask) == Qt::PreferDither
1919 || src->format == QImage::Format_ARGB32;
1920 uint alpha_mask = src->format == QImage::Format_RGB32 ? 0xff000000 : 0;
1921
1922 const int tablesize = 997; // prime
1923 QRgbMap table[tablesize];
1924 int pix=0;
1925
1926 if (!dst->colortable.isEmpty()) {
1927 QList<QRgb> ctbl = dst->colortable;
1928 dst->colortable.resize(size: 256);
1929 // Preload palette into table.
1930 // Almost same code as pixel insertion below
1931 for (int i = 0; i < dst->colortable.size(); ++i) {
1932 // Find in table...
1933 QRgb p = ctbl.at(i) | alpha_mask;
1934 int hash = p % tablesize;
1935 for (;;) {
1936 if (table[hash].used) {
1937 if (table[hash].rgb == p) {
1938 // Found previous insertion - use it
1939 break;
1940 } else {
1941 // Keep searching...
1942 if (++hash == tablesize) hash = 0;
1943 }
1944 } else {
1945 // Cannot be in table
1946 Q_ASSERT (pix != 256); // too many colors
1947 // Insert into table at this unused position
1948 dst->colortable[pix] = p;
1949 table[hash].pix = pix++;
1950 table[hash].rgb = p;
1951 table[hash].used = 1;
1952 break;
1953 }
1954 }
1955 }
1956 }
1957
1958 if ((flags & Qt::DitherMode_Mask) != Qt::PreferDither) {
1959 dst->colortable.resize(size: 256);
1960 const uchar *src_data = src->data;
1961 uchar *dest_data = dst->data;
1962 for (int y = 0; y < src->height; y++) { // check if <= 256 colors
1963 const QRgb *s = (const QRgb *)src_data;
1964 uchar *b = dest_data;
1965 for (int x = 0; x < src->width; ++x) {
1966 QRgb p = s[x] | alpha_mask;
1967 int hash = p % tablesize;
1968 for (;;) {
1969 if (table[hash].used) {
1970 if (table[hash].rgb == (p)) {
1971 // Found previous insertion - use it
1972 break;
1973 } else {
1974 // Keep searching...
1975 if (++hash == tablesize) hash = 0;
1976 }
1977 } else {
1978 // Cannot be in table
1979 if (pix == 256) { // too many colors
1980 do_quant = true;
1981 // Break right out
1982 x = src->width;
1983 y = src->height;
1984 } else {
1985 // Insert into table at this unused position
1986 dst->colortable[pix] = p;
1987 table[hash].pix = pix++;
1988 table[hash].rgb = p;
1989 table[hash].used = 1;
1990 }
1991 break;
1992 }
1993 }
1994 *b++ = table[hash].pix; // May occur once incorrectly
1995 }
1996 src_data += src->bytes_per_line;
1997 dest_data += dst->bytes_per_line;
1998 }
1999 }
2000 int numColors = do_quant ? 256 : pix;
2001
2002 dst->colortable.resize(size: numColors);
2003
2004 if (do_quant) { // quantization needed
2005
2006#define MAX_R 5
2007#define MAX_G 5
2008#define MAX_B 5
2009#define INDEXOF(r,g,b) (((r)*(MAX_G+1)+(g))*(MAX_B+1)+(b))
2010
2011 for (int rc=0; rc<=MAX_R; rc++) // build 6x6x6 color cube
2012 for (int gc=0; gc<=MAX_G; gc++)
2013 for (int bc=0; bc<=MAX_B; bc++)
2014 dst->colortable[INDEXOF(rc,gc,bc)] = 0xff000000 | qRgb(r: rc*255/MAX_R, g: gc*255/MAX_G, b: bc*255/MAX_B);
2015
2016 const uchar *src_data = src->data;
2017 uchar *dest_data = dst->data;
2018 if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither) {
2019 for (int y = 0; y < src->height; y++) {
2020 const QRgb *p = (const QRgb *)src_data;
2021 const QRgb *end = p + src->width;
2022 uchar *b = dest_data;
2023
2024 while (p < end) {
2025#define DITHER(p,m) ((uchar) ((p * (m) + 127) / 255))
2026 *b++ =
2027 INDEXOF(
2028 DITHER(qRed(*p), MAX_R),
2029 DITHER(qGreen(*p), MAX_G),
2030 DITHER(qBlue(*p), MAX_B)
2031 );
2032#undef DITHER
2033 p++;
2034 }
2035 src_data += src->bytes_per_line;
2036 dest_data += dst->bytes_per_line;
2037 }
2038 } else if ((flags & Qt::Dither_Mask) == Qt::DiffuseDither) {
2039 int* line1[3];
2040 int* line2[3];
2041 int* pv[3];
2042 QScopedArrayPointer<int> lineBuffer(new int[src->width * 9]);
2043 line1[0] = lineBuffer.data();
2044 line2[0] = lineBuffer.data() + src->width;
2045 line1[1] = lineBuffer.data() + src->width * 2;
2046 line2[1] = lineBuffer.data() + src->width * 3;
2047 line1[2] = lineBuffer.data() + src->width * 4;
2048 line2[2] = lineBuffer.data() + src->width * 5;
2049 pv[0] = lineBuffer.data() + src->width * 6;
2050 pv[1] = lineBuffer.data() + src->width * 7;
2051 pv[2] = lineBuffer.data() + src->width * 8;
2052
2053 int endian = (QSysInfo::ByteOrder == QSysInfo::BigEndian);
2054 for (int y = 0; y < src->height; y++) {
2055 const uchar* q = src_data;
2056 const uchar* q2 = y < src->height - 1 ? q + src->bytes_per_line : src->data;
2057 uchar *b = dest_data;
2058 for (int chan = 0; chan < 3; chan++) {
2059 int *l1 = (y&1) ? line2[chan] : line1[chan];
2060 int *l2 = (y&1) ? line1[chan] : line2[chan];
2061 if (y == 0) {
2062 for (int i = 0; i < src->width; i++)
2063 l1[i] = q[i*4+chan+endian];
2064 }
2065 if (y+1 < src->height) {
2066 for (int i = 0; i < src->width; i++)
2067 l2[i] = q2[i*4+chan+endian];
2068 }
2069 // Bi-directional error diffusion
2070 if (y&1) {
2071 for (int x = 0; x < src->width; x++) {
2072 int pix = qMax(a: qMin(a: 5, b: (l1[x] * 5 + 128)/ 255), b: 0);
2073 int err = l1[x] - pix * 255 / 5;
2074 pv[chan][x] = pix;
2075
2076 // Spread the error around...
2077 if (x + 1< src->width) {
2078 l1[x+1] += (err*7)>>4;
2079 l2[x+1] += err>>4;
2080 }
2081 l2[x]+=(err*5)>>4;
2082 if (x>1)
2083 l2[x-1]+=(err*3)>>4;
2084 }
2085 } else {
2086 for (int x = src->width; x-- > 0;) {
2087 int pix = qMax(a: qMin(a: 5, b: (l1[x] * 5 + 128)/ 255), b: 0);
2088 int err = l1[x] - pix * 255 / 5;
2089 pv[chan][x] = pix;
2090
2091 // Spread the error around...
2092 if (x > 0) {
2093 l1[x-1] += (err*7)>>4;
2094 l2[x-1] += err>>4;
2095 }
2096 l2[x]+=(err*5)>>4;
2097 if (x + 1 < src->width)
2098 l2[x+1]+=(err*3)>>4;
2099 }
2100 }
2101 }
2102 if (endian) {
2103 for (int x = 0; x < src->width; x++) {
2104 *b++ = INDEXOF(pv[0][x],pv[1][x],pv[2][x]);
2105 }
2106 } else {
2107 for (int x = 0; x < src->width; x++) {
2108 *b++ = INDEXOF(pv[2][x],pv[1][x],pv[0][x]);
2109 }
2110 }
2111 src_data += src->bytes_per_line;
2112 dest_data += dst->bytes_per_line;
2113 }
2114 } else { // OrderedDither
2115 for (int y = 0; y < src->height; y++) {
2116 const QRgb *p = (const QRgb *)src_data;
2117 const QRgb *end = p + src->width;
2118 uchar *b = dest_data;
2119
2120 int x = 0;
2121 while (p < end) {
2122 uint d = qt_bayer_matrix[y & 15][x & 15] << 8;
2123
2124#define DITHER(p, d, m) ((uchar) ((((256 * (m) + (m) + 1)) * (p) + (d)) >> 16))
2125 *b++ =
2126 INDEXOF(
2127 DITHER(qRed(*p), d, MAX_R),
2128 DITHER(qGreen(*p), d, MAX_G),
2129 DITHER(qBlue(*p), d, MAX_B)
2130 );
2131#undef DITHER
2132
2133 p++;
2134 x++;
2135 }
2136 src_data += src->bytes_per_line;
2137 dest_data += dst->bytes_per_line;
2138 }
2139 }
2140
2141 if (src->format != QImage::Format_RGB32
2142 && src->format != QImage::Format_RGB16) {
2143 const int trans = 216;
2144 Q_ASSERT(dst->colortable.size() > trans);
2145 dst->colortable[trans] = 0;
2146 QScopedPointer<QImageData> mask(QImageData::create(size: QSize(src->width, src->height), format: QImage::Format_Mono));
2147 dither_to_Mono(dst: mask.data(), src, flags, fromalpha: true);
2148 uchar *dst_data = dst->data;
2149 const uchar *mask_data = mask->data;
2150 for (int y = 0; y < src->height; y++) {
2151 for (int x = 0; x < src->width ; x++) {
2152 if (!(mask_data[x>>3] & (0x80 >> (x & 7))))
2153 dst_data[x] = trans;
2154 }
2155 mask_data += mask->bytes_per_line;
2156 dst_data += dst->bytes_per_line;
2157 }
2158 dst->has_alpha_clut = true;
2159 }
2160
2161#undef MAX_R
2162#undef MAX_G
2163#undef MAX_B
2164#undef INDEXOF
2165
2166 }
2167}
2168
2169static void convert_ARGB_PM_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
2170{
2171 QScopedPointer<QImageData> tmp(QImageData::create(size: QSize(src->width, src->height), format: QImage::Format_ARGB32));
2172 convert_generic(dest: tmp.data(), src, flags: Qt::AutoColor);
2173 convert_RGB_to_Indexed8(dst, src: tmp.data(), flags);
2174}
2175
2176static void convert_ARGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
2177{
2178 convert_RGB_to_Indexed8(dst, src, flags);
2179}
2180
2181static void convert_Indexed8_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2182{
2183 Q_ASSERT(src->format == QImage::Format_Indexed8);
2184 Q_ASSERT(dest->format == QImage::Format_RGB32
2185 || dest->format == QImage::Format_ARGB32
2186 || dest->format == QImage::Format_ARGB32_Premultiplied);
2187 Q_ASSERT(src->width == dest->width);
2188 Q_ASSERT(src->height == dest->height);
2189
2190 QList<QRgb> colorTable = src->has_alpha_clut ? fix_color_table(ctbl: src->colortable, format: dest->format) : src->colortable;
2191 if (colorTable.size() == 0) {
2192 colorTable.resize(size: 256);
2193 for (int i=0; i<256; ++i)
2194 colorTable[i] = qRgb(r: i, g: i, b: i);
2195 }
2196 if (colorTable.size() < 256) {
2197 int tableSize = colorTable.size();
2198 colorTable.resize(size: 256);
2199 QRgb fallbackColor = (dest->format == QImage::Format_RGB32) ? 0xff000000 : 0;
2200 for (int i=tableSize; i<256; ++i)
2201 colorTable[i] = fallbackColor;
2202 }
2203
2204 int w = src->width;
2205 const uchar *src_data = src->data;
2206 uchar *dest_data = dest->data;
2207 const QRgb *colorTablePtr = colorTable.constData();
2208 for (int y = 0; y < src->height; y++) {
2209 uint *p = reinterpret_cast<uint *>(dest_data);
2210 const uchar *b = src_data;
2211 uint *end = p + w;
2212
2213 while (p < end)
2214 *p++ = colorTablePtr[*b++];
2215
2216 src_data += src->bytes_per_line;
2217 dest_data += dest->bytes_per_line;
2218 }
2219}
2220
2221static void convert_Mono_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2222{
2223 Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
2224 Q_ASSERT(dest->format == QImage::Format_RGB32
2225 || dest->format == QImage::Format_ARGB32
2226 || dest->format == QImage::Format_ARGB32_Premultiplied);
2227 Q_ASSERT(src->width == dest->width);
2228 Q_ASSERT(src->height == dest->height);
2229
2230 QList<QRgb> colorTable = fix_color_table(ctbl: src->colortable, format: dest->format);
2231
2232 // Default to black / white colors
2233 if (colorTable.size() < 2) {
2234 if (colorTable.size() == 0)
2235 colorTable << 0xff000000;
2236 colorTable << 0xffffffff;
2237 }
2238
2239 const uchar *src_data = src->data;
2240 uchar *dest_data = dest->data;
2241 if (src->format == QImage::Format_Mono) {
2242 for (int y = 0; y < dest->height; y++) {
2243 uint *p = (uint *)dest_data;
2244 for (int x = 0; x < dest->width; x++)
2245 *p++ = colorTable.at(i: (src_data[x>>3] >> (7 - (x & 7))) & 1);
2246
2247 src_data += src->bytes_per_line;
2248 dest_data += dest->bytes_per_line;
2249 }
2250 } else {
2251 for (int y = 0; y < dest->height; y++) {
2252 uint *p = (uint *)dest_data;
2253 for (int x = 0; x < dest->width; x++)
2254 *p++ = colorTable.at(i: (src_data[x>>3] >> (x & 7)) & 1);
2255
2256 src_data += src->bytes_per_line;
2257 dest_data += dest->bytes_per_line;
2258 }
2259 }
2260}
2261
2262
2263static void convert_Mono_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2264{
2265 Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
2266 Q_ASSERT(dest->format == QImage::Format_Indexed8);
2267 Q_ASSERT(src->width == dest->width);
2268 Q_ASSERT(src->height == dest->height);
2269
2270 QList<QRgb> ctbl = src->colortable;
2271 if (ctbl.size() > 2) {
2272 ctbl.resize(size: 2);
2273 } else if (ctbl.size() < 2) {
2274 if (ctbl.size() == 0)
2275 ctbl << 0xff000000;
2276 ctbl << 0xffffffff;
2277 }
2278 dest->colortable = ctbl;
2279 dest->has_alpha_clut = src->has_alpha_clut;
2280
2281
2282 const uchar *src_data = src->data;
2283 uchar *dest_data = dest->data;
2284 if (src->format == QImage::Format_Mono) {
2285 for (int y = 0; y < dest->height; y++) {
2286 uchar *p = dest_data;
2287 for (int x = 0; x < dest->width; x++)
2288 *p++ = (src_data[x>>3] >> (7 - (x & 7))) & 1;
2289 src_data += src->bytes_per_line;
2290 dest_data += dest->bytes_per_line;
2291 }
2292 } else {
2293 for (int y = 0; y < dest->height; y++) {
2294 uchar *p = dest_data;
2295 for (int x = 0; x < dest->width; x++)
2296 *p++ = (src_data[x>>3] >> (x & 7)) & 1;
2297 src_data += src->bytes_per_line;
2298 dest_data += dest->bytes_per_line;
2299 }
2300 }
2301}
2302
2303static void copy_8bit_pixels(QImageData *dest, const QImageData *src)
2304{
2305 if (src->bytes_per_line == dest->bytes_per_line) {
2306 memcpy(dest: dest->data, src: src->data, n: src->bytes_per_line * src->height);
2307 } else {
2308 const uchar *sdata = src->data;
2309 uchar *ddata = dest->data;
2310 for (int y = 0; y < src->height; ++y) {
2311 memcpy(dest: ddata, src: sdata, n: src->width);
2312 sdata += src->bytes_per_line;
2313 ddata += dest->bytes_per_line;
2314 }
2315 }
2316}
2317
2318static void convert_Indexed8_to_Alpha8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2319{
2320 Q_ASSERT(src->format == QImage::Format_Indexed8);
2321 Q_ASSERT(dest->format == QImage::Format_Alpha8);
2322
2323 uchar translate[256];
2324 const QList<QRgb> &colors = src->colortable;
2325 bool simpleCase = (colors.size() == 256);
2326 for (int i = 0; i < colors.size(); ++i) {
2327 uchar alpha = qAlpha(rgb: colors[i]);
2328 translate[i] = alpha;
2329 simpleCase = simpleCase && (alpha == i);
2330 }
2331
2332 if (simpleCase)
2333 copy_8bit_pixels(dest, src);
2334 else {
2335 const uchar *sdata = src->data;
2336 uchar *ddata = dest->data;
2337 for (int y = 0; y < src->height; ++y) {
2338 for (int x = 0; x < src->width; ++x)
2339 ddata[x] = translate[sdata[x]];
2340 sdata += src->bytes_per_line;
2341 ddata += dest->bytes_per_line;
2342 }
2343 }
2344}
2345
2346static void convert_Indexed8_to_Grayscale8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2347{
2348 Q_ASSERT(src->format == QImage::Format_Indexed8);
2349 Q_ASSERT(dest->format == QImage::Format_Grayscale8);
2350
2351 uchar translate[256];
2352 const QList<QRgb> &colors = src->colortable;
2353 bool simpleCase = (colors.size() == 256);
2354 for (int i = 0; i < colors.size() && simpleCase; ++i) {
2355 if (colors[i] != qRgb(r: i, g: i, b: i))
2356 simpleCase = false;
2357 }
2358 if (simpleCase) {
2359 copy_8bit_pixels(dest, src);
2360 return;
2361 }
2362
2363 QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
2364 QColorTransform tf = QColorSpacePrivate::get(colorSpace&: fromCS)->transformationToXYZ();
2365 for (int i = 0; i < colors.size(); ++i) {
2366 QRgba64 c16 = tf.map(rgba64: QRgba64::fromArgb32(rgb: colors[i]));
2367 translate[i] = c16.green8(); // Y from XYZ ends up in the G channel
2368 }
2369
2370 const uchar *sdata = src->data;
2371 uchar *ddata = dest->data;
2372 for (int y = 0; y < src->height; ++y) {
2373 for (int x = 0; x < src->width; ++x)
2374 ddata[x] = translate[sdata[x]];
2375 sdata += src->bytes_per_line;
2376 ddata += dest->bytes_per_line;
2377 }
2378}
2379
2380static bool convert_Indexed8_to_Alpha8_inplace(QImageData *data, Qt::ImageConversionFlags)
2381{
2382 Q_ASSERT(data->format == QImage::Format_Indexed8);
2383
2384 // Just check if this is an Alpha8 in Indexed8 disguise.
2385 const QList<QRgb> &colors = data->colortable;
2386 if (colors.size() != 256)
2387 return false;
2388 for (int i = 0; i < colors.size(); ++i) {
2389 if (i != qAlpha(rgb: colors[i]))
2390 return false;
2391 }
2392
2393 data->colortable.clear();
2394 data->format = QImage::Format_Alpha8;
2395
2396 return true;
2397}
2398
2399static bool convert_Indexed8_to_Grayscale8_inplace(QImageData *data, Qt::ImageConversionFlags)
2400{
2401 Q_ASSERT(data->format == QImage::Format_Indexed8);
2402
2403 // Just check if this is a Grayscale8 in Indexed8 disguise.
2404 const QList<QRgb> &colors = data->colortable;
2405 if (colors.size() != 256)
2406 return false;
2407 for (int i = 0; i < colors.size(); ++i) {
2408 if (colors[i] != qRgb(r: i, g: i, b: i))
2409 return false;
2410 }
2411
2412 data->colortable.clear();
2413 data->format = QImage::Format_Grayscale8;
2414
2415 return true;
2416}
2417
2418static void convert_Alpha8_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2419{
2420 Q_ASSERT(src->format == QImage::Format_Alpha8);
2421 Q_ASSERT(dest->format == QImage::Format_Indexed8);
2422
2423 copy_8bit_pixels(dest, src);
2424
2425 dest->colortable = defaultColorTables->alpha;
2426}
2427
2428static void convert_Grayscale8_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2429{
2430 Q_ASSERT(src->format == QImage::Format_Grayscale8);
2431 Q_ASSERT(dest->format == QImage::Format_Indexed8);
2432
2433 copy_8bit_pixels(dest, src);
2434
2435 dest->colortable = defaultColorTables->gray;
2436}
2437
2438static bool convert_Alpha8_to_Indexed8_inplace(QImageData *data, Qt::ImageConversionFlags)
2439{
2440 Q_ASSERT(data->format == QImage::Format_Alpha8);
2441
2442 data->colortable = defaultColorTables->alpha;
2443 data->format = QImage::Format_Indexed8;
2444
2445 return true;
2446}
2447
2448static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageConversionFlags)
2449{
2450 Q_ASSERT(data->format == QImage::Format_Grayscale8);
2451
2452 data->colortable = defaultColorTables->gray;
2453 data->format = QImage::Format_Indexed8;
2454
2455 return true;
2456}
2457
2458template <bool SourceIsPremultiplied>
2459static void convert_ARGB32_to_CMYK8888(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2460{
2461 Q_ASSERT(src->format == QImage::Format_RGB32 ||
2462 src->format == QImage::Format_ARGB32 ||
2463 src->format == QImage::Format_ARGB32_Premultiplied);
2464 Q_ASSERT(dest->format == QImage::Format_CMYK8888);
2465 Q_ASSERT(src->width == dest->width);
2466 Q_ASSERT(src->height == dest->height);
2467
2468 const uchar *src_data = src->data;
2469 uchar *dest_data = dest->data;
2470 for (int y = 0; y < src->height; ++y) {
2471 const QRgb *srcRgba = reinterpret_cast<const QRgb *>(src_data);
2472 uint *destCmyk = reinterpret_cast<uint *>(dest_data);
2473
2474 for (int x = 0; x < src->width; ++x) {
2475 QRgb sourcePixel = srcRgba[x];
2476 if constexpr (SourceIsPremultiplied)
2477 sourcePixel = qUnpremultiply(p: sourcePixel);
2478
2479 destCmyk[x] = QCmyk32::fromRgba(rgba: sourcePixel).toUint();
2480 }
2481
2482 src_data += src->bytes_per_line;;
2483 dest_data += dest->bytes_per_line;
2484 }
2485}
2486
2487// first index source, second dest
2488Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = {};
2489InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] = {};
2490
2491static void qInitImageConversions()
2492{
2493 // Some conversions can not be generic, other are just hard to make as fast in the generic converter.
2494
2495 // All conversions to and from indexed formats can not be generic and needs to go over RGB32 or ARGB32
2496 qimage_converter_map[QImage::Format_Mono][QImage::Format_MonoLSB] = swap_bit_order;
2497 qimage_converter_map[QImage::Format_Mono][QImage::Format_Indexed8] = convert_Mono_to_Indexed8;
2498 qimage_converter_map[QImage::Format_Mono][QImage::Format_RGB32] = convert_Mono_to_X32;
2499 qimage_converter_map[QImage::Format_Mono][QImage::Format_ARGB32] = convert_Mono_to_X32;
2500 qimage_converter_map[QImage::Format_Mono][QImage::Format_ARGB32_Premultiplied] = convert_Mono_to_X32;
2501
2502 qimage_converter_map[QImage::Format_MonoLSB][QImage::Format_Mono] = swap_bit_order;
2503 qimage_converter_map[QImage::Format_MonoLSB][QImage::Format_Indexed8] = convert_Mono_to_Indexed8;
2504 qimage_converter_map[QImage::Format_MonoLSB][QImage::Format_RGB32] = convert_Mono_to_X32;
2505 qimage_converter_map[QImage::Format_MonoLSB][QImage::Format_ARGB32] = convert_Mono_to_X32;
2506 qimage_converter_map[QImage::Format_MonoLSB][QImage::Format_ARGB32_Premultiplied] = convert_Mono_to_X32;
2507
2508 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_Mono] = convert_X_to_Mono;
2509 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_MonoLSB] = convert_X_to_Mono;
2510 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_RGB32] = convert_Indexed8_to_X32;
2511 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_ARGB32] = convert_Indexed8_to_X32;
2512 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_ARGB32_Premultiplied] = convert_Indexed8_to_X32;
2513 // Indexed8, Alpha8 and Grayscale8 have a special relationship that can be short-cut.
2514 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_Grayscale8] = convert_Indexed8_to_Grayscale8;
2515 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_Alpha8] = convert_Indexed8_to_Alpha8;
2516
2517 qimage_converter_map[QImage::Format_RGB32][QImage::Format_Mono] = convert_X_to_Mono;
2518 qimage_converter_map[QImage::Format_RGB32][QImage::Format_MonoLSB] = convert_X_to_Mono;
2519 qimage_converter_map[QImage::Format_RGB32][QImage::Format_Indexed8] = convert_RGB_to_Indexed8;
2520 qimage_converter_map[QImage::Format_RGB32][QImage::Format_ARGB32] = mask_alpha_converter;
2521 qimage_converter_map[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = mask_alpha_converter;
2522 qimage_converter_map[QImage::Format_RGB32][QImage::Format_Grayscale8] = convert_ARGB_to_gray8<false>;
2523 qimage_converter_map[QImage::Format_RGB32][QImage::Format_Grayscale16] = convert_ARGB_to_gray16<false>;
2524
2525 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_Mono] = convert_X_to_Mono;
2526 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_MonoLSB] = convert_X_to_Mono;
2527 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_Indexed8] = convert_ARGB_to_Indexed8;
2528 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_RGB32] = mask_alpha_converter;
2529 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_RGBX8888] = convert_ARGB_to_RGBx;
2530 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_RGBA8888] = convert_ARGB_to_RGBA;
2531 // ARGB32 has higher precision than ARGB32PM and needs explicit conversions to other higher color-precision formats with alpha
2532 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_A2BGR30_Premultiplied] = convert_ARGB_to_A2RGB30<PixelOrderBGR, false>;
2533 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_A2RGB30_Premultiplied] = convert_ARGB_to_A2RGB30<PixelOrderRGB, false>;
2534 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_RGBA64] = convert_ARGB32_to_RGBA64<false>;
2535 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_Grayscale8] = convert_ARGB_to_gray8<false>;
2536 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_Grayscale16] = convert_ARGB_to_gray16<false>;
2537
2538 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_Mono] = convert_ARGB_PM_to_Mono;
2539 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_MonoLSB] = convert_ARGB_PM_to_Mono;
2540 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_Indexed8] = convert_ARGB_PM_to_Indexed8;
2541 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = convert_ARGB_to_RGBA;
2542 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_Grayscale8] = convert_ARGB_to_gray8<true>;
2543 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_Grayscale16] = convert_ARGB_to_gray16<true>;
2544
2545 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB<false>;
2546 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB<false>;
2547 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB<false>;
2548 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGBX8888] = convert_RGB888_to_RGB<true>;
2549 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGBA8888] = convert_RGB888_to_RGB<true>;
2550 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGBA8888_Premultiplied] = convert_RGB888_to_RGB<true>;
2551 qimage_converter_map[QImage::Format_RGB888][QImage::Format_BGR888] = convert_rgbswap_generic;
2552
2553 qimage_converter_map[QImage::Format_RGBX8888][QImage::Format_RGB32] = convert_RGBA_to_RGB;
2554 qimage_converter_map[QImage::Format_RGBX8888][QImage::Format_ARGB32] = convert_RGBA_to_ARGB;
2555 qimage_converter_map[QImage::Format_RGBX8888][QImage::Format_ARGB32_Premultiplied] = convert_RGBA_to_ARGB;
2556 qimage_converter_map[QImage::Format_RGBX8888][QImage::Format_RGBA8888] = convert_passthrough;
2557 qimage_converter_map[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = convert_passthrough;
2558
2559 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_RGB32] = convert_RGBA_to_RGB;
2560 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_ARGB32] = convert_RGBA_to_ARGB;
2561 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_RGBX8888] = mask_alpha_converter_RGBx;
2562 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_A2BGR30_Premultiplied] = convert_ARGB_to_A2RGB30<PixelOrderBGR, true>;
2563 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_A2RGB30_Premultiplied] = convert_ARGB_to_A2RGB30<PixelOrderRGB, true>;
2564 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_RGBA64] = convert_ARGB32_to_RGBA64<true>;
2565
2566 qimage_converter_map[QImage::Format_RGBA8888_Premultiplied][QImage::Format_ARGB32_Premultiplied] = convert_RGBA_to_ARGB;
2567
2568 qimage_converter_map[QImage::Format_BGR30][QImage::Format_A2BGR30_Premultiplied] = convert_passthrough;
2569 qimage_converter_map[QImage::Format_BGR30][QImage::Format_RGB30] = convert_rgbswap_generic;
2570 qimage_converter_map[QImage::Format_BGR30][QImage::Format_A2RGB30_Premultiplied] = convert_rgbswap_generic;
2571
2572 qimage_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_ARGB32] = convert_A2RGB30_PM_to_ARGB<PixelOrderBGR, false>;
2573 qimage_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_RGBA8888] = convert_A2RGB30_PM_to_ARGB<PixelOrderBGR, true>;
2574 qimage_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_BGR30] = convert_A2RGB30_PM_to_RGB30<false>;
2575 qimage_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_RGB30] = convert_A2RGB30_PM_to_RGB30<true>;
2576 qimage_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_A2RGB30_Premultiplied] = convert_rgbswap_generic;
2577
2578 qimage_converter_map[QImage::Format_RGB30][QImage::Format_BGR30] = convert_rgbswap_generic;
2579 qimage_converter_map[QImage::Format_RGB30][QImage::Format_A2BGR30_Premultiplied] = convert_rgbswap_generic;
2580 qimage_converter_map[QImage::Format_RGB30][QImage::Format_A2RGB30_Premultiplied] = convert_passthrough;
2581
2582 qimage_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_ARGB32] = convert_A2RGB30_PM_to_ARGB<PixelOrderRGB, false>;
2583 qimage_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_RGBA8888] = convert_A2RGB30_PM_to_ARGB<PixelOrderRGB, true>;
2584 qimage_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_BGR30] = convert_A2RGB30_PM_to_RGB30<true>;
2585 qimage_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_A2BGR30_Premultiplied] = convert_rgbswap_generic;
2586 qimage_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_RGB30] = convert_A2RGB30_PM_to_RGB30<false>;
2587
2588 qimage_converter_map[QImage::Format_Grayscale8][QImage::Format_Indexed8] = convert_Grayscale8_to_Indexed8;
2589 qimage_converter_map[QImage::Format_Alpha8][QImage::Format_Indexed8] = convert_Alpha8_to_Indexed8;
2590
2591 qimage_converter_map[QImage::Format_RGBX64][QImage::Format_RGBA64] = convert_passthrough;
2592 qimage_converter_map[QImage::Format_RGBX64][QImage::Format_RGBA64_Premultiplied] = convert_passthrough;
2593 qimage_converter_map[QImage::Format_RGBX64][QImage::Format_Grayscale8] = convert_RGBA64_to_gray8<false>;
2594 qimage_converter_map[QImage::Format_RGBX64][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16<false>;
2595
2596 qimage_converter_map[QImage::Format_RGBA64][QImage::Format_ARGB32] = convert_RGBA64_to_ARGB32<false>;
2597 qimage_converter_map[QImage::Format_RGBA64][QImage::Format_RGBA8888] = convert_RGBA64_to_ARGB32<true>;
2598 qimage_converter_map[QImage::Format_RGBA64][QImage::Format_RGBX64] = convert_RGBA64_to_RGBx64;
2599 qimage_converter_map[QImage::Format_RGBA64][QImage::Format_Grayscale8] = convert_RGBA64_to_gray8<false>;
2600 qimage_converter_map[QImage::Format_RGBA64][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16<false>;
2601
2602 qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_Grayscale8] = convert_RGBA64_to_gray8<true>;
2603 qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16<true>;
2604
2605 qimage_converter_map[QImage::Format_Grayscale16][QImage::Format_RGBX64] = convert_gray16_to_RGBA64;
2606 qimage_converter_map[QImage::Format_Grayscale16][QImage::Format_RGBA64] = convert_gray16_to_RGBA64;
2607 qimage_converter_map[QImage::Format_Grayscale16][QImage::Format_RGBA64_Premultiplied] = convert_gray16_to_RGBA64;
2608
2609 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGB888] = convert_rgbswap_generic;
2610#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
2611 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBX8888] = convert_RGB888_to_RGB<false>;
2612 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888] = convert_RGB888_to_RGB<false>;
2613 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888_Premultiplied] = convert_RGB888_to_RGB<false>;
2614#endif
2615
2616 qimage_converter_map[QImage::Format_RGBX16FPx4][QImage::Format_RGBA16FPx4] = convert_passthrough;
2617 qimage_converter_map[QImage::Format_RGBX16FPx4][QImage::Format_RGBA16FPx4_Premultiplied] = convert_passthrough;
2618
2619 qimage_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4] = convert_passthrough;
2620 qimage_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4_Premultiplied] = convert_passthrough;
2621
2622 qimage_converter_map[QImage::Format_CMYK8888][QImage::Format_CMYK8888] = convert_passthrough;
2623 qimage_converter_map[QImage::Format_RGB32][QImage::Format_CMYK8888] = convert_ARGB32_to_CMYK8888<false>;
2624 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_CMYK8888] = convert_ARGB32_to_CMYK8888<false>;
2625 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_CMYK8888] = convert_ARGB32_to_CMYK8888<true>;
2626
2627 // Inline converters:
2628 qimage_inplace_converter_map[QImage::Format_Indexed8][QImage::Format_Grayscale8] =
2629 convert_Indexed8_to_Grayscale8_inplace;
2630 qimage_inplace_converter_map[QImage::Format_Indexed8][QImage::Format_Alpha8] =
2631 convert_Indexed8_to_Alpha8_inplace;
2632
2633 qimage_inplace_converter_map[QImage::Format_RGB32][QImage::Format_ARGB32] =
2634 mask_alpha_converter_inplace<QImage::Format_ARGB32>;
2635 qimage_inplace_converter_map[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] =
2636 mask_alpha_converter_inplace<QImage::Format_ARGB32_Premultiplied>;
2637
2638 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_RGB32] =
2639 mask_alpha_converter_inplace<QImage::Format_RGB32>;
2640 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_RGBX8888] =
2641 convert_ARGB_to_RGBA_inplace<QImage::Format_RGBX8888>;
2642 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_RGBA8888] =
2643 convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888>;
2644 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_A2BGR30_Premultiplied] =
2645 convert_ARGB_to_A2RGB30_inplace<PixelOrderBGR, false>;
2646 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_A2RGB30_Premultiplied] =
2647 convert_ARGB_to_A2RGB30_inplace<PixelOrderRGB, false>;
2648
2649 qimage_inplace_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGBA8888_Premultiplied] =
2650 convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888_Premultiplied>;
2651
2652 qimage_inplace_converter_map[QImage::Format_RGB888][QImage::Format_BGR888] =
2653 convert_rgbswap_generic_inplace;
2654
2655 qimage_inplace_converter_map[QImage::Format_RGBX8888][QImage::Format_RGB32] =
2656 convert_RGBA_to_ARGB_inplace<QImage::Format_RGB32>;
2657 qimage_inplace_converter_map[QImage::Format_RGBX8888][QImage::Format_ARGB32] =
2658 convert_RGBA_to_ARGB_inplace<QImage::Format_ARGB32>;
2659 qimage_inplace_converter_map[QImage::Format_RGBX8888][QImage::Format_ARGB32_Premultiplied] =
2660 convert_RGBA_to_ARGB_inplace<QImage::Format_ARGB32_Premultiplied>;
2661 qimage_inplace_converter_map[QImage::Format_RGBX8888][QImage::Format_RGBA8888] =
2662 convert_passthrough_inplace<QImage::Format_RGBA8888>;
2663 qimage_inplace_converter_map[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] =
2664 convert_passthrough_inplace<QImage::Format_RGBA8888_Premultiplied>;
2665
2666 qimage_inplace_converter_map[QImage::Format_RGBA8888][QImage::Format_RGB32] =
2667 convert_RGBA_to_ARGB_inplace<QImage::Format_RGB32>;
2668 qimage_inplace_converter_map[QImage::Format_RGBA8888][QImage::Format_ARGB32] =
2669 convert_RGBA_to_ARGB_inplace<QImage::Format_ARGB32>;
2670 qimage_inplace_converter_map[QImage::Format_RGBA8888][QImage::Format_RGBX8888] =
2671 mask_alpha_converter_rgbx_inplace;
2672 qimage_inplace_converter_map[QImage::Format_RGBA8888][QImage::Format_A2BGR30_Premultiplied] =
2673 convert_ARGB_to_A2RGB30_inplace<PixelOrderBGR, true>;
2674 qimage_inplace_converter_map[QImage::Format_RGBA8888][QImage::Format_A2RGB30_Premultiplied] =
2675 convert_ARGB_to_A2RGB30_inplace<PixelOrderRGB, true>;
2676
2677 qimage_inplace_converter_map[QImage::Format_RGBA8888_Premultiplied][QImage::Format_ARGB32_Premultiplied] =
2678 convert_RGBA_to_ARGB_inplace<QImage::Format_ARGB32_Premultiplied>;
2679
2680 qimage_inplace_converter_map[QImage::Format_BGR30][QImage::Format_A2BGR30_Premultiplied] =
2681 convert_passthrough_inplace<QImage::Format_A2BGR30_Premultiplied>;
2682 qimage_inplace_converter_map[QImage::Format_BGR30][QImage::Format_RGB30] =
2683 convert_rgbswap_generic_inplace;
2684 qimage_inplace_converter_map[QImage::Format_BGR30][QImage::Format_A2RGB30_Premultiplied] =
2685 convert_BGR30_to_A2RGB30_inplace;
2686
2687 qimage_inplace_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_ARGB32] =
2688 convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderBGR, false>;
2689 qimage_inplace_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_RGBA8888] =
2690 convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderBGR, true>;
2691 qimage_inplace_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_BGR30] =
2692 convert_A2RGB30_PM_to_RGB30_inplace<false>;
2693 qimage_inplace_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_RGB30] =
2694 convert_A2RGB30_PM_to_RGB30_inplace<true>;
2695 qimage_inplace_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_A2RGB30_Premultiplied] =
2696 convert_rgbswap_generic_inplace;
2697
2698 qimage_inplace_converter_map[QImage::Format_RGB30][QImage::Format_BGR30] =
2699 convert_rgbswap_generic_inplace;
2700 qimage_inplace_converter_map[QImage::Format_RGB30][QImage::Format_A2BGR30_Premultiplied] =
2701 convert_BGR30_to_A2RGB30_inplace;
2702 qimage_inplace_converter_map[QImage::Format_RGB30][QImage::Format_A2RGB30_Premultiplied] =
2703 convert_passthrough_inplace<QImage::Format_A2RGB30_Premultiplied>;
2704
2705 qimage_inplace_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_ARGB32] =
2706 convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderRGB, false>;
2707 qimage_inplace_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_RGBA8888] =
2708 convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderRGB, true>;
2709 qimage_inplace_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_BGR30] =
2710 convert_A2RGB30_PM_to_RGB30_inplace<true>;
2711 qimage_inplace_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_A2BGR30_Premultiplied] =
2712 convert_rgbswap_generic_inplace;
2713 qimage_inplace_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_RGB30] =
2714 convert_A2RGB30_PM_to_RGB30_inplace<false>;
2715
2716 qimage_inplace_converter_map[QImage::Format_Grayscale8][QImage::Format_Indexed8] =
2717 convert_Grayscale8_to_Indexed8_inplace;
2718 qimage_inplace_converter_map[QImage::Format_Alpha8][QImage::Format_Indexed8] =
2719 convert_Alpha8_to_Indexed8_inplace;
2720
2721 qimage_inplace_converter_map[QImage::Format_RGBX64][QImage::Format_RGBA64] =
2722 convert_passthrough_inplace<QImage::Format_RGBA64>;
2723 qimage_inplace_converter_map[QImage::Format_RGBX64][QImage::Format_RGBA64_Premultiplied] =
2724 convert_passthrough_inplace<QImage::Format_RGBA64_Premultiplied>;
2725
2726 qimage_inplace_converter_map[QImage::Format_RGBA64][QImage::Format_RGBX64] =
2727 convert_RGBA64_to_RGBx64_inplace;
2728
2729 qimage_inplace_converter_map[QImage::Format_BGR888][QImage::Format_RGB888] =
2730 convert_rgbswap_generic_inplace;
2731
2732 qimage_inplace_converter_map[QImage::Format_RGBX16FPx4][QImage::Format_RGBA16FPx4] =
2733 convert_passthrough_inplace<QImage::Format_RGBA16FPx4>;
2734 qimage_inplace_converter_map[QImage::Format_RGBX16FPx4][QImage::Format_RGBA16FPx4_Premultiplied] =
2735 convert_passthrough_inplace<QImage::Format_RGBA16FPx4_Premultiplied>;
2736
2737 qimage_inplace_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4] =
2738 convert_passthrough_inplace<QImage::Format_RGBA32FPx4>;
2739 qimage_inplace_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4_Premultiplied] =
2740 convert_passthrough_inplace<QImage::Format_RGBA32FPx4_Premultiplied>;
2741
2742 // Now architecture specific conversions:
2743#if defined(__SSE2__) && defined(QT_COMPILER_SUPPORTS_SSSE3)
2744 if (qCpuHasFeature(SSSE3)) {
2745 extern void convert_RGB888_to_RGB32_ssse3(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
2746 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_ssse3;
2747 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_ssse3;
2748 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_ssse3;
2749 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBX8888] = convert_RGB888_to_RGB32_ssse3;
2750 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888] = convert_RGB888_to_RGB32_ssse3;
2751 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888_Premultiplied] = convert_RGB888_to_RGB32_ssse3;
2752 }
2753#endif
2754
2755#if defined(__ARM_NEON__)
2756 extern void convert_RGB888_to_RGB32_neon(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
2757 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_neon;
2758 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_neon;
2759 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_neon;
2760#endif
2761
2762#if defined(__MIPS_DSPR2__)
2763 extern bool convert_ARGB_to_ARGB_PM_inplace_mips_dspr2(QImageData *data, Qt::ImageConversionFlags);
2764 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_inplace_mips_dspr2;
2765
2766 extern void convert_RGB888_to_RGB32_mips_dspr2(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
2767 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_mips_dspr2;
2768 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_mips_dspr2;
2769 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_mips_dspr2;
2770#endif
2771}
2772
2773Q_CONSTRUCTOR_FUNCTION(qInitImageConversions);
2774
2775QT_END_NAMESPACE
2776

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/gui/image/qimage_conversions.cpp