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