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

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