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