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