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

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