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