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 = src->nbytes / (1<<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 = src->nbytes / (1<<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 = data->nbytes / (1<<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 = data->nbytes / (1<<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_RGBA64_to_RGBA64PM(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1263{
1264 Q_ASSERT(src->format == QImage::Format_RGBA64);
1265 Q_ASSERT(dest->format == QImage::Format_RGBA64_Premultiplied);
1266 Q_ASSERT(src->width == dest->width);
1267 Q_ASSERT(src->height == dest->height);
1268
1269 const int src_pad = (src->bytes_per_line >> 3) - src->width;
1270 const int dest_pad = (dest->bytes_per_line >> 3) - dest->width;
1271 const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data);
1272 QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data);
1273
1274 for (int i = 0; i < src->height; ++i) {
1275 const QRgba64 *end = src_data + src->width;
1276 while (src_data < end) {
1277 *dest_data = src_data->premultiplied();
1278 ++src_data;
1279 ++dest_data;
1280 }
1281 src_data += src_pad;
1282 dest_data += dest_pad;
1283 }
1284}
1285
1286static bool convert_RGBA64_to_RGBA64PM_inplace(QImageData *data, Qt::ImageConversionFlags)
1287{
1288 Q_ASSERT(data->format == QImage::Format_RGBA64);
1289
1290 const int pad = (data->bytes_per_line >> 3) - data->width;
1291 QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data);
1292
1293 for (int i = 0; i < data->height; ++i) {
1294 const QRgba64 *end = rgb_data + data->width;
1295 while (rgb_data < end) {
1296 *rgb_data = rgb_data->premultiplied();
1297 ++rgb_data;
1298 }
1299 rgb_data += pad;
1300 }
1301 data->format = QImage::Format_RGBA64_Premultiplied;
1302 return true;
1303}
1304
1305template<bool MaskAlpha>
1306static void convert_RGBA64PM_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1307{
1308 Q_ASSERT(src->format == QImage::Format_RGBA64_Premultiplied);
1309 Q_ASSERT(dest->format == QImage::Format_RGBA64 || dest->format == QImage::Format_RGBX64);
1310 Q_ASSERT(src->width == dest->width);
1311 Q_ASSERT(src->height == dest->height);
1312
1313 const int src_pad = (src->bytes_per_line >> 3) - src->width;
1314 const int dest_pad = (dest->bytes_per_line >> 3) - dest->width;
1315 const QRgba64 *src_data = reinterpret_cast<const QRgba64 *>(src->data);
1316 QRgba64 *dest_data = reinterpret_cast<QRgba64 *>(dest->data);
1317
1318 for (int i = 0; i < src->height; ++i) {
1319 const QRgba64 *end = src_data + src->width;
1320 while (src_data < end) {
1321 *dest_data = src_data->unpremultiplied();
1322 if (MaskAlpha)
1323 dest_data->setAlpha(65535);
1324 ++src_data;
1325 ++dest_data;
1326 }
1327 src_data += src_pad;
1328 dest_data += dest_pad;
1329 }
1330}
1331
1332template<bool MaskAlpha>
1333static bool convert_RGBA64PM_to_RGBA64_inplace(QImageData *data, Qt::ImageConversionFlags)
1334{
1335 Q_ASSERT(data->format == QImage::Format_RGBA64_Premultiplied);
1336
1337 const int pad = (data->bytes_per_line >> 3) - data->width;
1338 QRgba64 *rgb_data = reinterpret_cast<QRgba64 *>(data->data);
1339
1340 for (int i = 0; i < data->height; ++i) {
1341 const QRgba64 *end = rgb_data + data->width;
1342 while (rgb_data < end) {
1343 *rgb_data = rgb_data->unpremultiplied();
1344 if (MaskAlpha)
1345 rgb_data->setAlpha(65535);
1346 ++rgb_data;
1347 }
1348 rgb_data += pad;
1349 }
1350 data->format = MaskAlpha ? QImage::Format_RGBX64 : QImage::Format_RGBA64;
1351 return true;
1352}
1353
1354static void convert_gray16_to_RGBA64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1355{
1356 Q_ASSERT(src->format == QImage::Format_Grayscale16);
1357 Q_ASSERT(dest->format == QImage::Format_RGBA64 || dest->format == QImage::Format_RGBX64 ||
1358 dest->format == QImage::Format_RGBA64_Premultiplied);
1359 Q_ASSERT(src->width == dest->width);
1360 Q_ASSERT(src->height == dest->height);
1361
1362 const qsizetype sbpl = src->bytes_per_line;
1363 const qsizetype dbpl = dest->bytes_per_line;
1364 const uchar *src_data = src->data;
1365 uchar *dest_data = dest->data;
1366
1367 for (int i = 0; i < src->height; ++i) {
1368 const quint16 *src_line = reinterpret_cast<const quint16 *>(src_data);
1369 QRgba64 *dest_line = reinterpret_cast<QRgba64 *>(dest_data);
1370 for (int j = 0; j < src->width; ++j) {
1371 quint16 s = src_line[j];
1372 dest_line[j] = qRgba64(s, s, s, 0xFFFF);
1373 }
1374 src_data += sbpl;
1375 dest_data += dbpl;
1376 }
1377}
1378
1379static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1380{
1381 Q_ASSERT(dest->format == QImage::Format_Grayscale16);
1382 Q_ASSERT(src->format == QImage::Format_RGBX64 ||
1383 src->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 QRgba64 *src_line = reinterpret_cast<const QRgba64 *>(src_data);
1394 quint16 *dest_line = reinterpret_cast<quint16 *>(dest_data);
1395 for (int j = 0; j < src->width; ++j) {
1396 QRgba64 s = src_line[j].unpremultiplied();
1397 dest_line[j] = qGray(s.red(), s.green(), s.blue());
1398 }
1399 src_data += sbpl;
1400 dest_data += dbpl;
1401 }
1402}
1403
1404static QList<QRgb> fix_color_table(const QList<QRgb> &ctbl, QImage::Format format)
1405{
1406 QList<QRgb> colorTable = ctbl;
1407 if (format == QImage::Format_RGB32) {
1408 // check if the color table has alpha
1409 for (int i = 0; i < colorTable.size(); ++i)
1410 if (qAlpha(colorTable.at(i)) != 0xff)
1411 colorTable[i] = colorTable.at(i) | 0xff000000;
1412 } else if (format == QImage::Format_ARGB32_Premultiplied) {
1413 // check if the color table has alpha
1414 for (int i = 0; i < colorTable.size(); ++i)
1415 colorTable[i] = qPremultiply(colorTable.at(i));
1416 }
1417 return colorTable;
1418}
1419
1420//
1421// dither_to_1: Uses selected dithering algorithm.
1422//
1423
1424void dither_to_Mono(QImageData *dst, const QImageData *src,
1425 Qt::ImageConversionFlags flags, bool fromalpha)
1426{
1427 Q_ASSERT(src->width == dst->width);
1428 Q_ASSERT(src->height == dst->height);
1429 Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB);
1430
1431 dst->colortable.clear();
1432 dst->colortable.append(0xffffffff);
1433 dst->colortable.append(0xff000000);
1434
1435 enum { Threshold, Ordered, Diffuse } dithermode;
1436
1437 if (fromalpha) {
1438 if ((flags & Qt::AlphaDither_Mask) == Qt::DiffuseAlphaDither)
1439 dithermode = Diffuse;
1440 else if ((flags & Qt::AlphaDither_Mask) == Qt::OrderedAlphaDither)
1441 dithermode = Ordered;
1442 else
1443 dithermode = Threshold;
1444 } else {
1445 if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither)
1446 dithermode = Threshold;
1447 else if ((flags & Qt::Dither_Mask) == Qt::OrderedDither)
1448 dithermode = Ordered;
1449 else
1450 dithermode = Diffuse;
1451 }
1452
1453 int w = src->width;
1454 int h = src->height;
1455 int d = src->depth;
1456 uchar gray[256]; // gray map for 8 bit images
1457 bool use_gray = (d == 8);
1458 if (use_gray) { // make gray map
1459 if (fromalpha) {
1460 // Alpha 0x00 -> 0 pixels (white)
1461 // Alpha 0xFF -> 1 pixels (black)
1462 for (int i = 0; i < src->colortable.size(); i++)
1463 gray[i] = (255 - (src->colortable.at(i) >> 24));
1464 } else {
1465 // Pixel 0x00 -> 1 pixels (black)
1466 // Pixel 0xFF -> 0 pixels (white)
1467 for (int i = 0; i < src->colortable.size(); i++)
1468 gray[i] = qGray(src->colortable.at(i));
1469 }
1470 }
1471
1472 uchar *dst_data = dst->data;
1473 qsizetype dst_bpl = dst->bytes_per_line;
1474 const uchar *src_data = src->data;
1475 qsizetype src_bpl = src->bytes_per_line;
1476
1477 switch (dithermode) {
1478 case Diffuse: {
1479 QScopedArrayPointer<int> lineBuffer(new int[w * 2]);
1480 int *line1 = lineBuffer.data();
1481 int *line2 = lineBuffer.data() + w;
1482 int bmwidth = (w+7)/8;
1483
1484 int *b1, *b2;
1485 int wbytes = w * (d/8);
1486 const uchar *p = src->data;
1487 const uchar *end = p + wbytes;
1488 b2 = line2;
1489 if (use_gray) { // 8 bit image
1490 while (p < end)
1491 *b2++ = gray[*p++];
1492 } else { // 32 bit image
1493 if (fromalpha) {
1494 while (p < end) {
1495 *b2++ = 255 - (*(const uint*)p >> 24);
1496 p += 4;
1497 }
1498 } else {
1499 while (p < end) {
1500 *b2++ = qGray(*(const uint*)p);
1501 p += 4;
1502 }
1503 }
1504 }
1505 for (int y=0; y<h; y++) { // for each scan line...
1506 int *tmp = line1; line1 = line2; line2 = tmp;
1507 bool not_last_line = y < h - 1;
1508 if (not_last_line) { // calc. grayvals for next line
1509 p = src->data + (y+1)*src->bytes_per_line;
1510 end = p + wbytes;
1511 b2 = line2;
1512 if (use_gray) { // 8 bit image
1513 while (p < end)
1514 *b2++ = gray[*p++];
1515 } else { // 24 bit image
1516 if (fromalpha) {
1517 while (p < end) {
1518 *b2++ = 255 - (*(const uint*)p >> 24);
1519 p += 4;
1520 }
1521 } else {
1522 while (p < end) {
1523 *b2++ = qGray(*(const uint*)p);
1524 p += 4;
1525 }
1526 }
1527 }
1528 }
1529
1530 int err;
1531 uchar *p = dst->data + y*dst->bytes_per_line;
1532 memset(p, 0, bmwidth);
1533 b1 = line1;
1534 b2 = line2;
1535 int bit = 7;
1536 for (int x=1; x<=w; x++) {
1537 if (*b1 < 128) { // black pixel
1538 err = *b1++;
1539 *p |= 1 << bit;
1540 } else { // white pixel
1541 err = *b1++ - 255;
1542 }
1543 if (bit == 0) {
1544 p++;
1545 bit = 7;
1546 } else {
1547 bit--;
1548 }
1549 const int e7 = ((err * 7) + 8) >> 4;
1550 const int e5 = ((err * 5) + 8) >> 4;
1551 const int e3 = ((err * 3) + 8) >> 4;
1552 const int e1 = err - (e7 + e5 + e3);
1553 if (x < w)
1554 *b1 += e7; // spread error to right pixel
1555 if (not_last_line) {
1556 b2[0] += e5; // pixel below
1557 if (x > 1)
1558 b2[-1] += e3; // pixel below left
1559 if (x < w)
1560 b2[1] += e1; // pixel below right
1561 }
1562 b2++;
1563 }
1564 }
1565 } break;
1566 case Ordered: {
1567
1568 memset(dst->data, 0, dst->nbytes);
1569 if (d == 32) {
1570 for (int i=0; i<h; i++) {
1571 const uint *p = (const uint *)src_data;
1572 const uint *end = p + w;
1573 uchar *m = dst_data;
1574 int bit = 7;
1575 int j = 0;
1576 if (fromalpha) {
1577 while (p < end) {
1578 if ((*p++ >> 24) >= qt_bayer_matrix[j++&15][i&15])
1579 *m |= 1 << bit;
1580 if (bit == 0) {
1581 m++;
1582 bit = 7;
1583 } else {
1584 bit--;
1585 }
1586 }
1587 } else {
1588 while (p < end) {
1589 if ((uint)qGray(*p++) < qt_bayer_matrix[j++&15][i&15])
1590 *m |= 1 << bit;
1591 if (bit == 0) {
1592 m++;
1593 bit = 7;
1594 } else {
1595 bit--;
1596 }
1597 }
1598 }
1599 dst_data += dst_bpl;
1600 src_data += src_bpl;
1601 }
1602 } else if (d == 8) {
1603 for (int i=0; i<h; i++) {
1604 const uchar *p = src_data;
1605 const uchar *end = p + w;
1606 uchar *m = dst_data;
1607 int bit = 7;
1608 int j = 0;
1609 while (p < end) {
1610 if ((uint)gray[*p++] < qt_bayer_matrix[j++&15][i&15])
1611 *m |= 1 << bit;
1612 if (bit == 0) {
1613 m++;
1614 bit = 7;
1615 } else {
1616 bit--;
1617 }
1618 }
1619 dst_data += dst_bpl;
1620 src_data += src_bpl;
1621 }
1622 }
1623 } break;
1624 default: { // Threshold:
1625 memset(dst->data, 0, dst->nbytes);
1626 if (d == 32) {
1627 for (int i=0; i<h; i++) {
1628 const uint *p = (const uint *)src_data;
1629 const uint *end = p + w;
1630 uchar *m = dst_data;
1631 int bit = 7;
1632 if (fromalpha) {
1633 while (p < end) {
1634 if ((*p++ >> 24) >= 128)
1635 *m |= 1 << bit; // Set mask "on"
1636 if (bit == 0) {
1637 m++;
1638 bit = 7;
1639 } else {
1640 bit--;
1641 }
1642 }
1643 } else {
1644 while (p < end) {
1645 if (qGray(*p++) < 128)
1646 *m |= 1 << bit; // Set pixel "black"
1647 if (bit == 0) {
1648 m++;
1649 bit = 7;
1650 } else {
1651 bit--;
1652 }
1653 }
1654 }
1655 dst_data += dst_bpl;
1656 src_data += src_bpl;
1657 }
1658 } else
1659 if (d == 8) {
1660 for (int i=0; i<h; i++) {
1661 const uchar *p = src_data;
1662 const uchar *end = p + w;
1663 uchar *m = dst_data;
1664 int bit = 7;
1665 while (p < end) {
1666 if (gray[*p++] < 128)
1667 *m |= 1 << bit; // Set mask "on"/ pixel "black"
1668 if (bit == 0) {
1669 m++;
1670 bit = 7;
1671 } else {
1672 bit--;
1673 }
1674 }
1675 dst_data += dst_bpl;
1676 src_data += src_bpl;
1677 }
1678 }
1679 }
1680 }
1681
1682 if (dst->format == QImage::Format_MonoLSB) {
1683 // need to swap bit order
1684 uchar *sl = dst->data;
1685 int bpl = (dst->width + 7) * dst->depth / 8;
1686 int pad = dst->bytes_per_line - bpl;
1687 for (int y=0; y<dst->height; ++y) {
1688 for (int x=0; x<bpl; ++x) {
1689 *sl = bitflip[*sl];
1690 ++sl;
1691 }
1692 sl += pad;
1693 }
1694 }
1695}
1696
1697static void convert_X_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1698{
1699 dither_to_Mono(dst, src, flags, false);
1700}
1701
1702static void convert_ARGB_PM_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1703{
1704 QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32));
1705 convert_generic(tmp.data(), src, Qt::AutoColor);
1706 dither_to_Mono(dst, tmp.data(), flags, false);
1707}
1708
1709//
1710// convert_32_to_8: Converts a 32 bits depth (true color) to an 8 bit
1711// image with a colormap. If the 32 bit image has more than 256 colors,
1712// we convert the red,green and blue bytes into a single byte encoded
1713// as 6 shades of each of red, green and blue.
1714//
1715// if dithering is needed, only 1 color at most is available for alpha.
1716//
1717struct QRgbMap {
1718 inline QRgbMap() : used(0) { }
1719 uchar pix;
1720 uchar used;
1721 QRgb rgb;
1722};
1723
1724static void convert_RGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1725{
1726 Q_ASSERT(src->format == QImage::Format_RGB32 || src->format == QImage::Format_ARGB32);
1727 Q_ASSERT(dst->format == QImage::Format_Indexed8);
1728 Q_ASSERT(src->width == dst->width);
1729 Q_ASSERT(src->height == dst->height);
1730
1731 bool do_quant = (flags & Qt::DitherMode_Mask) == Qt::PreferDither
1732 || src->format == QImage::Format_ARGB32;
1733 uint alpha_mask = src->format == QImage::Format_RGB32 ? 0xff000000 : 0;
1734
1735 const int tablesize = 997; // prime
1736 QRgbMap table[tablesize];
1737 int pix=0;
1738
1739 if (!dst->colortable.isEmpty()) {
1740 QList<QRgb> ctbl = dst->colortable;
1741 dst->colortable.resize(256);
1742 // Preload palette into table.
1743 // Almost same code as pixel insertion below
1744 for (int i = 0; i < dst->colortable.size(); ++i) {
1745 // Find in table...
1746 QRgb p = ctbl.at(i) | alpha_mask;
1747 int hash = p % tablesize;
1748 for (;;) {
1749 if (table[hash].used) {
1750 if (table[hash].rgb == p) {
1751 // Found previous insertion - use it
1752 break;
1753 } else {
1754 // Keep searching...
1755 if (++hash == tablesize) hash = 0;
1756 }
1757 } else {
1758 // Cannot be in table
1759 Q_ASSERT (pix != 256); // too many colors
1760 // Insert into table at this unused position
1761 dst->colortable[pix] = p;
1762 table[hash].pix = pix++;
1763 table[hash].rgb = p;
1764 table[hash].used = 1;
1765 break;
1766 }
1767 }
1768 }
1769 }
1770
1771 if ((flags & Qt::DitherMode_Mask) != Qt::PreferDither) {
1772 dst->colortable.resize(256);
1773 const uchar *src_data = src->data;
1774 uchar *dest_data = dst->data;
1775 for (int y = 0; y < src->height; y++) { // check if <= 256 colors
1776 const QRgb *s = (const QRgb *)src_data;
1777 uchar *b = dest_data;
1778 for (int x = 0; x < src->width; ++x) {
1779 QRgb p = s[x] | alpha_mask;
1780 int hash = p % tablesize;
1781 for (;;) {
1782 if (table[hash].used) {
1783 if (table[hash].rgb == (p)) {
1784 // Found previous insertion - use it
1785 break;
1786 } else {
1787 // Keep searching...
1788 if (++hash == tablesize) hash = 0;
1789 }
1790 } else {
1791 // Cannot be in table
1792 if (pix == 256) { // too many colors
1793 do_quant = true;
1794 // Break right out
1795 x = src->width;
1796 y = src->height;
1797 } else {
1798 // Insert into table at this unused position
1799 dst->colortable[pix] = p;
1800 table[hash].pix = pix++;
1801 table[hash].rgb = p;
1802 table[hash].used = 1;
1803 }
1804 break;
1805 }
1806 }
1807 *b++ = table[hash].pix; // May occur once incorrectly
1808 }
1809 src_data += src->bytes_per_line;
1810 dest_data += dst->bytes_per_line;
1811 }
1812 }
1813 int numColors = do_quant ? 256 : pix;
1814
1815 dst->colortable.resize(numColors);
1816
1817 if (do_quant) { // quantization needed
1818
1819#define MAX_R 5
1820#define MAX_G 5
1821#define MAX_B 5
1822#define INDEXOF(r,g,b) (((r)*(MAX_G+1)+(g))*(MAX_B+1)+(b))
1823
1824 for (int rc=0; rc<=MAX_R; rc++) // build 6x6x6 color cube
1825 for (int gc=0; gc<=MAX_G; gc++)
1826 for (int bc=0; bc<=MAX_B; bc++)
1827 dst->colortable[INDEXOF(rc,gc,bc)] = 0xff000000 | qRgb(rc*255/MAX_R, gc*255/MAX_G, bc*255/MAX_B);
1828
1829 const uchar *src_data = src->data;
1830 uchar *dest_data = dst->data;
1831 if ((flags & Qt::Dither_Mask) == Qt::ThresholdDither) {
1832 for (int y = 0; y < src->height; y++) {
1833 const QRgb *p = (const QRgb *)src_data;
1834 const QRgb *end = p + src->width;
1835 uchar *b = dest_data;
1836
1837 while (p < end) {
1838#define DITHER(p,m) ((uchar) ((p * (m) + 127) / 255))
1839 *b++ =
1840 INDEXOF(
1841 DITHER(qRed(*p), MAX_R),
1842 DITHER(qGreen(*p), MAX_G),
1843 DITHER(qBlue(*p), MAX_B)
1844 );
1845#undef DITHER
1846 p++;
1847 }
1848 src_data += src->bytes_per_line;
1849 dest_data += dst->bytes_per_line;
1850 }
1851 } else if ((flags & Qt::Dither_Mask) == Qt::DiffuseDither) {
1852 int* line1[3];
1853 int* line2[3];
1854 int* pv[3];
1855 QScopedArrayPointer<int> lineBuffer(new int[src->width * 9]);
1856 line1[0] = lineBuffer.data();
1857 line2[0] = lineBuffer.data() + src->width;
1858 line1[1] = lineBuffer.data() + src->width * 2;
1859 line2[1] = lineBuffer.data() + src->width * 3;
1860 line1[2] = lineBuffer.data() + src->width * 4;
1861 line2[2] = lineBuffer.data() + src->width * 5;
1862 pv[0] = lineBuffer.data() + src->width * 6;
1863 pv[1] = lineBuffer.data() + src->width * 7;
1864 pv[2] = lineBuffer.data() + src->width * 8;
1865
1866 int endian = (QSysInfo::ByteOrder == QSysInfo::BigEndian);
1867 for (int y = 0; y < src->height; y++) {
1868 const uchar* q = src_data;
1869 const uchar* q2 = y < src->height - 1 ? q + src->bytes_per_line : src->data;
1870 uchar *b = dest_data;
1871 for (int chan = 0; chan < 3; chan++) {
1872 int *l1 = (y&1) ? line2[chan] : line1[chan];
1873 int *l2 = (y&1) ? line1[chan] : line2[chan];
1874 if (y == 0) {
1875 for (int i = 0; i < src->width; i++)
1876 l1[i] = q[i*4+chan+endian];
1877 }
1878 if (y+1 < src->height) {
1879 for (int i = 0; i < src->width; i++)
1880 l2[i] = q2[i*4+chan+endian];
1881 }
1882 // Bi-directional error diffusion
1883 if (y&1) {
1884 for (int x = 0; x < src->width; x++) {
1885 int pix = qMax(qMin(5, (l1[x] * 5 + 128)/ 255), 0);
1886 int err = l1[x] - pix * 255 / 5;
1887 pv[chan][x] = pix;
1888
1889 // Spread the error around...
1890 if (x + 1< src->width) {
1891 l1[x+1] += (err*7)>>4;
1892 l2[x+1] += err>>4;
1893 }
1894 l2[x]+=(err*5)>>4;
1895 if (x>1)
1896 l2[x-1]+=(err*3)>>4;
1897 }
1898 } else {
1899 for (int x = src->width; x-- > 0;) {
1900 int pix = qMax(qMin(5, (l1[x] * 5 + 128)/ 255), 0);
1901 int err = l1[x] - pix * 255 / 5;
1902 pv[chan][x] = pix;
1903
1904 // Spread the error around...
1905 if (x > 0) {
1906 l1[x-1] += (err*7)>>4;
1907 l2[x-1] += err>>4;
1908 }
1909 l2[x]+=(err*5)>>4;
1910 if (x + 1 < src->width)
1911 l2[x+1]+=(err*3)>>4;
1912 }
1913 }
1914 }
1915 if (endian) {
1916 for (int x = 0; x < src->width; x++) {
1917 *b++ = INDEXOF(pv[0][x],pv[1][x],pv[2][x]);
1918 }
1919 } else {
1920 for (int x = 0; x < src->width; x++) {
1921 *b++ = INDEXOF(pv[2][x],pv[1][x],pv[0][x]);
1922 }
1923 }
1924 src_data += src->bytes_per_line;
1925 dest_data += dst->bytes_per_line;
1926 }
1927 } else { // OrderedDither
1928 for (int y = 0; y < src->height; y++) {
1929 const QRgb *p = (const QRgb *)src_data;
1930 const QRgb *end = p + src->width;
1931 uchar *b = dest_data;
1932
1933 int x = 0;
1934 while (p < end) {
1935 uint d = qt_bayer_matrix[y & 15][x & 15] << 8;
1936
1937#define DITHER(p, d, m) ((uchar) ((((256 * (m) + (m) + 1)) * (p) + (d)) >> 16))
1938 *b++ =
1939 INDEXOF(
1940 DITHER(qRed(*p), d, MAX_R),
1941 DITHER(qGreen(*p), d, MAX_G),
1942 DITHER(qBlue(*p), d, MAX_B)
1943 );
1944#undef DITHER
1945
1946 p++;
1947 x++;
1948 }
1949 src_data += src->bytes_per_line;
1950 dest_data += dst->bytes_per_line;
1951 }
1952 }
1953
1954 if (src->format != QImage::Format_RGB32
1955 && src->format != QImage::Format_RGB16) {
1956 const int trans = 216;
1957 Q_ASSERT(dst->colortable.size() > trans);
1958 dst->colortable[trans] = 0;
1959 QScopedPointer<QImageData> mask(QImageData::create(QSize(src->width, src->height), QImage::Format_Mono));
1960 dither_to_Mono(mask.data(), src, flags, true);
1961 uchar *dst_data = dst->data;
1962 const uchar *mask_data = mask->data;
1963 for (int y = 0; y < src->height; y++) {
1964 for (int x = 0; x < src->width ; x++) {
1965 if (!(mask_data[x>>3] & (0x80 >> (x & 7))))
1966 dst_data[x] = trans;
1967 }
1968 mask_data += mask->bytes_per_line;
1969 dst_data += dst->bytes_per_line;
1970 }
1971 dst->has_alpha_clut = true;
1972 }
1973
1974#undef MAX_R
1975#undef MAX_G
1976#undef MAX_B
1977#undef INDEXOF
1978
1979 }
1980}
1981
1982static void convert_ARGB_PM_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1983{
1984 QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32));
1985 convert_generic(tmp.data(), src, Qt::AutoColor);
1986 convert_RGB_to_Indexed8(dst, tmp.data(), flags);
1987}
1988
1989static void convert_ARGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1990{
1991 convert_RGB_to_Indexed8(dst, src, flags);
1992}
1993
1994static void convert_Indexed8_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
1995{
1996 Q_ASSERT(src->format == QImage::Format_Indexed8);
1997 Q_ASSERT(dest->format == QImage::Format_RGB32
1998 || dest->format == QImage::Format_ARGB32
1999 || dest->format == QImage::Format_ARGB32_Premultiplied);
2000 Q_ASSERT(src->width == dest->width);
2001 Q_ASSERT(src->height == dest->height);
2002
2003 QList<QRgb> colorTable = src->has_alpha_clut ? fix_color_table(src->colortable, dest->format) : src->colortable;
2004 if (colorTable.size() == 0) {
2005 colorTable.resize(256);
2006 for (int i=0; i<256; ++i)
2007 colorTable[i] = qRgb(i, i, i);
2008 }
2009 if (colorTable.size() < 256) {
2010 int tableSize = colorTable.size();
2011 colorTable.resize(256);
2012 QRgb fallbackColor = (dest->format == QImage::Format_RGB32) ? 0xff000000 : 0;
2013 for (int i=tableSize; i<256; ++i)
2014 colorTable[i] = fallbackColor;
2015 }
2016
2017 int w = src->width;
2018 const uchar *src_data = src->data;
2019 uchar *dest_data = dest->data;
2020 const QRgb *colorTablePtr = colorTable.constData();
2021 for (int y = 0; y < src->height; y++) {
2022 uint *p = reinterpret_cast<uint *>(dest_data);
2023 const uchar *b = src_data;
2024 uint *end = p + w;
2025
2026 while (p < end)
2027 *p++ = colorTablePtr[*b++];
2028
2029 src_data += src->bytes_per_line;
2030 dest_data += dest->bytes_per_line;
2031 }
2032}
2033
2034static void convert_Mono_to_X32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2035{
2036 Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
2037 Q_ASSERT(dest->format == QImage::Format_RGB32
2038 || dest->format == QImage::Format_ARGB32
2039 || dest->format == QImage::Format_ARGB32_Premultiplied);
2040 Q_ASSERT(src->width == dest->width);
2041 Q_ASSERT(src->height == dest->height);
2042
2043 QList<QRgb> colorTable = fix_color_table(src->colortable, dest->format);
2044
2045 // Default to black / white colors
2046 if (colorTable.size() < 2) {
2047 if (colorTable.size() == 0)
2048 colorTable << 0xff000000;
2049 colorTable << 0xffffffff;
2050 }
2051
2052 const uchar *src_data = src->data;
2053 uchar *dest_data = dest->data;
2054 if (src->format == QImage::Format_Mono) {
2055 for (int y = 0; y < dest->height; y++) {
2056 uint *p = (uint *)dest_data;
2057 for (int x = 0; x < dest->width; x++)
2058 *p++ = colorTable.at((src_data[x>>3] >> (7 - (x & 7))) & 1);
2059
2060 src_data += src->bytes_per_line;
2061 dest_data += dest->bytes_per_line;
2062 }
2063 } else {
2064 for (int y = 0; y < dest->height; y++) {
2065 uint *p = (uint *)dest_data;
2066 for (int x = 0; x < dest->width; x++)
2067 *p++ = colorTable.at((src_data[x>>3] >> (x & 7)) & 1);
2068
2069 src_data += src->bytes_per_line;
2070 dest_data += dest->bytes_per_line;
2071 }
2072 }
2073}
2074
2075
2076static void convert_Mono_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2077{
2078 Q_ASSERT(src->format == QImage::Format_Mono || src->format == QImage::Format_MonoLSB);
2079 Q_ASSERT(dest->format == QImage::Format_Indexed8);
2080 Q_ASSERT(src->width == dest->width);
2081 Q_ASSERT(src->height == dest->height);
2082
2083 QList<QRgb> ctbl = src->colortable;
2084 if (ctbl.size() > 2) {
2085 ctbl.resize(2);
2086 } else if (ctbl.size() < 2) {
2087 if (ctbl.size() == 0)
2088 ctbl << 0xff000000;
2089 ctbl << 0xffffffff;
2090 }
2091 dest->colortable = ctbl;
2092 dest->has_alpha_clut = src->has_alpha_clut;
2093
2094
2095 const uchar *src_data = src->data;
2096 uchar *dest_data = dest->data;
2097 if (src->format == QImage::Format_Mono) {
2098 for (int y = 0; y < dest->height; y++) {
2099 uchar *p = dest_data;
2100 for (int x = 0; x < dest->width; x++)
2101 *p++ = (src_data[x>>3] >> (7 - (x & 7))) & 1;
2102 src_data += src->bytes_per_line;
2103 dest_data += dest->bytes_per_line;
2104 }
2105 } else {
2106 for (int y = 0; y < dest->height; y++) {
2107 uchar *p = dest_data;
2108 for (int x = 0; x < dest->width; x++)
2109 *p++ = (src_data[x>>3] >> (x & 7)) & 1;
2110 src_data += src->bytes_per_line;
2111 dest_data += dest->bytes_per_line;
2112 }
2113 }
2114}
2115
2116static void copy_8bit_pixels(QImageData *dest, const QImageData *src)
2117{
2118 if (src->bytes_per_line == dest->bytes_per_line) {
2119 memcpy(dest->data, src->data, src->bytes_per_line * src->height);
2120 } else {
2121 const uchar *sdata = src->data;
2122 uchar *ddata = dest->data;
2123 for (int y = 0; y < src->height; ++y) {
2124 memcpy(ddata, sdata, src->width);
2125 sdata += src->bytes_per_line;
2126 ddata += dest->bytes_per_line;
2127 }
2128 }
2129}
2130
2131static void convert_Indexed8_to_Alpha8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2132{
2133 Q_ASSERT(src->format == QImage::Format_Indexed8);
2134 Q_ASSERT(dest->format == QImage::Format_Alpha8);
2135
2136 uchar translate[256];
2137 const QList<QRgb> &colors = src->colortable;
2138 bool simpleCase = (colors.size() == 256);
2139 for (int i = 0; i < colors.size(); ++i) {
2140 uchar alpha = qAlpha(colors[i]);
2141 translate[i] = alpha;
2142 simpleCase = simpleCase && (alpha == i);
2143 }
2144
2145 if (simpleCase)
2146 copy_8bit_pixels(dest, src);
2147 else {
2148 const uchar *sdata = src->data;
2149 uchar *ddata = dest->data;
2150 for (int y = 0; y < src->height; ++y) {
2151 for (int x = 0; x < src->width; ++x)
2152 ddata[x] = translate[sdata[x]];
2153 sdata += src->bytes_per_line;
2154 ddata += dest->bytes_per_line;
2155 }
2156 }
2157}
2158
2159static void convert_Indexed8_to_Grayscale8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2160{
2161 Q_ASSERT(src->format == QImage::Format_Indexed8);
2162 Q_ASSERT(dest->format == QImage::Format_Grayscale8);
2163
2164 uchar translate[256];
2165 const QList<QRgb> &colors = src->colortable;
2166 bool simpleCase = (colors.size() == 256);
2167 for (int i = 0; i < colors.size(); ++i) {
2168 uchar gray = qGray(colors[i]);
2169 translate[i] = gray;
2170 simpleCase = simpleCase && (gray == i);
2171 }
2172
2173 if (simpleCase)
2174 copy_8bit_pixels(dest, src);
2175 else {
2176 const uchar *sdata = src->data;
2177 uchar *ddata = dest->data;
2178 for (int y = 0; y < src->height; ++y) {
2179 for (int x = 0; x < src->width; ++x)
2180 ddata[x] = translate[sdata[x]];
2181 sdata += src->bytes_per_line;
2182 ddata += dest->bytes_per_line;
2183 }
2184 }
2185}
2186
2187static bool convert_Indexed8_to_Alpha8_inplace(QImageData *data, Qt::ImageConversionFlags)
2188{
2189 Q_ASSERT(data->format == QImage::Format_Indexed8);
2190
2191 // Just check if this is an Alpha8 in Indexed8 disguise.
2192 const QList<QRgb> &colors = data->colortable;
2193 if (colors.size() != 256)
2194 return false;
2195 for (int i = 0; i < colors.size(); ++i) {
2196 if (i != qAlpha(colors[i]))
2197 return false;
2198 }
2199
2200 data->colortable.clear();
2201 data->format = QImage::Format_Alpha8;
2202
2203 return true;
2204}
2205
2206static bool convert_Indexed8_to_Grayscale8_inplace(QImageData *data, Qt::ImageConversionFlags)
2207{
2208 Q_ASSERT(data->format == QImage::Format_Indexed8);
2209
2210 // Just check if this is a Grayscale8 in Indexed8 disguise.
2211 const QList<QRgb> &colors = data->colortable;
2212 if (colors.size() != 256)
2213 return false;
2214 for (int i = 0; i < colors.size(); ++i) {
2215 if (i != qGray(colors[i]))
2216 return false;
2217 }
2218
2219 data->colortable.clear();
2220 data->format = QImage::Format_Grayscale8;
2221
2222 return true;
2223}
2224
2225static void convert_Alpha8_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2226{
2227 Q_ASSERT(src->format == QImage::Format_Alpha8);
2228 Q_ASSERT(dest->format == QImage::Format_Indexed8);
2229
2230 copy_8bit_pixels(dest, src);
2231
2232 dest->colortable = defaultColorTables->alpha;
2233}
2234
2235static void convert_Grayscale8_to_Indexed8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
2236{
2237 Q_ASSERT(src->format == QImage::Format_Grayscale8);
2238 Q_ASSERT(dest->format == QImage::Format_Indexed8);
2239
2240 copy_8bit_pixels(dest, src);
2241
2242 dest->colortable = defaultColorTables->gray;
2243}
2244
2245static bool convert_Alpha8_to_Indexed8_inplace(QImageData *data, Qt::ImageConversionFlags)
2246{
2247 Q_ASSERT(data->format == QImage::Format_Alpha8);
2248
2249 data->colortable = defaultColorTables->alpha;
2250 data->format = QImage::Format_Indexed8;
2251
2252 return true;
2253}
2254
2255static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageConversionFlags)
2256{
2257 Q_ASSERT(data->format == QImage::Format_Grayscale8);
2258
2259 data->colortable = defaultColorTables->gray;
2260 data->format = QImage::Format_Indexed8;
2261
2262 return true;
2263}
2264
2265
2266// first index source, second dest
2267Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = {};
2268InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] = {};
2269
2270static void qInitImageConversions()
2271{
2272 // Some conversions can not be generic, other are just hard to make as fast in the generic converter.
2273
2274 // All conversions to and from indexed formats can not be generic and needs to go over RGB32 or ARGB32
2275 qimage_converter_map[QImage::Format_Mono][QImage::Format_MonoLSB] = swap_bit_order;
2276 qimage_converter_map[QImage::Format_Mono][QImage::Format_Indexed8] = convert_Mono_to_Indexed8;
2277 qimage_converter_map[QImage::Format_Mono][QImage::Format_RGB32] = convert_Mono_to_X32;
2278 qimage_converter_map[QImage::Format_Mono][QImage::Format_ARGB32] = convert_Mono_to_X32;
2279 qimage_converter_map[QImage::Format_Mono][QImage::Format_ARGB32_Premultiplied] = convert_Mono_to_X32;
2280
2281 qimage_converter_map[QImage::Format_MonoLSB][QImage::Format_Mono] = swap_bit_order;
2282 qimage_converter_map[QImage::Format_MonoLSB][QImage::Format_Indexed8] = convert_Mono_to_Indexed8;
2283 qimage_converter_map[QImage::Format_MonoLSB][QImage::Format_RGB32] = convert_Mono_to_X32;
2284 qimage_converter_map[QImage::Format_MonoLSB][QImage::Format_ARGB32] = convert_Mono_to_X32;
2285 qimage_converter_map[QImage::Format_MonoLSB][QImage::Format_ARGB32_Premultiplied] = convert_Mono_to_X32;
2286
2287 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_Mono] = convert_X_to_Mono;
2288 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_MonoLSB] = convert_X_to_Mono;
2289 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_RGB32] = convert_Indexed8_to_X32;
2290 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_ARGB32] = convert_Indexed8_to_X32;
2291 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_ARGB32_Premultiplied] = convert_Indexed8_to_X32;
2292 // Indexed8, Alpha8 and Grayscale8 have a special relationship that can be short-cut.
2293 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_Grayscale8] = convert_Indexed8_to_Grayscale8;
2294 qimage_converter_map[QImage::Format_Indexed8][QImage::Format_Alpha8] = convert_Indexed8_to_Alpha8;
2295
2296 qimage_converter_map[QImage::Format_RGB32][QImage::Format_Mono] = convert_X_to_Mono;
2297 qimage_converter_map[QImage::Format_RGB32][QImage::Format_MonoLSB] = convert_X_to_Mono;
2298 qimage_converter_map[QImage::Format_RGB32][QImage::Format_Indexed8] = convert_RGB_to_Indexed8;
2299 qimage_converter_map[QImage::Format_RGB32][QImage::Format_ARGB32] = mask_alpha_converter;
2300 qimage_converter_map[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = mask_alpha_converter;
2301
2302 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_Mono] = convert_X_to_Mono;
2303 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_MonoLSB] = convert_X_to_Mono;
2304 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_Indexed8] = convert_ARGB_to_Indexed8;
2305 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_RGB32] = mask_alpha_converter;
2306 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_RGBX8888] = convert_ARGB_to_RGBx;
2307 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_RGBA8888] = convert_ARGB_to_RGBA;
2308 // ARGB32 has higher precision than ARGB32PM and needs explicit conversions to other higher color-precision formats with alpha
2309 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_A2BGR30_Premultiplied] = convert_ARGB_to_A2RGB30<PixelOrderBGR, false>;
2310 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_A2RGB30_Premultiplied] = convert_ARGB_to_A2RGB30<PixelOrderRGB, false>;
2311 qimage_converter_map[QImage::Format_ARGB32][QImage::Format_RGBA64] = convert_ARGB32_to_RGBA64<false>;
2312
2313 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_Mono] = convert_ARGB_PM_to_Mono;
2314 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_MonoLSB] = convert_ARGB_PM_to_Mono;
2315 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_Indexed8] = convert_ARGB_PM_to_Indexed8;
2316 qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = convert_ARGB_to_RGBA;
2317
2318 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB<false>;
2319 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB<false>;
2320 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB<false>;
2321 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGBX8888] = convert_RGB888_to_RGB<true>;
2322 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGBA8888] = convert_RGB888_to_RGB<true>;
2323 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGBA8888_Premultiplied] = convert_RGB888_to_RGB<true>;
2324 qimage_converter_map[QImage::Format_RGB888][QImage::Format_BGR888] = convert_rgbswap_generic;
2325
2326 qimage_converter_map[QImage::Format_RGBX8888][QImage::Format_RGB32] = convert_RGBA_to_RGB;
2327 qimage_converter_map[QImage::Format_RGBX8888][QImage::Format_ARGB32] = convert_RGBA_to_ARGB;
2328 qimage_converter_map[QImage::Format_RGBX8888][QImage::Format_ARGB32_Premultiplied] = convert_RGBA_to_ARGB;
2329 qimage_converter_map[QImage::Format_RGBX8888][QImage::Format_RGBA8888] = convert_passthrough;
2330 qimage_converter_map[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] = convert_passthrough;
2331
2332 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_RGB32] = convert_RGBA_to_RGB;
2333 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_ARGB32] = convert_RGBA_to_ARGB;
2334 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_RGBX8888] = mask_alpha_converter_RGBx;
2335 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_A2BGR30_Premultiplied] = convert_ARGB_to_A2RGB30<PixelOrderBGR, true>;
2336 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_A2RGB30_Premultiplied] = convert_ARGB_to_A2RGB30<PixelOrderRGB, true>;
2337 qimage_converter_map[QImage::Format_RGBA8888][QImage::Format_RGBA64] = convert_ARGB32_to_RGBA64<true>;
2338
2339 qimage_converter_map[QImage::Format_RGBA8888_Premultiplied][QImage::Format_ARGB32_Premultiplied] = convert_RGBA_to_ARGB;
2340
2341 qimage_converter_map[QImage::Format_BGR30][QImage::Format_A2BGR30_Premultiplied] = convert_passthrough;
2342 qimage_converter_map[QImage::Format_BGR30][QImage::Format_RGB30] = convert_rgbswap_generic;
2343 qimage_converter_map[QImage::Format_BGR30][QImage::Format_A2RGB30_Premultiplied] = convert_rgbswap_generic;
2344
2345 qimage_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_ARGB32] = convert_A2RGB30_PM_to_ARGB<PixelOrderBGR, false>;
2346 qimage_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_RGBA8888] = convert_A2RGB30_PM_to_ARGB<PixelOrderBGR, true>;
2347 qimage_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_BGR30] = convert_A2RGB30_PM_to_RGB30<false>;
2348 qimage_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_RGB30] = convert_A2RGB30_PM_to_RGB30<true>;
2349 qimage_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_A2RGB30_Premultiplied] = convert_rgbswap_generic;
2350
2351 qimage_converter_map[QImage::Format_RGB30][QImage::Format_BGR30] = convert_rgbswap_generic;
2352 qimage_converter_map[QImage::Format_RGB30][QImage::Format_A2BGR30_Premultiplied] = convert_rgbswap_generic;
2353 qimage_converter_map[QImage::Format_RGB30][QImage::Format_A2RGB30_Premultiplied] = convert_passthrough;
2354
2355 qimage_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_ARGB32] = convert_A2RGB30_PM_to_ARGB<PixelOrderRGB, false>;
2356 qimage_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_RGBA8888] = convert_A2RGB30_PM_to_ARGB<PixelOrderRGB, true>;
2357 qimage_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_BGR30] = convert_A2RGB30_PM_to_RGB30<true>;
2358 qimage_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_A2BGR30_Premultiplied] = convert_rgbswap_generic;
2359 qimage_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_RGB30] = convert_A2RGB30_PM_to_RGB30<false>;
2360
2361 qimage_converter_map[QImage::Format_Grayscale8][QImage::Format_Indexed8] = convert_Grayscale8_to_Indexed8;
2362 qimage_converter_map[QImage::Format_Alpha8][QImage::Format_Indexed8] = convert_Alpha8_to_Indexed8;
2363
2364 qimage_converter_map[QImage::Format_RGBX64][QImage::Format_RGBA64] = convert_passthrough;
2365 qimage_converter_map[QImage::Format_RGBX64][QImage::Format_RGBA64_Premultiplied] = convert_passthrough;
2366 qimage_converter_map[QImage::Format_RGBX64][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16;
2367
2368 qimage_converter_map[QImage::Format_RGBA64][QImage::Format_ARGB32] = convert_RGBA64_to_ARGB32<false>;
2369 qimage_converter_map[QImage::Format_RGBA64][QImage::Format_RGBA8888] = convert_RGBA64_to_ARGB32<true>;
2370 qimage_converter_map[QImage::Format_RGBA64][QImage::Format_RGBX64] = convert_RGBA64_to_RGBx64;
2371 qimage_converter_map[QImage::Format_RGBA64][QImage::Format_RGBA64_Premultiplied] = convert_RGBA64_to_RGBA64PM;
2372
2373 qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_RGBX64] = convert_RGBA64PM_to_RGBA64<true>;
2374 qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_RGBA64] = convert_RGBA64PM_to_RGBA64<false>;
2375 qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16;
2376
2377 qimage_converter_map[QImage::Format_Grayscale16][QImage::Format_RGBX64] = convert_gray16_to_RGBA64;
2378 qimage_converter_map[QImage::Format_Grayscale16][QImage::Format_RGBA64] = convert_gray16_to_RGBA64;
2379 qimage_converter_map[QImage::Format_Grayscale16][QImage::Format_RGBA64_Premultiplied] = convert_gray16_to_RGBA64;
2380
2381 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGB888] = convert_rgbswap_generic;
2382#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
2383 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBX8888] = convert_RGB888_to_RGB<false>;
2384 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888] = convert_RGB888_to_RGB<false>;
2385 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888_Premultiplied] = convert_RGB888_to_RGB<false>;
2386#endif
2387
2388 // Inline converters:
2389 qimage_inplace_converter_map[QImage::Format_Indexed8][QImage::Format_Grayscale8] =
2390 convert_Indexed8_to_Grayscale8_inplace;
2391 qimage_inplace_converter_map[QImage::Format_Indexed8][QImage::Format_Alpha8] =
2392 convert_Indexed8_to_Alpha8_inplace;
2393
2394 qimage_inplace_converter_map[QImage::Format_RGB32][QImage::Format_ARGB32] =
2395 mask_alpha_converter_inplace<QImage::Format_ARGB32>;
2396 qimage_inplace_converter_map[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] =
2397 mask_alpha_converter_inplace<QImage::Format_ARGB32_Premultiplied>;
2398
2399 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_RGB32] =
2400 mask_alpha_converter_inplace<QImage::Format_RGB32>;
2401 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_RGBX8888] =
2402 convert_ARGB_to_RGBA_inplace<QImage::Format_RGBX8888>;
2403 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_RGBA8888] =
2404 convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888>;
2405 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_A2BGR30_Premultiplied] =
2406 convert_ARGB_to_A2RGB30_inplace<PixelOrderBGR, false>;
2407 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_A2RGB30_Premultiplied] =
2408 convert_ARGB_to_A2RGB30_inplace<PixelOrderRGB, false>;
2409
2410 qimage_inplace_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGBA8888_Premultiplied] =
2411 convert_ARGB_to_RGBA_inplace<QImage::Format_RGBA8888_Premultiplied>;
2412
2413 qimage_inplace_converter_map[QImage::Format_RGB888][QImage::Format_BGR888] =
2414 convert_rgbswap_generic_inplace;
2415
2416 qimage_inplace_converter_map[QImage::Format_RGBX8888][QImage::Format_RGB32] =
2417 convert_RGBA_to_ARGB_inplace<QImage::Format_RGB32>;
2418 qimage_inplace_converter_map[QImage::Format_RGBX8888][QImage::Format_ARGB32] =
2419 convert_RGBA_to_ARGB_inplace<QImage::Format_ARGB32>;
2420 qimage_inplace_converter_map[QImage::Format_RGBX8888][QImage::Format_ARGB32_Premultiplied] =
2421 convert_RGBA_to_ARGB_inplace<QImage::Format_ARGB32_Premultiplied>;
2422 qimage_inplace_converter_map[QImage::Format_RGBX8888][QImage::Format_RGBA8888] =
2423 convert_passthrough_inplace<QImage::Format_RGBA8888>;
2424 qimage_inplace_converter_map[QImage::Format_RGBX8888][QImage::Format_RGBA8888_Premultiplied] =
2425 convert_passthrough_inplace<QImage::Format_RGBA8888_Premultiplied>;
2426
2427 qimage_inplace_converter_map[QImage::Format_RGBA8888][QImage::Format_RGB32] =
2428 convert_RGBA_to_ARGB_inplace<QImage::Format_RGB32>;
2429 qimage_inplace_converter_map[QImage::Format_RGBA8888][QImage::Format_ARGB32] =
2430 convert_RGBA_to_ARGB_inplace<QImage::Format_ARGB32>;
2431 qimage_inplace_converter_map[QImage::Format_RGBA8888][QImage::Format_RGBX8888] =
2432 mask_alpha_converter_rgbx_inplace;
2433 qimage_inplace_converter_map[QImage::Format_RGBA8888][QImage::Format_A2BGR30_Premultiplied] =
2434 convert_ARGB_to_A2RGB30_inplace<PixelOrderBGR, true>;
2435 qimage_inplace_converter_map[QImage::Format_RGBA8888][QImage::Format_A2RGB30_Premultiplied] =
2436 convert_ARGB_to_A2RGB30_inplace<PixelOrderRGB, true>;
2437
2438 qimage_inplace_converter_map[QImage::Format_RGBA8888_Premultiplied][QImage::Format_ARGB32_Premultiplied] =
2439 convert_RGBA_to_ARGB_inplace<QImage::Format_ARGB32_Premultiplied>;
2440
2441 qimage_inplace_converter_map[QImage::Format_BGR30][QImage::Format_A2BGR30_Premultiplied] =
2442 convert_passthrough_inplace<QImage::Format_A2BGR30_Premultiplied>;
2443 qimage_inplace_converter_map[QImage::Format_BGR30][QImage::Format_RGB30] =
2444 convert_rgbswap_generic_inplace;
2445 qimage_inplace_converter_map[QImage::Format_BGR30][QImage::Format_A2RGB30_Premultiplied] =
2446 convert_BGR30_to_A2RGB30_inplace;
2447
2448 qimage_inplace_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_ARGB32] =
2449 convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderBGR, false>;
2450 qimage_inplace_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_RGBA8888] =
2451 convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderBGR, true>;
2452 qimage_inplace_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_BGR30] =
2453 convert_A2RGB30_PM_to_RGB30_inplace<false>;
2454 qimage_inplace_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_RGB30] =
2455 convert_A2RGB30_PM_to_RGB30_inplace<true>;
2456 qimage_inplace_converter_map[QImage::Format_A2BGR30_Premultiplied][QImage::Format_A2RGB30_Premultiplied] =
2457 convert_rgbswap_generic_inplace;
2458
2459 qimage_inplace_converter_map[QImage::Format_RGB30][QImage::Format_BGR30] =
2460 convert_rgbswap_generic_inplace;
2461 qimage_inplace_converter_map[QImage::Format_RGB30][QImage::Format_A2BGR30_Premultiplied] =
2462 convert_BGR30_to_A2RGB30_inplace;
2463 qimage_inplace_converter_map[QImage::Format_RGB30][QImage::Format_A2RGB30_Premultiplied] =
2464 convert_passthrough_inplace<QImage::Format_A2RGB30_Premultiplied>;
2465
2466 qimage_inplace_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_ARGB32] =
2467 convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderRGB, false>;
2468 qimage_inplace_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_RGBA8888] =
2469 convert_A2RGB30_PM_to_ARGB_inplace<PixelOrderRGB, true>;
2470 qimage_inplace_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_BGR30] =
2471 convert_A2RGB30_PM_to_RGB30_inplace<true>;
2472 qimage_inplace_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_A2BGR30_Premultiplied] =
2473 convert_rgbswap_generic_inplace;
2474 qimage_inplace_converter_map[QImage::Format_A2RGB30_Premultiplied][QImage::Format_RGB30] =
2475 convert_A2RGB30_PM_to_RGB30_inplace<false>;
2476
2477 qimage_inplace_converter_map[QImage::Format_Grayscale8][QImage::Format_Indexed8] =
2478 convert_Grayscale8_to_Indexed8_inplace;
2479 qimage_inplace_converter_map[QImage::Format_Alpha8][QImage::Format_Indexed8] =
2480 convert_Alpha8_to_Indexed8_inplace;
2481
2482 qimage_inplace_converter_map[QImage::Format_RGBX64][QImage::Format_RGBA64] =
2483 convert_passthrough_inplace<QImage::Format_RGBA64>;
2484 qimage_inplace_converter_map[QImage::Format_RGBX64][QImage::Format_RGBA64_Premultiplied] =
2485 convert_passthrough_inplace<QImage::Format_RGBA64_Premultiplied>;
2486
2487 qimage_inplace_converter_map[QImage::Format_RGBA64][QImage::Format_RGBX64] =
2488 convert_RGBA64_to_RGBx64_inplace;
2489 qimage_inplace_converter_map[QImage::Format_RGBA64][QImage::Format_RGBA64_Premultiplied] =
2490 convert_RGBA64_to_RGBA64PM_inplace;
2491
2492 qimage_inplace_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_RGBX64] =
2493 convert_RGBA64PM_to_RGBA64_inplace<true>;
2494 qimage_inplace_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_RGBA64] =
2495 convert_RGBA64PM_to_RGBA64_inplace<false>;
2496
2497 qimage_inplace_converter_map[QImage::Format_BGR888][QImage::Format_RGB888] =
2498 convert_rgbswap_generic_inplace;
2499
2500 // Now architecture specific conversions:
2501#if defined(__SSE2__) && defined(QT_COMPILER_SUPPORTS_SSSE3)
2502 if (qCpuHasFeature(SSSE3)) {
2503 extern void convert_RGB888_to_RGB32_ssse3(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
2504 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_ssse3;
2505 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_ssse3;
2506 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_ssse3;
2507 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBX8888] = convert_RGB888_to_RGB32_ssse3;
2508 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888] = convert_RGB888_to_RGB32_ssse3;
2509 qimage_converter_map[QImage::Format_BGR888][QImage::Format_RGBA8888_Premultiplied] = convert_RGB888_to_RGB32_ssse3;
2510 }
2511#endif
2512
2513#if defined(__ARM_NEON__)
2514 extern void convert_RGB888_to_RGB32_neon(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
2515 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_neon;
2516 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_neon;
2517 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_neon;
2518#endif
2519
2520#if defined(__MIPS_DSPR2__)
2521 extern bool convert_ARGB_to_ARGB_PM_inplace_mips_dspr2(QImageData *data, Qt::ImageConversionFlags);
2522 qimage_inplace_converter_map[QImage::Format_ARGB32][QImage::Format_ARGB32_Premultiplied] = convert_ARGB_to_ARGB_PM_inplace_mips_dspr2;
2523
2524 extern void convert_RGB888_to_RGB32_mips_dspr2(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags);
2525 qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB32_mips_dspr2;
2526 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB32_mips_dspr2;
2527 qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32_Premultiplied] = convert_RGB888_to_RGB32_mips_dspr2;
2528#endif
2529}
2530
2531Q_CONSTRUCTOR_FUNCTION(qInitImageConversions);
2532
2533QT_END_NAMESPACE
2534