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 
58 QT_BEGIN_NAMESPACE
59 
60 struct QDefaultColorTables
61 {
QDefaultColorTablesQDefaultColorTables62     QDefaultColorTables()
63         : gray(256), alpha(256)
64     {
65         for (int i = 0; i < 256; ++i) {
66             gray[i] = qRgb(i, i, i);
67             alpha[i] = qRgba(0, 0, 0, i);
68         }
69     }
70 
71     QVector<QRgb> gray, alpha;
72 };
73 
74 Q_GLOBAL_STATIC(QDefaultColorTables, defaultColorTables);
75 
76 // table to flip bits
77 static 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 
qt_get_bitflip_array()106 const uchar *qt_get_bitflip_array()
107 {
108     return bitflip;
109 }
110 
qGamma_correct_back_to_linear_cs(QImage * image)111 void 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(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)
storeRGB32FromARGB32PM(uchar * dest,const uint * src,int index,int count,const QVector<QRgb> *,QDitherInfo *)133 static 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(src[i]);
139 }
140 #endif
141 
storeRGB32FromARGB32(uchar * dest,const uint * src,int index,int count,const QVector<QRgb> *,QDitherInfo *)142 static 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 
fetchRGB32ToARGB32PM(uint * buffer,const uchar * src,int index,int count,const QVector<QRgb> *,QDitherInfo *)150 static 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
160 extern 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)
163 extern void QT_FASTCALL storeRGB32FromARGB32PM_neon(uchar *dest, const uint *src, int index, int count,
164                                                     const QVector<QRgb> *, QDitherInfo *);
165 #endif
166 
convert_generic(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags flags)167 void 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(l, 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(segments, src->height);
238 
239     QThreadPool *threadPool = QThreadPool::globalInstance();
240     if (segments <= 1 || !threadPool || threadPool->contains(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([&, y, yn]() {
248             convertSegment(y, y + yn);
249             semaphore.release(1);
250         });
251         y += yn;
252     }
253     semaphore.acquire(segments);
254 #else
255     convertSegment(0, src->height);
256 #endif
257 }
258 
convert_generic_to_rgb64(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)259 void 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(l, 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(segments, src->height);
293 
294     QThreadPool *threadPool = QThreadPool::globalInstance();
295     if (segments <= 1 || !threadPool || threadPool->contains(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([&, y, yn]() {
303             convertSegment(y, y + yn);
304             semaphore.release(1);
305         });
306         y += yn;
307     }
308     semaphore.acquire(segments);
309 #else
310     convertSegment(0, src->height);
311 #endif
312 }
313 
convert_generic_inplace(QImageData * data,QImage::Format dst_format,Qt::ImageConversionFlags flags)314 bool 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(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(data->format, !destLayout->hasAlphaChannel)
329             && qt_highColorPrecision(dst_format, !srcLayout->hasAlphaChannel))
330         return false;
331 
332     QImageData::ImageSizeParameters params = { data->bytes_per_line, data->nbytes };
333     if (data->depth != destDepth) {
334         params = QImageData::calculateImageParameters(data->width, data->height, destDepth);
335         if (!params.isValid())
336             return false;
337     }
338 
339     Q_ASSERT(destLayout->bpp != QPixelLayout::BPP64);
340     FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM;
341     ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM;
342     if (!srcLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
343         // If the source doesn't have an alpha channel, we can use the faster storeFromRGB32 method.
344         store = destLayout->storeFromRGB32;
345     } else {
346         if (data->format == QImage::Format_RGB32)
347             fetch = fetchRGB32ToARGB32PM;
348         if (dst_format == QImage::Format_RGB32) {
349 #ifdef QT_COMPILER_SUPPORTS_SSE4_1
350             if (qCpuHasFeature(SSE4_1))
351                 store = storeRGB32FromARGB32PM_sse4;
352             else
353                 store = storeRGB32FromARGB32PM;
354 #elif defined(__ARM_NEON__) && (Q_BYTE_ORDER == Q_LITTLE_ENDIAN)
355             store = storeRGB32FromARGB32PM_neon;
356 #else
357             store = storeRGB32FromARGB32PM;
358 #endif
359         }
360     }
361     if (srcLayout->hasAlphaChannel && !srcLayout->premultiplied &&
362             !destLayout->hasAlphaChannel && destLayout->storeFromRGB32) {
363         // Avoid unnecessary premultiply and unpremultiply when converting from unpremultiplied src format.
364         fetch = qPixelLayouts[data->format + 1].fetchToARGB32PM;
365         if (data->format == QImage::Format_RGB32)
366             store = storeRGB32FromARGB32;
367         else
368             store = destLayout->storeFromRGB32;
369     }
370 
371     auto convertSegment = [=](int yStart, int yEnd) {
372         uint buf[BufferSize];
373         uint *buffer = buf;
374         uchar *srcData = data->data + data->bytes_per_line * yStart;
375         uchar *destData = srcData; // This can be temporarily wrong if we doing a shrinking conversion
376         QDitherInfo dither;
377         QDitherInfo *ditherPtr = nullptr;
378         if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither)
379             ditherPtr = &dither;
380         for (int y = yStart; y < yEnd; ++y) {
381             dither.y = y;
382             int x = 0;
383             while (x < data->width) {
384                 dither.x = x;
385                 int l = data->width - x;
386                 if (srcLayout->bpp == QPixelLayout::BPP32)
387                     buffer = reinterpret_cast<uint *>(srcData) + x;
388                 else
389                     l = qMin(l, BufferSize);
390                 const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr);
391                 store(destData, ptr, x, l, nullptr, ditherPtr);
392                 x += l;
393             }
394             srcData += data->bytes_per_line;
395             destData += params.bytesPerLine;
396         }
397     };
398 #ifdef QT_USE_THREAD_PARALLEL_IMAGE_CONVERSIONS
399     int segments = data->nbytes / (1<<16);
400     segments = std::min(segments, data->height);
401     QThreadPool *threadPool = QThreadPool::globalInstance();
402     if (segments > 1 && threadPool && !threadPool->contains(QThread::currentThread())) {
403         QSemaphore semaphore;
404         int y = 0;
405         for (int i = 0; i < segments; ++i) {
406             int yn = (data->height - y) / (segments - i);
407             threadPool->start([&, y, yn]() {
408                 convertSegment(y, y + yn);
409                 semaphore.release(1);
410             });
411             y += yn;
412         }
413         semaphore.acquire(segments);
414         if (data->bytes_per_line != params.bytesPerLine) {
415             // Compress segments to a continuous block
416             y = 0;
417             for (int i = 0; i < segments; ++i) {
418                 int yn = (data->height - y) / (segments - i);
419                 uchar *srcData = data->data + data->bytes_per_line * y;
420                 uchar *destData = data->data + params.bytesPerLine * y;
421                 if (srcData != destData)
422                     memmove(destData, srcData, params.bytesPerLine * yn);
423                 y += yn;
424             }
425         }
426     } else
427 #endif
428         convertSegment(0, data->height);
429     if (params.totalSize != data->nbytes) {
430         Q_ASSERT(params.totalSize < data->nbytes);
431         void *newData = realloc(data->data, params.totalSize);
432         if (newData) {
433             data->data = (uchar *)newData;
434             data->nbytes = params.totalSize;
435         }
436         data->bytes_per_line = params.bytesPerLine;
437     }
438     data->depth = destDepth;
439     data->format = dst_format;
440     return true;
441 }
442 
convert_passthrough(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)443 static 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_data, src_data, src_bpl);
455         src_data += src_bpl;
456         dest_data += dest_bpl;
457     }
458 }
459 
460 template<QImage::Format Format>
convert_passthrough_inplace(QImageData * data,Qt::ImageConversionFlags)461 static bool convert_passthrough_inplace(QImageData *data, Qt::ImageConversionFlags)
462 {
463     data->format = Format;
464     return true;
465 }
466 
qt_convert_rgb888_to_rgb32(quint32 * dest_data,const uchar * src_data,int len)467 Q_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 
qt_convert_rgb888_to_rgbx8888(quint32 * dest_data,const uchar * src_data,int len)502 Q_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(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(0xff000000 | (src_data[0] << 16) | (src_data[1] << 8) | (src_data[2]));
539         src_data += 3;
540         ++dest_data;
541     }
542 }
543 
544 typedef void (QT_FASTCALL *Rgb888ToRgbConverter)(quint32 *dst, const uchar *src, int len);
545 
546 template <bool rgbx>
convert_RGB888_to_RGB(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)547 static 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 
convert_ARGB_to_RGBx(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)569 static 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(0xff000000 | *src_data);
585             ++src_data;
586             ++dest_data;
587         }
588         src_data += src_pad;
589         dest_data += dest_pad;
590     }
591 }
592 
convert_ARGB_to_RGBA(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)593 static 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(*src_data);
609             ++src_data;
610             ++dest_data;
611         }
612         src_data += src_pad;
613         dest_data += dest_pad;
614     }
615 }
616 
617 template<QImage::Format DestFormat>
convert_ARGB_to_RGBA_inplace(QImageData * data,Qt::ImageConversionFlags)618 static 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(*rgb_data | mask);
630             ++rgb_data;
631         }
632         rgb_data += pad;
633     }
634 
635     data->format = DestFormat;
636     return true;
637 }
638 
convert_RGBA_to_ARGB(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)639 static 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(*src_data);
655             ++src_data;
656             ++dest_data;
657         }
658         src_data += src_pad;
659         dest_data += dest_pad;
660     }
661 }
662 
663 template<QImage::Format DestFormat>
convert_RGBA_to_ARGB_inplace(QImageData * data,Qt::ImageConversionFlags)664 static 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(*rgb_data);
676             ++rgb_data;
677         }
678         rgb_data += pad;
679     }
680     data->format = DestFormat;
681     return true;
682 }
683 
convert_rgbswap_generic(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)684 static 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 
convert_rgbswap_generic_inplace(QImageData * data,Qt::ImageConversionFlags)705 static 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 
745 template<QtPixelOrder PixelOrder, bool RGBA>
convert_ARGB_to_A2RGB30(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)746 static 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(c);
767             const uint alpha = (qAlpha(c) >> 6) * 85;
768             c = BYTE_MUL(c, 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 
778 template<QtPixelOrder PixelOrder, bool RGBA>
convert_ARGB_to_A2RGB30_inplace(QImageData * data,Qt::ImageConversionFlags)779 static 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(c);
793             const uint alpha = (qAlpha(c) >> 6) * 85;
794             c = BYTE_MUL(c, 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 
qUnpremultiplyRgb30(uint rgb30)806 static 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 
829 template<bool rgbswap>
convert_A2RGB30_PM_to_RGB30(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)830 static 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(*src_data);
846             *dest_data = (rgbswap) ? qRgbSwapRgb30(p) : p;
847             ++src_data;
848             ++dest_data;
849         }
850         src_data += src_pad;
851         dest_data += dest_pad;
852     }
853 }
854 
855 template<bool rgbswap>
convert_A2RGB30_PM_to_RGB30_inplace(QImageData * data,Qt::ImageConversionFlags)856 static 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(*rgb_data);
867             *rgb_data = (rgbswap) ? qRgbSwapRgb30(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 
convert_BGR30_to_A2RGB30_inplace(QImageData * data,Qt::ImageConversionFlags flags)880 static 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 
893 template<QtPixelOrder PixelOrder, bool RGBA>
convert_A2RGB30_PM_to_ARGB(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)894 static 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(*src_data));
910             if (RGBA)
911                 *dest_data = ARGB2RGBA(*dest_data);
912             ++src_data;
913             ++dest_data;
914         }
915         src_data += src_pad;
916         dest_data += dest_pad;
917     }
918 }
919 
920 template<QtPixelOrder PixelOrder, bool RGBA>
convert_A2RGB30_PM_to_ARGB_inplace(QImageData * data,Qt::ImageConversionFlags)921 static 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(*rgb_data));
932             if (RGBA)
933                 *rgb_data = ARGB2RGBA(*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 
convert_RGBA_to_RGB(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)945 static 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(*src_data) | 0xff000000;
961             ++src_data;
962             ++dest_data;
963         }
964         src_data += src_pad;
965         dest_data += dest_pad;
966     }
967 }
968 
swap_bit_order(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)969 static 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 
mask_alpha_converter(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)990 static 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 
1012 template<QImage::Format DestFormat>
mask_alpha_converter_inplace(QImageData * data,Qt::ImageConversionFlags)1013 static 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 
mask_alpha_converter_RGBx(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags flags)1033 static 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 
mask_alpha_converter_rgbx_inplace(QImageData * data,Qt::ImageConversionFlags flags)1060 static 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 
1083 template<bool RGBA>
convert_RGBA64_to_ARGB32(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)1084 static 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 
1104 template<bool RGBA>
convert_ARGB32_to_RGBA64(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)1105 static 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 
convert_RGBA64_to_RGBx64(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)1124 static 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 
convert_RGBA64_to_RGBx64_inplace(QImageData * data,Qt::ImageConversionFlags)1149 static 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 
convert_RGBA64_to_RGBA64PM(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)1168 static 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 
convert_RGBA64_to_RGBA64PM_inplace(QImageData * data,Qt::ImageConversionFlags)1192 static 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 
1211 template<bool MaskAlpha>
convert_RGBA64PM_to_RGBA64(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)1212 static 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 
1238 template<bool MaskAlpha>
convert_RGBA64PM_to_RGBA64_inplace(QImageData * data,Qt::ImageConversionFlags)1239 static 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 
convert_gray16_to_RGBA64(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)1260 static 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(s, s, s, 0xFFFF);
1279         }
1280         src_data += sbpl;
1281         dest_data += dbpl;
1282     }
1283 }
1284 
convert_RGBA64_to_gray16(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)1285 static 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(s.red(), s.green(), s.blue());
1304         }
1305         src_data += sbpl;
1306         dest_data += dbpl;
1307     }
1308 }
1309 
fix_color_table(const QVector<QRgb> & ctbl,QImage::Format format)1310 static 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(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(colorTable.at(i));
1322     }
1323     return colorTable;
1324 }
1325 
1326 //
1327 // dither_to_1:  Uses selected dithering algorithm.
1328 //
1329 
dither_to_Mono(QImageData * dst,const QImageData * src,Qt::ImageConversionFlags flags,bool fromalpha)1330 void 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(0xffffffff);
1339     dst->colortable.append(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(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(*(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(*(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(p, 0, 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(dst->data, 0, 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(*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(dst->data, 0, 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(*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 
convert_X_to_Mono(QImageData * dst,const QImageData * src,Qt::ImageConversionFlags flags)1603 static void convert_X_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1604 {
1605     dither_to_Mono(dst, src, flags, false);
1606 }
1607 
convert_ARGB_PM_to_Mono(QImageData * dst,const QImageData * src,Qt::ImageConversionFlags flags)1608 static void convert_ARGB_PM_to_Mono(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1609 {
1610     QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32));
1611     convert_generic(tmp.data(), src, Qt::AutoColor);
1612     dither_to_Mono(dst, tmp.data(), flags, 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 //
1623 struct QRgbMap {
QRgbMapQRgbMap1624     inline QRgbMap() : used(0) { }
1625     uchar  pix;
1626     uchar used;
1627     QRgb  rgb;
1628 };
1629 
convert_RGB_to_Indexed8(QImageData * dst,const QImageData * src,Qt::ImageConversionFlags flags)1630 static 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(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(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(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(rc*255/MAX_R, gc*255/MAX_G, 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(qMin(5, (l1[x] * 5 + 128)/ 255), 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(qMin(5, (l1[x] * 5 + 128)/ 255), 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(QSize(src->width, src->height), QImage::Format_Mono));
1866             dither_to_Mono(mask.data(), src, flags, 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 
convert_ARGB_PM_to_Indexed8(QImageData * dst,const QImageData * src,Qt::ImageConversionFlags flags)1888 static void convert_ARGB_PM_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1889 {
1890     QScopedPointer<QImageData> tmp(QImageData::create(QSize(src->width, src->height), QImage::Format_ARGB32));
1891     convert_generic(tmp.data(), src, Qt::AutoColor);
1892     convert_RGB_to_Indexed8(dst, tmp.data(), flags);
1893 }
1894 
convert_ARGB_to_Indexed8(QImageData * dst,const QImageData * src,Qt::ImageConversionFlags flags)1895 static void convert_ARGB_to_Indexed8(QImageData *dst, const QImageData *src, Qt::ImageConversionFlags flags)
1896 {
1897     convert_RGB_to_Indexed8(dst, src, flags);
1898 }
1899 
convert_Indexed8_to_X32(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)1900 static 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(src->colortable, dest->format) : src->colortable;
1910     if (colorTable.size() == 0) {
1911         colorTable.resize(256);
1912         for (int i=0; i<256; ++i)
1913             colorTable[i] = qRgb(i, i, i);
1914     }
1915     if (colorTable.size() < 256) {
1916         int tableSize = colorTable.size();
1917         colorTable.resize(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 
convert_Mono_to_X32(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)1940 static 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(src->colortable, 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((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((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 
convert_Mono_to_Indexed8(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)1982 static 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(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 
copy_8bit_pixels(QImageData * dest,const QImageData * src)2022 static void copy_8bit_pixels(QImageData *dest, const QImageData *src)
2023 {
2024     if (src->bytes_per_line == dest->bytes_per_line) {
2025         memcpy(dest->data, src->data, 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(ddata, sdata, src->width);
2031             sdata += src->bytes_per_line;
2032             ddata += dest->bytes_per_line;
2033         }
2034     }
2035 }
2036 
convert_Indexed8_to_Alpha8(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)2037 static 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(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 
convert_Indexed8_to_Grayscale8(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)2065 static 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(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 
convert_Indexed8_to_Alpha8_inplace(QImageData * data,Qt::ImageConversionFlags)2093 static 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(colors[i]))
2103             return false;
2104     }
2105 
2106     data->colortable.clear();
2107     data->format = QImage::Format_Alpha8;
2108 
2109     return true;
2110 }
2111 
convert_Indexed8_to_Grayscale8_inplace(QImageData * data,Qt::ImageConversionFlags)2112 static 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(colors[i]))
2122             return false;
2123     }
2124 
2125     data->colortable.clear();
2126     data->format = QImage::Format_Grayscale8;
2127 
2128     return true;
2129 }
2130 
convert_Alpha8_to_Indexed8(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)2131 static 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 
convert_Grayscale8_to_Indexed8(QImageData * dest,const QImageData * src,Qt::ImageConversionFlags)2141 static 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 
convert_Alpha8_to_Indexed8_inplace(QImageData * data,Qt::ImageConversionFlags)2151 static 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 
convert_Grayscale8_to_Indexed8_inplace(QImageData * data,Qt::ImageConversionFlags)2161 static 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
2173 Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = {};
2174 InPlace_Image_Converter qimage_inplace_converter_map[QImage::NImageFormats][QImage::NImageFormats] = {};
2175 
qInitImageConversions()2176 static 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 
2437 Q_CONSTRUCTOR_FUNCTION(qInitImageConversions);
2438 
2439 QT_END_NAMESPACE
2440