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