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