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