1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include "qimage.h"
6
7#include "qbuffer.h"
8#include "qdatastream.h"
9#include "qcolortransform.h"
10#include "qfloat16.h"
11#include "qmap.h"
12#include "qtransform.h"
13#include "qimagereader.h"
14#include "qimagewriter.h"
15#include "qrgbafloat.h"
16#include "qstringlist.h"
17#include "qvariant.h"
18#include "qimagepixmapcleanuphooks_p.h"
19#include <qpa/qplatformintegration.h>
20#include <private/qguiapplication_p.h>
21#include <ctype.h>
22#include <stdlib.h>
23#include <limits.h>
24#include <qpa/qplatformpixmap.h>
25#include <qalloc.h>
26#include <private/qcolorspace_p.h>
27#include <private/qcolortransform_p.h>
28#include <private/qmemrotate_p.h>
29#include <private/qimagescale_p.h>
30#include <private/qpixellayout_p.h>
31#include <private/qsimd_p.h>
32
33#include <qhash.h>
34
35#include <private/qpaintengine_raster_p.h>
36
37#include <private/qimage_p.h>
38#include <private/qfont_p.h>
39
40#if QT_CONFIG(qtgui_threadpool)
41#include <qsemaphore.h>
42#include <qthreadpool.h>
43#include <private/qthreadpool_p.h>
44#endif
45
46#include <qtgui_tracepoints_p.h>
47
48#include <memory>
49
50QT_BEGIN_NAMESPACE
51class QCmyk32;
52
53using namespace Qt::StringLiterals;
54
55// MSVC 19.28 does show spurious warning "C4723: potential divide by 0" for code that divides
56// by height() in release builds. Anyhow, all the code paths in this file are only executed
57// for valid QImage's, where height() cannot be 0. Therefore disable the warning.
58QT_WARNING_DISABLE_MSVC(4723)
59
60#if defined(Q_CC_DEC) && defined(__alpha) && (__DECCXX_VER-0 >= 50190001)
61#pragma message disable narrowptr
62#endif
63
64
65#define QIMAGE_SANITYCHECK_MEMORY(image) \
66 if ((image).isNull()) { \
67 qWarning("QImage: out of memory, returning null image"); \
68 return QImage(); \
69 }
70
71Q_TRACE_PREFIX(qtgui,
72 "#include <qimagereader.h>"
73);
74
75Q_TRACE_METADATA(qtgui,
76"ENUM { } QImage::Format;" \
77"FLAGS { } Qt::ImageConversionFlags;"
78);
79
80Q_TRACE_PARAM_REPLACE(Qt::AspectRatioMode, int);
81Q_TRACE_PARAM_REPLACE(Qt::TransformationMode, int);
82
83static QImage rotated90(const QImage &src);
84static QImage rotated180(const QImage &src);
85static QImage rotated270(const QImage &src);
86
87static int next_qimage_serial_number()
88{
89 Q_CONSTINIT static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
90 return 1 + serial.fetchAndAddRelaxed(valueToAdd: 1);
91}
92
93QImageData::QImageData()
94 : ref(0), width(0), height(0), depth(0), nbytes(0), devicePixelRatio(1.0), data(nullptr),
95 format(QImage::Format_ARGB32), bytes_per_line(0),
96 ser_no(next_qimage_serial_number()),
97 detach_no(0),
98 dpmx(qt_defaultDpiX() * 100 / qreal(2.54)),
99 dpmy(qt_defaultDpiY() * 100 / qreal(2.54)),
100 offset(0, 0), own_data(true), ro_data(false), has_alpha_clut(false),
101 is_cached(false), cleanupFunction(nullptr), cleanupInfo(nullptr),
102 paintEngine(nullptr)
103{
104}
105
106/*! \fn QImageData * QImageData::create(const QSize &size, QImage::Format format)
107
108 \internal
109
110 Creates a new image data.
111 Returns \nullptr if invalid parameters are give or anything else failed.
112*/
113QImageData * Q_TRACE_INSTRUMENT(qtgui) QImageData::create(const QSize &size, QImage::Format format)
114{
115 if (size.isEmpty() || format <= QImage::Format_Invalid || format >= QImage::NImageFormats)
116 return nullptr; // invalid parameter(s)
117
118 Q_TRACE_SCOPE(QImageData_create, size, format);
119
120 int width = size.width();
121 int height = size.height();
122 int depth = qt_depthForFormat(format);
123 auto params = calculateImageParameters(width, height, depth);
124 if (!params.isValid())
125 return nullptr;
126
127 auto d = std::make_unique<QImageData>();
128
129 switch (format) {
130 case QImage::Format_Mono:
131 case QImage::Format_MonoLSB:
132 d->colortable.resize(size: 2);
133 d->colortable[0] = QColor(Qt::black).rgba();
134 d->colortable[1] = QColor(Qt::white).rgba();
135 break;
136 default:
137 break;
138 }
139
140 d->width = width;
141 d->height = height;
142 d->depth = depth;
143 d->format = format;
144 d->has_alpha_clut = false;
145 d->is_cached = false;
146
147 d->bytes_per_line = params.bytesPerLine;
148 d->nbytes = params.totalSize;
149 d->data = (uchar *)malloc(size: d->nbytes);
150
151 if (!d->data)
152 return nullptr;
153
154 d->ref.ref();
155 return d.release();
156}
157
158QImageData::~QImageData()
159{
160 if (cleanupFunction)
161 cleanupFunction(cleanupInfo);
162 if (is_cached)
163 QImagePixmapCleanupHooks::executeImageHooks(key: (((qint64) ser_no) << 32) | ((qint64) detach_no));
164 delete paintEngine;
165 if (data && own_data)
166 QtPrivate::sizedFree(ptr: data, allocSize: nbytes);
167 data = nullptr;
168}
169
170#if defined(_M_ARM) && defined(_MSC_VER)
171#pragma optimize("", off)
172#endif
173
174bool QImageData::checkForAlphaPixels() const
175{
176 bool has_alpha_pixels = false;
177
178 switch (format) {
179
180 case QImage::Format_Mono:
181 case QImage::Format_MonoLSB:
182 case QImage::Format_Indexed8:
183 has_alpha_pixels = has_alpha_clut;
184 break;
185 case QImage::Format_Alpha8:
186 has_alpha_pixels = true;
187 break;
188 case QImage::Format_ARGB32:
189 case QImage::Format_ARGB32_Premultiplied: {
190 const uchar *bits = data;
191 for (int y=0; y<height && !has_alpha_pixels; ++y) {
192 uint alphaAnd = 0xff000000;
193 for (int x=0; x<width; ++x)
194 alphaAnd &= reinterpret_cast<const uint*>(bits)[x];
195 has_alpha_pixels = (alphaAnd != 0xff000000);
196 bits += bytes_per_line;
197 }
198 } break;
199
200 case QImage::Format_RGBA8888:
201 case QImage::Format_RGBA8888_Premultiplied: {
202 const uchar *bits = data;
203 for (int y=0; y<height && !has_alpha_pixels; ++y) {
204 uchar alphaAnd = 0xff;
205 for (int x=0; x<width; ++x)
206 alphaAnd &= bits[x * 4+ 3];
207 has_alpha_pixels = (alphaAnd != 0xff);
208 bits += bytes_per_line;
209 }
210 } break;
211
212 case QImage::Format_A2BGR30_Premultiplied:
213 case QImage::Format_A2RGB30_Premultiplied: {
214 const uchar *bits = data;
215 for (int y=0; y<height && !has_alpha_pixels; ++y) {
216 uint alphaAnd = 0xc0000000;
217 for (int x=0; x<width; ++x)
218 alphaAnd &= reinterpret_cast<const uint*>(bits)[x];
219 has_alpha_pixels = (alphaAnd != 0xc0000000);
220 bits += bytes_per_line;
221 }
222 } break;
223
224 case QImage::Format_ARGB8555_Premultiplied:
225 case QImage::Format_ARGB8565_Premultiplied: {
226 const uchar *bits = data;
227 const uchar *end_bits = data + bytes_per_line;
228
229 for (int y=0; y<height && !has_alpha_pixels; ++y) {
230 uchar alphaAnd = 0xff;
231 while (bits < end_bits) {
232 alphaAnd &= bits[0];
233 bits += 3;
234 }
235 has_alpha_pixels = (alphaAnd != 0xff);
236 bits = end_bits;
237 end_bits += bytes_per_line;
238 }
239 } break;
240
241 case QImage::Format_ARGB6666_Premultiplied: {
242 const uchar *bits = data;
243 const uchar *end_bits = data + bytes_per_line;
244
245 for (int y=0; y<height && !has_alpha_pixels; ++y) {
246 uchar alphaAnd = 0xfc;
247 while (bits < end_bits) {
248 alphaAnd &= bits[0];
249 bits += 3;
250 }
251 has_alpha_pixels = (alphaAnd != 0xfc);
252 bits = end_bits;
253 end_bits += bytes_per_line;
254 }
255 } break;
256
257 case QImage::Format_ARGB4444_Premultiplied: {
258 const uchar *bits = data;
259 for (int y=0; y<height && !has_alpha_pixels; ++y) {
260 ushort alphaAnd = 0xf000;
261 for (int x=0; x<width; ++x)
262 alphaAnd &= reinterpret_cast<const ushort*>(bits)[x];
263 has_alpha_pixels = (alphaAnd != 0xf000);
264 bits += bytes_per_line;
265 }
266 } break;
267 case QImage::Format_RGBA64:
268 case QImage::Format_RGBA64_Premultiplied: {
269 uchar *bits = data;
270 for (int y=0; y<height && !has_alpha_pixels; ++y) {
271 for (int x=0; x<width; ++x) {
272 has_alpha_pixels |= !(((QRgba64 *)bits)[x].isOpaque());
273 }
274 bits += bytes_per_line;
275 }
276 } break;
277 case QImage::Format_RGBA16FPx4:
278 case QImage::Format_RGBA16FPx4_Premultiplied: {
279 uchar *bits = data;
280 for (int y = 0; y < height && !has_alpha_pixels; ++y) {
281 for (int x = 0; x < width; ++x)
282 has_alpha_pixels |= ((qfloat16 *)bits)[x * 4 + 3] < 1.0f;
283 bits += bytes_per_line;
284 }
285 } break;
286 case QImage::Format_RGBA32FPx4:
287 case QImage::Format_RGBA32FPx4_Premultiplied: {
288 uchar *bits = data;
289 for (int y = 0; y < height && !has_alpha_pixels; ++y) {
290 for (int x = 0; x < width; ++x)
291 has_alpha_pixels |= ((float *)bits)[x * 4 + 3] < 1.0f;
292 bits += bytes_per_line;
293 }
294 } break;
295
296 case QImage::Format_RGB32:
297 case QImage::Format_RGB16:
298 case QImage::Format_RGB444:
299 case QImage::Format_RGB555:
300 case QImage::Format_RGB666:
301 case QImage::Format_RGB888:
302 case QImage::Format_BGR888:
303 case QImage::Format_RGBX8888:
304 case QImage::Format_BGR30:
305 case QImage::Format_RGB30:
306 case QImage::Format_Grayscale8:
307 case QImage::Format_Grayscale16:
308 case QImage::Format_RGBX64:
309 case QImage::Format_RGBX16FPx4:
310 case QImage::Format_RGBX32FPx4:
311 case QImage::Format_CMYK8888:
312 break;
313 case QImage::Format_Invalid:
314 case QImage::NImageFormats:
315 Q_UNREACHABLE();
316 break;
317 }
318
319 return has_alpha_pixels;
320}
321#if defined(_M_ARM) && defined(_MSC_VER)
322#pragma optimize("", on)
323#endif
324
325/*!
326 \class QImage
327
328 \inmodule QtGui
329 \ingroup painting
330 \ingroup shared
331
332 \reentrant
333
334 \brief The QImage class provides a hardware-independent image
335 representation that allows direct access to the pixel data, and
336 can be used as a paint device.
337
338 Qt provides four classes for handling image data: QImage, QPixmap,
339 QBitmap and QPicture. QImage is designed and optimized for I/O,
340 and for direct pixel access and manipulation, while QPixmap is
341 designed and optimized for showing images on screen. QBitmap is
342 only a convenience class that inherits QPixmap, ensuring a
343 depth of 1. Finally, the QPicture class is a paint device that
344 records and replays QPainter commands.
345
346 Because QImage is a QPaintDevice subclass, QPainter can be used to
347 draw directly onto images. When using QPainter on a QImage, the
348 painting can be performed in another thread than the current GUI
349 thread.
350
351 The QImage class supports several image formats described by the
352 \l Format enum. These include monochrome, 8-bit, 32-bit and
353 alpha-blended images which are available in all versions of Qt
354 4.x.
355
356 QImage provides a collection of functions that can be used to
357 obtain a variety of information about the image. There are also
358 several functions that enables transformation of the image.
359
360 QImage objects can be passed around by value since the QImage
361 class uses \l{Implicit Data Sharing}{implicit data
362 sharing}. QImage objects can also be streamed and compared.
363
364 \note If you would like to load QImage objects in a static build of Qt,
365 refer to the \l{How to Create Qt Plugins}{Plugin HowTo}.
366
367 \warning Painting on a QImage with the format
368 QImage::Format_Indexed8 or QImage::Format_CMYK8888 is not supported.
369
370 \section1 Reading and Writing Image Files
371
372 QImage provides several ways of loading an image file: The file
373 can be loaded when constructing the QImage object, or by using the
374 load() or loadFromData() functions later on. QImage also provides
375 the static fromData() function, constructing a QImage from the
376 given data. When loading an image, the file name can either refer
377 to an actual file on disk or to one of the application's embedded
378 resources. See \l{The Qt Resource System} overview for details
379 on how to embed images and other resource files in the
380 application's executable.
381
382 Simply call the save() function to save a QImage object.
383
384 The complete list of supported file formats are available through
385 the QImageReader::supportedImageFormats() and
386 QImageWriter::supportedImageFormats() functions. New file formats
387 can be added as plugins. By default, Qt supports the following
388 formats:
389
390 \table
391 \header \li Format \li Description \li Qt's support
392 \row \li BMP \li Windows Bitmap \li Read/write
393 \row \li GIF \li Graphic Interchange Format (optional) \li Read
394 \row \li JPG \li Joint Photographic Experts Group \li Read/write
395 \row \li JPEG \li Joint Photographic Experts Group \li Read/write
396 \row \li PNG \li Portable Network Graphics \li Read/write
397 \row \li PBM \li Portable Bitmap \li Read
398 \row \li PGM \li Portable Graymap \li Read
399 \row \li PPM \li Portable Pixmap \li Read/write
400 \row \li XBM \li X11 Bitmap \li Read/write
401 \row \li XPM \li X11 Pixmap \li Read/write
402 \endtable
403
404 \section1 Image Information
405
406 QImage provides a collection of functions that can be used to
407 obtain a variety of information about the image:
408
409 \table
410 \header
411 \li \li Available Functions
412
413 \row
414 \li Geometry
415 \li
416
417 The size(), width(), height(), dotsPerMeterX(), and
418 dotsPerMeterY() functions provide information about the image size
419 and aspect ratio.
420
421 The rect() function returns the image's enclosing rectangle. The
422 valid() function tells if a given pair of coordinates is within
423 this rectangle. The offset() function returns the number of pixels
424 by which the image is intended to be offset by when positioned
425 relative to other images, which also can be manipulated using the
426 setOffset() function.
427
428 \row
429 \li Colors
430 \li
431
432 The color of a pixel can be retrieved by passing its coordinates
433 to the pixel() function. The pixel() function returns the color
434 as a QRgb value independent of the image's format.
435
436 In case of monochrome and 8-bit images, the colorCount() and
437 colorTable() functions provide information about the color
438 components used to store the image data: The colorTable() function
439 returns the image's entire color table. To obtain a single entry,
440 use the pixelIndex() function to retrieve the pixel index for a
441 given pair of coordinates, then use the color() function to
442 retrieve the color. Note that if you create an 8-bit image
443 manually, you have to set a valid color table on the image as
444 well.
445
446 The hasAlphaChannel() function tells if the image's format
447 respects the alpha channel, or not. The allGray() and
448 isGrayscale() functions tell whether an image's colors are all
449 shades of gray.
450
451 See also the \l {QImage#Pixel Manipulation}{Pixel Manipulation}
452 and \l {QImage#Image Transformations}{Image Transformations}
453 sections.
454
455 \row
456 \li Text
457 \li
458
459 The text() function returns the image text associated with the
460 given text key. An image's text keys can be retrieved using the
461 textKeys() function. Use the setText() function to alter an
462 image's text.
463
464 \row
465 \li Low-level information
466 \li
467
468 The depth() function returns the depth of the image. The supported
469 depths are 1 (monochrome), 8, 16, 24 and 32 bits. The
470 bitPlaneCount() function tells how many of those bits that are
471 used. For more information see the
472 \l {QImage#Image Formats}{Image Formats} section.
473
474 The format(), bytesPerLine(), and sizeInBytes() functions provide
475 low-level information about the data stored in the image.
476
477 The cacheKey() function returns a number that uniquely
478 identifies the contents of this QImage object.
479 \endtable
480
481 \section1 Pixel Manipulation
482
483 The functions used to manipulate an image's pixels depend on the
484 image format. The reason is that monochrome and 8-bit images are
485 index-based and use a color lookup table, while 32-bit images
486 store ARGB values directly. For more information on image formats,
487 see the \l {Image Formats} section.
488
489 In case of a 32-bit image, the setPixel() function can be used to
490 alter the color of the pixel at the given coordinates to any other
491 color specified as an ARGB quadruplet. To make a suitable QRgb
492 value, use the qRgb() (adding a default alpha component to the
493 given RGB values, i.e. creating an opaque color) or qRgba()
494 function. For example:
495
496 \table
497 \header
498 \li {2,1}32-bit
499 \row
500 \li \inlineimage qimage-32bit_scaled.png
501 \li
502 \snippet code/src_gui_image_qimage.cpp 0
503 \endtable
504
505 In case of a 8-bit and monchrome images, the pixel value is only
506 an index from the image's color table. So the setPixel() function
507 can only be used to alter the color of the pixel at the given
508 coordinates to a predefined color from the image's color table,
509 i.e. it can only change the pixel's index value. To alter or add a
510 color to an image's color table, use the setColor() function.
511
512 An entry in the color table is an ARGB quadruplet encoded as an
513 QRgb value. Use the qRgb() and qRgba() functions to make a
514 suitable QRgb value for use with the setColor() function. For
515 example:
516
517 \table
518 \header
519 \li {2,1} 8-bit
520 \row
521 \li \inlineimage qimage-8bit_scaled.png
522 \li
523 \snippet code/src_gui_image_qimage.cpp 1
524 \endtable
525
526 For images with more than 8-bit per color-channel. The methods
527 setPixelColor() and pixelColor() can be used to set and get
528 with QColor values.
529
530 QImage also provide the scanLine() function which returns a
531 pointer to the pixel data at the scanline with the given index,
532 and the bits() function which returns a pointer to the first pixel
533 data (this is equivalent to \c scanLine(0)).
534
535 \section1 Image Formats
536
537 Each pixel stored in a QImage is represented by an integer. The
538 size of the integer varies depending on the format. QImage
539 supports several image formats described by the \l Format
540 enum.
541
542 Monochrome images are stored using 1-bit indexes into a color table
543 with at most two colors. There are two different types of
544 monochrome images: big endian (MSB first) or little endian (LSB
545 first) bit order.
546
547 8-bit images are stored using 8-bit indexes into a color table,
548 i.e. they have a single byte per pixel. The color table is a
549 QList<QRgb>, and the QRgb typedef is equivalent to an unsigned
550 int containing an ARGB quadruplet on the format 0xAARRGGBB.
551
552 32-bit images have no color table; instead, each pixel contains an
553 QRgb value. There are three different types of 32-bit images
554 storing RGB (i.e. 0xffRRGGBB), ARGB and premultiplied ARGB
555 values respectively. In the premultiplied format the red, green,
556 and blue channels are multiplied by the alpha component divided by
557 255.
558
559 An image's format can be retrieved using the format()
560 function. Use the convertToFormat() functions to convert an image
561 into another format. The allGray() and isGrayscale() functions
562 tell whether a color image can safely be converted to a grayscale
563 image.
564
565 \section1 Image Transformations
566
567 QImage supports a number of functions for creating a new image
568 that is a transformed version of the original: The
569 createAlphaMask() function builds and returns a 1-bpp mask from
570 the alpha buffer in this image, and the createHeuristicMask()
571 function creates and returns a 1-bpp heuristic mask for this
572 image. The latter function works by selecting a color from one of
573 the corners, then chipping away pixels of that color starting at
574 all the edges.
575
576 The mirrored() function returns a mirror of the image in the
577 desired direction, the scaled() returns a copy of the image scaled
578 to a rectangle of the desired measures, and the rgbSwapped() function
579 constructs a BGR image from a RGB image.
580
581 The scaledToWidth() and scaledToHeight() functions return scaled
582 copies of the image.
583
584 The transformed() function returns a copy of the image that is
585 transformed with the given transformation matrix and
586 transformation mode: Internally, the transformation matrix is
587 adjusted to compensate for unwanted translation,
588 i.e. transformed() returns the smallest image containing all
589 transformed points of the original image. The static trueMatrix()
590 function returns the actual matrix used for transforming the
591 image.
592
593 There are also functions for changing attributes of an image
594 in-place:
595
596 \table
597 \header \li Function \li Description
598 \row
599 \li setDotsPerMeterX()
600 \li Defines the aspect ratio by setting the number of pixels that fit
601 horizontally in a physical meter.
602 \row
603 \li setDotsPerMeterY()
604 \li Defines the aspect ratio by setting the number of pixels that fit
605 vertically in a physical meter.
606 \row
607 \li fill()
608 \li Fills the entire image with the given pixel value.
609 \row
610 \li invertPixels()
611 \li Inverts all pixel values in the image using the given InvertMode value.
612 \row
613 \li setColorTable()
614 \li Sets the color table used to translate color indexes. Only
615 monochrome and 8-bit formats.
616 \row
617 \li setColorCount()
618 \li Resizes the color table. Only monochrome and 8-bit formats.
619
620 \endtable
621
622 \sa QImageReader, QImageWriter, QPixmap, QSvgRenderer,
623 {Image Composition Example}, {Scribble Example}
624*/
625
626/*!
627 \fn QImage::QImage(QImage &&other)
628
629 Move-constructs a QImage instance, making it point at the same
630 object that \a other was pointing to.
631
632 \since 5.2
633*/
634
635/*!
636 \fn QImage &QImage::operator=(QImage &&other)
637
638 Move-assigns \a other to this QImage instance.
639
640 \since 5.2
641*/
642
643/*!
644 \typedef QImageCleanupFunction
645 \relates QImage
646 \since 5.0
647
648 A function with the following signature that can be used to
649 implement basic image memory management:
650
651 \code
652 void myImageCleanupHandler(void *info);
653 \endcode
654*/
655
656/*!
657 \enum QImage::InvertMode
658
659 This enum type is used to describe how pixel values should be
660 inverted in the invertPixels() function.
661
662 \value InvertRgb Invert only the RGB values and leave the alpha
663 channel unchanged.
664
665 \value InvertRgba Invert all channels, including the alpha channel.
666
667 \sa invertPixels()
668*/
669
670/*!
671 \enum QImage::Format
672
673 The following image formats are available in Qt.
674 See the notes after the table.
675
676 \value Format_Invalid The image is invalid.
677 \value Format_Mono The image is stored using 1-bit per pixel. Bytes are
678 packed with the most significant bit (MSB) first.
679 \value Format_MonoLSB The image is stored using 1-bit per pixel. Bytes are
680 packed with the less significant bit (LSB) first.
681
682 \value Format_Indexed8 The image is stored using 8-bit indexes
683 into a colormap.
684
685 \value Format_RGB32 The image is stored using a 32-bit RGB format (0xffRRGGBB).
686
687 \value Format_ARGB32 The image is stored using a 32-bit ARGB
688 format (0xAARRGGBB).
689
690 \value Format_ARGB32_Premultiplied The image is stored using a premultiplied 32-bit
691 ARGB format (0xAARRGGBB), i.e. the red,
692 green, and blue channels are multiplied
693 by the alpha component divided by 255. (If RR, GG, or BB
694 has a higher value than the alpha channel, the results are
695 undefined.) Certain operations (such as image composition
696 using alpha blending) are faster using premultiplied ARGB32
697 than with plain ARGB32.
698
699 \value Format_RGB16 The image is stored using a 16-bit RGB format (5-6-5).
700
701 \value Format_ARGB8565_Premultiplied The image is stored using a
702 premultiplied 24-bit ARGB format (8-5-6-5).
703 \value Format_RGB666 The image is stored using a 24-bit RGB format (6-6-6).
704 The unused most significant bits is always zero.
705 \value Format_ARGB6666_Premultiplied The image is stored using a
706 premultiplied 24-bit ARGB format (6-6-6-6).
707 \value Format_RGB555 The image is stored using a 16-bit RGB format (5-5-5).
708 The unused most significant bit is always zero.
709 \value Format_ARGB8555_Premultiplied The image is stored using a
710 premultiplied 24-bit ARGB format (8-5-5-5).
711 \value Format_RGB888 The image is stored using a 24-bit RGB format (8-8-8).
712 \value Format_RGB444 The image is stored using a 16-bit RGB format (4-4-4).
713 The unused bits are always zero.
714 \value Format_ARGB4444_Premultiplied The image is stored using a
715 premultiplied 16-bit ARGB format (4-4-4-4).
716 \value [since 5.2]
717 Format_RGBX8888 The image is stored using a 32-bit byte-ordered RGB(x) format (8-8-8-8).
718 This is the same as the Format_RGBA8888 except alpha must always be 255.
719 \value [since 5.2]
720 Format_RGBA8888 The image is stored using a 32-bit byte-ordered RGBA format (8-8-8-8).
721 Unlike ARGB32 this is a byte-ordered format, which means the 32bit
722 encoding differs between big endian and little endian architectures,
723 being respectively (0xRRGGBBAA) and (0xAABBGGRR). The order of the colors
724 is the same on any architecture if read as bytes 0xRR,0xGG,0xBB,0xAA.
725 \value [since 5.2]
726 Format_RGBA8888_Premultiplied The image is stored using a
727 premultiplied 32-bit byte-ordered RGBA format (8-8-8-8).
728 \value [since 5.4]
729 Format_BGR30 The image is stored using a 32-bit BGR format (x-10-10-10).
730 \value [since 5.4]
731 Format_A2BGR30_Premultiplied The image is stored using a 32-bit premultiplied ABGR format (2-10-10-10).
732 \value [since 5.4]
733 Format_RGB30 The image is stored using a 32-bit RGB format (x-10-10-10).
734 \value [since 5.4]
735 Format_A2RGB30_Premultiplied The image is stored using a 32-bit premultiplied ARGB format (2-10-10-10).
736 \value [since 5.5]
737 Format_Alpha8 The image is stored using an 8-bit alpha only format.
738 \value [since 5.5]
739 Format_Grayscale8 The image is stored using an 8-bit grayscale format.
740 \value [since 5.13]
741 Format_Grayscale16 The image is stored using an 16-bit grayscale format.
742 \value [since 5.12]
743 Format_RGBX64 The image is stored using a 64-bit halfword-ordered RGB(x) format (16-16-16-16).
744 This is the same as the Format_RGBA64 except alpha must always be 65535.
745 \value [since 5.12]
746 Format_RGBA64 The image is stored using a 64-bit halfword-ordered RGBA format (16-16-16-16).
747 \value [since 5.12]
748 Format_RGBA64_Premultiplied The image is stored using a premultiplied 64-bit halfword-ordered
749 RGBA format (16-16-16-16).
750 \value [since 5.14]
751 Format_BGR888 The image is stored using a 24-bit BGR format.
752 \value [since 6.2]
753 Format_RGBX16FPx4 The image is stored using a four 16-bit halfword floating point RGBx format (16FP-16FP-16FP-16FP).
754 This is the same as the Format_RGBA16FPx4 except alpha must always be 1.0.
755 \value [since 6.2]
756 Format_RGBA16FPx4 The image is stored using a four 16-bit halfword floating point RGBA format (16FP-16FP-16FP-16FP).
757 \value [since 6.2]
758 Format_RGBA16FPx4_Premultiplied The image is stored using a premultiplied four 16-bit halfword floating point
759 RGBA format (16FP-16FP-16FP-16FP).
760 \value [since 6.2]
761 Format_RGBX32FPx4 The image is stored using a four 32-bit floating point RGBx format (32FP-32FP-32FP-32FP).
762 This is the same as the Format_RGBA32FPx4 except alpha must always be 1.0.
763 \value [since 6.2]
764 Format_RGBA32FPx4 The image is stored using a four 32-bit floating point RGBA format (32FP-32FP-32FP-32FP).
765 \value [since 6.2]
766 Format_RGBA32FPx4_Premultiplied The image is stored using a premultiplied four 32-bit floating point
767 RGBA format (32FP-32FP-32FP-32FP).
768 \value [since 6.8]
769 Format_CMYK8888 The image is stored using a 32-bit byte-ordered CMYK format.
770
771 \note Drawing into a QImage with format QImage::Format_Indexed8 or QImage::Format_CMYK8888 is not
772 supported.
773
774 \note Avoid most rendering directly to most of these formats using QPainter. Rendering
775 is best optimized to the \c Format_RGB32 and \c Format_ARGB32_Premultiplied formats, and secondarily for rendering to the
776 \c Format_RGB16, \c Format_RGBX8888, \c Format_RGBA8888_Premultiplied, \c Format_RGBX64 and \c Format_RGBA64_Premultiplied formats
777
778 \sa format(), convertToFormat()
779*/
780
781/*****************************************************************************
782 QImage member functions
783 *****************************************************************************/
784
785/*!
786 Constructs a null image.
787
788 \sa isNull()
789*/
790
791QImage::QImage() noexcept
792 : QPaintDevice()
793{
794 d = nullptr;
795}
796
797/*!
798 Constructs an image with the given \a width, \a height and \a
799 format.
800
801 A \l{isNull()}{null} image will be returned if memory cannot be allocated.
802
803 \warning This will create a QImage with uninitialized data. Call
804 fill() to fill the image with an appropriate pixel value before
805 drawing onto it with QPainter.
806*/
807QImage::QImage(int width, int height, Format format)
808 : QImage(QSize(width, height), format)
809{
810}
811
812/*!
813 Constructs an image with the given \a size and \a format.
814
815 A \l{isNull()}{null} image is returned if memory cannot be allocated.
816
817 \warning This will create a QImage with uninitialized data. Call
818 fill() to fill the image with an appropriate pixel value before
819 drawing onto it with QPainter.
820*/
821QImage::QImage(const QSize &size, Format format)
822 : QPaintDevice()
823{
824 d = QImageData::create(size, format);
825}
826
827
828
829QImageData *QImageData::create(uchar *data, int width, int height, qsizetype bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
830{
831 if (width <= 0 || height <= 0 || !data || format <= QImage::Format_Invalid || format >= QImage::NImageFormats)
832 return nullptr;
833
834 const int depth = qt_depthForFormat(format);
835 auto params = calculateImageParameters(width, height, depth);
836 if (!params.isValid())
837 return nullptr;
838
839 if (bpl > 0) {
840 // can't overflow, because has calculateImageParameters already done this multiplication
841 const qsizetype min_bytes_per_line = (qsizetype(width) * depth + 7)/8;
842 if (bpl < min_bytes_per_line)
843 return nullptr;
844
845 // recalculate the total with this value
846 params.bytesPerLine = bpl;
847 if (qMulOverflow<qsizetype>(v1: bpl, v2: height, r: &params.totalSize))
848 return nullptr;
849 }
850
851 QImageData *d = new QImageData;
852 d->ref.ref();
853
854 d->own_data = false;
855 d->ro_data = readOnly;
856 d->data = data;
857 d->width = width;
858 d->height = height;
859 d->depth = depth;
860 d->format = format;
861
862 d->bytes_per_line = params.bytesPerLine;
863 d->nbytes = params.totalSize;
864
865 d->cleanupFunction = cleanupFunction;
866 d->cleanupInfo = cleanupInfo;
867
868 return d;
869}
870
871/*!
872 Constructs an image with the given \a width, \a height and \a
873 format, that uses an existing memory buffer, \a data. The \a width
874 and \a height must be specified in pixels, \a data must be 32-bit aligned,
875 and each scanline of data in the image must also be 32-bit aligned.
876
877 The buffer must remain valid throughout the life of the QImage and
878 all copies that have not been modified or otherwise detached from
879 the original buffer. The image does not delete the buffer at destruction.
880 You can provide a function pointer \a cleanupFunction along with an
881 extra pointer \a cleanupInfo that will be called when the last copy
882 is destroyed.
883
884 If \a format is an indexed color format, the image color table is
885 initially empty and must be sufficiently expanded with
886 setColorCount() or setColorTable() before the image is used.
887*/
888QImage::QImage(uchar* data, int width, int height, Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
889 : QPaintDevice()
890{
891 d = QImageData::create(data, width, height, bpl: 0, format, readOnly: false, cleanupFunction, cleanupInfo);
892}
893
894/*!
895 Constructs an image with the given \a width, \a height and \a
896 format, that uses an existing read-only memory buffer, \a
897 data. The \a width and \a height must be specified in pixels, \a
898 data must be 32-bit aligned, and each scanline of data in the
899 image must also be 32-bit aligned.
900
901 The buffer must remain valid throughout the life of the QImage and
902 all copies that have not been modified or otherwise detached from
903 the original buffer. The image does not delete the buffer at destruction.
904 You can provide a function pointer \a cleanupFunction along with an
905 extra pointer \a cleanupInfo that will be called when the last copy
906 is destroyed.
907
908 If \a format is an indexed color format, the image color table is
909 initially empty and must be sufficiently expanded with
910 setColorCount() or setColorTable() before the image is used.
911
912 Unlike the similar QImage constructor that takes a non-const data buffer,
913 this version will never alter the contents of the buffer. For example,
914 calling QImage::bits() will return a deep copy of the image, rather than
915 the buffer passed to the constructor. This allows for the efficiency of
916 constructing a QImage from raw data, without the possibility of the raw
917 data being changed.
918*/
919QImage::QImage(const uchar* data, int width, int height, Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
920 : QPaintDevice()
921{
922 d = QImageData::create(data: const_cast<uchar*>(data), width, height, bpl: 0, format, readOnly: true, cleanupFunction, cleanupInfo);
923}
924
925/*!
926 Constructs an image with the given \a width, \a height and \a
927 format, that uses an existing memory buffer, \a data. The \a width
928 and \a height must be specified in pixels. \a bytesPerLine
929 specifies the number of bytes per line (stride).
930
931 The buffer must remain valid throughout the life of the QImage and
932 all copies that have not been modified or otherwise detached from
933 the original buffer. The image does not delete the buffer at destruction.
934 You can provide a function pointer \a cleanupFunction along with an
935 extra pointer \a cleanupInfo that will be called when the last copy
936 is destroyed.
937
938 If \a format is an indexed color format, the image color table is
939 initially empty and must be sufficiently expanded with
940 setColorCount() or setColorTable() before the image is used.
941*/
942
943QImage::QImage(uchar *data, int width, int height, qsizetype bytesPerLine, Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
944 :QPaintDevice()
945{
946 d = QImageData::create(data, width, height, bpl: bytesPerLine, format, readOnly: false, cleanupFunction, cleanupInfo);
947}
948
949/*!
950 Constructs an image with the given \a width, \a height and \a
951 format, that uses an existing memory buffer, \a data. The \a width
952 and \a height must be specified in pixels. \a bytesPerLine
953 specifies the number of bytes per line (stride).
954
955 The buffer must remain valid throughout the life of the QImage and
956 all copies that have not been modified or otherwise detached from
957 the original buffer. The image does not delete the buffer at destruction.
958 You can provide a function pointer \a cleanupFunction along with an
959 extra pointer \a cleanupInfo that will be called when the last copy
960 is destroyed.
961
962 If \a format is an indexed color format, the image color table is
963 initially empty and must be sufficiently expanded with
964 setColorCount() or setColorTable() before the image is used.
965
966 Unlike the similar QImage constructor that takes a non-const data buffer,
967 this version will never alter the contents of the buffer. For example,
968 calling QImage::bits() will return a deep copy of the image, rather than
969 the buffer passed to the constructor. This allows for the efficiency of
970 constructing a QImage from raw data, without the possibility of the raw
971 data being changed.
972*/
973
974QImage::QImage(const uchar *data, int width, int height, qsizetype bytesPerLine, Format format, QImageCleanupFunction cleanupFunction, void *cleanupInfo)
975 :QPaintDevice()
976{
977 d = QImageData::create(data: const_cast<uchar*>(data), width, height, bpl: bytesPerLine, format, readOnly: true, cleanupFunction, cleanupInfo);
978}
979
980/*!
981 Constructs an image and tries to load the image from the file with
982 the given \a fileName.
983
984 The loader attempts to read the image using the specified \a
985 format. If the \a format is not specified (which is the default),
986 it is auto-detected based on the file's suffix and header. For
987 details, see {QImageReader::setAutoDetectImageFormat()}{QImageReader}.
988
989 If the loading of the image failed, this object is a null image.
990
991 The file name can either refer to an actual file on disk or to one
992 of the application's embedded resources. See the
993 \l{resources.html}{Resource System} overview for details on how to
994 embed images and other resource files in the application's
995 executable.
996
997 \sa isNull(), {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
998*/
999
1000QImage::QImage(const QString &fileName, const char *format)
1001 : QPaintDevice()
1002{
1003 d = nullptr;
1004 load(fileName, format);
1005}
1006
1007#ifndef QT_NO_IMAGEFORMAT_XPM
1008extern bool qt_read_xpm_image_or_array(QIODevice *device, const char * const *source, QImage &image);
1009
1010/*!
1011 Constructs an image from the given \a xpm image.
1012
1013 Make sure that the image is a valid XPM image. Errors are silently
1014 ignored.
1015
1016 Note that it's possible to squeeze the XPM variable a little bit
1017 by using an unusual declaration:
1018
1019 \snippet code/src_gui_image_qimage.cpp 2
1020
1021 The extra \c const makes the entire definition read-only, which is
1022 slightly more efficient (e.g., when the code is in a shared
1023 library) and able to be stored in ROM with the application.
1024*/
1025
1026QImage::QImage(const char * const xpm[])
1027 : QPaintDevice()
1028{
1029 d = nullptr;
1030 if (!xpm)
1031 return;
1032 if (!qt_read_xpm_image_or_array(device: nullptr, source: xpm, image&: *this))
1033 // Issue: Warning because the constructor may be ambiguous
1034 qWarning(msg: "QImage::QImage(), XPM is not supported");
1035}
1036#endif // QT_NO_IMAGEFORMAT_XPM
1037
1038/*!
1039 Constructs a shallow copy of the given \a image.
1040
1041 For more information about shallow copies, see the \l {Implicit
1042 Data Sharing} documentation.
1043
1044 \sa copy()
1045*/
1046
1047QImage::QImage(const QImage &image)
1048 : QPaintDevice()
1049{
1050 if (image.paintingActive()) {
1051 d = nullptr;
1052 image.copy().swap(other&: *this);
1053 } else {
1054 d = image.d;
1055 if (d)
1056 d->ref.ref();
1057 }
1058}
1059
1060/*!
1061 Destroys the image and cleans up.
1062*/
1063
1064QImage::~QImage()
1065{
1066 if (d && !d->ref.deref())
1067 delete d;
1068}
1069
1070/*!
1071 Assigns a shallow copy of the given \a image to this image and
1072 returns a reference to this image.
1073
1074 For more information about shallow copies, see the \l {Implicit
1075 Data Sharing} documentation.
1076
1077 \sa copy(), QImage()
1078*/
1079
1080QImage &QImage::operator=(const QImage &image)
1081{
1082 if (image.paintingActive()) {
1083 operator=(other: image.copy());
1084 } else {
1085 if (image.d)
1086 image.d->ref.ref();
1087 if (d && !d->ref.deref())
1088 delete d;
1089 d = image.d;
1090 }
1091 return *this;
1092}
1093
1094/*!
1095 \fn void QImage::swap(QImage &other)
1096 \memberswap{image}
1097*/
1098
1099/*!
1100 \internal
1101*/
1102int QImage::devType() const
1103{
1104 return QInternal::Image;
1105}
1106
1107/*!
1108 Returns the image as a QVariant.
1109*/
1110QImage::operator QVariant() const
1111{
1112 return QVariant::fromValue(value: *this);
1113}
1114
1115/*!
1116 \internal
1117
1118 If multiple images share common data, this image makes a copy of
1119 the data and detaches itself from the sharing mechanism, making
1120 sure that this image is the only one referring to the data.
1121
1122 Nothing is done if there is just a single reference.
1123
1124 \sa copy(), {QImage::isDetached()}{isDetached()}, {Implicit Data Sharing}
1125*/
1126void QImage::detach()
1127{
1128 if (d) {
1129 if (d->is_cached && d->ref.loadRelaxed() == 1)
1130 QImagePixmapCleanupHooks::executeImageHooks(key: cacheKey());
1131
1132 if (d->ref.loadRelaxed() != 1 || d->ro_data)
1133 *this = copy();
1134
1135 if (d)
1136 ++d->detach_no;
1137 }
1138}
1139
1140
1141/*!
1142 \internal
1143
1144 A variant for metadata-only detach, which will not detach readonly image data,
1145 and only invalidate caches of the image data if asked to.
1146
1147 \sa detach(), isDetached()
1148*/
1149void QImage::detachMetadata(bool invalidateCache)
1150{
1151 if (d) {
1152 if (d->is_cached && d->ref.loadRelaxed() == 1)
1153 QImagePixmapCleanupHooks::executeImageHooks(key: cacheKey());
1154
1155 if (d->ref.loadRelaxed() != 1)
1156 *this = copy();
1157
1158 if (d && invalidateCache)
1159 ++d->detach_no;
1160 }
1161}
1162
1163static void copyPhysicalMetadata(QImageData *dst, const QImageData *src)
1164{
1165 dst->dpmx = src->dpmx;
1166 dst->dpmy = src->dpmy;
1167 dst->devicePixelRatio = src->devicePixelRatio;
1168}
1169
1170static void copyMetadata(QImageData *dst, const QImageData *src)
1171{
1172 // Doesn't copy colortable and alpha_clut.
1173 copyPhysicalMetadata(dst, src);
1174 dst->text = src->text;
1175 dst->offset = src->offset;
1176 dst->colorSpace = src->colorSpace;
1177}
1178
1179static void copyMetadata(QImage *dst, const QImage &src)
1180{
1181 dst->setDotsPerMeterX(src.dotsPerMeterX());
1182 dst->setDotsPerMeterY(src.dotsPerMeterY());
1183 dst->setDevicePixelRatio(src.devicePixelRatio());
1184 const auto textKeys = src.textKeys();
1185 for (const auto &key: textKeys)
1186 dst->setText(key, value: src.text(key));
1187
1188}
1189
1190/*!
1191 \fn QImage QImage::copy(int x, int y, int width, int height) const
1192 \overload
1193
1194 The returned image is copied from the position (\a x, \a y) in
1195 this image, and will always have the given \a width and \a height.
1196 In areas beyond this image, pixels are set to 0.
1197
1198*/
1199
1200/*!
1201 \fn QImage QImage::copy(const QRect& rectangle) const
1202
1203 Returns a sub-area of the image as a new image.
1204
1205 The returned image is copied from the position (\a
1206 {rectangle}.x(), \a{rectangle}.y()) in this image, and will always
1207 have the size of the given \a rectangle.
1208
1209 In areas beyond this image, pixels are set to 0. For 32-bit RGB
1210 images, this means black; for 32-bit ARGB images, this means
1211 transparent black; for 8-bit images, this means the color with
1212 index 0 in the color table which can be anything; for 1-bit
1213 images, this means Qt::color0.
1214
1215 If the given \a rectangle is a null rectangle the entire image is
1216 copied.
1217
1218 \sa QImage()
1219*/
1220QImage Q_TRACE_INSTRUMENT(qtgui) QImage::copy(const QRect& r) const
1221{
1222 Q_TRACE_SCOPE(QImage_copy, r);
1223 if (!d)
1224 return QImage();
1225
1226 if (r.isNull()) {
1227 QImage image(d->width, d->height, d->format);
1228 if (image.isNull())
1229 return image;
1230
1231 // Qt for Embedded Linux can create images with non-default bpl
1232 // make sure we don't crash.
1233 if (image.d->nbytes != d->nbytes) {
1234 qsizetype bpl = qMin(a: bytesPerLine(), b: image.bytesPerLine());
1235 for (int i = 0; i < height(); i++)
1236 memcpy(dest: image.scanLine(i), src: scanLine(i), n: bpl);
1237 } else
1238 memcpy(dest: image.bits(), src: bits(), n: d->nbytes);
1239 image.d->colortable = d->colortable;
1240 image.d->has_alpha_clut = d->has_alpha_clut;
1241 copyMetadata(dst: image.d, src: d);
1242 return image;
1243 }
1244
1245 int x = r.x();
1246 int y = r.y();
1247 int w = r.width();
1248 int h = r.height();
1249
1250 int dx = 0;
1251 int dy = 0;
1252 if (w <= 0 || h <= 0)
1253 return QImage();
1254
1255 QImage image(w, h, d->format);
1256 if (image.isNull())
1257 return image;
1258
1259 if (x < 0 || y < 0 || x + w > d->width || y + h > d->height) {
1260 // bitBlt will not cover entire image - clear it.
1261 image.fill(pixel: 0);
1262 if (x < 0) {
1263 dx = -x;
1264 x = 0;
1265 }
1266 if (y < 0) {
1267 dy = -y;
1268 y = 0;
1269 }
1270 }
1271
1272 image.d->colortable = d->colortable;
1273
1274 int pixels_to_copy = qMax(a: w - dx, b: 0);
1275 if (x > d->width)
1276 pixels_to_copy = 0;
1277 else if (pixels_to_copy > d->width - x)
1278 pixels_to_copy = d->width - x;
1279 int lines_to_copy = qMax(a: h - dy, b: 0);
1280 if (y > d->height)
1281 lines_to_copy = 0;
1282 else if (lines_to_copy > d->height - y)
1283 lines_to_copy = d->height - y;
1284
1285 bool byteAligned = true;
1286 if (d->format == Format_Mono || d->format == Format_MonoLSB)
1287 byteAligned = !(dx & 7) && !(x & 7) && !(pixels_to_copy & 7);
1288
1289 if (byteAligned) {
1290 const uchar *src = d->data + ((x * d->depth) >> 3) + y * d->bytes_per_line;
1291 uchar *dest = image.d->data + ((dx * d->depth) >> 3) + dy * image.d->bytes_per_line;
1292 const qsizetype bytes_to_copy = (qsizetype(pixels_to_copy) * d->depth) >> 3;
1293 for (int i = 0; i < lines_to_copy; ++i) {
1294 memcpy(dest: dest, src: src, n: bytes_to_copy);
1295 src += d->bytes_per_line;
1296 dest += image.d->bytes_per_line;
1297 }
1298 } else if (d->format == Format_Mono) {
1299 const uchar *src = d->data + y * d->bytes_per_line;
1300 uchar *dest = image.d->data + dy * image.d->bytes_per_line;
1301 for (int i = 0; i < lines_to_copy; ++i) {
1302 for (int j = 0; j < pixels_to_copy; ++j) {
1303 if (src[(x + j) >> 3] & (0x80 >> ((x + j) & 7)))
1304 dest[(dx + j) >> 3] |= (0x80 >> ((dx + j) & 7));
1305 else
1306 dest[(dx + j) >> 3] &= ~(0x80 >> ((dx + j) & 7));
1307 }
1308 src += d->bytes_per_line;
1309 dest += image.d->bytes_per_line;
1310 }
1311 } else { // Format_MonoLSB
1312 Q_ASSERT(d->format == Format_MonoLSB);
1313 const uchar *src = d->data + y * d->bytes_per_line;
1314 uchar *dest = image.d->data + dy * image.d->bytes_per_line;
1315 for (int i = 0; i < lines_to_copy; ++i) {
1316 for (int j = 0; j < pixels_to_copy; ++j) {
1317 if (src[(x + j) >> 3] & (0x1 << ((x + j) & 7)))
1318 dest[(dx + j) >> 3] |= (0x1 << ((dx + j) & 7));
1319 else
1320 dest[(dx + j) >> 3] &= ~(0x1 << ((dx + j) & 7));
1321 }
1322 src += d->bytes_per_line;
1323 dest += image.d->bytes_per_line;
1324 }
1325 }
1326
1327 copyMetadata(dst: image.d, src: d);
1328 image.d->has_alpha_clut = d->has_alpha_clut;
1329 return image;
1330}
1331
1332
1333/*!
1334 \fn bool QImage::isNull() const
1335
1336 Returns \c true if it is a null image, otherwise returns \c false.
1337
1338 A null image has all parameters set to zero and no allocated data.
1339*/
1340bool QImage::isNull() const
1341{
1342 return !d;
1343}
1344
1345/*!
1346 \fn int QImage::width() const
1347
1348 Returns the width of the image.
1349
1350 \sa {QImage#Image Information}{Image Information}
1351*/
1352int QImage::width() const
1353{
1354 return d ? d->width : 0;
1355}
1356
1357/*!
1358 \fn int QImage::height() const
1359
1360 Returns the height of the image.
1361
1362 \sa {QImage#Image Information}{Image Information}
1363*/
1364int QImage::height() const
1365{
1366 return d ? d->height : 0;
1367}
1368
1369/*!
1370 \fn QSize QImage::size() const
1371
1372 Returns the size of the image, i.e. its width() and height().
1373
1374 \sa {QImage#Image Information}{Image Information}, deviceIndependentSize()
1375*/
1376QSize QImage::size() const
1377{
1378 return d ? QSize(d->width, d->height) : QSize(0, 0);
1379}
1380
1381/*!
1382 \fn QRect QImage::rect() const
1383
1384 Returns the enclosing rectangle (0, 0, width(), height()) of the
1385 image.
1386
1387 \sa {QImage#Image Information}{Image Information}
1388*/
1389QRect QImage::rect() const
1390{
1391 return d ? QRect(0, 0, d->width, d->height) : QRect();
1392}
1393
1394/*!
1395 Returns the depth of the image.
1396
1397 The image depth is the number of bits used to store a single
1398 pixel, also called bits per pixel (bpp).
1399
1400 The supported depths are 1, 8, 16, 24, 32 and 64.
1401
1402 \sa bitPlaneCount(), convertToFormat(), {QImage#Image Formats}{Image Formats},
1403 {QImage#Image Information}{Image Information}
1404
1405*/
1406int QImage::depth() const
1407{
1408 return d ? d->depth : 0;
1409}
1410
1411/*!
1412 \fn int QImage::colorCount() const
1413
1414 Returns the size of the color table for the image.
1415
1416 Notice that colorCount() returns 0 for 32-bpp images because these
1417 images do not use color tables, but instead encode pixel values as
1418 ARGB quadruplets.
1419
1420 \sa setColorCount(), {QImage#Image Information}{Image Information}
1421*/
1422int QImage::colorCount() const
1423{
1424 return d ? d->colortable.size() : 0;
1425}
1426
1427/*!
1428 Sets the color table used to translate color indexes to QRgb
1429 values, to the specified \a colors.
1430
1431 When the image is used, the color table must be large enough to
1432 have entries for all the pixel/index values present in the image,
1433 otherwise the results are undefined.
1434
1435 \sa colorTable(), setColor(), {QImage#Image Transformations}{Image
1436 Transformations}
1437*/
1438void QImage::setColorTable(const QList<QRgb> &colors)
1439{
1440 if (!d)
1441 return;
1442 detachMetadata(invalidateCache: true);
1443
1444 // In case detach() ran out of memory
1445 if (!d)
1446 return;
1447
1448 d->colortable = colors;
1449 d->has_alpha_clut = false;
1450 for (int i = 0; i < d->colortable.size(); ++i) {
1451 if (qAlpha(rgb: d->colortable.at(i)) != 255) {
1452 d->has_alpha_clut = true;
1453 break;
1454 }
1455 }
1456}
1457
1458/*!
1459 Returns a list of the colors contained in the image's color table,
1460 or an empty list if the image does not have a color table
1461
1462 \sa setColorTable(), colorCount(), color()
1463*/
1464QList<QRgb> QImage::colorTable() const
1465{
1466 return d ? d->colortable : QList<QRgb>();
1467}
1468
1469/*!
1470 Returns the device pixel ratio for the image. This is the
1471 ratio between \e{device pixels} and \e{device independent pixels}.
1472
1473 Use this function when calculating layout geometry based on
1474 the image size: QSize layoutSize = image.size() / image.devicePixelRatio()
1475
1476 The default value is 1.0.
1477
1478 \sa setDevicePixelRatio(), QImageReader
1479*/
1480qreal QImage::devicePixelRatio() const
1481{
1482 if (!d)
1483 return 1.0;
1484 return d->devicePixelRatio;
1485}
1486
1487/*!
1488 Sets the device pixel ratio for the image. This is the
1489 ratio between image pixels and device-independent pixels.
1490
1491 The default \a scaleFactor is 1.0. Setting it to something else has
1492 two effects:
1493
1494 QPainters that are opened on the image will be scaled. For
1495 example, painting on a 200x200 image if with a ratio of 2.0
1496 will result in effective (device-independent) painting bounds
1497 of 100x100.
1498
1499 Code paths in Qt that calculate layout geometry based on the
1500 image size will take the ratio into account:
1501 QSize layoutSize = image.size() / image.devicePixelRatio()
1502 The net effect of this is that the image is displayed as
1503 high-DPI image rather than a large image
1504 (see \l{Drawing High Resolution Versions of Pixmaps and Images}).
1505
1506 \sa devicePixelRatio(), deviceIndependentSize()
1507*/
1508void QImage::setDevicePixelRatio(qreal scaleFactor)
1509{
1510 if (!d)
1511 return;
1512
1513 if (scaleFactor == d->devicePixelRatio)
1514 return;
1515
1516 detachMetadata();
1517 if (d)
1518 d->devicePixelRatio = scaleFactor;
1519}
1520
1521/*!
1522 Returns the size of the image in device independent pixels.
1523
1524 This value should be used when using the image size in user interface
1525 size calculations.
1526
1527 The return value is equivalent to image.size() / image.devicePixelRatio().
1528
1529 \since 6.2
1530*/
1531QSizeF QImage::deviceIndependentSize() const
1532{
1533 if (!d)
1534 return QSizeF(0, 0);
1535 return QSizeF(d->width, d->height) / d->devicePixelRatio;
1536}
1537
1538
1539/*!
1540 \since 5.10
1541 Returns the image data size in bytes.
1542
1543 \sa bytesPerLine(), bits(), {QImage#Image Information}{Image
1544 Information}
1545*/
1546qsizetype QImage::sizeInBytes() const
1547{
1548 return d ? d->nbytes : 0;
1549}
1550
1551/*!
1552 Returns the number of bytes per image scanline.
1553
1554 This is equivalent to sizeInBytes() / height() if height() is non-zero.
1555
1556 \sa scanLine()
1557*/
1558qsizetype QImage::bytesPerLine() const
1559{
1560 return d ? d->bytes_per_line : 0;
1561}
1562
1563
1564/*!
1565 Returns the color in the color table at index \a i. The first
1566 color is at index 0.
1567
1568 The colors in an image's color table are specified as ARGB
1569 quadruplets (QRgb). Use the qAlpha(), qRed(), qGreen(), and
1570 qBlue() functions to get the color value components.
1571
1572 \sa setColor(), pixelIndex(), {QImage#Pixel Manipulation}{Pixel
1573 Manipulation}
1574*/
1575QRgb QImage::color(int i) const
1576{
1577 Q_ASSERT(i < colorCount());
1578 return d ? d->colortable.at(i) : QRgb(uint(-1));
1579}
1580
1581/*!
1582 \fn void QImage::setColor(int index, QRgb colorValue)
1583
1584 Sets the color at the given \a index in the color table, to the
1585 given to \a colorValue. The color value is an ARGB quadruplet.
1586
1587 If \a index is outside the current size of the color table, it is
1588 expanded with setColorCount().
1589
1590 \sa color(), colorCount(), setColorTable(), {QImage#Pixel Manipulation}{Pixel
1591 Manipulation}
1592*/
1593void QImage::setColor(int i, QRgb c)
1594{
1595 if (!d)
1596 return;
1597 if (i < 0 || d->depth > 8 || i >= 1<<d->depth) {
1598 qWarning(msg: "QImage::setColor: Index out of bound %d", i);
1599 return;
1600 }
1601 detachMetadata(invalidateCache: true);
1602
1603 // In case detach() run out of memory
1604 if (!d)
1605 return;
1606
1607 if (i >= d->colortable.size())
1608 setColorCount(i+1);
1609 d->colortable[i] = c;
1610 d->has_alpha_clut |= (qAlpha(rgb: c) != 255);
1611}
1612
1613/*!
1614 Returns a pointer to the pixel data at the scanline with index \a
1615 i. The first scanline is at index 0.
1616
1617 The scanline data is as minimum 32-bit aligned. For 64-bit formats
1618 it follows the native alignment of 64-bit integers (64-bit for most
1619 platforms, but notably 32-bit on i386).
1620
1621 For example, to remove the green component of each pixel in an image:
1622
1623 \snippet code/src_gui_image_qimage.cpp scanLine
1624
1625 \warning If you are accessing 32-bpp image data, cast the returned
1626 pointer to \c{QRgb*} (QRgb has a 32-bit size) and use it to
1627 read/write the pixel value. You cannot use the \c{uchar*} pointer
1628 directly, because the pixel format depends on the byte order on
1629 the underlying platform. Use qRed(), qGreen(), qBlue(), and
1630 qAlpha() to access the pixels.
1631
1632 \sa bytesPerLine(), bits(), {QImage#Pixel Manipulation}{Pixel
1633 Manipulation}, constScanLine()
1634*/
1635uchar *QImage::scanLine(int i)
1636{
1637 if (!d)
1638 return nullptr;
1639
1640 detach();
1641
1642 // In case detach() ran out of memory
1643 if (!d)
1644 return nullptr;
1645
1646 return d->data + i * d->bytes_per_line;
1647}
1648
1649/*!
1650 \overload
1651*/
1652const uchar *QImage::scanLine(int i) const
1653{
1654 if (!d)
1655 return nullptr;
1656
1657 Q_ASSERT(i >= 0 && i < height());
1658 return d->data + i * d->bytes_per_line;
1659}
1660
1661
1662/*!
1663 Returns a pointer to the pixel data at the scanline with index \a
1664 i. The first scanline is at index 0.
1665
1666 The scanline data is as minimum 32-bit aligned. For 64-bit formats
1667 it follows the native alignment of 64-bit integers (64-bit for most
1668 platforms, but notably 32-bit on i386).
1669
1670 Note that QImage uses \l{Implicit Data Sharing} {implicit data
1671 sharing}, but this function does \e not perform a deep copy of the
1672 shared pixel data, because the returned data is const.
1673
1674 \sa scanLine(), constBits()
1675*/
1676const uchar *QImage::constScanLine(int i) const
1677{
1678 if (!d)
1679 return nullptr;
1680
1681 Q_ASSERT(i >= 0 && i < height());
1682 return d->data + i * d->bytes_per_line;
1683}
1684
1685/*!
1686 Returns a pointer to the first pixel data. This is equivalent to
1687 scanLine(0).
1688
1689 Note that QImage uses \l{Implicit Data Sharing} {implicit data
1690 sharing}. This function performs a deep copy of the shared pixel
1691 data, thus ensuring that this QImage is the only one using the
1692 current return value.
1693
1694 \sa scanLine(), sizeInBytes(), constBits()
1695*/
1696uchar *QImage::bits()
1697{
1698 if (!d)
1699 return nullptr;
1700 detach();
1701
1702 // In case detach ran out of memory...
1703 if (!d)
1704 return nullptr;
1705
1706 return d->data;
1707}
1708
1709/*!
1710 \overload
1711
1712 Note that QImage uses \l{Implicit Data Sharing} {implicit data
1713 sharing}, but this function does \e not perform a deep copy of the
1714 shared pixel data, because the returned data is const.
1715*/
1716const uchar *QImage::bits() const
1717{
1718 return d ? d->data : nullptr;
1719}
1720
1721
1722/*!
1723 Returns a pointer to the first pixel data.
1724
1725 Note that QImage uses \l{Implicit Data Sharing} {implicit data
1726 sharing}, but this function does \e not perform a deep copy of the
1727 shared pixel data, because the returned data is const.
1728
1729 \sa bits(), constScanLine()
1730*/
1731const uchar *QImage::constBits() const
1732{
1733 return d ? d->data : nullptr;
1734}
1735
1736/*!
1737 \fn void QImage::fill(uint pixelValue)
1738
1739 Fills the entire image with the given \a pixelValue.
1740
1741 If the depth of this image is 1, only the lowest bit is used. If
1742 you say fill(0), fill(2), etc., the image is filled with 0s. If
1743 you say fill(1), fill(3), etc., the image is filled with 1s. If
1744 the depth is 8, the lowest 8 bits are used and if the depth is 16
1745 the lowest 16 bits are used.
1746
1747 If the image depth is higher than 32bit the result is undefined.
1748
1749 \note There are no corresponding value getter, though QImage::pixelIndex()
1750 will return the same value for indexed formats, and QImage::pixel() for
1751 RGB32, ARGB32, and ARGB32PM formats.
1752
1753 \sa depth(), {QImage#Image Transformations}{Image Transformations}
1754*/
1755
1756void QImage::fill(uint pixel)
1757{
1758 if (!d)
1759 return;
1760
1761 detach();
1762
1763 // In case detach() ran out of memory
1764 if (!d)
1765 return;
1766
1767 if (d->depth == 1 || d->depth == 8) {
1768 int w = d->width;
1769 if (d->depth == 1) {
1770 if (pixel & 1)
1771 pixel = 0xffffffff;
1772 else
1773 pixel = 0;
1774 w = (w + 7) / 8;
1775 } else {
1776 pixel &= 0xff;
1777 }
1778 qt_rectfill<quint8>(dest: d->data, value: pixel, x: 0, y: 0,
1779 width: w, height: d->height, stride: d->bytes_per_line);
1780 return;
1781 } else if (d->depth == 16) {
1782 if (d->format == Format_RGB444)
1783 pixel |= 0xf000;
1784 qt_rectfill<quint16>(dest: reinterpret_cast<quint16*>(d->data), value: pixel,
1785 x: 0, y: 0, width: d->width, height: d->height, stride: d->bytes_per_line);
1786 return;
1787 } else if (d->depth == 24) {
1788 if (d->format == Format_RGB666)
1789 pixel |= 0xfc0000;
1790 qt_rectfill<quint24>(dest: reinterpret_cast<quint24*>(d->data), value: pixel,
1791 x: 0, y: 0, width: d->width, height: d->height, stride: d->bytes_per_line);
1792 return;
1793 } else if (d->format >= QImage::Format_RGBX64 && d->format <= QImage::Format_RGBA64_Premultiplied) {
1794 qt_rectfill<quint64>(dest: reinterpret_cast<quint64*>(d->data), value: QRgba64::fromArgb32(rgb: pixel),
1795 x: 0, y: 0, width: d->width, height: d->height, stride: d->bytes_per_line);
1796 return;
1797 } else if (d->format >= QImage::Format_RGBX16FPx4 && d->format <= QImage::Format_RGBA16FPx4_Premultiplied) {
1798 quint64 cu;
1799 QRgbaFloat16 cf = QRgbaFloat16::fromArgb32(rgb: pixel);
1800 ::memcpy(dest: &cu, src: &cf, n: sizeof(quint64));
1801 qt_rectfill<quint64>(dest: reinterpret_cast<quint64*>(d->data), value: cu,
1802 x: 0, y: 0, width: d->width, height: d->height, stride: d->bytes_per_line);
1803 return;
1804 } else if (d->format >= QImage::Format_RGBX32FPx4 && d->format <= QImage::Format_RGBA32FPx4_Premultiplied) {
1805 QRgbaFloat32 cf = QRgbaFloat32::fromArgb32(rgb: pixel);
1806 uchar *data = d->data;
1807 for (int y = 0; y < d->height; ++y) {
1808 QRgbaFloat32 *line = reinterpret_cast<QRgbaFloat32 *>(data);
1809 for (int x = 0; x < d->width; ++x)
1810 line[x] = cf;
1811 data += d->bytes_per_line;
1812 }
1813 return;
1814 }
1815 Q_ASSERT(d->depth == 32);
1816
1817 if (d->format == Format_RGB32)
1818 pixel |= 0xff000000;
1819 if (d->format == Format_RGBX8888)
1820#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
1821 pixel |= 0xff000000;
1822#else
1823 pixel |= 0x000000ff;
1824#endif
1825 if (d->format == Format_BGR30 || d->format == Format_RGB30)
1826 pixel |= 0xc0000000;
1827
1828 qt_rectfill<uint>(dest: reinterpret_cast<uint*>(d->data), value: pixel,
1829 x: 0, y: 0, width: d->width, height: d->height, stride: d->bytes_per_line);
1830}
1831
1832
1833/*!
1834 \fn void QImage::fill(Qt::GlobalColor color)
1835 \overload
1836
1837 Fills the image with the given \a color, described as a standard global
1838 color.
1839 */
1840
1841void QImage::fill(Qt::GlobalColor color)
1842{
1843 fill(color: QColor(color));
1844}
1845
1846
1847
1848/*!
1849 \fn void QImage::fill(const QColor &color)
1850
1851 \overload
1852
1853 Fills the entire image with the given \a color.
1854
1855 If the depth of the image is 1, the image will be filled with 1 if
1856 \a color equals Qt::color1; it will otherwise be filled with 0.
1857
1858 If the depth of the image is 8, the image will be filled with the
1859 index corresponding the \a color in the color table if present; it
1860 will otherwise be filled with 0.
1861*/
1862
1863void QImage::fill(const QColor &color)
1864{
1865 if (!d)
1866 return;
1867 detach();
1868
1869 // In case we run out of memory
1870 if (!d)
1871 return;
1872
1873 QRgba64 opaque = color.rgba64();
1874 opaque.setAlpha(65535);
1875 switch (d->format) {
1876 case QImage::Format_RGB32:
1877 case QImage::Format_ARGB32:
1878 fill(pixel: color.rgba());
1879 break;
1880 case QImage::Format_ARGB32_Premultiplied:
1881 fill(pixel: qPremultiply(x: color.rgba()));
1882 break;
1883 case QImage::Format_RGBX8888:
1884 fill(pixel: ARGB2RGBA(x: color.rgba() | 0xff000000));
1885 break;
1886 case QImage::Format_RGBA8888:
1887 fill(pixel: ARGB2RGBA(x: color.rgba()));
1888 break;
1889 case QImage::Format_RGBA8888_Premultiplied:
1890 fill(pixel: ARGB2RGBA(x: qPremultiply(x: color.rgba())));
1891 break;
1892 case QImage::Format_BGR30:
1893 fill(pixel: qConvertRgb64ToRgb30<PixelOrderBGR>(c: opaque));
1894 break;
1895 case QImage::Format_RGB30:
1896 fill(pixel: qConvertRgb64ToRgb30<PixelOrderRGB>(c: opaque));
1897 break;
1898 case QImage::Format_RGB16:
1899 fill(pixel: (uint) qConvertRgb32To16(c: color.rgba()));
1900 break;
1901 case QImage::Format_Indexed8: {
1902 uint pixel = 0;
1903 for (int i=0; i<d->colortable.size(); ++i) {
1904 if (color.rgba() == d->colortable.at(i)) {
1905 pixel = i;
1906 break;
1907 }
1908 }
1909 fill(pixel);
1910 break;
1911 }
1912 case QImage::Format_Mono:
1913 case QImage::Format_MonoLSB:
1914 if (color == Qt::color1)
1915 fill(pixel: (uint) 1);
1916 else
1917 fill(pixel: (uint) 0);
1918 break;
1919 case QImage::Format_RGBX64:
1920 qt_rectfill<quint64>(dest: reinterpret_cast<quint64*>(d->data), value: opaque,
1921 x: 0, y: 0, width: d->width, height: d->height, stride: d->bytes_per_line);
1922 break;
1923 case QImage::Format_RGBA64:
1924 qt_rectfill<quint64>(dest: reinterpret_cast<quint64*>(d->data), value: color.rgba64(),
1925 x: 0, y: 0, width: d->width, height: d->height, stride: d->bytes_per_line);
1926 break;
1927 case QImage::Format_RGBA64_Premultiplied:
1928 qt_rectfill<quint64>(dest: reinterpret_cast<quint64 *>(d->data), value: color.rgba64().premultiplied(),
1929 x: 0, y: 0, width: d->width, height: d->height, stride: d->bytes_per_line);
1930 break;
1931 case QImage::Format_RGBX16FPx4:
1932 case QImage::Format_RGBA16FPx4:
1933 case QImage::Format_RGBA16FPx4_Premultiplied:
1934 case QImage::Format_RGBX32FPx4:
1935 case QImage::Format_RGBA32FPx4:
1936 case QImage::Format_RGBA32FPx4_Premultiplied:{
1937 float r, g, b, a;
1938 color.getRgbF(r: &r, g: &g, b: &b, a: &a);
1939 if (!hasAlphaChannel())
1940 a = 1.0f;
1941 if (depth() == 64) {
1942 QRgbaFloat16 c16{.r: qfloat16(r), .g: qfloat16(g), .b: qfloat16(b), .a: qfloat16(a)};
1943 if (d->format == Format_RGBA16FPx4_Premultiplied)
1944 c16 = c16.premultiplied();
1945 qt_rectfill<QRgbaFloat16>(dest: reinterpret_cast<QRgbaFloat16 *>(d->data), value: c16,
1946 x: 0, y: 0, width: d->width, height: d->height, stride: d->bytes_per_line);
1947 } else {
1948 QRgbaFloat32 c32{.r: r, .g: g, .b: b, .a: a};
1949 if (d->format == Format_RGBA32FPx4_Premultiplied)
1950 c32 = c32.premultiplied();
1951 qt_rectfill<QRgbaFloat32>(dest: reinterpret_cast<QRgbaFloat32 *>(d->data), value: c32,
1952 x: 0, y: 0, width: d->width, height: d->height, stride: d->bytes_per_line);
1953 }
1954 break;
1955 }
1956 default: {
1957 QPainter p(this);
1958 p.setCompositionMode(QPainter::CompositionMode_Source);
1959 p.fillRect(rect(), color);
1960 }}
1961}
1962
1963
1964
1965/*!
1966 Inverts all pixel values in the image.
1967
1968 The given invert \a mode only have a meaning when the image's
1969 depth is 32. The default \a mode is InvertRgb, which leaves the
1970 alpha channel unchanged. If the \a mode is InvertRgba, the alpha
1971 bits are also inverted.
1972
1973 Inverting an 8-bit image means to replace all pixels using color
1974 index \e i with a pixel using color index 255 minus \e i. The same
1975 is the case for a 1-bit image. Note that the color table is \e not
1976 changed.
1977
1978 If the image has a premultiplied alpha channel, the image is first
1979 converted to an unpremultiplied image format to be inverted and
1980 then converted back.
1981
1982 \sa {QImage#Image Transformations}{Image Transformations}
1983*/
1984
1985void QImage::invertPixels(InvertMode mode)
1986{
1987 if (!d)
1988 return;
1989
1990 detach();
1991
1992 // In case detach() ran out of memory
1993 if (!d)
1994 return;
1995
1996 QImage::Format originalFormat = d->format;
1997 // Inverting premultiplied pixels would produce invalid image data.
1998 if (hasAlphaChannel() && qPixelLayouts[d->format].premultiplied) {
1999 if (d->format == QImage::Format_RGBA16FPx4_Premultiplied) {
2000 if (!d->convertInPlace(newFormat: QImage::Format_RGBA16FPx4, { }))
2001 *this = convertToFormat(f: QImage::Format_RGBA16FPx4);
2002 } else if (d->format == QImage::Format_RGBA32FPx4_Premultiplied) {
2003 if (!d->convertInPlace(newFormat: QImage::Format_RGBA32FPx4, { }))
2004 *this = convertToFormat(f: QImage::Format_RGBA32FPx4);
2005 } else if (depth() > 32) {
2006 if (!d->convertInPlace(newFormat: QImage::Format_RGBA64, { }))
2007 *this = convertToFormat(f: QImage::Format_RGBA64);
2008 } else {
2009 if (!d->convertInPlace(newFormat: QImage::Format_ARGB32, { }))
2010 *this = convertToFormat(f: QImage::Format_ARGB32);
2011 }
2012 }
2013
2014 if (depth() < 32) {
2015 // This assumes no alpha-channel as the only formats with non-premultipled alpha are 32bit.
2016 qsizetype bpl = (qsizetype(d->width) * d->depth + 7) / 8;
2017 int pad = d->bytes_per_line - bpl;
2018 uchar *sl = d->data;
2019 for (int y=0; y<d->height; ++y) {
2020 for (qsizetype x=0; x<bpl; ++x)
2021 *sl++ ^= 0xff;
2022 sl += pad;
2023 }
2024 } else if (format() >= QImage::Format_RGBX16FPx4 && format() <= QImage::Format_RGBA16FPx4_Premultiplied) {
2025 qfloat16 *p = reinterpret_cast<qfloat16 *>(d->data);
2026 qfloat16 *end = reinterpret_cast<qfloat16 *>(d->data + d->nbytes);
2027 while (p < end) {
2028 p[0] = qfloat16(1) - p[0];
2029 p[1] = qfloat16(1) - p[1];
2030 p[2] = qfloat16(1) - p[2];
2031 if (mode == InvertRgba)
2032 p[3] = qfloat16(1) - p[3];
2033 p += 4;
2034 }
2035 } else if (format() >= QImage::Format_RGBX32FPx4 && format() <= QImage::Format_RGBA32FPx4_Premultiplied) {
2036 uchar *data = d->data;
2037 for (int y = 0; y < d->height; ++y) {
2038 float *p = reinterpret_cast<float *>(data);
2039 for (int x = 0; x < d->width; ++x) {
2040 p[0] = 1.0f - p[0];
2041 p[1] = 1.0f - p[1];
2042 p[2] = 1.0f - p[2];
2043 if (mode == InvertRgba)
2044 p[3] = 1.0f - p[3];
2045 p += 4;
2046 }
2047 data += d->bytes_per_line;
2048 }
2049 } else if (depth() == 64) {
2050 quint16 *p = (quint16*)d->data;
2051 quint16 *end = (quint16*)(d->data + d->nbytes);
2052 quint16 xorbits = 0xffff;
2053 while (p < end) {
2054 *p++ ^= xorbits;
2055 *p++ ^= xorbits;
2056 *p++ ^= xorbits;
2057 if (mode == InvertRgba)
2058 *p++ ^= xorbits;
2059 else
2060 p++;
2061 }
2062 } else {
2063 quint32 *p = (quint32*)d->data;
2064 quint32 *end = (quint32*)(d->data + d->nbytes);
2065 quint32 xorbits = 0xffffffff;
2066 switch (d->format) {
2067 case QImage::Format_RGBA8888:
2068 if (mode == InvertRgba)
2069 break;
2070 Q_FALLTHROUGH();
2071 case QImage::Format_RGBX8888:
2072#if Q_BYTE_ORDER == Q_BIG_ENDIAN
2073 xorbits = 0xffffff00;
2074 break;
2075#else
2076 xorbits = 0x00ffffff;
2077 break;
2078#endif
2079 case QImage::Format_ARGB32:
2080 if (mode == InvertRgba)
2081 break;
2082 Q_FALLTHROUGH();
2083 case QImage::Format_RGB32:
2084 xorbits = 0x00ffffff;
2085 break;
2086 case QImage::Format_BGR30:
2087 case QImage::Format_RGB30:
2088 xorbits = 0x3fffffff;
2089 break;
2090 default:
2091 Q_UNREACHABLE();
2092 xorbits = 0;
2093 break;
2094 }
2095 while (p < end)
2096 *p++ ^= xorbits;
2097 }
2098
2099 if (originalFormat != d->format) {
2100 if (!d->convertInPlace(newFormat: originalFormat, { }))
2101 *this = convertToFormat(f: originalFormat);
2102 }
2103}
2104
2105// Windows defines these
2106#if defined(write)
2107# undef write
2108#endif
2109#if defined(close)
2110# undef close
2111#endif
2112#if defined(read)
2113# undef read
2114#endif
2115
2116/*!
2117 Resizes the color table to contain \a colorCount entries.
2118
2119 If the color table is expanded, all the extra colors will be set to
2120 transparent (i.e qRgba(0, 0, 0, 0)).
2121
2122 When the image is used, the color table must be large enough to
2123 have entries for all the pixel/index values present in the image,
2124 otherwise the results are undefined.
2125
2126 \sa colorCount(), colorTable(), setColor(), {QImage#Image
2127 Transformations}{Image Transformations}
2128*/
2129
2130void QImage::setColorCount(int colorCount)
2131{
2132 if (!d) {
2133 qWarning(msg: "QImage::setColorCount: null image");
2134 return;
2135 }
2136
2137 detachMetadata(invalidateCache: true);
2138
2139 // In case detach() ran out of memory
2140 if (!d)
2141 return;
2142
2143 if (colorCount == d->colortable.size())
2144 return;
2145 if (colorCount <= 0) { // use no color table
2146 d->colortable.clear();
2147 return;
2148 }
2149 int nc = d->colortable.size();
2150 d->colortable.resize(size: colorCount);
2151 for (int i = nc; i < colorCount; ++i)
2152 d->colortable[i] = 0;
2153}
2154
2155/*!
2156 Returns the format of the image.
2157
2158 \sa {QImage#Image Formats}{Image Formats}
2159*/
2160QImage::Format QImage::format() const
2161{
2162 if (d) {
2163 // Class Invariant Check
2164 Q_ASSERT(d->format < NImageFormats);
2165 Q_ASSERT(d->format > Format_Invalid);
2166 }
2167 return d ? d->format : Format_Invalid;
2168}
2169
2170/*!
2171 \fn QImage QImage::convertToFormat(Format format, Qt::ImageConversionFlags flags) const &
2172 \fn QImage QImage::convertToFormat(Format format, Qt::ImageConversionFlags flags) &&
2173
2174 Returns a copy of the image in the given \a format.
2175
2176 The specified image conversion \a flags control how the image data
2177 is handled during the conversion process.
2178
2179 \sa convertTo(), {Image Formats}
2180*/
2181
2182/*!
2183 \fn QImage QImage::convertedTo(Format format, Qt::ImageConversionFlags flags) const &
2184 \fn QImage QImage::convertedTo(Format format, Qt::ImageConversionFlags flags) &&
2185 \since 6.0
2186
2187 Returns a copy of the image in the given \a format.
2188
2189 The specified image conversion \a flags control how the image data
2190 is handled during the conversion process.
2191
2192 \sa convertTo(), {Image Formats}
2193*/
2194
2195/*!
2196 \internal
2197*/
2198QImage QImage::convertToFormat_helper(Format format, Qt::ImageConversionFlags flags) const
2199{
2200 if (!d || d->format == format)
2201 return *this;
2202
2203 if (d->format == Format_Invalid || format <= Format_Invalid || format >= NImageFormats)
2204 return QImage();
2205
2206 const QPixelLayout *destLayout = &qPixelLayouts[format];
2207 Image_Converter converter = qimage_converter_map[d->format][format];
2208 if (!converter && format > QImage::Format_Indexed8 && d->format > QImage::Format_Indexed8) {
2209 if (qt_highColorPrecision(format: d->format, opaque: !destLayout->hasAlphaChannel)
2210 && qt_highColorPrecision(format, opaque: !hasAlphaChannel())) {
2211#if QT_CONFIG(raster_fp)
2212 if (qt_fpColorPrecision(format: d->format) && qt_fpColorPrecision(format))
2213 converter = convert_generic_over_rgba32f;
2214 else
2215#endif
2216 converter = convert_generic_over_rgb64;
2217 } else
2218 converter = convert_generic;
2219 }
2220 if (converter) {
2221 QImage image(d->width, d->height, format);
2222
2223 QIMAGE_SANITYCHECK_MEMORY(image);
2224
2225 copyMetadata(dst: image.d, src: d);
2226
2227 converter(image.d, d, flags);
2228 return image;
2229 }
2230
2231 // Convert indexed formats over ARGB32 or RGB32 to the final format.
2232 Q_ASSERT(format != QImage::Format_ARGB32 && format != QImage::Format_RGB32);
2233 Q_ASSERT(d->format != QImage::Format_ARGB32 && d->format != QImage::Format_RGB32);
2234
2235 if (!hasAlphaChannel())
2236 return convertToFormat(f: Format_RGB32, flags).convertToFormat(f: format, flags);
2237
2238 return convertToFormat(f: Format_ARGB32, flags).convertToFormat(f: format, flags);
2239}
2240
2241/*!
2242 \internal
2243*/
2244bool QImage::convertToFormat_inplace(Format format, Qt::ImageConversionFlags flags)
2245{
2246 return d && d->convertInPlace(newFormat: format, flags);
2247}
2248
2249static inline int pixel_distance(QRgb p1, QRgb p2) {
2250 int r1 = qRed(rgb: p1);
2251 int g1 = qGreen(rgb: p1);
2252 int b1 = qBlue(rgb: p1);
2253 int a1 = qAlpha(rgb: p1);
2254
2255 int r2 = qRed(rgb: p2);
2256 int g2 = qGreen(rgb: p2);
2257 int b2 = qBlue(rgb: p2);
2258 int a2 = qAlpha(rgb: p2);
2259
2260 return abs(x: r1 - r2) + abs(x: g1 - g2) + abs(x: b1 - b2) + abs(x: a1 - a2);
2261}
2262
2263static inline int closestMatch(QRgb pixel, const QList<QRgb> &clut) {
2264 int idx = 0;
2265 int current_distance = INT_MAX;
2266 for (int i=0; i<clut.size(); ++i) {
2267 int dist = pixel_distance(p1: pixel, p2: clut.at(i));
2268 if (dist < current_distance) {
2269 current_distance = dist;
2270 idx = i;
2271 }
2272 }
2273 return idx;
2274}
2275
2276static QImage convertWithPalette(const QImage &src, QImage::Format format,
2277 const QList<QRgb> &clut) {
2278 QImage dest(src.size(), format);
2279 dest.setColorTable(clut);
2280
2281 copyMetadata(dst: QImageData::get(img&: dest), src: QImageData::get(img: src));
2282
2283 int h = src.height();
2284 int w = src.width();
2285
2286 QHash<QRgb, int> cache;
2287
2288 if (format == QImage::Format_Indexed8) {
2289 for (int y=0; y<h; ++y) {
2290 const QRgb *src_pixels = (const QRgb *) src.scanLine(i: y);
2291 uchar *dest_pixels = (uchar *) dest.scanLine(i: y);
2292 for (int x=0; x<w; ++x) {
2293 int src_pixel = src_pixels[x];
2294 int value = cache.value(key: src_pixel, defaultValue: -1);
2295 if (value == -1) {
2296 value = closestMatch(pixel: src_pixel, clut);
2297 cache.insert(key: src_pixel, value);
2298 }
2299 dest_pixels[x] = (uchar) value;
2300 }
2301 }
2302 } else {
2303 QList<QRgb> table = clut;
2304 table.resize(size: 2);
2305 for (int y=0; y<h; ++y) {
2306 const QRgb *src_pixels = (const QRgb *) src.scanLine(i: y);
2307 for (int x=0; x<w; ++x) {
2308 int src_pixel = src_pixels[x];
2309 int value = cache.value(key: src_pixel, defaultValue: -1);
2310 if (value == -1) {
2311 value = closestMatch(pixel: src_pixel, clut: table);
2312 cache.insert(key: src_pixel, value);
2313 }
2314 dest.setPixel(x, y, index_or_rgb: value);
2315 }
2316 }
2317 }
2318
2319 return dest;
2320}
2321
2322/*!
2323 \overload
2324
2325 Returns a copy of the image converted to the given \a format,
2326 using the specified \a colorTable.
2327
2328 Conversion from RGB formats to indexed formats is a slow operation
2329 and will use a straightforward nearest color approach, with no
2330 dithering.
2331*/
2332QImage QImage::convertToFormat(Format format, const QList<QRgb> &colorTable, Qt::ImageConversionFlags flags) const
2333{
2334 if (!d || d->format == format)
2335 return *this;
2336
2337 if (format <= QImage::Format_Invalid || format >= QImage::NImageFormats)
2338 return QImage();
2339 if (format <= QImage::Format_Indexed8)
2340 return convertWithPalette(src: convertToFormat(f: QImage::Format_ARGB32, flags), format, clut: colorTable);
2341
2342 return convertToFormat(f: format, flags);
2343}
2344
2345/*!
2346 \since 5.9
2347
2348 Changes the format of the image to \a format without changing the
2349 data. Only works between formats of the same depth.
2350
2351 Returns \c true if successful.
2352
2353 This function can be used to change images with alpha-channels to
2354 their corresponding opaque formats if the data is known to be opaque-only,
2355 or to change the format of a given image buffer before overwriting
2356 it with new data.
2357
2358 \warning The function does not check if the image data is valid in the
2359 new format and will still return \c true if the depths are compatible.
2360 Operations on an image with invalid data are undefined.
2361
2362 \warning If the image is not detached, this will cause the data to be
2363 copied.
2364
2365 \sa hasAlphaChannel(), convertToFormat()
2366*/
2367
2368bool QImage::reinterpretAsFormat(Format format)
2369{
2370 if (format <= Format_Invalid || format >= NImageFormats)
2371 return false;
2372 if (!d)
2373 return false;
2374 if (d->format == format)
2375 return true;
2376 if (qt_depthForFormat(format) != qt_depthForFormat(format: d->format))
2377 return false;
2378 if (!isDetached()) { // Detach only if shared, not for read-only data.
2379 QImageData *oldD = d;
2380 detach();
2381 // In case detach() ran out of memory
2382 if (!d) {
2383 d = oldD;
2384 d->ref.ref();
2385 return false;
2386 }
2387 }
2388
2389 d->format = format;
2390 return true;
2391}
2392
2393/*!
2394 \since 5.13
2395
2396 Converts the image to the given \a format in place, detaching if necessary.
2397
2398 The specified image conversion \a flags control how the image data
2399 is handled during the conversion process.
2400
2401 \sa convertedTo()
2402*/
2403
2404void QImage::convertTo(Format format, Qt::ImageConversionFlags flags)
2405{
2406 if (!d || format <= QImage::Format_Invalid || format >= QImage::NImageFormats)
2407 return;
2408
2409 if (d->format == format)
2410 return;
2411
2412 detach();
2413 if (convertToFormat_inplace(format, flags))
2414 return;
2415
2416 *this = convertToFormat_helper(format, flags);
2417}
2418
2419/*!
2420 \fn bool QImage::valid(const QPoint &pos) const
2421
2422 Returns \c true if \a pos is a valid coordinate pair within the
2423 image; otherwise returns \c false.
2424
2425 \sa rect(), QRect::contains()
2426*/
2427
2428/*!
2429 \overload
2430
2431 Returns \c true if QPoint(\a x, \a y) is a valid coordinate pair
2432 within the image; otherwise returns \c false.
2433*/
2434bool QImage::valid(int x, int y) const
2435{
2436 return d
2437 && x >= 0 && x < d->width
2438 && y >= 0 && y < d->height;
2439}
2440
2441/*!
2442 \fn int QImage::pixelIndex(const QPoint &position) const
2443
2444 Returns the pixel index at the given \a position.
2445
2446 If \a position is not valid, or if the image is not a paletted
2447 image (depth() > 8), the results are undefined.
2448
2449 \sa valid(), depth(), {QImage#Pixel Manipulation}{Pixel Manipulation}
2450*/
2451
2452/*!
2453 \overload
2454
2455 Returns the pixel index at (\a x, \a y).
2456*/
2457int QImage::pixelIndex(int x, int y) const
2458{
2459 if (!d || x < 0 || x >= d->width || y < 0 || y >= height()) {
2460 qWarning(msg: "QImage::pixelIndex: coordinate (%d,%d) out of range", x, y);
2461 return -12345;
2462 }
2463 const uchar * s = scanLine(i: y);
2464 switch(d->format) {
2465 case Format_Mono:
2466 return (*(s + (x >> 3)) >> (7- (x & 7))) & 1;
2467 case Format_MonoLSB:
2468 return (*(s + (x >> 3)) >> (x & 7)) & 1;
2469 case Format_Indexed8:
2470 return (int)s[x];
2471 default:
2472 qWarning(msg: "QImage::pixelIndex: Not applicable for %d-bpp images (no palette)", d->depth);
2473 }
2474 return 0;
2475}
2476
2477
2478/*!
2479 \fn QRgb QImage::pixel(const QPoint &position) const
2480
2481 Returns the color of the pixel at the given \a position.
2482
2483 If the \a position is not valid, the results are undefined.
2484
2485 \warning This function is expensive when used for massive pixel
2486 manipulations. Use constBits() or constScanLine() when many
2487 pixels needs to be read.
2488
2489 \sa setPixel(), valid(), constBits(), constScanLine(), {QImage#Pixel Manipulation}{Pixel
2490 Manipulation}
2491*/
2492
2493/*!
2494 \overload
2495
2496 Returns the color of the pixel at coordinates (\a x, \a y).
2497*/
2498QRgb QImage::pixel(int x, int y) const
2499{
2500 if (!d || x < 0 || x >= d->width || y < 0 || y >= d->height) {
2501 qWarning(msg: "QImage::pixel: coordinate (%d,%d) out of range", x, y);
2502 return 12345;
2503 }
2504
2505 const uchar *s = d->data + y * d->bytes_per_line;
2506
2507 int index = -1;
2508 switch (d->format) {
2509 case Format_Mono:
2510 index = (*(s + (x >> 3)) >> (~x & 7)) & 1;
2511 break;
2512 case Format_MonoLSB:
2513 index = (*(s + (x >> 3)) >> (x & 7)) & 1;
2514 break;
2515 case Format_Indexed8:
2516 index = s[x];
2517 break;
2518 default:
2519 break;
2520 }
2521 if (index >= 0) { // Indexed format
2522 if (index >= d->colortable.size()) {
2523 qWarning(msg: "QImage::pixel: color table index %d out of range.", index);
2524 return 0;
2525 }
2526 return d->colortable.at(i: index);
2527 }
2528
2529 switch (d->format) {
2530 case Format_RGB32:
2531 return 0xff000000 | reinterpret_cast<const QRgb *>(s)[x];
2532 case Format_ARGB32: // Keep old behaviour.
2533 case Format_ARGB32_Premultiplied:
2534 return reinterpret_cast<const QRgb *>(s)[x];
2535 case Format_RGBX8888:
2536 case Format_RGBA8888: // Match ARGB32 behavior.
2537 case Format_RGBA8888_Premultiplied:
2538 return RGBA2ARGB(x: reinterpret_cast<const quint32 *>(s)[x]);
2539 case Format_BGR30:
2540 case Format_A2BGR30_Premultiplied:
2541 return qConvertA2rgb30ToArgb32<PixelOrderBGR>(c: reinterpret_cast<const quint32 *>(s)[x]);
2542 case Format_RGB30:
2543 case Format_A2RGB30_Premultiplied:
2544 return qConvertA2rgb30ToArgb32<PixelOrderRGB>(c: reinterpret_cast<const quint32 *>(s)[x]);
2545 case Format_RGB16:
2546 return qConvertRgb16To32(c: reinterpret_cast<const quint16 *>(s)[x]);
2547 case Format_RGBX64:
2548 case Format_RGBA64: // Match ARGB32 behavior.
2549 case Format_RGBA64_Premultiplied:
2550 return reinterpret_cast<const QRgba64 *>(s)[x].toArgb32();
2551 case Format_RGBX16FPx4:
2552 case Format_RGBA16FPx4: // Match ARGB32 behavior.
2553 case Format_RGBA16FPx4_Premultiplied:
2554 return reinterpret_cast<const QRgbaFloat16 *>(s)[x].toArgb32();
2555 case Format_RGBX32FPx4:
2556 case Format_RGBA32FPx4: // Match ARGB32 behavior.
2557 case Format_RGBA32FPx4_Premultiplied:
2558 return reinterpret_cast<const QRgbaFloat32 *>(s)[x].toArgb32();
2559 default:
2560 break;
2561 }
2562 const QPixelLayout *layout = &qPixelLayouts[d->format];
2563 uint result;
2564 return *layout->fetchToARGB32PM(&result, s, x, 1, nullptr, nullptr);
2565}
2566
2567/*!
2568 \fn void QImage::setPixel(const QPoint &position, uint index_or_rgb)
2569
2570 Sets the pixel index or color at the given \a position to \a
2571 index_or_rgb.
2572
2573 If the image's format is either monochrome or paletted, the given \a
2574 index_or_rgb value must be an index in the image's color table,
2575 otherwise the parameter must be a QRgb value.
2576
2577 If \a position is not a valid coordinate pair in the image, or if
2578 \a index_or_rgb >= colorCount() in the case of monochrome and
2579 paletted images, the result is undefined.
2580
2581 \warning This function is expensive due to the call of the internal
2582 \c{detach()} function called within; if performance is a concern, we
2583 recommend the use of scanLine() or bits() to access pixel data directly.
2584
2585 \sa pixel(), {QImage#Pixel Manipulation}{Pixel Manipulation}
2586*/
2587
2588/*!
2589 \overload
2590
2591 Sets the pixel index or color at (\a x, \a y) to \a index_or_rgb.
2592*/
2593void QImage::setPixel(int x, int y, uint index_or_rgb)
2594{
2595 if (!d || x < 0 || x >= width() || y < 0 || y >= height()) {
2596 qWarning(msg: "QImage::setPixel: coordinate (%d,%d) out of range", x, y);
2597 return;
2598 }
2599 // detach is called from within scanLine
2600 uchar * s = scanLine(i: y);
2601 switch(d->format) {
2602 case Format_Mono:
2603 case Format_MonoLSB:
2604 if (index_or_rgb > 1) {
2605 qWarning(msg: "QImage::setPixel: Index %d out of range", index_or_rgb);
2606 } else if (format() == Format_MonoLSB) {
2607 if (index_or_rgb==0)
2608 *(s + (x >> 3)) &= ~(1 << (x & 7));
2609 else
2610 *(s + (x >> 3)) |= (1 << (x & 7));
2611 } else {
2612 if (index_or_rgb==0)
2613 *(s + (x >> 3)) &= ~(1 << (7-(x & 7)));
2614 else
2615 *(s + (x >> 3)) |= (1 << (7-(x & 7)));
2616 }
2617 return;
2618 case Format_Indexed8:
2619 if (index_or_rgb >= (uint)d->colortable.size()) {
2620 qWarning(msg: "QImage::setPixel: Index %d out of range", index_or_rgb);
2621 return;
2622 }
2623 s[x] = index_or_rgb;
2624 return;
2625 case Format_RGB32:
2626 //make sure alpha is 255, we depend on it in qdrawhelper for cases
2627 // when image is set as a texture pattern on a qbrush
2628 ((uint *)s)[x] = 0xff000000 | index_or_rgb;
2629 return;
2630 case Format_ARGB32:
2631 case Format_ARGB32_Premultiplied:
2632 ((uint *)s)[x] = index_or_rgb;
2633 return;
2634 case Format_RGB16:
2635 ((quint16 *)s)[x] = qConvertRgb32To16(c: index_or_rgb);
2636 return;
2637 case Format_RGBX8888:
2638 ((uint *)s)[x] = ARGB2RGBA(x: 0xff000000 | index_or_rgb);
2639 return;
2640 case Format_RGBA8888:
2641 case Format_RGBA8888_Premultiplied:
2642 ((uint *)s)[x] = ARGB2RGBA(x: index_or_rgb);
2643 return;
2644 case Format_BGR30:
2645 ((uint *)s)[x] = qConvertRgb32ToRgb30<PixelOrderBGR>(c: index_or_rgb);
2646 return;
2647 case Format_A2BGR30_Premultiplied:
2648 ((uint *)s)[x] = qConvertArgb32ToA2rgb30<PixelOrderBGR>(c: index_or_rgb);
2649 return;
2650 case Format_RGB30:
2651 ((uint *)s)[x] = qConvertRgb32ToRgb30<PixelOrderRGB>(c: index_or_rgb);
2652 return;
2653 case Format_A2RGB30_Premultiplied:
2654 ((uint *)s)[x] = qConvertArgb32ToA2rgb30<PixelOrderRGB>(c: index_or_rgb);
2655 return;
2656 case Format_RGBX64:
2657 ((QRgba64 *)s)[x] = QRgba64::fromArgb32(rgb: index_or_rgb | 0xff000000);
2658 return;
2659 case Format_RGBA64:
2660 case Format_RGBA64_Premultiplied:
2661 ((QRgba64 *)s)[x] = QRgba64::fromArgb32(rgb: index_or_rgb);
2662 return;
2663 case Format_RGBX16FPx4:
2664 ((QRgbaFloat16 *)s)[x] = QRgbaFloat16::fromArgb32(rgb: index_or_rgb | 0xff000000);
2665 return;
2666 case Format_RGBA16FPx4:
2667 case Format_RGBA16FPx4_Premultiplied:
2668 ((QRgbaFloat16 *)s)[x] = QRgbaFloat16::fromArgb32(rgb: index_or_rgb);
2669 return;
2670 case Format_RGBX32FPx4:
2671 ((QRgbaFloat32 *)s)[x] = QRgbaFloat32::fromArgb32(rgb: index_or_rgb | 0xff000000);
2672 return;
2673 case Format_RGBA32FPx4:
2674 case Format_RGBA32FPx4_Premultiplied:
2675 ((QRgbaFloat32 *)s)[x] = QRgbaFloat32::fromArgb32(rgb: index_or_rgb);
2676 return;
2677 case Format_Invalid:
2678 case NImageFormats:
2679 Q_ASSERT(false);
2680 return;
2681 default:
2682 break;
2683 }
2684
2685 const QPixelLayout *layout = &qPixelLayouts[d->format];
2686 if (!hasAlphaChannel())
2687 layout->storeFromRGB32(s, &index_or_rgb, x, 1, nullptr, nullptr);
2688 else
2689 layout->storeFromARGB32PM(s, &index_or_rgb, x, 1, nullptr, nullptr);
2690}
2691
2692/*!
2693 \fn QColor QImage::pixelColor(const QPoint &position) const
2694 \since 5.6
2695
2696 Returns the color of the pixel at the given \a position as a QColor.
2697
2698 If the \a position is not valid, an invalid QColor is returned.
2699
2700 \warning This function is expensive when used for massive pixel
2701 manipulations. Use constBits() or constScanLine() when many
2702 pixels needs to be read.
2703
2704 \sa setPixel(), valid(), constBits(), constScanLine(), {QImage#Pixel Manipulation}{Pixel
2705 Manipulation}
2706*/
2707
2708/*!
2709 \overload
2710 \since 5.6
2711
2712 Returns the color of the pixel at coordinates (\a x, \a y) as a QColor.
2713*/
2714QColor QImage::pixelColor(int x, int y) const
2715{
2716 if (!d || x < 0 || x >= d->width || y < 0 || y >= height()) {
2717 qWarning(msg: "QImage::pixelColor: coordinate (%d,%d) out of range", x, y);
2718 return QColor();
2719 }
2720
2721 QRgba64 c;
2722 const uchar * s = constScanLine(i: y);
2723 switch (d->format) {
2724 case Format_BGR30:
2725 case Format_A2BGR30_Premultiplied:
2726 c = qConvertA2rgb30ToRgb64<PixelOrderBGR>(rgb: reinterpret_cast<const quint32 *>(s)[x]);
2727 break;
2728 case Format_RGB30:
2729 case Format_A2RGB30_Premultiplied:
2730 c = qConvertA2rgb30ToRgb64<PixelOrderRGB>(rgb: reinterpret_cast<const quint32 *>(s)[x]);
2731 break;
2732 case Format_RGBX64:
2733 case Format_RGBA64:
2734 case Format_RGBA64_Premultiplied:
2735 c = reinterpret_cast<const QRgba64 *>(s)[x];
2736 break;
2737 case Format_Grayscale16: {
2738 quint16 v = reinterpret_cast<const quint16 *>(s)[x];
2739 return QColor(qRgba64(r: v, g: v, b: v, a: 0xffff));
2740 }
2741 case Format_RGBX16FPx4:
2742 case Format_RGBA16FPx4:
2743 case Format_RGBA16FPx4_Premultiplied: {
2744 QRgbaFloat16 p = reinterpret_cast<const QRgbaFloat16 *>(s)[x];
2745 if (d->format == Format_RGBA16FPx4_Premultiplied)
2746 p = p.unpremultiplied();
2747 QColor color;
2748 color.setRgbF(r: p.red(), g: p.green(), b: p.blue(), a: p.alpha());
2749 return color;
2750 }
2751 case Format_RGBX32FPx4:
2752 case Format_RGBA32FPx4:
2753 case Format_RGBA32FPx4_Premultiplied: {
2754 QRgbaFloat32 p = reinterpret_cast<const QRgbaFloat32 *>(s)[x];
2755 if (d->format == Format_RGBA32FPx4_Premultiplied)
2756 p = p.unpremultiplied();
2757 QColor color;
2758 color.setRgbF(r: p.red(), g: p.green(), b: p.blue(), a: p.alpha());
2759 return color;
2760 }
2761 default:
2762 c = QRgba64::fromArgb32(rgb: pixel(x, y));
2763 break;
2764 }
2765 // QColor is always unpremultiplied
2766 if (hasAlphaChannel() && qPixelLayouts[d->format].premultiplied)
2767 c = c.unpremultiplied();
2768 return QColor(c);
2769}
2770
2771/*!
2772 \fn void QImage::setPixelColor(const QPoint &position, const QColor &color)
2773 \since 5.6
2774
2775 Sets the color at the given \a position to \a color.
2776
2777 If \a position is not a valid coordinate pair in the image, or
2778 the image's format is either monochrome or paletted, the result is undefined.
2779
2780 \warning This function is expensive due to the call of the internal
2781 \c{detach()} function called within; if performance is a concern, we
2782 recommend the use of scanLine() or bits() to access pixel data directly.
2783
2784 \sa pixel(), bits(), scanLine(), {QImage#Pixel Manipulation}{Pixel Manipulation}
2785*/
2786
2787/*!
2788 \overload
2789 \since 5.6
2790
2791 Sets the pixel color at (\a x, \a y) to \a color.
2792*/
2793void QImage::setPixelColor(int x, int y, const QColor &color)
2794{
2795 if (!d || x < 0 || x >= width() || y < 0 || y >= height()) {
2796 qWarning(msg: "QImage::setPixelColor: coordinate (%d,%d) out of range", x, y);
2797 return;
2798 }
2799
2800 if (!color.isValid()) {
2801 qWarning(msg: "QImage::setPixelColor: color is invalid");
2802 return;
2803 }
2804
2805 // QColor is always unpremultiplied
2806 QRgba64 c = color.rgba64();
2807 if (!hasAlphaChannel())
2808 c.setAlpha(65535);
2809 else if (qPixelLayouts[d->format].premultiplied)
2810 c = c.premultiplied();
2811 // detach is called from within scanLine
2812 uchar * s = scanLine(i: y);
2813 switch (d->format) {
2814 case Format_Mono:
2815 case Format_MonoLSB:
2816 case Format_Indexed8:
2817 qWarning(msg: "QImage::setPixelColor: called on monochrome or indexed format");
2818 return;
2819 case Format_BGR30:
2820 ((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderBGR>(c) | 0xc0000000;
2821 return;
2822 case Format_A2BGR30_Premultiplied:
2823 ((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderBGR>(c);
2824 return;
2825 case Format_RGB30:
2826 ((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderRGB>(c) | 0xc0000000;
2827 return;
2828 case Format_A2RGB30_Premultiplied:
2829 ((uint *)s)[x] = qConvertRgb64ToRgb30<PixelOrderRGB>(c);
2830 return;
2831 case Format_RGBX64:
2832 case Format_RGBA64:
2833 case Format_RGBA64_Premultiplied:
2834 ((QRgba64 *)s)[x] = c;
2835 return;
2836 case Format_RGBX16FPx4:
2837 case Format_RGBA16FPx4:
2838 case Format_RGBA16FPx4_Premultiplied: {
2839 float r, g, b, a;
2840 color.getRgbF(r: &r, g: &g, b: &b, a: &a);
2841 if (d->format == Format_RGBX16FPx4)
2842 a = 1.0f;
2843 QRgbaFloat16 c16f{.r: qfloat16(r), .g: qfloat16(g), .b: qfloat16(b), .a: qfloat16(a)};
2844 if (d->format == Format_RGBA16FPx4_Premultiplied)
2845 c16f = c16f.premultiplied();
2846 ((QRgbaFloat16 *)s)[x] = c16f;
2847 return;
2848 }
2849 case Format_RGBX32FPx4:
2850 case Format_RGBA32FPx4:
2851 case Format_RGBA32FPx4_Premultiplied: {
2852 float r, g, b, a;
2853 color.getRgbF(r: &r, g: &g, b: &b, a: &a);
2854 if (d->format == Format_RGBX32FPx4)
2855 a = 1.0f;
2856 QRgbaFloat32 c32f{.r: r, .g: g, .b: b, .a: a};
2857 if (d->format == Format_RGBA32FPx4_Premultiplied)
2858 c32f = c32f.premultiplied();
2859 ((QRgbaFloat32 *)s)[x] = c32f;
2860 return;
2861 }
2862 default:
2863 setPixel(x, y, index_or_rgb: c.toArgb32());
2864 return;
2865 }
2866}
2867
2868/*!
2869 Returns \c true if all the colors in the image are shades of gray
2870 (i.e. their red, green and blue components are equal); otherwise
2871 false.
2872
2873 Note that this function is slow for images without color table.
2874
2875 \sa isGrayscale()
2876*/
2877bool QImage::allGray() const
2878{
2879 if (!d)
2880 return true;
2881
2882 switch (d->format) {
2883 case Format_Mono:
2884 case Format_MonoLSB:
2885 case Format_Indexed8:
2886 for (int i = 0; i < d->colortable.size(); ++i) {
2887 if (!qIsGray(rgb: d->colortable.at(i)))
2888 return false;
2889 }
2890 return true;
2891 case Format_Alpha8:
2892 return false;
2893 case Format_Grayscale8:
2894 case Format_Grayscale16:
2895 return true;
2896 case Format_RGB32:
2897 case Format_ARGB32:
2898 case Format_ARGB32_Premultiplied:
2899#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
2900 case Format_RGBX8888:
2901 case Format_RGBA8888:
2902 case Format_RGBA8888_Premultiplied:
2903#endif
2904 for (int j = 0; j < d->height; ++j) {
2905 const QRgb *b = (const QRgb *)constScanLine(i: j);
2906 for (int i = 0; i < d->width; ++i) {
2907 if (!qIsGray(rgb: b[i]))
2908 return false;
2909 }
2910 }
2911 return true;
2912 case Format_RGB16:
2913 for (int j = 0; j < d->height; ++j) {
2914 const quint16 *b = (const quint16 *)constScanLine(i: j);
2915 for (int i = 0; i < d->width; ++i) {
2916 if (!qIsGray(rgb: qConvertRgb16To32(c: b[i])))
2917 return false;
2918 }
2919 }
2920 return true;
2921 default:
2922 break;
2923 }
2924
2925 Q_DECL_UNINITIALIZED uint buffer[BufferSize];
2926 const QPixelLayout *layout = &qPixelLayouts[d->format];
2927 const auto fetch = layout->fetchToARGB32PM;
2928 for (int j = 0; j < d->height; ++j) {
2929 const uchar *b = constScanLine(i: j);
2930 int x = 0;
2931 while (x < d->width) {
2932 int l = qMin(a: d->width - x, b: BufferSize);
2933 const uint *ptr = fetch(buffer, b, x, l, nullptr, nullptr);
2934 for (int i = 0; i < l; ++i) {
2935 if (!qIsGray(rgb: ptr[i]))
2936 return false;
2937 }
2938 x += l;
2939 }
2940 }
2941 return true;
2942}
2943
2944/*!
2945 For 32-bit images, this function is equivalent to allGray().
2946
2947 For color indexed images, this function returns \c true if
2948 color(i) is QRgb(i, i, i) for all indexes of the color table;
2949 otherwise returns \c false.
2950
2951 \sa allGray(), {QImage#Image Formats}{Image Formats}
2952*/
2953bool QImage::isGrayscale() const
2954{
2955 if (!d)
2956 return false;
2957
2958 if (d->format == QImage::Format_Alpha8)
2959 return false;
2960
2961 if (d->format == QImage::Format_Grayscale8 || d->format == QImage::Format_Grayscale16)
2962 return true;
2963
2964 switch (depth()) {
2965 case 32:
2966 case 24:
2967 case 16:
2968 return allGray();
2969 case 8: {
2970 Q_ASSERT(d->format == QImage::Format_Indexed8);
2971 for (int i = 0; i < colorCount(); i++)
2972 if (d->colortable.at(i) != qRgb(r: i,g: i,b: i))
2973 return false;
2974 return true;
2975 }
2976 }
2977 return false;
2978}
2979
2980/*!
2981 \fn QImage QImage::scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode,
2982 Qt::TransformationMode transformMode) const
2983 \overload
2984
2985 Returns a copy of the image scaled to a rectangle with the given
2986 \a width and \a height according to the given \a aspectRatioMode
2987 and \a transformMode.
2988
2989 If either the \a width or the \a height is zero or negative, this
2990 function returns a null image.
2991*/
2992
2993/*!
2994 \fn QImage QImage::scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode,
2995 Qt::TransformationMode transformMode) const
2996
2997 Returns a copy of the image scaled to a rectangle defined by the
2998 given \a size according to the given \a aspectRatioMode and \a
2999 transformMode.
3000
3001 \image qimage-scaling.png
3002
3003 \list
3004 \li If \a aspectRatioMode is Qt::IgnoreAspectRatio, the image
3005 is scaled to \a size.
3006 \li If \a aspectRatioMode is Qt::KeepAspectRatio, the image is
3007 scaled to a rectangle as large as possible inside \a size, preserving the aspect ratio.
3008 \li If \a aspectRatioMode is Qt::KeepAspectRatioByExpanding,
3009 the image is scaled to a rectangle as small as possible
3010 outside \a size, preserving the aspect ratio.
3011 \endlist
3012
3013 If the given \a size is empty, this function returns a null image.
3014
3015 \sa isNull(), {QImage#Image Transformations}{Image
3016 Transformations}
3017*/
3018QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaled(const QSize& s, Qt::AspectRatioMode aspectMode, Qt::TransformationMode mode) const
3019{
3020 if (!d) {
3021 qWarning(msg: "QImage::scaled: Image is a null image");
3022 return QImage();
3023 }
3024 if (s.isEmpty())
3025 return QImage();
3026
3027 QSize newSize = size();
3028 newSize.scale(s, mode: aspectMode);
3029 newSize.rwidth() = qMax(a: newSize.width(), b: 1);
3030 newSize.rheight() = qMax(a: newSize.height(), b: 1);
3031 if (newSize == size())
3032 return *this;
3033
3034 Q_TRACE_SCOPE(QImage_scaled, s, aspectMode, mode);
3035
3036 QTransform wm = QTransform::fromScale(dx: (qreal)newSize.width() / width(), dy: (qreal)newSize.height() / height());
3037 QImage img = transformed(matrix: wm, mode);
3038 return img;
3039}
3040
3041/*!
3042 \fn QImage QImage::scaledToWidth(int width, Qt::TransformationMode mode) const
3043
3044 Returns a scaled copy of the image. The returned image is scaled
3045 to the given \a width using the specified transformation \a
3046 mode.
3047
3048 This function automatically calculates the height of the image so
3049 that its aspect ratio is preserved.
3050
3051 If the given \a width is 0 or negative, a null image is returned.
3052
3053 \sa {QImage#Image Transformations}{Image Transformations}
3054*/
3055QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaledToWidth(int w, Qt::TransformationMode mode) const
3056{
3057 if (!d) {
3058 qWarning(msg: "QImage::scaleWidth: Image is a null image");
3059 return QImage();
3060 }
3061 if (w <= 0)
3062 return QImage();
3063
3064 Q_TRACE_SCOPE(QImage_scaledToWidth, w, mode);
3065
3066 qreal factor = (qreal) w / width();
3067 QTransform wm = QTransform::fromScale(dx: factor, dy: factor);
3068 return transformed(matrix: wm, mode);
3069}
3070
3071/*!
3072 \fn QImage QImage::scaledToHeight(int height, Qt::TransformationMode mode) const
3073
3074 Returns a scaled copy of the image. The returned image is scaled
3075 to the given \a height using the specified transformation \a
3076 mode.
3077
3078 This function automatically calculates the width of the image so that
3079 the ratio of the image is preserved.
3080
3081 If the given \a height is 0 or negative, a null image is returned.
3082
3083 \sa {QImage#Image Transformations}{Image Transformations}
3084*/
3085QImage Q_TRACE_INSTRUMENT(qtgui) QImage::scaledToHeight(int h, Qt::TransformationMode mode) const
3086{
3087 if (!d) {
3088 qWarning(msg: "QImage::scaleHeight: Image is a null image");
3089 return QImage();
3090 }
3091 if (h <= 0)
3092 return QImage();
3093
3094 Q_TRACE_SCOPE(QImage_scaledToHeight, h, mode);
3095
3096 qreal factor = (qreal) h / height();
3097 QTransform wm = QTransform::fromScale(dx: factor, dy: factor);
3098 return transformed(matrix: wm, mode);
3099}
3100
3101/*!
3102 Builds and returns a 1-bpp mask from the alpha buffer in this
3103 image. Returns a null image if the image's format is
3104 QImage::Format_RGB32.
3105
3106 The \a flags argument is a bitwise-OR of the
3107 Qt::ImageConversionFlags, and controls the conversion
3108 process. Passing 0 for flags sets all the default options.
3109
3110 The returned image has little-endian bit order (i.e. the image's
3111 format is QImage::Format_MonoLSB), which you can convert to
3112 big-endian (QImage::Format_Mono) using the convertToFormat()
3113 function.
3114
3115 \sa createHeuristicMask(), {QImage#Image Transformations}{Image
3116 Transformations}
3117*/
3118QImage Q_TRACE_INSTRUMENT(qtgui) QImage::createAlphaMask(Qt::ImageConversionFlags flags) const
3119{
3120 if (!d || d->format == QImage::Format_RGB32)
3121 return QImage();
3122
3123 if (d->depth == 1) {
3124 // A monochrome pixmap, with alpha channels on those two colors.
3125 // Pretty unlikely, so use less efficient solution.
3126 return convertToFormat(f: Format_Indexed8, flags).createAlphaMask(flags);
3127 }
3128
3129 QImage mask(d->width, d->height, Format_MonoLSB);
3130 if (!mask.isNull()) {
3131 dither_to_Mono(dst: mask.d, src: d, flags, fromalpha: true);
3132 copyPhysicalMetadata(dst: mask.d, src: d);
3133 }
3134 return mask;
3135}
3136
3137#ifndef QT_NO_IMAGE_HEURISTIC_MASK
3138/*!
3139 Creates and returns a 1-bpp heuristic mask for this image.
3140
3141 The function works by selecting a color from one of the corners,
3142 then chipping away pixels of that color starting at all the edges.
3143 The four corners vote for which color is to be masked away. In
3144 case of a draw (this generally means that this function is not
3145 applicable to the image), the result is arbitrary.
3146
3147 The returned image has little-endian bit order (i.e. the image's
3148 format is QImage::Format_MonoLSB), which you can convert to
3149 big-endian (QImage::Format_Mono) using the convertToFormat()
3150 function.
3151
3152 If \a clipTight is true (the default) the mask is just large
3153 enough to cover the pixels; otherwise, the mask is larger than the
3154 data pixels.
3155
3156 Note that this function disregards the alpha buffer.
3157
3158 \sa createAlphaMask(), {QImage#Image Transformations}{Image
3159 Transformations}
3160*/
3161
3162QImage QImage::createHeuristicMask(bool clipTight) const
3163{
3164 if (!d)
3165 return QImage();
3166
3167 if (d->depth != 32) {
3168 QImage img32 = convertToFormat(f: Format_RGB32);
3169 return img32.createHeuristicMask(clipTight);
3170 }
3171
3172#define PIX(x,y) (*((const QRgb*)scanLine(y)+x) & 0x00ffffff)
3173
3174 int w = width();
3175 int h = height();
3176 QImage m(w, h, Format_MonoLSB);
3177 QIMAGE_SANITYCHECK_MEMORY(m);
3178 m.setColorCount(2);
3179 m.setColor(i: 0, c: QColor(Qt::color0).rgba());
3180 m.setColor(i: 1, c: QColor(Qt::color1).rgba());
3181 m.fill(pixel: 0xff);
3182
3183 QRgb background = PIX(0,0);
3184 if (background != PIX(w-1,0) &&
3185 background != PIX(0,h-1) &&
3186 background != PIX(w-1,h-1)) {
3187 background = PIX(w-1,0);
3188 if (background != PIX(w-1,h-1) &&
3189 background != PIX(0,h-1) &&
3190 PIX(0,h-1) == PIX(w-1,h-1)) {
3191 background = PIX(w-1,h-1);
3192 }
3193 }
3194
3195 int x,y;
3196 bool done = false;
3197 uchar *ypp, *ypc, *ypn;
3198 while(!done) {
3199 done = true;
3200 ypn = m.scanLine(i: 0);
3201 ypc = nullptr;
3202 for (y = 0; y < h; y++) {
3203 ypp = ypc;
3204 ypc = ypn;
3205 ypn = (y == h-1) ? nullptr : m.scanLine(i: y+1);
3206 const QRgb *p = (const QRgb *)scanLine(i: y);
3207 for (x = 0; x < w; x++) {
3208 // slowness here - it's possible to do six of these tests
3209 // together in one go. oh well.
3210 if ((x == 0 || y == 0 || x == w-1 || y == h-1 ||
3211 !(*(ypc + ((x-1) >> 3)) & (1 << ((x-1) & 7))) ||
3212 !(*(ypc + ((x+1) >> 3)) & (1 << ((x+1) & 7))) ||
3213 !(*(ypp + (x >> 3)) & (1 << (x & 7))) ||
3214 !(*(ypn + (x >> 3)) & (1 << (x & 7)))) &&
3215 ( (*(ypc + (x >> 3)) & (1 << (x & 7)))) &&
3216 ((*p & 0x00ffffff) == background)) {
3217 done = false;
3218 *(ypc + (x >> 3)) &= ~(1 << (x & 7));
3219 }
3220 p++;
3221 }
3222 }
3223 }
3224
3225 if (!clipTight) {
3226 ypn = m.scanLine(i: 0);
3227 ypc = nullptr;
3228 for (y = 0; y < h; y++) {
3229 ypp = ypc;
3230 ypc = ypn;
3231 ypn = (y == h-1) ? nullptr : m.scanLine(i: y+1);
3232 const QRgb *p = (const QRgb *)scanLine(i: y);
3233 for (x = 0; x < w; x++) {
3234 if ((*p & 0x00ffffff) != background) {
3235 if (x > 0)
3236 *(ypc + ((x-1) >> 3)) |= (1 << ((x-1) & 7));
3237 if (x < w-1)
3238 *(ypc + ((x+1) >> 3)) |= (1 << ((x+1) & 7));
3239 if (y > 0)
3240 *(ypp + (x >> 3)) |= (1 << (x & 7));
3241 if (y < h-1)
3242 *(ypn + (x >> 3)) |= (1 << (x & 7));
3243 }
3244 p++;
3245 }
3246 }
3247 }
3248
3249#undef PIX
3250
3251 copyPhysicalMetadata(dst: m.d, src: d);
3252 return m;
3253}
3254#endif //QT_NO_IMAGE_HEURISTIC_MASK
3255
3256/*!
3257 Creates and returns a mask for this image based on the given \a
3258 color value. If the \a mode is MaskInColor (the default value),
3259 all pixels matching \a color will be opaque pixels in the mask. If
3260 \a mode is MaskOutColor, all pixels matching the given color will
3261 be transparent.
3262
3263 \sa createAlphaMask(), createHeuristicMask()
3264*/
3265
3266QImage QImage::createMaskFromColor(QRgb color, Qt::MaskMode mode) const
3267{
3268 if (!d)
3269 return QImage();
3270 QImage maskImage(size(), QImage::Format_MonoLSB);
3271 QIMAGE_SANITYCHECK_MEMORY(maskImage);
3272 maskImage.fill(pixel: 0);
3273 uchar *s = maskImage.bits();
3274 if (!s)
3275 return QImage();
3276
3277 if (depth() == 32) {
3278 for (int h = 0; h < d->height; h++) {
3279 const uint *sl = (const uint *) scanLine(i: h);
3280 for (int w = 0; w < d->width; w++) {
3281 if (sl[w] == color)
3282 *(s + (w >> 3)) |= (1 << (w & 7));
3283 }
3284 s += maskImage.bytesPerLine();
3285 }
3286 } else {
3287 for (int h = 0; h < d->height; h++) {
3288 for (int w = 0; w < d->width; w++) {
3289 if ((uint) pixel(x: w, y: h) == color)
3290 *(s + (w >> 3)) |= (1 << (w & 7));
3291 }
3292 s += maskImage.bytesPerLine();
3293 }
3294 }
3295 if (mode == Qt::MaskOutColor)
3296 maskImage.invertPixels();
3297
3298 copyPhysicalMetadata(dst: maskImage.d, src: d);
3299 return maskImage;
3300}
3301
3302/*!
3303 \fn QImage QImage::mirrored(bool horizontal = false, bool vertical = true) const &
3304 \fn QImage QImage::mirrored(bool horizontal = false, bool vertical = true) &&
3305 \deprecated [6.13] Use flipped(Qt::Orientations) instead.
3306
3307 Returns a mirror of the image, mirrored in the horizontal and/or
3308 the vertical direction depending on whether \a horizontal and \a
3309 vertical are set to true or false.
3310
3311 Note that the original image is not changed.
3312
3313 \sa mirror(), {QImage#Image Transformations}{Image Transformations}
3314*/
3315
3316/*!
3317 \fn void QImage::mirror(bool horizontal = false, bool vertical = true)
3318 \since 6.0
3319 \deprecated [6.13] Use flip(Qt::Orientations) instead.
3320
3321 Mirrors of the image in the horizontal and/or the vertical direction depending
3322 on whether \a horizontal and \a vertical are set to true or false.
3323
3324 \sa mirrored(), {QImage#Image Transformations}{Image Transformations}
3325*/
3326
3327/*!
3328 \fn QImage QImage::flipped(Qt::Orientations orient) const &
3329 \fn QImage QImage::flipped(Qt::Orientations orient) &&
3330 \since 6.9
3331
3332 Returns a flipped or mirror version of the image, mirrored in the horizontal and/or
3333 the vertical direction depending on \a orient.
3334
3335 Note that the original image is not changed.
3336
3337 \sa flip(Qt::Orientations), {QImage#Image Transformations}{Image Transformations}
3338*/
3339
3340/*!
3341 \fn void QImage::flip(Qt::Orientations orient)
3342 \since 6.9
3343
3344 Flips or mirrors the image in the horizontal and/or the vertical direction depending
3345 on \a orient.
3346
3347 \sa flipped(Qt::Orientations), {QImage#Image Transformations}{Image Transformations}
3348*/
3349
3350template<class T> inline void do_mirror_data(QImageData *dst, QImageData *src,
3351 int dstX0, int dstY0,
3352 int dstXIncr, int dstYIncr,
3353 int w, int h)
3354{
3355 if (dst == src) {
3356 // When mirroring in-place, stop in the middle for one of the directions, since we
3357 // are swapping the bytes instead of merely copying.
3358 const int srcXEnd = (dstX0 && !dstY0) ? w / 2 : w;
3359 const int srcYEnd = dstY0 ? h / 2 : h;
3360 for (int srcY = 0, dstY = dstY0; srcY < srcYEnd; ++srcY, dstY += dstYIncr) {
3361 T *srcPtr = (T *) (src->data + srcY * src->bytes_per_line);
3362 T *dstPtr = (T *) (dst->data + dstY * dst->bytes_per_line);
3363 for (int srcX = 0, dstX = dstX0; srcX < srcXEnd; ++srcX, dstX += dstXIncr)
3364 std::swap(srcPtr[srcX], dstPtr[dstX]);
3365 }
3366 // If mirroring both ways, the middle line needs to be mirrored horizontally only.
3367 if (dstX0 && dstY0 && (h & 1)) {
3368 int srcY = h / 2;
3369 int srcXEnd2 = w / 2;
3370 T *srcPtr = (T *) (src->data + srcY * src->bytes_per_line);
3371 for (int srcX = 0, dstX = dstX0; srcX < srcXEnd2; ++srcX, dstX += dstXIncr)
3372 std::swap(srcPtr[srcX], srcPtr[dstX]);
3373 }
3374 } else {
3375 for (int srcY = 0, dstY = dstY0; srcY < h; ++srcY, dstY += dstYIncr) {
3376 T *srcPtr = (T *) (src->data + srcY * src->bytes_per_line);
3377 T *dstPtr = (T *) (dst->data + dstY * dst->bytes_per_line);
3378 for (int srcX = 0, dstX = dstX0; srcX < w; ++srcX, dstX += dstXIncr)
3379 dstPtr[dstX] = srcPtr[srcX];
3380 }
3381 }
3382}
3383
3384inline void do_flip(QImageData *dst, QImageData *src, int w, int h, int depth)
3385{
3386 const int data_bytes_per_line = w * (depth / 8);
3387 if (dst == src) {
3388 uint *srcPtr = reinterpret_cast<uint *>(src->data);
3389 uint *dstPtr = reinterpret_cast<uint *>(dst->data + (h - 1) * dst->bytes_per_line);
3390 h = h / 2;
3391 const int uint_per_line = (data_bytes_per_line + 3) >> 2; // bytes per line must be a multiple of 4
3392 for (int y = 0; y < h; ++y) {
3393 // This is auto-vectorized, no need for SSE2 or NEON versions:
3394 for (int x = 0; x < uint_per_line; x++) {
3395 const uint d = dstPtr[x];
3396 const uint s = srcPtr[x];
3397 dstPtr[x] = s;
3398 srcPtr[x] = d;
3399 }
3400 srcPtr += src->bytes_per_line >> 2;
3401 dstPtr -= dst->bytes_per_line >> 2;
3402 }
3403
3404 } else {
3405 const uchar *srcPtr = src->data;
3406 uchar *dstPtr = dst->data + (h - 1) * dst->bytes_per_line;
3407 for (int y = 0; y < h; ++y) {
3408 memcpy(dest: dstPtr, src: srcPtr, n: data_bytes_per_line);
3409 srcPtr += src->bytes_per_line;
3410 dstPtr -= dst->bytes_per_line;
3411 }
3412 }
3413}
3414
3415inline void do_mirror(QImageData *dst, QImageData *src, bool horizontal, bool vertical)
3416{
3417 Q_ASSERT(src->width == dst->width && src->height == dst->height && src->depth == dst->depth);
3418 int w = src->width;
3419 int h = src->height;
3420 int depth = src->depth;
3421
3422 if (src->depth == 1) {
3423 w = (w + 7) / 8; // byte aligned width
3424 depth = 8;
3425 }
3426
3427 if (vertical && !horizontal) {
3428 // This one is simple and common, so do it a little more optimized
3429 do_flip(dst, src, w, h, depth);
3430 return;
3431 }
3432
3433 int dstX0 = 0, dstXIncr = 1;
3434 int dstY0 = 0, dstYIncr = 1;
3435 if (horizontal) {
3436 // 0 -> w-1, 1 -> w-2, 2 -> w-3, ...
3437 dstX0 = w - 1;
3438 dstXIncr = -1;
3439 }
3440 if (vertical) {
3441 // 0 -> h-1, 1 -> h-2, 2 -> h-3, ...
3442 dstY0 = h - 1;
3443 dstYIncr = -1;
3444 }
3445
3446 switch (depth) {
3447 case 128:
3448 do_mirror_data<QRgbaFloat32>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
3449 break;
3450 case 64:
3451 do_mirror_data<quint64>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
3452 break;
3453 case 32:
3454 do_mirror_data<quint32>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
3455 break;
3456 case 24:
3457 do_mirror_data<quint24>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
3458 break;
3459 case 16:
3460 do_mirror_data<quint16>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
3461 break;
3462 case 8:
3463 do_mirror_data<quint8>(dst, src, dstX0, dstY0, dstXIncr, dstYIncr, w, h);
3464 break;
3465 default:
3466 Q_ASSERT(false);
3467 break;
3468 }
3469
3470 // The bytes are now all in the correct place. In addition, the bits in the individual
3471 // bytes have to be flipped too when horizontally mirroring a 1 bit-per-pixel image.
3472 if (horizontal && dst->depth == 1) {
3473 Q_ASSERT(dst->format == QImage::Format_Mono || dst->format == QImage::Format_MonoLSB);
3474 const int shift = 8 - (dst->width % 8);
3475 const uchar *bitflip = qt_get_bitflip_array();
3476 for (int y = 0; y < h; ++y) {
3477 uchar *begin = dst->data + y * dst->bytes_per_line;
3478 uchar *end = begin + dst->bytes_per_line;
3479 for (uchar *p = begin; p < end; ++p) {
3480 *p = bitflip[*p];
3481 // When the data is non-byte aligned, an extra bit shift (of the number of
3482 // unused bits at the end) is needed for the entire scanline.
3483 if (shift != 8 && p != begin) {
3484 if (dst->format == QImage::Format_Mono) {
3485 for (int i = 0; i < shift; ++i) {
3486 p[-1] <<= 1;
3487 p[-1] |= (*p & (128 >> i)) >> (7 - i);
3488 }
3489 } else {
3490 for (int i = 0; i < shift; ++i) {
3491 p[-1] >>= 1;
3492 p[-1] |= (*p & (1 << i)) << (7 - i);
3493 }
3494 }
3495 }
3496 }
3497 if (shift != 8) {
3498 if (dst->format == QImage::Format_Mono)
3499 end[-1] <<= shift;
3500 else
3501 end[-1] >>= shift;
3502 }
3503 }
3504 }
3505}
3506
3507/*!
3508 \internal
3509*/
3510QImage QImage::mirrored_helper(bool horizontal, bool vertical) const
3511{
3512 if (!d)
3513 return QImage();
3514
3515 if ((d->width <= 1 && d->height <= 1) || (!horizontal && !vertical))
3516 return *this;
3517
3518 // Create result image, copy colormap
3519 QImage result(d->width, d->height, d->format);
3520 QIMAGE_SANITYCHECK_MEMORY(result);
3521
3522 // check if we ran out of of memory..
3523 if (!result.d)
3524 return QImage();
3525
3526 result.d->colortable = d->colortable;
3527 result.d->has_alpha_clut = d->has_alpha_clut;
3528 copyMetadata(dst: result.d, src: d);
3529
3530 do_mirror(dst: result.d, src: d, horizontal, vertical);
3531
3532 return result;
3533}
3534
3535/*!
3536 \internal
3537*/
3538void QImage::mirrored_inplace(bool horizontal, bool vertical)
3539{
3540 if (!d || (d->width <= 1 && d->height <= 1) || (!horizontal && !vertical))
3541 return;
3542
3543 detach();
3544 if (!d)
3545 return;
3546 if (!d->own_data)
3547 *this = copy();
3548
3549 do_mirror(dst: d, src: d, horizontal, vertical);
3550}
3551
3552/*!
3553 \fn QImage QImage::rgbSwapped() const &
3554 \fn QImage QImage::rgbSwapped() &&
3555
3556 Returns a QImage in which the values of the red and blue
3557 components of all pixels have been swapped, effectively converting
3558 an RGB image to an BGR image.
3559
3560 The original QImage is not changed.
3561
3562 \sa rgbSwap(), {QImage#Image Transformations}{Image Transformations}
3563*/
3564
3565/*!
3566 \fn void QImage::rgbSwap()
3567 \since 6.0
3568
3569 Swaps the values of the red and blue components of all pixels, effectively converting
3570 an RGB image to an BGR image.
3571
3572 \sa rgbSwapped(), {QImage#Image Transformations}{Image Transformations}
3573*/
3574
3575static inline void rgbSwapped_generic(int width, int height, const QImage *src, QImage *dst, const QPixelLayout* layout)
3576{
3577 const RbSwapFunc func = layout->rbSwap;
3578 if (!func) {
3579 qWarning(msg: "Trying to rb-swap an image format where it doesn't make sense");
3580 if (src != dst)
3581 *dst = *src;
3582 return;
3583 }
3584
3585 for (int i = 0; i < height; ++i) {
3586 uchar *q = dst->scanLine(i);
3587 const uchar *p = src->constScanLine(i);
3588 func(q, p, width);
3589 }
3590}
3591
3592/*!
3593 \internal
3594*/
3595QImage Q_TRACE_INSTRUMENT(qtgui) QImage::rgbSwapped_helper() const
3596{
3597 if (isNull())
3598 return *this;
3599
3600 Q_TRACE_SCOPE(QImage_rgbSwapped_helper);
3601
3602 QImage res;
3603
3604 switch (d->format) {
3605 case Format_Invalid:
3606 case NImageFormats:
3607 Q_ASSERT(false);
3608 break;
3609 case Format_Alpha8:
3610 case Format_Grayscale8:
3611 case Format_Grayscale16:
3612 return *this;
3613 case Format_Mono:
3614 case Format_MonoLSB:
3615 case Format_Indexed8:
3616 res = copy();
3617 for (int i = 0; i < res.d->colortable.size(); i++) {
3618 QRgb c = res.d->colortable.at(i);
3619 res.d->colortable[i] = QRgb(((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00));
3620 }
3621 break;
3622 case Format_RGBX8888:
3623 case Format_RGBA8888:
3624 case Format_RGBA8888_Premultiplied:
3625#if Q_BYTE_ORDER == Q_BIG_ENDIAN
3626 res = QImage(d->width, d->height, d->format);
3627 QIMAGE_SANITYCHECK_MEMORY(res);
3628 for (int i = 0; i < d->height; i++) {
3629 uint *q = (uint*)res.scanLine(i);
3630 const uint *p = (const uint*)constScanLine(i);
3631 const uint *end = p + d->width;
3632 while (p < end) {
3633 uint c = *p;
3634 *q = ((c << 16) & 0xff000000) | ((c >> 16) & 0xff00) | (c & 0x00ff00ff);
3635 p++;
3636 q++;
3637 }
3638 }
3639 break;
3640#else
3641 // On little-endian rgba8888 is abgr32 and can use same rgb-swap as argb32
3642 Q_FALLTHROUGH();
3643#endif
3644 case Format_RGB32:
3645 case Format_ARGB32:
3646 case Format_ARGB32_Premultiplied:
3647 res = QImage(d->width, d->height, d->format);
3648 QIMAGE_SANITYCHECK_MEMORY(res);
3649 for (int i = 0; i < d->height; i++) {
3650 uint *q = (uint*)res.scanLine(i);
3651 const uint *p = (const uint*)constScanLine(i);
3652 const uint *end = p + d->width;
3653 while (p < end) {
3654 uint c = *p;
3655 *q = ((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00);
3656 p++;
3657 q++;
3658 }
3659 }
3660 break;
3661 case Format_RGB16:
3662 res = QImage(d->width, d->height, d->format);
3663 QIMAGE_SANITYCHECK_MEMORY(res);
3664 for (int i = 0; i < d->height; i++) {
3665 ushort *q = (ushort*)res.scanLine(i);
3666 const ushort *p = (const ushort*)constScanLine(i);
3667 const ushort *end = p + d->width;
3668 while (p < end) {
3669 ushort c = *p;
3670 *q = ((c << 11) & 0xf800) | ((c >> 11) & 0x1f) | (c & 0x07e0);
3671 p++;
3672 q++;
3673 }
3674 }
3675 break;
3676 default:
3677 res = QImage(d->width, d->height, d->format);
3678 QIMAGE_SANITYCHECK_MEMORY(res);
3679 rgbSwapped_generic(width: d->width, height: d->height, src: this, dst: &res, layout: &qPixelLayouts[d->format]);
3680 break;
3681 }
3682 copyMetadata(dst: res.d, src: d);
3683 return res;
3684}
3685
3686/*!
3687 \internal
3688*/
3689void QImage::rgbSwapped_inplace()
3690{
3691 if (isNull())
3692 return;
3693
3694 detach();
3695 if (!d)
3696 return;
3697 if (!d->own_data)
3698 *this = copy();
3699
3700 switch (d->format) {
3701 case Format_Invalid:
3702 case NImageFormats:
3703 Q_ASSERT(false);
3704 break;
3705 case Format_Alpha8:
3706 case Format_Grayscale8:
3707 case Format_Grayscale16:
3708 return;
3709 case Format_Mono:
3710 case Format_MonoLSB:
3711 case Format_Indexed8:
3712 for (int i = 0; i < d->colortable.size(); i++) {
3713 QRgb c = d->colortable.at(i);
3714 d->colortable[i] = QRgb(((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00));
3715 }
3716 break;
3717 case Format_RGBX8888:
3718 case Format_RGBA8888:
3719 case Format_RGBA8888_Premultiplied:
3720#if Q_BYTE_ORDER == Q_BIG_ENDIAN
3721 for (int i = 0; i < d->height; i++) {
3722 uint *p = (uint*)scanLine(i);
3723 uint *end = p + d->width;
3724 while (p < end) {
3725 uint c = *p;
3726 *p = ((c << 16) & 0xff000000) | ((c >> 16) & 0xff00) | (c & 0x00ff00ff);
3727 p++;
3728 }
3729 }
3730 break;
3731#else
3732 // On little-endian rgba8888 is abgr32 and can use same rgb-swap as argb32
3733 Q_FALLTHROUGH();
3734#endif
3735 case Format_RGB32:
3736 case Format_ARGB32:
3737 case Format_ARGB32_Premultiplied:
3738 for (int i = 0; i < d->height; i++) {
3739 uint *p = (uint*)scanLine(i);
3740 uint *end = p + d->width;
3741 while (p < end) {
3742 uint c = *p;
3743 *p = ((c << 16) & 0xff0000) | ((c >> 16) & 0xff) | (c & 0xff00ff00);
3744 p++;
3745 }
3746 }
3747 break;
3748 case Format_RGB16:
3749 for (int i = 0; i < d->height; i++) {
3750 ushort *p = (ushort*)scanLine(i);
3751 ushort *end = p + d->width;
3752 while (p < end) {
3753 ushort c = *p;
3754 *p = ((c << 11) & 0xf800) | ((c >> 11) & 0x1f) | (c & 0x07e0);
3755 p++;
3756 }
3757 }
3758 break;
3759 case Format_BGR30:
3760 case Format_A2BGR30_Premultiplied:
3761 case Format_RGB30:
3762 case Format_A2RGB30_Premultiplied:
3763 for (int i = 0; i < d->height; i++) {
3764 uint *p = (uint*)scanLine(i);
3765 uint *end = p + d->width;
3766 while (p < end) {
3767 *p = qRgbSwapRgb30(c: *p);
3768 p++;
3769 }
3770 }
3771 break;
3772 default:
3773 rgbSwapped_generic(width: d->width, height: d->height, src: this, dst: this, layout: &qPixelLayouts[d->format]);
3774 break;
3775 }
3776}
3777
3778/*!
3779 Loads an image from the file with the given \a fileName. Returns \c true if
3780 the image was successfully loaded; otherwise invalidates the image
3781 and returns \c false.
3782
3783 The loader attempts to read the image using the specified \a format, e.g.,
3784 PNG or JPG. If \a format is not specified (which is the default), it is
3785 auto-detected based on the file's suffix and header. For details, see
3786 QImageReader::setAutoDetectImageFormat().
3787
3788 The file name can either refer to an actual file on disk or to one
3789 of the application's embedded resources. See the
3790 \l{resources.html}{Resource System} overview for details on how to
3791 embed images and other resource files in the application's
3792 executable.
3793
3794 \sa {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
3795*/
3796
3797bool QImage::load(const QString &fileName, const char* format)
3798{
3799 *this = QImageReader(fileName, format).read();
3800 return !isNull();
3801}
3802
3803/*!
3804 \overload
3805
3806 This function reads a QImage from the given \a device. This can,
3807 for example, be used to load an image directly into a QByteArray.
3808*/
3809
3810bool QImage::load(QIODevice* device, const char* format)
3811{
3812 *this = QImageReader(device, format).read();
3813 return !isNull();
3814}
3815
3816/*!
3817 \since 6.2
3818
3819 Loads an image from the given QByteArrayView \a data. Returns \c true if the image was
3820 successfully loaded; otherwise invalidates the image and returns \c false.
3821
3822 The loader attempts to read the image using the specified \a format, e.g.,
3823 PNG or JPG. If \a format is not specified (which is the default), the
3824 loader probes the file for a header to guess the file format.
3825
3826 \sa {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
3827*/
3828
3829bool QImage::loadFromData(QByteArrayView data, const char *format)
3830{
3831 *this = fromData(data, format);
3832 return !isNull();
3833}
3834
3835/*!
3836 \fn bool QImage::loadFromData(const uchar *data, int len, const char *format)
3837
3838 \overload
3839
3840 Loads an image from the first \a len bytes of the given binary \a data.
3841*/
3842
3843bool QImage::loadFromData(const uchar *buf, int len, const char *format)
3844{
3845 return loadFromData(data: QByteArrayView(buf, len), format);
3846}
3847
3848/*!
3849 \fn bool QImage::loadFromData(const QByteArray &data, const char *format)
3850
3851 \overload
3852
3853 Loads an image from the given QByteArray \a data.
3854*/
3855
3856/*!
3857 \since 6.2
3858
3859 Constructs an image from the given QByteArrayView \a data. The loader attempts to read the image
3860 using the specified \a format. If \a format is not specified (which is the default), the loader
3861 probes the data for a header to guess the file format.
3862
3863 If \a format is specified, it must be one of the values returned by
3864 QImageReader::supportedImageFormats().
3865
3866 If the loading of the image fails, the image returned will be a null image.
3867
3868 \sa load(), save(), {QImage#Reading and Writing Image Files}{Reading and Writing Image Files}
3869 */
3870
3871QImage QImage::fromData(QByteArrayView data, const char *format)
3872{
3873 QByteArray a = QByteArray::fromRawData(data: data.constData(), size: data.size());
3874 QBuffer b;
3875 b.setData(a);
3876 b.open(openMode: QIODevice::ReadOnly);
3877 return QImageReader(&b, format).read();
3878}
3879
3880/*!
3881 \fn QImage QImage::fromData(const uchar *data, int size, const char *format)
3882
3883 \overload
3884
3885 Constructs a QImage from the first \a size bytes of the given binary \a data.
3886*/
3887
3888QImage QImage::fromData(const uchar *data, int size, const char *format)
3889{
3890 return fromData(data: QByteArrayView(data, size), format);
3891}
3892
3893/*!
3894 \fn QImage QImage::fromData(const QByteArray &data, const char *format)
3895
3896 \overload
3897
3898 Constructs a QImage from the given QByteArray \a data.
3899
3900*/
3901
3902/*!
3903 Saves the image to the file with the given \a fileName, using the
3904 given image file \a format and \a quality factor. If \a format is
3905 \nullptr, QImage will attempt to guess the format by looking at
3906 \a fileName's suffix.
3907
3908 The \a quality factor must be in the range 0 to 100 or -1. Specify
3909 0 to obtain small compressed files, 100 for large uncompressed
3910 files, and -1 (the default) to use the default settings.
3911
3912 Returns \c true if the image was successfully saved; otherwise
3913 returns \c false.
3914
3915 \sa {QImage#Reading and Writing Image Files}{Reading and Writing
3916 Image Files}
3917*/
3918bool QImage::save(const QString &fileName, const char *format, int quality) const
3919{
3920 if (isNull())
3921 return false;
3922 QImageWriter writer(fileName, format);
3923 return d->doImageIO(image: this, io: &writer, quality);
3924}
3925
3926/*!
3927 \overload
3928
3929 This function writes a QImage to the given \a device.
3930
3931 This can, for example, be used to save an image directly into a
3932 QByteArray:
3933
3934 \snippet image/image.cpp 0
3935*/
3936
3937bool QImage::save(QIODevice* device, const char* format, int quality) const
3938{
3939 if (isNull())
3940 return false; // nothing to save
3941 QImageWriter writer(device, format);
3942 return d->doImageIO(image: this, io: &writer, quality);
3943}
3944
3945/* \internal
3946*/
3947
3948bool QImageData::doImageIO(const QImage *image, QImageWriter *writer, int quality) const
3949{
3950 if (quality > 100 || quality < -1)
3951 qWarning(msg: "QImage::save: Quality out of range [-1, 100]");
3952 if (quality >= 0)
3953 writer->setQuality(qMin(a: quality,b: 100));
3954 const bool result = writer->write(image: *image);
3955#ifdef QT_DEBUG
3956 if (!result)
3957 qWarning(msg: "QImage::save: failed to write image - %s", qPrintable(writer->errorString()));
3958#endif
3959 return result;
3960}
3961
3962/*****************************************************************************
3963 QImage stream functions
3964 *****************************************************************************/
3965#if !defined(QT_NO_DATASTREAM)
3966/*!
3967 \fn QDataStream &operator<<(QDataStream &stream, const QImage &image)
3968 \relates QImage
3969
3970 Writes the given \a image to the given \a stream as a PNG image,
3971 or as a BMP image if the stream's version is 1. Note that writing
3972 the stream to a file will not produce a valid image file.
3973
3974 \sa QImage::save(), {Serializing Qt Data Types}
3975*/
3976
3977QDataStream &operator<<(QDataStream &s, const QImage &image)
3978{
3979 if (s.version() >= 5) {
3980 if (image.isNull()) {
3981 s << (qint32) 0; // null image marker
3982 return s;
3983 } else {
3984 s << (qint32) 1;
3985 // continue ...
3986 }
3987 }
3988 QImageWriter writer(s.device(), s.version() == 1 ? "bmp" : "png");
3989 writer.write(image);
3990 return s;
3991}
3992
3993/*!
3994 \fn QDataStream &operator>>(QDataStream &stream, QImage &image)
3995 \relates QImage
3996
3997 Reads an image from the given \a stream and stores it in the given
3998 \a image.
3999
4000 \sa QImage::load(), {Serializing Qt Data Types}
4001*/
4002
4003QDataStream &operator>>(QDataStream &s, QImage &image)
4004{
4005 if (s.version() >= 5) {
4006 qint32 nullMarker;
4007 s >> nullMarker;
4008 if (!nullMarker) {
4009 image = QImage(); // null image
4010 return s;
4011 }
4012 }
4013 image = QImageReader(s.device(), s.version() == 1 ? "bmp" : "png").read();
4014 if (image.isNull() && s.version() >= 5)
4015 s.setStatus(QDataStream::ReadPastEnd);
4016 return s;
4017}
4018#endif // QT_NO_DATASTREAM
4019
4020
4021
4022/*!
4023 \fn bool QImage::operator==(const QImage & image) const
4024
4025 Returns \c true if this image and the given \a image have the same
4026 contents; otherwise returns \c false.
4027
4028 The comparison can be slow, unless there is some obvious
4029 difference (e.g. different size or format), in which case the
4030 function will return quickly.
4031
4032 \sa operator=()
4033*/
4034
4035bool QImage::operator==(const QImage & i) const
4036{
4037 // same object, or shared?
4038 if (i.d == d)
4039 return true;
4040 if (!i.d || !d)
4041 return false;
4042
4043 // obviously different stuff?
4044 if (i.d->height != d->height || i.d->width != d->width || i.d->format != d->format || i.d->colorSpace != d->colorSpace)
4045 return false;
4046
4047 if (d->format != Format_RGB32) {
4048 if (d->format >= Format_ARGB32) { // all bits defined
4049 const int n = d->width * d->depth / 8;
4050 if (n == d->bytes_per_line && n == i.d->bytes_per_line) {
4051 if (memcmp(s1: bits(), s2: i.bits(), n: d->nbytes))
4052 return false;
4053 } else {
4054 for (int y = 0; y < d->height; ++y) {
4055 if (memcmp(s1: scanLine(i: y), s2: i.scanLine(i: y), n: n))
4056 return false;
4057 }
4058 }
4059 } else {
4060 const int w = width();
4061 const int h = height();
4062 const QList<QRgb> &colortable = d->colortable;
4063 const QList<QRgb> &icolortable = i.d->colortable;
4064 for (int y=0; y<h; ++y) {
4065 for (int x=0; x<w; ++x) {
4066 if (colortable[pixelIndex(x, y)] != icolortable[i.pixelIndex(x, y)])
4067 return false;
4068 }
4069 }
4070 }
4071 } else {
4072 //alpha channel undefined, so we must mask it out
4073 for(int l = 0; l < d->height; l++) {
4074 int w = d->width;
4075 const uint *p1 = reinterpret_cast<const uint*>(scanLine(i: l));
4076 const uint *p2 = reinterpret_cast<const uint*>(i.scanLine(i: l));
4077 while (w--) {
4078 if ((*p1++ & 0x00ffffff) != (*p2++ & 0x00ffffff))
4079 return false;
4080 }
4081 }
4082 }
4083 return true;
4084}
4085
4086
4087/*!
4088 \fn bool QImage::operator!=(const QImage & image) const
4089
4090 Returns \c true if this image and the given \a image have different
4091 contents; otherwise returns \c false.
4092
4093 The comparison can be slow, unless there is some obvious
4094 difference, such as different widths, in which case the function
4095 will return quickly.
4096
4097 \sa operator=()
4098*/
4099
4100bool QImage::operator!=(const QImage & i) const
4101{
4102 return !(*this == i);
4103}
4104
4105
4106
4107
4108/*!
4109 Returns the number of pixels that fit horizontally in a physical
4110 meter. Together with dotsPerMeterY(), this number defines the
4111 intended scale and aspect ratio of the image.
4112
4113 \sa setDotsPerMeterX(), {QImage#Image Information}{Image
4114 Information}
4115*/
4116int QImage::dotsPerMeterX() const
4117{
4118 return d ? qRound(d: d->dpmx) : 0;
4119}
4120
4121/*!
4122 Returns the number of pixels that fit vertically in a physical
4123 meter. Together with dotsPerMeterX(), this number defines the
4124 intended scale and aspect ratio of the image.
4125
4126 \sa setDotsPerMeterY(), {QImage#Image Information}{Image
4127 Information}
4128*/
4129int QImage::dotsPerMeterY() const
4130{
4131 return d ? qRound(d: d->dpmy) : 0;
4132}
4133
4134/*!
4135 Sets the number of pixels that fit horizontally in a physical
4136 meter, to \a x.
4137
4138 Together with dotsPerMeterY(), this number defines the intended
4139 scale and aspect ratio of the image, and determines the scale
4140 at which QPainter will draw graphics on the image. It does not
4141 change the scale or aspect ratio of the image when it is rendered
4142 on other paint devices.
4143
4144 \sa dotsPerMeterX(), {QImage#Image Information}{Image Information}
4145*/
4146void QImage::setDotsPerMeterX(int x)
4147{
4148 if (!d || !x || d->dpmx == x)
4149 return;
4150 detachMetadata();
4151
4152 if (d)
4153 d->dpmx = x;
4154}
4155
4156/*!
4157 Sets the number of pixels that fit vertically in a physical meter,
4158 to \a y.
4159
4160 Together with dotsPerMeterX(), this number defines the intended
4161 scale and aspect ratio of the image, and determines the scale
4162 at which QPainter will draw graphics on the image. It does not
4163 change the scale or aspect ratio of the image when it is rendered
4164 on other paint devices.
4165
4166 \sa dotsPerMeterY(), {QImage#Image Information}{Image Information}
4167*/
4168void QImage::setDotsPerMeterY(int y)
4169{
4170 if (!d || !y || d->dpmy == y)
4171 return;
4172 detachMetadata();
4173
4174 if (d)
4175 d->dpmy = y;
4176}
4177
4178/*!
4179 \fn QPoint QImage::offset() const
4180
4181 Returns the number of pixels by which the image is intended to be
4182 offset by when positioning relative to other images.
4183
4184 \sa setOffset(), {QImage#Image Information}{Image Information}
4185*/
4186QPoint QImage::offset() const
4187{
4188 return d ? d->offset : QPoint();
4189}
4190
4191
4192/*!
4193 \fn void QImage::setOffset(const QPoint& offset)
4194
4195 Sets the number of pixels by which the image is intended to be
4196 offset by when positioning relative to other images, to \a offset.
4197
4198 \sa offset(), {QImage#Image Information}{Image Information}
4199*/
4200void QImage::setOffset(const QPoint& p)
4201{
4202 if (!d || d->offset == p)
4203 return;
4204 detachMetadata();
4205
4206 if (d)
4207 d->offset = p;
4208}
4209
4210/*!
4211 Returns the text keys for this image.
4212
4213 You can use these keys with text() to list the image text for a
4214 certain key.
4215
4216 \sa text()
4217*/
4218QStringList QImage::textKeys() const
4219{
4220 return d ? QStringList(d->text.keys()) : QStringList();
4221}
4222
4223/*!
4224 Returns the image text associated with the given \a key. If the
4225 specified \a key is an empty string, the whole image text is
4226 returned, with each key-text pair separated by a newline.
4227
4228 \sa setText(), textKeys()
4229*/
4230QString QImage::text(const QString &key) const
4231{
4232 if (!d)
4233 return QString();
4234
4235 if (!key.isEmpty())
4236 return d->text.value(key);
4237
4238 QString tmp;
4239 for (auto it = d->text.begin(), end = d->text.end(); it != end; ++it)
4240 tmp += it.key() + ": "_L1 + it.value().simplified() + "\n\n"_L1;
4241 if (!tmp.isEmpty())
4242 tmp.chop(n: 2); // remove final \n\n
4243 return tmp;
4244}
4245
4246/*!
4247 \fn void QImage::setText(const QString &key, const QString &text)
4248
4249 Sets the image text to the given \a text and associate it with the
4250 given \a key.
4251
4252 If you just want to store a single text block (i.e., a "comment"
4253 or just a description), you can either pass an empty key, or use a
4254 generic key like "Description".
4255
4256 The image text is embedded into the image data when you
4257 call save() or QImageWriter::write().
4258
4259 Not all image formats support embedded text. You can find out
4260 if a specific image or format supports embedding text
4261 by using QImageWriter::supportsOption(). We give an example:
4262
4263 \snippet image/supportedformat.cpp 0
4264
4265 You can use QImageWriter::supportedImageFormats() to find out
4266 which image formats are available to you.
4267
4268 \sa text(), textKeys()
4269*/
4270void QImage::setText(const QString &key, const QString &value)
4271{
4272 if (!d)
4273 return;
4274 detachMetadata();
4275
4276 if (d)
4277 d->text.insert(key, value);
4278}
4279
4280/*!
4281 \internal
4282
4283 Used by QPainter to retrieve a paint engine for the image.
4284*/
4285QPaintEngine *QImage::paintEngine() const
4286{
4287 if (!d)
4288 return nullptr;
4289
4290 if (!d->paintEngine) {
4291 QPaintDevice *paintDevice = const_cast<QImage *>(this);
4292 QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
4293 if (platformIntegration)
4294 d->paintEngine = platformIntegration->createImagePaintEngine(paintDevice);
4295 if (!d->paintEngine)
4296 d->paintEngine = new QRasterPaintEngine(paintDevice);
4297 }
4298
4299 return d->paintEngine;
4300}
4301
4302
4303/*!
4304 \internal
4305
4306 Returns the size for the specified \a metric on the device.
4307*/
4308int QImage::metric(PaintDeviceMetric metric) const
4309{
4310 if (!d)
4311 return 0;
4312
4313 switch (metric) {
4314 case PdmWidth:
4315 return d->width;
4316
4317 case PdmHeight:
4318 return d->height;
4319
4320 case PdmWidthMM:
4321 return qRound(d: d->width * 1000 / d->dpmx);
4322
4323 case PdmHeightMM:
4324 return qRound(d: d->height * 1000 / d->dpmy);
4325
4326 case PdmNumColors:
4327 return d->colortable.size();
4328
4329 case PdmDepth:
4330 return d->depth;
4331
4332 case PdmDpiX:
4333 return qRound(d: d->dpmx * 0.0254);
4334 break;
4335
4336 case PdmDpiY:
4337 return qRound(d: d->dpmy * 0.0254);
4338 break;
4339
4340 case PdmPhysicalDpiX:
4341 return qRound(d: d->dpmx * 0.0254);
4342 break;
4343
4344 case PdmPhysicalDpiY:
4345 return qRound(d: d->dpmy * 0.0254);
4346 break;
4347
4348 case PdmDevicePixelRatio:
4349 return d->devicePixelRatio;
4350 break;
4351
4352 case PdmDevicePixelRatioScaled:
4353 return d->devicePixelRatio * QPaintDevice::devicePixelRatioFScale();
4354 break;
4355
4356 case PdmDevicePixelRatioF_EncodedA:
4357 Q_FALLTHROUGH();
4358 case PdmDevicePixelRatioF_EncodedB:
4359 return QPaintDevice::encodeMetricF(metric, value: d->devicePixelRatio);
4360 break;
4361
4362 default:
4363 qWarning(msg: "QImage::metric(): Unhandled metric type %d", metric);
4364 break;
4365 }
4366 return 0;
4367}
4368
4369
4370
4371/*****************************************************************************
4372 QPixmap (and QImage) helper functions
4373 *****************************************************************************/
4374/*
4375 This internal function contains the common (i.e. platform independent) code
4376 to do a transformation of pixel data. It is used by QPixmap::transform() and by
4377 QImage::transform().
4378
4379 \a trueMat is the true transformation matrix (see QPixmap::trueMatrix()) and
4380 \a xoffset is an offset to the matrix.
4381
4382 \a msbfirst specifies for 1bpp images, if the MSB or LSB comes first and \a
4383 depth specifies the colordepth of the data.
4384
4385 \a dptr is a pointer to the destination data, \a dbpl specifies the bits per
4386 line for the destination data, \a p_inc is the offset that we advance for
4387 every scanline and \a dHeight is the height of the destination image.
4388
4389 \a sprt is the pointer to the source data, \a sbpl specifies the bits per
4390 line of the source data, \a sWidth and \a sHeight are the width and height of
4391 the source data.
4392*/
4393
4394#undef IWX_MSB
4395#define IWX_MSB(b) if (trigx < maxws && trigy < maxhs) { \
4396 if (*(sptr+sbpl*(trigy>>12)+(trigx>>15)) & \
4397 (1 << (7-((trigx>>12)&7)))) \
4398 *dptr |= b; \
4399 } \
4400 trigx += m11; \
4401 trigy += m12;
4402 // END OF MACRO
4403#undef IWX_LSB
4404#define IWX_LSB(b) if (trigx < maxws && trigy < maxhs) { \
4405 if (*(sptr+sbpl*(trigy>>12)+(trigx>>15)) & \
4406 (1 << ((trigx>>12)&7))) \
4407 *dptr |= b; \
4408 } \
4409 trigx += m11; \
4410 trigy += m12;
4411 // END OF MACRO
4412#undef IWX_PIX
4413#define IWX_PIX(b) if (trigx < maxws && trigy < maxhs) { \
4414 if ((*(sptr+sbpl*(trigy>>12)+(trigx>>15)) & \
4415 (1 << (7-((trigx>>12)&7)))) == 0) \
4416 *dptr &= ~b; \
4417 } \
4418 trigx += m11; \
4419 trigy += m12;
4420 // END OF MACRO
4421bool qt_xForm_helper(const QTransform &trueMat, int xoffset, int type, int depth,
4422 uchar *dptr, qsizetype dbpl, int p_inc, int dHeight,
4423 const uchar *sptr, qsizetype sbpl, int sWidth, int sHeight)
4424{
4425 int m11 = int(trueMat.m11()*4096.0);
4426 int m12 = int(trueMat.m12()*4096.0);
4427 int m21 = int(trueMat.m21()*4096.0);
4428 int m22 = int(trueMat.m22()*4096.0);
4429 int dx = qRound(d: trueMat.dx()*4096.0);
4430 int dy = qRound(d: trueMat.dy()*4096.0);
4431
4432 int m21ydx = dx + (xoffset<<16) + (m11 + m21) / 2;
4433 int m22ydy = dy + (m12 + m22) / 2;
4434 uint trigx;
4435 uint trigy;
4436 uint maxws = sWidth<<12;
4437 uint maxhs = sHeight<<12;
4438
4439 for (int y=0; y<dHeight; y++) { // for each target scanline
4440 trigx = m21ydx;
4441 trigy = m22ydy;
4442 uchar *maxp = dptr + dbpl;
4443 if (depth != 1) {
4444 switch (depth) {
4445 case 8: // 8 bpp transform
4446 while (dptr < maxp) {
4447 if (trigx < maxws && trigy < maxhs)
4448 *dptr = *(sptr+sbpl*(trigy>>12)+(trigx>>12));
4449 trigx += m11;
4450 trigy += m12;
4451 dptr++;
4452 }
4453 break;
4454
4455 case 16: // 16 bpp transform
4456 while (dptr < maxp) {
4457 if (trigx < maxws && trigy < maxhs)
4458 *((ushort*)dptr) = *((const ushort *)(sptr+sbpl*(trigy>>12) +
4459 ((trigx>>12)<<1)));
4460 trigx += m11;
4461 trigy += m12;
4462 dptr++;
4463 dptr++;
4464 }
4465 break;
4466
4467 case 24: // 24 bpp transform
4468 while (dptr < maxp) {
4469 if (trigx < maxws && trigy < maxhs) {
4470 const uchar *p2 = sptr+sbpl*(trigy>>12) + ((trigx>>12)*3);
4471 dptr[0] = p2[0];
4472 dptr[1] = p2[1];
4473 dptr[2] = p2[2];
4474 }
4475 trigx += m11;
4476 trigy += m12;
4477 dptr += 3;
4478 }
4479 break;
4480
4481 case 32: // 32 bpp transform
4482 while (dptr < maxp) {
4483 if (trigx < maxws && trigy < maxhs)
4484 *((uint*)dptr) = *((const uint *)(sptr+sbpl*(trigy>>12) +
4485 ((trigx>>12)<<2)));
4486 trigx += m11;
4487 trigy += m12;
4488 dptr += 4;
4489 }
4490 break;
4491
4492 default: {
4493 return false;
4494 }
4495 }
4496 } else {
4497 switch (type) {
4498 case QT_XFORM_TYPE_MSBFIRST:
4499 while (dptr < maxp) {
4500 IWX_MSB(128);
4501 IWX_MSB(64);
4502 IWX_MSB(32);
4503 IWX_MSB(16);
4504 IWX_MSB(8);
4505 IWX_MSB(4);
4506 IWX_MSB(2);
4507 IWX_MSB(1);
4508 dptr++;
4509 }
4510 break;
4511 case QT_XFORM_TYPE_LSBFIRST:
4512 while (dptr < maxp) {
4513 IWX_LSB(1);
4514 IWX_LSB(2);
4515 IWX_LSB(4);
4516 IWX_LSB(8);
4517 IWX_LSB(16);
4518 IWX_LSB(32);
4519 IWX_LSB(64);
4520 IWX_LSB(128);
4521 dptr++;
4522 }
4523 break;
4524 }
4525 }
4526 m21ydx += m21;
4527 m22ydy += m22;
4528 dptr += p_inc;
4529 }
4530 return true;
4531}
4532#undef IWX_MSB
4533#undef IWX_LSB
4534#undef IWX_PIX
4535
4536/*!
4537 Returns a number that identifies the contents of this QImage
4538 object. Distinct QImage objects can only have the same key if they
4539 refer to the same contents.
4540
4541 The key will change when the image is altered.
4542*/
4543qint64 QImage::cacheKey() const
4544{
4545 if (!d)
4546 return 0;
4547 else
4548 return (((qint64) d->ser_no) << 32) | ((qint64) d->detach_no);
4549}
4550
4551/*!
4552 \internal
4553
4554 Returns \c true if the image is detached; otherwise returns \c false.
4555
4556 \sa detach(), {Implicit Data Sharing}
4557*/
4558
4559bool QImage::isDetached() const
4560{
4561 return d && d->ref.loadRelaxed() == 1;
4562}
4563
4564
4565/*!
4566 Sets the alpha channel of this image to the given \a alphaChannel.
4567
4568 If \a alphaChannel is an 8 bit alpha image, the alpha values are
4569 used directly. Otherwise, \a alphaChannel is converted to 8 bit
4570 grayscale and the intensity of the pixel values is used.
4571
4572 If the image already has an alpha channel, the existing alpha channel
4573 is multiplied with the new one. If the image doesn't have an alpha
4574 channel it will be converted to a format that does.
4575
4576 The operation is similar to painting \a alphaChannel as an alpha image
4577 over this image using \c QPainter::CompositionMode_DestinationIn.
4578
4579 \sa hasAlphaChannel(),
4580 {QImage#Image Transformations}{Image Transformations},
4581 {QImage#Image Formats}{Image Formats}
4582*/
4583
4584void QImage::setAlphaChannel(const QImage &alphaChannel)
4585{
4586 if (!d || alphaChannel.isNull())
4587 return;
4588
4589 if (d->paintEngine && d->paintEngine->isActive()) {
4590 qWarning(msg: "QImage::setAlphaChannel: "
4591 "Unable to set alpha channel while image is being painted on");
4592 return;
4593 }
4594
4595 const Format alphaFormat = qt_alphaVersionForPainting(format: d->format);
4596 if (d->format == alphaFormat)
4597 detach();
4598 else
4599 convertTo(format: alphaFormat);
4600
4601 if (isNull())
4602 return;
4603
4604 QImage sourceImage;
4605 if (alphaChannel.format() == QImage::Format_Alpha8 || (alphaChannel.d->depth == 8 && alphaChannel.isGrayscale()))
4606 sourceImage = alphaChannel;
4607 else
4608 sourceImage = alphaChannel.convertToFormat(f: QImage::Format_Grayscale8);
4609 if (!sourceImage.reinterpretAsFormat(format: QImage::Format_Alpha8))
4610 return;
4611
4612 QPainter painter(this);
4613 if (sourceImage.size() != size())
4614 painter.setRenderHint(hint: QPainter::SmoothPixmapTransform);
4615 painter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
4616 painter.drawImage(r: rect(), image: sourceImage);
4617}
4618
4619/*!
4620 Returns \c true if the image has a format that respects the alpha
4621 channel, otherwise returns \c false.
4622
4623 \sa {QImage#Image Information}{Image Information}
4624*/
4625bool QImage::hasAlphaChannel() const
4626{
4627 if (!d)
4628 return false;
4629 const QPixelFormat format = pixelFormat();
4630 if (format.alphaUsage() == QPixelFormat::UsesAlpha)
4631 return true;
4632 if (format.colorModel() == QPixelFormat::Indexed)
4633 return d->has_alpha_clut;
4634 return false;
4635}
4636
4637/*!
4638 Returns the number of bit planes in the image.
4639
4640 The number of bit planes is the number of bits of color and
4641 transparency information for each pixel. This is different from
4642 (i.e. smaller than) the depth when the image format contains
4643 unused bits.
4644
4645 \sa depth(), format(), {QImage#Image Formats}{Image Formats}
4646*/
4647int QImage::bitPlaneCount() const
4648{
4649 if (!d)
4650 return 0;
4651 int bpc = 0;
4652 switch (d->format) {
4653 case QImage::Format_Invalid:
4654 break;
4655 case QImage::Format_BGR30:
4656 case QImage::Format_RGB30:
4657 bpc = 30;
4658 break;
4659 case QImage::Format_RGB32:
4660 case QImage::Format_RGBX8888:
4661 bpc = 24;
4662 break;
4663 case QImage::Format_RGB666:
4664 bpc = 18;
4665 break;
4666 case QImage::Format_RGB555:
4667 bpc = 15;
4668 break;
4669 case QImage::Format_ARGB8555_Premultiplied:
4670 bpc = 23;
4671 break;
4672 case QImage::Format_RGB444:
4673 bpc = 12;
4674 break;
4675 case QImage::Format_RGBX64:
4676 case QImage::Format_RGBX16FPx4:
4677 bpc = 48;
4678 break;
4679 case QImage::Format_RGBX32FPx4:
4680 bpc = 96;
4681 break;
4682 default:
4683 bpc = qt_depthForFormat(format: d->format);
4684 break;
4685 }
4686 return bpc;
4687}
4688
4689/*!
4690 \internal
4691 Returns a smoothly scaled copy of the image. The returned image has a size
4692 of width \a w by height \a h pixels.
4693
4694 The function operates internally on \c Format_RGB32, \c Format_ARGB32_Premultiplied,
4695 \c Format_RGBX8888, \c Format_RGBA8888_Premultiplied, \c Format_RGBX64,
4696 or \c Format_RGBA64_Premultiplied and will convert to those formats
4697 if necessary. To avoid unnecessary conversion the result is returned in the format
4698 internally used, and not in the original format.
4699*/
4700QImage QImage::smoothScaled(int w, int h) const
4701{
4702 QImage src = *this;
4703 switch (src.format()) {
4704 case QImage::Format_RGB32:
4705 case QImage::Format_ARGB32_Premultiplied:
4706#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
4707 case QImage::Format_RGBX8888:
4708#endif
4709 case QImage::Format_RGBA8888_Premultiplied:
4710#if QT_CONFIG(raster_64bit)
4711 case QImage::Format_RGBX64:
4712 case QImage::Format_RGBA64_Premultiplied:
4713 break;
4714 case QImage::Format_RGBA64:
4715 case QImage::Format_Grayscale16:
4716 src.convertTo(format: QImage::Format_RGBA64_Premultiplied);
4717 break;
4718#endif
4719#if QT_CONFIG(raster_fp)
4720 case QImage::Format_RGBX32FPx4:
4721 case QImage::Format_RGBA32FPx4_Premultiplied:
4722 break;
4723 case QImage::Format_RGBX16FPx4:
4724 src.convertTo(format: QImage::Format_RGBX32FPx4);
4725 break;
4726 case QImage::Format_RGBA16FPx4:
4727 case QImage::Format_RGBA16FPx4_Premultiplied:
4728 case QImage::Format_RGBA32FPx4:
4729 src.convertTo(format: QImage::Format_RGBA32FPx4_Premultiplied);
4730 break;
4731#endif
4732 case QImage::Format_CMYK8888:
4733 break;
4734 default:
4735 if (src.hasAlphaChannel())
4736 src.convertTo(format: QImage::Format_ARGB32_Premultiplied);
4737 else
4738 src.convertTo(format: QImage::Format_RGB32);
4739 }
4740 src = qSmoothScaleImage(img: src, w, h);
4741 if (!src.isNull())
4742 copyMetadata(dst: src.d, src: d);
4743 return src;
4744}
4745
4746static QImage rotated90(const QImage &image)
4747{
4748 QImage out(image.height(), image.width(), image.format());
4749 if (out.isNull())
4750 return out;
4751 copyMetadata(dst: QImageData::get(img&: out), src: QImageData::get(img: image));
4752 if (image.colorCount() > 0)
4753 out.setColorTable(image.colorTable());
4754 int w = image.width();
4755 int h = image.height();
4756 const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][2];
4757 if (memrotate) {
4758 memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine());
4759 } else {
4760 for (int y=0; y<h; ++y) {
4761 if (image.colorCount())
4762 for (int x=0; x<w; ++x)
4763 out.setPixel(x: h-y-1, y: x, index_or_rgb: image.pixelIndex(x, y));
4764 else
4765 for (int x=0; x<w; ++x)
4766 out.setPixel(x: h-y-1, y: x, index_or_rgb: image.pixel(x, y));
4767 }
4768 }
4769 return out;
4770}
4771
4772static QImage rotated180(const QImage &image)
4773{
4774 const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][1];
4775 if (!memrotate)
4776 return image.flipped(orient: Qt::Horizontal | Qt::Vertical);
4777
4778 QImage out(image.width(), image.height(), image.format());
4779 if (out.isNull())
4780 return out;
4781 copyMetadata(dst: QImageData::get(img&: out), src: QImageData::get(img: image));
4782 if (image.colorCount() > 0)
4783 out.setColorTable(image.colorTable());
4784 int w = image.width();
4785 int h = image.height();
4786 memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine());
4787 return out;
4788}
4789
4790static QImage rotated270(const QImage &image)
4791{
4792 QImage out(image.height(), image.width(), image.format());
4793 if (out.isNull())
4794 return out;
4795 copyMetadata(dst: QImageData::get(img&: out), src: QImageData::get(img: image));
4796 if (image.colorCount() > 0)
4797 out.setColorTable(image.colorTable());
4798 int w = image.width();
4799 int h = image.height();
4800 const MemRotateFunc memrotate = qMemRotateFunctions[qPixelLayouts[image.format()].bpp][0];
4801 if (memrotate) {
4802 memrotate(image.constBits(), w, h, image.bytesPerLine(), out.bits(), out.bytesPerLine());
4803 } else {
4804 for (int y=0; y<h; ++y) {
4805 if (image.colorCount())
4806 for (int x=0; x<w; ++x)
4807 out.setPixel(x: y, y: w-x-1, index_or_rgb: image.pixelIndex(x, y));
4808 else
4809 for (int x=0; x<w; ++x)
4810 out.setPixel(x: y, y: w-x-1, index_or_rgb: image.pixel(x, y));
4811 }
4812 }
4813 return out;
4814}
4815
4816/*!
4817 Returns a copy of the image that is transformed using the given
4818 transformation \a matrix and transformation \a mode.
4819
4820 The returned image will normally have the same {Image Formats}{format} as
4821 the original image. However, a complex transformation may result in an
4822 image where not all pixels are covered by the transformed pixels of the
4823 original image. In such cases, those background pixels will be assigned a
4824 transparent color value, and the transformed image will be given a format
4825 with an alpha channel, even if the original image did not have that.
4826
4827 The transformation \a matrix is internally adjusted to compensate
4828 for unwanted translation; i.e. the image produced is the smallest
4829 image that contains all the transformed points of the original
4830 image. Use the trueMatrix() function to retrieve the actual matrix
4831 used for transforming an image.
4832
4833 Unlike the other overload, this function can be used to perform perspective
4834 transformations on images.
4835
4836 \sa trueMatrix(), {QImage#Image Transformations}{Image
4837 Transformations}
4838*/
4839
4840QImage Q_TRACE_INSTRUMENT(qtgui) QImage::transformed(const QTransform &matrix, Qt::TransformationMode mode ) const
4841{
4842 if (!d)
4843 return QImage();
4844
4845 Q_TRACE_PARAM_REPLACE(const QTransform &, double[9]);
4846 Q_TRACE_SCOPE(QImage_transformed, QList<double>({matrix.m11(), matrix.m12(), matrix.m13(),
4847 matrix.m21(), matrix.m22(), matrix.m23(),
4848 matrix.m31(), matrix.m32(), matrix.m33()}).data(), mode);
4849
4850 // source image data
4851 const int ws = width();
4852 const int hs = height();
4853
4854 // target image data
4855 int wd;
4856 int hd;
4857
4858 // compute size of target image
4859 QTransform mat = trueMatrix(matrix, w: ws, h: hs);
4860 bool complex_xform = false;
4861 bool scale_xform = false;
4862 bool nonpaintable_scale_xform = false;
4863 if (mat.type() <= QTransform::TxScale) {
4864 if (mat.type() == QTransform::TxNone) // identity matrix
4865 return *this;
4866 else if (mat.m11() == -1. && mat.m22() == -1.)
4867 return rotated180(image: *this);
4868
4869 hd = qRound(d: qAbs(t: mat.m22()) * hs);
4870 wd = qRound(d: qAbs(t: mat.m11()) * ws);
4871 scale_xform = true;
4872 // The paint-based scaling is only bilinear, and has problems
4873 // with scaling smoothly more than 2x down.
4874 if (hd * 2 < hs || wd * 2 < ws)
4875 nonpaintable_scale_xform = true;
4876 // We cannot paint on a CMYK image, so don't try to do so
4877 if (format() == QImage::Format_CMYK8888)
4878 nonpaintable_scale_xform = true;
4879 } else {
4880 if (mat.type() <= QTransform::TxRotate && mat.m11() == 0 && mat.m22() == 0) {
4881 if (mat.m12() == 1. && mat.m21() == -1.)
4882 return rotated90(image: *this);
4883 else if (mat.m12() == -1. && mat.m21() == 1.)
4884 return rotated270(image: *this);
4885 }
4886
4887 QPolygonF a(QRectF(0, 0, ws, hs));
4888 a = mat.map(a);
4889 QRect r = a.boundingRect().toAlignedRect();
4890 wd = r.width();
4891 hd = r.height();
4892 complex_xform = true;
4893 }
4894
4895 if (wd == 0 || hd == 0)
4896 return QImage();
4897
4898 if (scale_xform && mode == Qt::SmoothTransformation) {
4899 switch (format()) {
4900 case QImage::Format_RGB32:
4901 case QImage::Format_ARGB32_Premultiplied:
4902#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
4903 case QImage::Format_RGBX8888:
4904#endif
4905 case QImage::Format_RGBA8888_Premultiplied:
4906#if QT_CONFIG(raster_64bit)
4907 case QImage::Format_RGBX64:
4908 case QImage::Format_RGBA64_Premultiplied:
4909#endif
4910 case QImage::Format_CMYK8888:
4911 // Use smoothScaled for scaling when we can do so without conversion.
4912 if (mat.m11() > 0.0F && mat.m22() > 0.0F)
4913 return smoothScaled(w: wd, h: hd);
4914 break;
4915 default:
4916 break;
4917 }
4918 // Otherwise only use it when the scaling factor demands it, or the image is large enough to scale multi-threaded
4919 if (nonpaintable_scale_xform
4920#if QT_CONFIG(qtgui_threadpool)
4921 || (ws * hs) >= (1<<20)
4922#endif
4923 ) {
4924 QImage scaledImage;
4925 if (mat.m11() < 0.0F && mat.m22() < 0.0F) { // horizontal/vertical flip
4926 scaledImage = smoothScaled(w: wd, h: hd).flipped(orient: Qt::Horizontal | Qt::Vertical);
4927 } else if (mat.m11() < 0.0F) { // horizontal flip
4928 scaledImage = smoothScaled(w: wd, h: hd).flipped(orient: Qt::Horizontal);
4929 } else if (mat.m22() < 0.0F) { // vertical flip
4930 scaledImage = smoothScaled(w: wd, h: hd).flipped(orient: Qt::Vertical);
4931 } else { // no flipping
4932 scaledImage = smoothScaled(w: wd, h: hd);
4933 }
4934
4935 switch (format()) {
4936 case QImage::Format_Mono:
4937 case QImage::Format_MonoLSB:
4938 case QImage::Format_Indexed8:
4939 return scaledImage;
4940 default:
4941 return scaledImage.convertToFormat(f: format());
4942 }
4943 }
4944 }
4945
4946 int bpp = depth();
4947
4948 qsizetype sbpl = bytesPerLine();
4949 const uchar *sptr = bits();
4950
4951 QImage::Format target_format = d->format;
4952
4953 if (complex_xform || mode == Qt::SmoothTransformation) {
4954 if (d->format < QImage::Format_RGB32 || (!hasAlphaChannel() && complex_xform)) {
4955 target_format = qt_alphaVersion(format: d->format);
4956 }
4957 }
4958
4959 QImage dImage(wd, hd, target_format);
4960 QIMAGE_SANITYCHECK_MEMORY(dImage);
4961
4962 if (target_format == QImage::Format_MonoLSB
4963 || target_format == QImage::Format_Mono
4964 || target_format == QImage::Format_Indexed8) {
4965 dImage.d->colortable = d->colortable;
4966 dImage.d->has_alpha_clut = d->has_alpha_clut | complex_xform;
4967 }
4968
4969 // initizialize the data
4970 if (target_format == QImage::Format_Indexed8) {
4971 if (dImage.d->colortable.size() < 256) {
4972 // colors are left in the color table, so pick that one as transparent
4973 dImage.d->colortable.append(t: 0x0);
4974 memset(s: dImage.bits(), c: dImage.d->colortable.size() - 1, n: dImage.d->nbytes);
4975 } else {
4976 memset(s: dImage.bits(), c: 0, n: dImage.d->nbytes);
4977 }
4978 } else
4979 memset(s: dImage.bits(), c: 0x00, n: dImage.d->nbytes);
4980
4981 if (target_format >= QImage::Format_RGB32 && target_format != QImage::Format_CMYK8888) {
4982 // Prevent QPainter from applying devicePixelRatio corrections
4983 QImage sImage = (devicePixelRatio() != 1) ? QImage(constBits(), width(), height(), format()) : *this;
4984 if (sImage.d != d
4985 && (d->format == QImage::Format_MonoLSB
4986 || d->format == QImage::Format_Mono
4987 || d->format == QImage::Format_Indexed8)) {
4988 sImage.d->colortable = d->colortable;
4989 sImage.d->has_alpha_clut = d->has_alpha_clut;
4990 }
4991
4992 Q_ASSERT(sImage.devicePixelRatio() == 1);
4993 Q_ASSERT(sImage.devicePixelRatio() == dImage.devicePixelRatio());
4994
4995 QPainter p(&dImage);
4996 if (mode == Qt::SmoothTransformation) {
4997 p.setRenderHint(hint: QPainter::Antialiasing);
4998 p.setRenderHint(hint: QPainter::SmoothPixmapTransform);
4999 }
5000 p.setTransform(transform: mat);
5001 p.drawImage(p: QPoint(0, 0), image: sImage);
5002 } else {
5003 bool invertible;
5004 mat = mat.inverted(invertible: &invertible); // invert matrix
5005 if (!invertible) // error, return null image
5006 return QImage();
5007
5008 // create target image (some of the code is from QImage::copy())
5009 int type = format() == Format_Mono ? QT_XFORM_TYPE_MSBFIRST : QT_XFORM_TYPE_LSBFIRST;
5010 qsizetype dbpl = dImage.bytesPerLine();
5011 qt_xForm_helper(trueMat: mat, xoffset: 0, type, depth: bpp, dptr: dImage.bits(), dbpl, p_inc: 0, dHeight: hd, sptr, sbpl, sWidth: ws, sHeight: hs);
5012 }
5013 copyMetadata(dst: dImage.d, src: d);
5014
5015 return dImage;
5016}
5017
5018/*!
5019 \fn QTransform QImage::trueMatrix(const QTransform &matrix, int width, int height)
5020
5021 Returns the actual matrix used for transforming an image with the
5022 given \a width, \a height and \a matrix.
5023
5024 When transforming an image using the transformed() function, the
5025 transformation matrix is internally adjusted to compensate for
5026 unwanted translation, i.e. transformed() returns the smallest
5027 image containing all transformed points of the original image.
5028 This function returns the modified matrix, which maps points
5029 correctly from the original image into the new image.
5030
5031 Unlike the other overload, this function creates transformation
5032 matrices that can be used to perform perspective
5033 transformations on images.
5034
5035 \sa transformed(), {QImage#Image Transformations}{Image
5036 Transformations}
5037*/
5038
5039QTransform QImage::trueMatrix(const QTransform &matrix, int w, int h)
5040{
5041 const QRectF rect(0, 0, w, h);
5042 const QRect mapped = matrix.mapRect(rect).toAlignedRect();
5043 const QPoint delta = mapped.topLeft();
5044 return matrix * QTransform().translate(dx: -delta.x(), dy: -delta.y());
5045}
5046
5047/*!
5048 \since 5.14
5049
5050 Sets the image color space to \a colorSpace without performing any conversions on image data.
5051
5052 \sa colorSpace()
5053*/
5054void QImage::setColorSpace(const QColorSpace &colorSpace)
5055{
5056 if (!d)
5057 return;
5058 if (d->colorSpace == colorSpace)
5059 return;
5060 if (colorSpace.isValid() && !qt_compatibleColorModelSource(data: pixelFormat().colorModel(), cs: colorSpace.colorModel()))
5061 return;
5062
5063 detachMetadata(invalidateCache: false);
5064 if (d)
5065 d->colorSpace = colorSpace;
5066}
5067
5068/*!
5069 \since 5.14
5070
5071 Converts the image to \a colorSpace.
5072
5073 If the image has no valid color space, the method does nothing.
5074
5075 \note If \a colorSpace is not compatible with the current format, the image
5076 will be converted to one that is.
5077
5078 \sa convertedToColorSpace(), setColorSpace()
5079*/
5080void QImage::convertToColorSpace(const QColorSpace &colorSpace)
5081{
5082 if (!d || !d->colorSpace.isValid())
5083 return;
5084 if (!colorSpace.isValidTarget()) {
5085 qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid";
5086 return;
5087 }
5088 if (d->colorSpace == colorSpace)
5089 return;
5090 if (!qt_compatibleColorModelTarget(data: pixelFormat().colorModel(),
5091 cs: colorSpace.colorModel(), tm: colorSpace.transformModel())) {
5092 *this = convertedToColorSpace(colorSpace);
5093 return;
5094 }
5095 applyColorTransform(transform: d->colorSpace.transformationToColorSpace(colorspace: colorSpace));
5096 if (d->ref.loadRelaxed() != 1)
5097 detachMetadata(invalidateCache: false);
5098 d->colorSpace = colorSpace;
5099}
5100
5101/*!
5102 \since 6.8
5103
5104 Converts the image to \a colorSpace and \a format.
5105
5106 If the image has no valid color space, the method does nothing,
5107 nor if the color space is not compatible with with the format.
5108
5109 The specified image conversion \a flags control how the image data
5110 is handled during the format conversion process.
5111
5112 \sa convertedToColorSpace(), setColorSpace()
5113*/
5114void QImage::convertToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags)
5115{
5116 if (!d || !d->colorSpace.isValid())
5117 return;
5118 if (!colorSpace.isValidTarget()) {
5119 qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid";
5120 return;
5121 }
5122 if (!qt_compatibleColorModelTarget(data: toPixelFormat(format).colorModel(),
5123 cs: colorSpace.colorModel(), tm: colorSpace.transformModel())) {
5124 qWarning() << "QImage::convertToColorSpace: Color space is not compatible with format";
5125 return;
5126 }
5127
5128 if (d->colorSpace == colorSpace)
5129 return convertTo(format, flags);
5130 applyColorTransform(transform: d->colorSpace.transformationToColorSpace(colorspace: colorSpace), format, flags);
5131 d->colorSpace = colorSpace;
5132}
5133
5134/*!
5135 \since 5.14
5136
5137 Returns the image converted to \a colorSpace.
5138
5139 If the image has no valid color space, a null QImage is returned.
5140
5141 \note If \a colorSpace is not compatible with the current format,
5142 the returned image will also be converted to a format this is.
5143 For more control over returned image format, see the three argument
5144 overload of this method.
5145
5146 \sa convertToColorSpace(), colorTransformed()
5147*/
5148QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace) const
5149{
5150 if (!d || !d->colorSpace.isValid())
5151 return QImage();
5152 if (!colorSpace.isValidTarget()) {
5153 qWarning() << "QImage::convertedToColorSpace: Output colorspace is not valid";
5154 return QImage();
5155 }
5156 if (d->colorSpace == colorSpace)
5157 return *this;
5158 QImage image = colorTransformed(transform: d->colorSpace.transformationToColorSpace(colorspace: colorSpace));
5159 image.setColorSpace(colorSpace);
5160 return image;
5161}
5162
5163/*!
5164 \fn QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags) const &
5165 \fn QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags) &&
5166 \since 6.8
5167
5168 Returns the image converted to \a colorSpace and \a format.
5169
5170 If the image has no valid color space, a null QImage is returned.
5171
5172 The specified image conversion \a flags control how the image data
5173 is handled during the format conversion process.
5174
5175 \sa colorTransformed()
5176*/
5177QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags) const &
5178{
5179 if (!d || !d->colorSpace.isValid())
5180 return QImage();
5181 if (!colorSpace.isValidTarget()) {
5182 qWarning() << "QImage::convertedToColorSpace: Output colorspace is not valid";
5183 return QImage();
5184 }
5185 if (!qt_compatibleColorModelTarget(data: toPixelFormat(format).colorModel(),
5186 cs: colorSpace.colorModel(), tm: colorSpace.transformModel())) {
5187 qWarning() << "QImage::convertedToColorSpace: Color space is not compatible with format";
5188 return QImage();
5189 }
5190 if (d->colorSpace == colorSpace)
5191 return convertedTo(f: format, flags);
5192 QImage image = colorTransformed(transform: d->colorSpace.transformationToColorSpace(colorspace: colorSpace), format, flags);
5193 image.setColorSpace(colorSpace);
5194 return image;
5195}
5196
5197QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace, QImage::Format format, Qt::ImageConversionFlags flags) &&
5198{
5199 if (!d || !d->colorSpace.isValid())
5200 return QImage();
5201 if (!colorSpace.isValidTarget()) {
5202 qWarning() << "QImage::convertedToColorSpace: Output colorspace is not valid";
5203 return QImage();
5204 }
5205 if (!qt_compatibleColorModelTarget(data: toPixelFormat(format).colorModel(),
5206 cs: colorSpace.colorModel(), tm: colorSpace.transformModel())) {
5207 qWarning() << "QImage::convertedToColorSpace: Color space is not compatible with format";
5208 return QImage();
5209 }
5210 if (d->colorSpace == colorSpace)
5211 return convertedTo(f: format, flags);
5212 applyColorTransform(transform: d->colorSpace.transformationToColorSpace(colorspace: colorSpace), format, flags);
5213 return std::move(*this);
5214}
5215
5216/*!
5217 \since 5.14
5218
5219 Returns the color space of the image if a color space is defined.
5220*/
5221QColorSpace QImage::colorSpace() const
5222{
5223 if (!d)
5224 return QColorSpace();
5225 return d->colorSpace;
5226}
5227
5228/*!
5229 \since 5.14
5230
5231 Applies the color transformation \a transform to all pixels in the image.
5232*/
5233void QImage::applyColorTransform(const QColorTransform &transform)
5234{
5235 if (transform.isIdentity())
5236 return;
5237
5238 if (!qt_compatibleColorModelSource(data: pixelFormat().colorModel(), cs: QColorTransformPrivate::get(q: transform)->colorSpaceIn->colorModel) ||
5239 !qt_compatibleColorModelTarget(data: pixelFormat().colorModel(), cs: QColorTransformPrivate::get(q: transform)->colorSpaceOut->colorModel,
5240 tm: QColorTransformPrivate::get(q: transform)->colorSpaceOut->transformModel)) {
5241 qWarning() << "QImage::applyColorTransform can not apply format switching transform without switching format";
5242 return;
5243 }
5244
5245 detach();
5246 if (!d)
5247 return;
5248 if (pixelFormat().colorModel() == QPixelFormat::Indexed) {
5249 for (int i = 0; i < d->colortable.size(); ++i)
5250 d->colortable[i] = transform.map(argb: d->colortable[i]);
5251 return;
5252 }
5253 QImage::Format oldFormat = format();
5254 if (qt_fpColorPrecision(format: oldFormat)) {
5255 if (oldFormat != QImage::Format_RGBX32FPx4 && oldFormat != QImage::Format_RGBA32FPx4
5256 && oldFormat != QImage::Format_RGBA32FPx4_Premultiplied)
5257 convertTo(format: QImage::Format_RGBA32FPx4);
5258 } else if (depth() > 32) {
5259 if (oldFormat != QImage::Format_RGBX64 && oldFormat != QImage::Format_RGBA64
5260 && oldFormat != QImage::Format_RGBA64_Premultiplied)
5261 convertTo(format: QImage::Format_RGBA64);
5262 } else if (oldFormat != QImage::Format_ARGB32 && oldFormat != QImage::Format_RGB32
5263 && oldFormat != QImage::Format_ARGB32_Premultiplied && oldFormat != QImage::Format_CMYK8888
5264 && oldFormat != QImage::Format_Grayscale8 && oldFormat != QImage::Format_Grayscale16) {
5265 if (hasAlphaChannel())
5266 convertTo(format: QImage::Format_ARGB32);
5267 else
5268 convertTo(format: QImage::Format_RGB32);
5269 }
5270
5271 QColorTransformPrivate::TransformFlags flags = QColorTransformPrivate::Unpremultiplied;
5272 switch (format()) {
5273 case Format_ARGB32_Premultiplied:
5274 case Format_RGBA64_Premultiplied:
5275 case Format_RGBA32FPx4_Premultiplied:
5276 flags = QColorTransformPrivate::Premultiplied;
5277 break;
5278 case Format_Grayscale8:
5279 case Format_Grayscale16:
5280 case Format_RGB32:
5281 case Format_CMYK8888:
5282 case Format_RGBX64:
5283 case Format_RGBX32FPx4:
5284 flags = QColorTransformPrivate::InputOpaque;
5285 break;
5286 case Format_ARGB32:
5287 case Format_RGBA64:
5288 case Format_RGBA32FPx4:
5289 break;
5290 default:
5291 Q_UNREACHABLE();
5292 }
5293
5294 std::function<void(int,int)> transformSegment;
5295
5296 if (format() == Format_Grayscale8) {
5297 transformSegment = [&](int yStart, int yEnd) {
5298 for (int y = yStart; y < yEnd; ++y) {
5299 uint8_t *scanline = reinterpret_cast<uint8_t *>(d->data + y * d->bytes_per_line);
5300 QColorTransformPrivate::get(q: transform)->apply(dst: scanline, src: scanline, count: width(), flags);
5301 }
5302 };
5303 } else if (format() == Format_Grayscale16) {
5304 transformSegment = [&](int yStart, int yEnd) {
5305 for (int y = yStart; y < yEnd; ++y) {
5306 uint16_t *scanline = reinterpret_cast<uint16_t *>(d->data + y * d->bytes_per_line);
5307 QColorTransformPrivate::get(q: transform)->apply(dst: scanline, src: scanline, count: width(), flags);
5308 }
5309 };
5310 } else if (qt_fpColorPrecision(format: format())) {
5311 transformSegment = [&](int yStart, int yEnd) {
5312 for (int y = yStart; y < yEnd; ++y) {
5313 QRgbaFloat32 *scanline = reinterpret_cast<QRgbaFloat32 *>(d->data + y * d->bytes_per_line);
5314 QColorTransformPrivate::get(q: transform)->apply(dst: scanline, src: scanline, count: width(), flags);
5315 }
5316 };
5317 } else if (depth() > 32) {
5318 transformSegment = [&](int yStart, int yEnd) {
5319 for (int y = yStart; y < yEnd; ++y) {
5320 QRgba64 *scanline = reinterpret_cast<QRgba64 *>(d->data + y * d->bytes_per_line);
5321 QColorTransformPrivate::get(q: transform)->apply(dst: scanline, src: scanline, count: width(), flags);
5322 }
5323 };
5324 } else if (oldFormat == QImage::Format_CMYK8888) {
5325 transformSegment = [&](int yStart, int yEnd) {
5326 for (int y = yStart; y < yEnd; ++y) {
5327 QCmyk32 *scanline = reinterpret_cast<QCmyk32 *>(d->data + y * d->bytes_per_line);
5328 QColorTransformPrivate::get(q: transform)->apply(dst: scanline, src: scanline, count: width(), flags);
5329 }
5330 };
5331 } else {
5332 transformSegment = [&](int yStart, int yEnd) {
5333 for (int y = yStart; y < yEnd; ++y) {
5334 QRgb *scanline = reinterpret_cast<QRgb *>(d->data + y * d->bytes_per_line);
5335 QColorTransformPrivate::get(q: transform)->apply(dst: scanline, src: scanline, count: width(), flags);
5336 }
5337 };
5338 }
5339
5340#if QT_CONFIG(qtgui_threadpool)
5341 int segments = (qsizetype(width()) * height()) >> 16;
5342 segments = std::min(a: segments, b: height());
5343 QThreadPool *threadPool = QGuiApplicationPrivate::qtGuiThreadPool();
5344 if (segments > 1 && threadPool && !threadPool->contains(thread: QThread::currentThread())) {
5345 QSemaphore semaphore;
5346 int y = 0;
5347 for (int i = 0; i < segments; ++i) {
5348 int yn = (height() - y) / (segments - i);
5349 threadPool->start(functionToRun: [&, y, yn]() {
5350 transformSegment(y, y + yn);
5351 semaphore.release(n: 1);
5352 });
5353 y += yn;
5354 }
5355 semaphore.acquire(n: segments);
5356 } else
5357#endif
5358 transformSegment(0, height());
5359
5360 if (oldFormat != format())
5361 *this = std::move(*this).convertToFormat(f: oldFormat);
5362}
5363
5364/*!
5365 \since 6.8
5366
5367 Applies the color transformation \a transform to all pixels in the image, and converts the format of the image to \a toFormat.
5368
5369 The specified image conversion \a flags control how the image data
5370 is handled during the format conversion process.
5371*/
5372void QImage::applyColorTransform(const QColorTransform &transform, QImage::Format toFormat, Qt::ImageConversionFlags flags)
5373{
5374 if (!d)
5375 return;
5376 if (transform.isIdentity())
5377 return convertTo(format: toFormat, flags);
5378
5379 *this = colorTransformed(transform, format: toFormat, flags);
5380}
5381
5382/*!
5383 \since 6.4
5384
5385 Returns the image color transformed using \a transform on all pixels in the image.
5386
5387 \note If \a transform has a source color space which is incompatible with the format of this image,
5388 returns a null QImage. If \a transform has a target color space which is incompatible with the format
5389 of this image, the image will also be converted to a compatible format. For more control about the
5390 choice of the target pixel format, see the three argument overload of this method.
5391
5392 \sa applyColorTransform()
5393*/
5394QImage QImage::colorTransformed(const QColorTransform &transform) const &
5395{
5396 if (!d)
5397 return QImage();
5398 if (transform.isIdentity())
5399 return *this;
5400
5401 const QColorSpacePrivate *inColorSpace = QColorTransformPrivate::get(q: transform)->colorSpaceIn.constData();
5402 const QColorSpacePrivate *outColorSpace = QColorTransformPrivate::get(q: transform)->colorSpaceOut.constData();
5403 if (!qt_compatibleColorModelSource(data: pixelFormat().colorModel(), cs: inColorSpace->colorModel)) {
5404 qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
5405 return QImage();
5406 }
5407 if (!qt_compatibleColorModelTarget(data: pixelFormat().colorModel(), cs: outColorSpace->colorModel, tm: outColorSpace->transformModel)) {
5408 // All model switching transforms are opaque in at least one end.
5409 switch (outColorSpace->colorModel) {
5410 case QColorSpace::ColorModel::Rgb:
5411 return colorTransformed(transform, format: qt_highColorPrecision(format: format(), opaque: true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
5412 case QColorSpace::ColorModel::Gray:
5413 return colorTransformed(transform, format: qt_highColorPrecision(format: format(), opaque: true) ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
5414 case QColorSpace::ColorModel::Cmyk:
5415 return colorTransformed(transform, format: QImage::Format_CMYK8888);
5416 case QColorSpace::ColorModel::Undefined:
5417 break;
5418 }
5419 return QImage();
5420 }
5421
5422 QImage image = copy();
5423 image.applyColorTransform(transform);
5424 return image;
5425}
5426
5427static bool isRgb32Data(QImage::Format f)
5428{
5429 switch (f) {
5430 case QImage::Format_RGB32:
5431 case QImage::Format_ARGB32:
5432 case QImage::Format_ARGB32_Premultiplied:
5433 return true;
5434 default:
5435 break;
5436 }
5437 return false;
5438}
5439
5440static bool isRgb64Data(QImage::Format f)
5441{
5442 switch (f) {
5443 case QImage::Format_RGBX64:
5444 case QImage::Format_RGBA64:
5445 case QImage::Format_RGBA64_Premultiplied:
5446 return true;
5447 default:
5448 break;
5449 }
5450 return false;
5451}
5452
5453static bool isRgb32fpx4Data(QImage::Format f)
5454{
5455 switch (f) {
5456 case QImage::Format_RGBX32FPx4:
5457 case QImage::Format_RGBA32FPx4:
5458 case QImage::Format_RGBA32FPx4_Premultiplied:
5459 return true;
5460 default:
5461 break;
5462 }
5463 return false;
5464}
5465
5466/*!
5467 \since 6.8
5468
5469 Returns the image color transformed using \a transform on all pixels in the image, returning an image of format \a toFormat.
5470
5471 The specified image conversion \a flags control how the image data
5472 is handled during the format conversion process.
5473
5474 \note If \a transform has a source color space which is incompatible with the format of this image,
5475 or a target color space that is incompatible with \a toFormat, returns a null QImage.
5476
5477 \sa applyColorTransform()
5478*/
5479QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format toFormat, Qt::ImageConversionFlags flags) const &
5480{
5481 if (!d)
5482 return QImage();
5483 if (toFormat == QImage::Format_Invalid)
5484 toFormat = format();
5485 if (transform.isIdentity())
5486 return convertedTo(f: toFormat, flags);
5487
5488 const QColorSpacePrivate *inColorSpace = QColorTransformPrivate::get(q: transform)->colorSpaceIn.constData();
5489 const QColorSpacePrivate *outColorSpace = QColorTransformPrivate::get(q: transform)->colorSpaceOut.constData();
5490 if (!qt_compatibleColorModelSource(data: pixelFormat().colorModel(), cs: inColorSpace->colorModel)) {
5491 qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
5492 return QImage();
5493 }
5494 if (!qt_compatibleColorModelTarget(data: toPixelFormat(format: toFormat).colorModel(), cs: outColorSpace->colorModel, tm: outColorSpace->transformModel)) {
5495 qWarning() << "QImage::colorTransformed: Invalid output color space for transform";
5496 return QImage();
5497 }
5498
5499 QImage fromImage = *this;
5500
5501 QImage::Format tmpFormat = toFormat;
5502 switch (toFormat) {
5503 case QImage::Format_RGB32:
5504 case QImage::Format_ARGB32:
5505 case QImage::Format_ARGB32_Premultiplied:
5506 case QImage::Format_RGBX32FPx4:
5507 case QImage::Format_RGBA32FPx4:
5508 case QImage::Format_RGBA32FPx4_Premultiplied:
5509 case QImage::Format_RGBX64:
5510 case QImage::Format_RGBA64:
5511 case QImage::Format_RGBA64_Premultiplied:
5512 case QImage::Format_Grayscale8:
5513 case QImage::Format_Grayscale16:
5514 case QImage::Format_CMYK8888:
5515 // can be output natively
5516 break;
5517 case QImage::Format_RGB16:
5518 case QImage::Format_RGB444:
5519 case QImage::Format_RGB555:
5520 case QImage::Format_RGB666:
5521 case QImage::Format_RGB888:
5522 case QImage::Format_BGR888:
5523 case QImage::Format_RGBX8888:
5524 tmpFormat = QImage::Format_RGB32;
5525 break;
5526 case QImage::Format_Mono:
5527 case QImage::Format_MonoLSB:
5528 case QImage::Format_Indexed8:
5529 case QImage::Format_ARGB8565_Premultiplied:
5530 case QImage::Format_ARGB6666_Premultiplied:
5531 case QImage::Format_ARGB8555_Premultiplied:
5532 case QImage::Format_ARGB4444_Premultiplied:
5533 case QImage::Format_RGBA8888:
5534 case QImage::Format_RGBA8888_Premultiplied:
5535 tmpFormat = QImage::Format_ARGB32;
5536 break;
5537 case QImage::Format_BGR30:
5538 case QImage::Format_RGB30:
5539 tmpFormat = QImage::Format_RGBX64;
5540 break;
5541 case QImage::Format_A2BGR30_Premultiplied:
5542 case QImage::Format_A2RGB30_Premultiplied:
5543 tmpFormat = QImage::Format_RGBA64;
5544 break;
5545 case QImage::Format_RGBX16FPx4:
5546 case QImage::Format_RGBA16FPx4:
5547 case QImage::Format_RGBA16FPx4_Premultiplied:
5548 tmpFormat = QImage::Format_RGBA32FPx4;
5549 break;
5550 case QImage::Format_Alpha8:
5551 return convertedTo(f: QImage::Format_Alpha8);
5552 case QImage::Format_Invalid:
5553 case QImage::NImageFormats:
5554 Q_UNREACHABLE();
5555 break;
5556 }
5557 QColorSpace::ColorModel inColorData = qt_csColorData(format: pixelFormat().colorModel());
5558 QColorSpace::ColorModel outColorData = qt_csColorData(format: toPixelFormat(format: toFormat).colorModel());
5559 // Ensure only precision increasing transforms
5560 if (inColorData != outColorData) {
5561 if (fromImage.format() == QImage::Format_Grayscale8 && outColorData == QColorSpace::ColorModel::Rgb)
5562 tmpFormat = QImage::Format_RGB32;
5563 else if (tmpFormat == QImage::Format_Grayscale8 && qt_highColorPrecision(format: fromImage.format()))
5564 tmpFormat = QImage::Format_Grayscale16;
5565 else if (fromImage.format() == QImage::Format_Grayscale16 && outColorData == QColorSpace::ColorModel::Rgb)
5566 tmpFormat = QImage::Format_RGBX64;
5567 } else {
5568 if (tmpFormat == QImage::Format_Grayscale8 && fromImage.format() == QImage::Format_Grayscale16)
5569 tmpFormat = QImage::Format_Grayscale16;
5570 else if (qt_fpColorPrecision(format: fromImage.format()) && !qt_fpColorPrecision(format: tmpFormat))
5571 tmpFormat = QImage::Format_RGBA32FPx4;
5572 else if (isRgb32Data(f: tmpFormat) && qt_highColorPrecision(format: fromImage.format(), opaque: true))
5573 tmpFormat = QImage::Format_RGBA64;
5574 }
5575
5576 QImage toImage(size(), tmpFormat);
5577 copyMetadata(dst: &toImage, src: *this);
5578
5579 std::function<void(int, int)> transformSegment;
5580 QColorTransformPrivate::TransformFlags transFlags = QColorTransformPrivate::Unpremultiplied;
5581
5582 if (inColorData != outColorData) {
5583 // Needs color model switching transform
5584 if (inColorData == QColorSpace::ColorModel::Gray && outColorData == QColorSpace::ColorModel::Rgb) {
5585 // Gray -> RGB
5586 if (format() == QImage::Format_Grayscale8) {
5587 transformSegment = [&](int yStart, int yEnd) {
5588 for (int y = yStart; y < yEnd; ++y) {
5589 const quint8 *in_scanline = reinterpret_cast<const quint8 *>(d->data + y * d->bytes_per_line);
5590 QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
5591 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5592 }
5593 };
5594 } else {
5595 transformSegment = [&](int yStart, int yEnd) {
5596 for (int y = yStart; y < yEnd; ++y) {
5597 const quint16 *in_scanline = reinterpret_cast<const quint16 *>(d->data + y * d->bytes_per_line);
5598 QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
5599 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5600 }
5601 };
5602 }
5603 } else if (inColorData == QColorSpace::ColorModel::Gray && outColorData == QColorSpace::ColorModel::Cmyk) {
5604 // Gray -> CMYK
5605 if (format() == QImage::Format_Grayscale8) {
5606 transformSegment = [&](int yStart, int yEnd) {
5607 for (int y = yStart; y < yEnd; ++y) {
5608 const quint8 *in_scanline = reinterpret_cast<const quint8 *>(d->data + y * d->bytes_per_line);
5609 QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
5610 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5611 }
5612 };
5613 } else {
5614 transformSegment = [&](int yStart, int yEnd) {
5615 for (int y = yStart; y < yEnd; ++y) {
5616 const quint16 *in_scanline = reinterpret_cast<const quint16 *>(d->data + y * d->bytes_per_line);
5617 QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
5618 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5619 }
5620 };
5621 }
5622 } else if (inColorData == QColorSpace::ColorModel::Rgb && outColorData == QColorSpace::ColorModel::Gray) {
5623 // RGB -> Gray
5624 if (tmpFormat == QImage::Format_Grayscale8) {
5625 fromImage.convertTo(format: QImage::Format_RGB32);
5626 transformSegment = [&](int yStart, int yEnd) {
5627 for (int y = yStart; y < yEnd; ++y) {
5628 const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5629 quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
5630 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5631 }
5632 };
5633 } else {
5634 fromImage.convertTo(format: QImage::Format_RGBX64);
5635 transformSegment = [&](int yStart, int yEnd) {
5636 for (int y = yStart; y < yEnd; ++y) {
5637 const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5638 quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
5639 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5640 }
5641 };
5642 }
5643 } else if (inColorData == QColorSpace::ColorModel::Cmyk && outColorData == QColorSpace::ColorModel::Gray) {
5644 // CMYK -> Gray
5645 if (tmpFormat == QImage::Format_Grayscale8) {
5646 transformSegment = [&](int yStart, int yEnd) {
5647 for (int y = yStart; y < yEnd; ++y) {
5648 const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5649 quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
5650 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5651 }
5652 };
5653 } else {
5654 transformSegment = [&](int yStart, int yEnd) {
5655 for (int y = yStart; y < yEnd; ++y) {
5656 const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5657 quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
5658 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5659 }
5660 };
5661 }
5662 } else if (inColorData == QColorSpace::ColorModel::Cmyk && outColorData == QColorSpace::ColorModel::Rgb) {
5663 // CMYK -> RGB
5664 if (isRgb32Data(f: tmpFormat) ) {
5665 transformSegment = [&](int yStart, int yEnd) {
5666 for (int y = yStart; y < yEnd; ++y) {
5667 const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5668 QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
5669 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5670 }
5671 };
5672 } else if (isRgb64Data(f: tmpFormat)) {
5673 transformSegment = [&](int yStart, int yEnd) {
5674 for (int y = yStart; y < yEnd; ++y) {
5675 const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5676 QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
5677 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5678 }
5679 };
5680 } else {
5681 Q_ASSERT(isRgb32fpx4Data(tmpFormat));
5682 transformSegment = [&](int yStart, int yEnd) {
5683 for (int y = yStart; y < yEnd; ++y) {
5684 const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5685 QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
5686 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: QColorTransformPrivate::InputOpaque);
5687 }
5688 };
5689 }
5690 } else if (inColorData == QColorSpace::ColorModel::Rgb && outColorData == QColorSpace::ColorModel::Cmyk) {
5691 // RGB -> CMYK
5692 if (!fromImage.hasAlphaChannel())
5693 transFlags = QColorTransformPrivate::InputOpaque;
5694 else if (qPixelLayouts[fromImage.format()].premultiplied)
5695 transFlags = QColorTransformPrivate::Premultiplied;
5696 if (isRgb32Data(f: fromImage.format()) ) {
5697 transformSegment = [&](int yStart, int yEnd) {
5698 for (int y = yStart; y < yEnd; ++y) {
5699 const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5700 QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
5701 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5702 }
5703 };
5704 } else if (isRgb64Data(f: fromImage.format())) {
5705 transformSegment = [&](int yStart, int yEnd) {
5706 for (int y = yStart; y < yEnd; ++y) {
5707 const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5708 QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
5709 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5710 }
5711 };
5712 } else {
5713 Q_ASSERT(isRgb32fpx4Data(fromImage.format()));
5714 transformSegment = [&](int yStart, int yEnd) {
5715 for (int y = yStart; y < yEnd; ++y) {
5716 const QRgbaFloat32 *in_scanline = reinterpret_cast<const QRgbaFloat32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5717 QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
5718 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5719 }
5720 };
5721 }
5722 } else {
5723 Q_UNREACHABLE();
5724 }
5725 } else {
5726 // Conversion on same color model
5727 if (pixelFormat().colorModel() == QPixelFormat::Indexed) {
5728 for (int i = 0; i < d->colortable.size(); ++i)
5729 fromImage.d->colortable[i] = transform.map(argb: d->colortable[i]);
5730 return fromImage.convertedTo(f: toFormat, flags);
5731 }
5732
5733 QImage::Format oldFormat = format();
5734 if (qt_fpColorPrecision(format: oldFormat)) {
5735 if (oldFormat != QImage::Format_RGBX32FPx4 && oldFormat != QImage::Format_RGBA32FPx4
5736 && oldFormat != QImage::Format_RGBA32FPx4_Premultiplied)
5737 fromImage.convertTo(format: QImage::Format_RGBA32FPx4);
5738 } else if (qt_highColorPrecision(format: oldFormat, opaque: true)) {
5739 if (oldFormat != QImage::Format_RGBX64 && oldFormat != QImage::Format_RGBA64
5740 && oldFormat != QImage::Format_RGBA64_Premultiplied && oldFormat != QImage::Format_Grayscale16)
5741 fromImage.convertTo(format: QImage::Format_RGBA64);
5742 } else if (oldFormat != QImage::Format_ARGB32 && oldFormat != QImage::Format_RGB32
5743 && oldFormat != QImage::Format_ARGB32_Premultiplied && oldFormat != QImage::Format_CMYK8888
5744 && oldFormat != QImage::Format_Grayscale8 && oldFormat != QImage::Format_Grayscale16) {
5745 if (hasAlphaChannel())
5746 fromImage.convertTo(format: QImage::Format_ARGB32);
5747 else
5748 fromImage.convertTo(format: QImage::Format_RGB32);
5749 }
5750
5751 if (!fromImage.hasAlphaChannel())
5752 transFlags = QColorTransformPrivate::InputOpaque;
5753 else if (qPixelLayouts[fromImage.format()].premultiplied)
5754 transFlags = QColorTransformPrivate::Premultiplied;
5755
5756 if (fromImage.format() == Format_Grayscale8) {
5757 transformSegment = [&](int yStart, int yEnd) {
5758 for (int y = yStart; y < yEnd; ++y) {
5759 const quint8 *in_scanline = reinterpret_cast<const quint8 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5760 if (tmpFormat == Format_Grayscale8) {
5761 quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
5762 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5763 } else {
5764 Q_ASSERT(tmpFormat == Format_Grayscale16);
5765 quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
5766 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5767 }
5768 }
5769 };
5770 } else if (fromImage.format() == Format_Grayscale16) {
5771 transformSegment = [&](int yStart, int yEnd) {
5772 for (int y = yStart; y < yEnd; ++y) {
5773 const quint16 *in_scanline = reinterpret_cast<const quint16 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5774 quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
5775 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5776 }
5777 };
5778 } else if (fromImage.format() == Format_CMYK8888) {
5779 Q_ASSERT(tmpFormat == Format_CMYK8888);
5780 transformSegment = [&](int yStart, int yEnd) {
5781 for (int y = yStart; y < yEnd; ++y) {
5782 const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5783 QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
5784 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5785 }
5786 };
5787 } else if (isRgb32fpx4Data(f: fromImage.format())) {
5788 Q_ASSERT(isRgb32fpx4Data(tmpFormat));
5789 transformSegment = [&](int yStart, int yEnd) {
5790 for (int y = yStart; y < yEnd; ++y) {
5791 const QRgbaFloat32 *in_scanline = reinterpret_cast<const QRgbaFloat32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5792 QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
5793 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5794 }
5795 };
5796 } else if (isRgb64Data(f: fromImage.format())) {
5797 transformSegment = [&](int yStart, int yEnd) {
5798 for (int y = yStart; y < yEnd; ++y) {
5799 const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5800 if (isRgb32fpx4Data(f: tmpFormat)) {
5801 QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
5802 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5803 } else {
5804 Q_ASSERT(isRgb64Data(tmpFormat));
5805 QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
5806 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5807 }
5808 }
5809 };
5810 } else {
5811 transformSegment = [&](int yStart, int yEnd) {
5812 for (int y = yStart; y < yEnd; ++y) {
5813 const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
5814 if (isRgb32fpx4Data(f: tmpFormat)) {
5815 QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
5816 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5817 } else if (isRgb64Data(f: tmpFormat)) {
5818 QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
5819 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5820 } else {
5821 Q_ASSERT(isRgb32Data(tmpFormat));
5822 QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
5823 QColorTransformPrivate::get(q: transform)->apply(dst: out_scanline, src: in_scanline, count: width(), flags: transFlags);
5824 }
5825 }
5826 };
5827 }
5828 }
5829
5830#if QT_CONFIG(qtgui_threadpool)
5831 int segments = (qsizetype(width()) * height()) >> 16;
5832 segments = std::min(a: segments, b: height());
5833 QThreadPool *threadPool = QGuiApplicationPrivate::qtGuiThreadPool();
5834 if (segments > 1 && threadPool && !threadPool->contains(thread: QThread::currentThread())) {
5835 QSemaphore semaphore;
5836 int y = 0;
5837 for (int i = 0; i < segments; ++i) {
5838 int yn = (height() - y) / (segments - i);
5839 threadPool->start(functionToRun: [&, y, yn]() {
5840 transformSegment(y, y + yn);
5841 semaphore.release(n: 1);
5842 });
5843 y += yn;
5844 }
5845 semaphore.acquire(n: segments);
5846 } else
5847#endif
5848 transformSegment(0, height());
5849
5850 if (tmpFormat != toFormat)
5851 toImage.convertTo(format: toFormat);
5852
5853 return toImage;
5854}
5855
5856/*!
5857 \since 6.4
5858 \overload
5859
5860 Returns the image color transformed using \a transform on all pixels in the image.
5861
5862 \sa applyColorTransform()
5863*/
5864QImage QImage::colorTransformed(const QColorTransform &transform) &&
5865{
5866 if (!d)
5867 return QImage();
5868
5869 const QColorSpacePrivate *inColorSpace = QColorTransformPrivate::get(q: transform)->colorSpaceIn.constData();
5870 const QColorSpacePrivate *outColorSpace = QColorTransformPrivate::get(q: transform)->colorSpaceOut.constData();
5871 if (!qt_compatibleColorModelSource(data: pixelFormat().colorModel(), cs: inColorSpace->colorModel)) {
5872 qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
5873 return QImage();
5874 }
5875 if (!qt_compatibleColorModelTarget(data: pixelFormat().colorModel(), cs: outColorSpace->colorModel, tm: outColorSpace->transformModel)) {
5876 // There is currently no inplace conversion of both colorspace and format, so just use the normal version.
5877 switch (outColorSpace->colorModel) {
5878 case QColorSpace::ColorModel::Rgb:
5879 return colorTransformed(transform, toFormat: qt_highColorPrecision(format: format(), opaque: true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
5880 case QColorSpace::ColorModel::Gray:
5881 return colorTransformed(transform, toFormat: qt_highColorPrecision(format: format(), opaque: true) ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
5882 case QColorSpace::ColorModel::Cmyk:
5883 return colorTransformed(transform, toFormat: QImage::Format_CMYK8888);
5884 case QColorSpace::ColorModel::Undefined:
5885 break;
5886 }
5887 return QImage();
5888 }
5889
5890 applyColorTransform(transform);
5891 return std::move(*this);
5892}
5893
5894/*!
5895 \since 6.8
5896 \overload
5897
5898 Returns the image color transformed using \a transform on all pixels in the image.
5899
5900 \sa applyColorTransform()
5901*/
5902QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format format, Qt::ImageConversionFlags flags) &&
5903{
5904 // There is currently no inplace conversion of both colorspace and format, so just use the normal version.
5905 return colorTransformed(transform, toFormat: format, flags);
5906}
5907
5908bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags flags)
5909{
5910 if (format == newFormat)
5911 return true;
5912
5913 // No in-place conversion if we have to detach
5914 if (ref.loadRelaxed() > 1 || !own_data)
5915 return false;
5916
5917 InPlace_Image_Converter converter = qimage_inplace_converter_map[format][newFormat];
5918 if (converter)
5919 return converter(this, flags);
5920 if (format > QImage::Format_Indexed8 && newFormat > QImage::Format_Indexed8 && !qimage_converter_map[format][newFormat]) {
5921 // Convert inplace generic, but only if there are no direct converters,
5922 // any direct ones are probably better even if not inplace.
5923 if (qt_highColorPrecision(format: newFormat, opaque: !qPixelLayouts[newFormat].hasAlphaChannel)
5924 && qt_highColorPrecision(format, opaque: !qPixelLayouts[format].hasAlphaChannel)) {
5925#if QT_CONFIG(raster_fp)
5926 if (qt_fpColorPrecision(format) && qt_fpColorPrecision(format: newFormat))
5927 return convert_generic_inplace_over_rgba32f(data: this, dst_format: newFormat, flags);
5928#endif
5929 return convert_generic_inplace_over_rgb64(data: this, dst_format: newFormat, flags);
5930 }
5931 return convert_generic_inplace(data: this, dst_format: newFormat, flags);
5932 }
5933 return false;
5934}
5935
5936/*!
5937 \typedef QImage::DataPtr
5938 \internal
5939*/
5940
5941/*!
5942 \fn DataPtr & QImage::data_ptr()
5943 \internal
5944*/
5945
5946#ifndef QT_NO_DEBUG_STREAM
5947QDebug operator<<(QDebug dbg, const QImage &i)
5948{
5949 QDebugStateSaver saver(dbg);
5950 dbg.nospace();
5951 dbg.noquote();
5952 dbg << "QImage(";
5953 if (i.isNull()) {
5954 dbg << "null";
5955 } else {
5956 dbg << i.size() << ",format=" << i.format() << ",depth=" << i.depth();
5957 if (i.colorCount())
5958 dbg << ",colorCount=" << i.colorCount();
5959 const int bytesPerLine = i.bytesPerLine();
5960 dbg << ",devicePixelRatio=" << i.devicePixelRatio()
5961 << ",bytesPerLine=" << bytesPerLine << ",sizeInBytes=" << i.sizeInBytes();
5962 if (dbg.verbosity() > 2 && i.height() > 0) {
5963 const int outputLength = qMin(a: bytesPerLine, b: 24);
5964 dbg << ",line0="
5965 << QByteArray(reinterpret_cast<const char *>(i.scanLine(i: 0)), outputLength).toHex()
5966 << "...";
5967 }
5968 }
5969 dbg << ')';
5970 return dbg;
5971}
5972#endif
5973
5974static constexpr QPixelFormat pixelformats[] = {
5975 //QImage::Format_Invalid:
5976 QPixelFormat(),
5977 //QImage::Format_Mono:
5978 QPixelFormat(QPixelFormat::Indexed,
5979 /*RED*/ 1,
5980 /*GREEN*/ 0,
5981 /*BLUE*/ 0,
5982 /*FOURTH*/ 0,
5983 /*FIFTH*/ 0,
5984 /*ALPHA*/ 0,
5985 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
5986 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
5987 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
5988 /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
5989 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
5990 //QImage::Format_MonoLSB:
5991 QPixelFormat(QPixelFormat::Indexed,
5992 /*RED*/ 1,
5993 /*GREEN*/ 0,
5994 /*BLUE*/ 0,
5995 /*FOURTH*/ 0,
5996 /*FIFTH*/ 0,
5997 /*ALPHA*/ 0,
5998 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
5999 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6000 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6001 /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
6002 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6003 //QImage::Format_Indexed8:
6004 QPixelFormat(QPixelFormat::Indexed,
6005 /*RED*/ 8,
6006 /*GREEN*/ 0,
6007 /*BLUE*/ 0,
6008 /*FOURTH*/ 0,
6009 /*FIFTH*/ 0,
6010 /*ALPHA*/ 0,
6011 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6012 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6013 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6014 /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
6015 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6016 //QImage::Format_RGB32:
6017 QPixelFormat(QPixelFormat::RGB,
6018 /*RED*/ 8,
6019 /*GREEN*/ 8,
6020 /*BLUE*/ 8,
6021 /*FOURTH*/ 0,
6022 /*FIFTH*/ 0,
6023 /*ALPHA*/ 8,
6024 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6025 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6026 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6027 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6028 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6029 //QImage::Format_ARGB32:
6030 QPixelFormat(QPixelFormat::RGB,
6031 /*RED*/ 8,
6032 /*GREEN*/ 8,
6033 /*BLUE*/ 8,
6034 /*FOURTH*/ 0,
6035 /*FIFTH*/ 0,
6036 /*ALPHA*/ 8,
6037 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6038 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6039 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6040 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6041 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6042 //QImage::Format_ARGB32_Premultiplied:
6043 QPixelFormat(QPixelFormat::RGB,
6044 /*RED*/ 8,
6045 /*GREEN*/ 8,
6046 /*BLUE*/ 8,
6047 /*FOURTH*/ 0,
6048 /*FIFTH*/ 0,
6049 /*ALPHA*/ 8,
6050 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6051 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6052 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6053 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6054 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6055 //QImage::Format_RGB16:
6056 QPixelFormat(QPixelFormat::RGB,
6057 /*RED*/ 5,
6058 /*GREEN*/ 6,
6059 /*BLUE*/ 5,
6060 /*FOURTH*/ 0,
6061 /*FIFTH*/ 0,
6062 /*ALPHA*/ 0,
6063 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6064 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6065 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6066 /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
6067 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6068 //QImage::Format_ARGB8565_Premultiplied:
6069 QPixelFormat(QPixelFormat::RGB,
6070 /*RED*/ 5,
6071 /*GREEN*/ 6,
6072 /*BLUE*/ 5,
6073 /*FOURTH*/ 0,
6074 /*FIFTH*/ 0,
6075 /*ALPHA*/ 8,
6076 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6077 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6078 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6079 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6080 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6081 //QImage::Format_RGB666:
6082 QPixelFormat(QPixelFormat::RGB,
6083 /*RED*/ 6,
6084 /*GREEN*/ 6,
6085 /*BLUE*/ 6,
6086 /*FOURTH*/ 0,
6087 /*FIFTH*/ 0,
6088 /*ALPHA*/ 0,
6089 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6090 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6091 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6092 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6093 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6094 //QImage::Format_ARGB6666_Premultiplied:
6095 QPixelFormat(QPixelFormat::RGB,
6096 /*RED*/ 6,
6097 /*GREEN*/ 6,
6098 /*BLUE*/ 6,
6099 /*FOURTH*/ 0,
6100 /*FIFTH*/ 0,
6101 /*ALPHA*/ 6,
6102 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6103 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6104 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6105 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6106 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6107 //QImage::Format_RGB555:
6108 QPixelFormat(QPixelFormat::RGB,
6109 /*RED*/ 5,
6110 /*GREEN*/ 5,
6111 /*BLUE*/ 5,
6112 /*FOURTH*/ 0,
6113 /*FIFTH*/ 0,
6114 /*ALPHA*/ 0,
6115 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6116 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6117 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6118 /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
6119 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6120 //QImage::Format_ARGB8555_Premultiplied:
6121 QPixelFormat(QPixelFormat::RGB,
6122 /*RED*/ 5,
6123 /*GREEN*/ 5,
6124 /*BLUE*/ 5,
6125 /*FOURTH*/ 0,
6126 /*FIFTH*/ 0,
6127 /*ALPHA*/ 8,
6128 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6129 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6130 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6131 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6132 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6133 //QImage::Format_RGB888:
6134 QPixelFormat(QPixelFormat::RGB,
6135 /*RED*/ 8,
6136 /*GREEN*/ 8,
6137 /*BLUE*/ 8,
6138 /*FOURTH*/ 0,
6139 /*FIFTH*/ 0,
6140 /*ALPHA*/ 0,
6141 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6142 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6143 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6144 /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
6145 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6146 //QImage::Format_RGB444:
6147 QPixelFormat(QPixelFormat::RGB,
6148 /*RED*/ 4,
6149 /*GREEN*/ 4,
6150 /*BLUE*/ 4,
6151 /*FOURTH*/ 0,
6152 /*FIFTH*/ 0,
6153 /*ALPHA*/ 0,
6154 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6155 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6156 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6157 /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
6158 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6159 //QImage::Format_ARGB4444_Premultiplied:
6160 QPixelFormat(QPixelFormat::RGB,
6161 /*RED*/ 4,
6162 /*GREEN*/ 4,
6163 /*BLUE*/ 4,
6164 /*FOURTH*/ 0,
6165 /*FIFTH*/ 0,
6166 /*ALPHA*/ 4,
6167 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6168 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6169 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6170 /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
6171 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6172 //QImage::Format_RGBX8888:
6173 QPixelFormat(QPixelFormat::RGB,
6174 /*RED*/ 8,
6175 /*GREEN*/ 8,
6176 /*BLUE*/ 8,
6177 /*FOURTH*/ 0,
6178 /*FIFTH*/ 0,
6179 /*ALPHA*/ 8,
6180 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6181 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6182 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6183 /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
6184 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6185 //QImage::Format_RGBA8888:
6186 QPixelFormat(QPixelFormat::RGB,
6187 /*RED*/ 8,
6188 /*GREEN*/ 8,
6189 /*BLUE*/ 8,
6190 /*FOURTH*/ 0,
6191 /*FIFTH*/ 0,
6192 /*ALPHA*/ 8,
6193 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6194 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6195 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6196 /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
6197 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6198 //QImage::Format_RGBA8888_Premultiplied:
6199 QPixelFormat(QPixelFormat::RGB,
6200 /*RED*/ 8,
6201 /*GREEN*/ 8,
6202 /*BLUE*/ 8,
6203 /*FOURTH*/ 0,
6204 /*FIFTH*/ 0,
6205 /*ALPHA*/ 8,
6206 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6207 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6208 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6209 /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
6210 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6211 //QImage::Format_BGR30:
6212 QPixelFormat(QPixelFormat::BGR,
6213 /*RED*/ 10,
6214 /*GREEN*/ 10,
6215 /*BLUE*/ 10,
6216 /*FOURTH*/ 0,
6217 /*FIFTH*/ 0,
6218 /*ALPHA*/ 2,
6219 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6220 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6221 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6222 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6223 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6224 //QImage::Format_A2BGR30_Premultiplied:
6225 QPixelFormat(QPixelFormat::BGR,
6226 /*RED*/ 10,
6227 /*GREEN*/ 10,
6228 /*BLUE*/ 10,
6229 /*FOURTH*/ 0,
6230 /*FIFTH*/ 0,
6231 /*ALPHA*/ 2,
6232 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6233 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6234 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6235 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6236 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6237 //QImage::Format_RGB30:
6238 QPixelFormat(QPixelFormat::RGB,
6239 /*RED*/ 10,
6240 /*GREEN*/ 10,
6241 /*BLUE*/ 10,
6242 /*FOURTH*/ 0,
6243 /*FIFTH*/ 0,
6244 /*ALPHA*/ 2,
6245 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6246 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6247 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6248 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6249 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6250 //QImage::Format_A2RGB30_Premultiplied:
6251 QPixelFormat(QPixelFormat::RGB,
6252 /*RED*/ 10,
6253 /*GREEN*/ 10,
6254 /*BLUE*/ 10,
6255 /*FOURTH*/ 0,
6256 /*FIFTH*/ 0,
6257 /*ALPHA*/ 2,
6258 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6259 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6260 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6261 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6262 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6263 //QImage::Format_Alpha8:
6264 QPixelFormat(QPixelFormat::Alpha,
6265 /*First*/ 0,
6266 /*SECOND*/ 0,
6267 /*THIRD*/ 0,
6268 /*FOURTH*/ 0,
6269 /*FIFTH*/ 0,
6270 /*ALPHA*/ 8,
6271 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6272 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6273 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6274 /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
6275 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6276 //QImage::Format_Grayscale8:
6277 QPixelFormat(QPixelFormat::Grayscale,
6278 /*GRAY*/ 8,
6279 /*SECOND*/ 0,
6280 /*THIRD*/ 0,
6281 /*FOURTH*/ 0,
6282 /*FIFTH*/ 0,
6283 /*ALPHA*/ 0,
6284 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6285 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6286 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6287 /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
6288 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6289 //QImage::Format_RGBX64:
6290 QPixelFormat(QPixelFormat::RGB,
6291 /*RED*/ 16,
6292 /*GREEN*/ 16,
6293 /*BLUE*/ 16,
6294 /*FOURTH*/ 0,
6295 /*FIFTH*/ 0,
6296 /*ALPHA*/ 16,
6297 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6298 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6299 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6300 /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
6301 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6302 //QImage::Format_RGBA64:
6303 QPixelFormat(QPixelFormat::RGB,
6304 /*RED*/ 16,
6305 /*GREEN*/ 16,
6306 /*BLUE*/ 16,
6307 /*FOURTH*/ 0,
6308 /*FIFTH*/ 0,
6309 /*ALPHA*/ 16,
6310 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6311 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6312 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6313 /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
6314 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6315 //QImage::Format_RGBA64_Premultiplied:
6316 QPixelFormat(QPixelFormat::RGB,
6317 /*RED*/ 16,
6318 /*GREEN*/ 16,
6319 /*BLUE*/ 16,
6320 /*FOURTH*/ 0,
6321 /*FIFTH*/ 0,
6322 /*ALPHA*/ 16,
6323 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6324 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6325 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6326 /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
6327 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6328 //QImage::Format_Grayscale16:
6329 QPixelFormat(QPixelFormat::Grayscale,
6330 /*GRAY*/ 16,
6331 /*SECOND*/ 0,
6332 /*THIRD*/ 0,
6333 /*FOURTH*/ 0,
6334 /*FIFTH*/ 0,
6335 /*ALPHA*/ 0,
6336 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6337 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6338 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6339 /*INTERPRETATION*/ QPixelFormat::UnsignedShort,
6340 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6341 //QImage::Format_BGR888:
6342 QPixelFormat(QPixelFormat::BGR,
6343 /*RED*/ 8,
6344 /*GREEN*/ 8,
6345 /*BLUE*/ 8,
6346 /*FOURTH*/ 0,
6347 /*FIFTH*/ 0,
6348 /*ALPHA*/ 0,
6349 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6350 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6351 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6352 /*INTERPRETATION*/ QPixelFormat::UnsignedByte,
6353 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6354 //QImage::Format_RGBX16FPx4:
6355 QPixelFormat(QPixelFormat::RGB,
6356 /*RED*/ 16,
6357 /*GREEN*/ 16,
6358 /*BLUE*/ 16,
6359 /*FOURTH*/ 0,
6360 /*FIFTH*/ 0,
6361 /*ALPHA*/ 16,
6362 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6363 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6364 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6365 /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
6366 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6367 //QImage::Format_RGBA16FPx4:
6368 QPixelFormat(QPixelFormat::RGB,
6369 /*RED*/ 16,
6370 /*GREEN*/ 16,
6371 /*BLUE*/ 16,
6372 /*FOURTH*/ 0,
6373 /*FIFTH*/ 0,
6374 /*ALPHA*/ 16,
6375 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6376 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6377 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6378 /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
6379 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6380 //QImage::Format_RGBA16FPx4_Premultiplied:
6381 QPixelFormat(QPixelFormat::RGB,
6382 /*RED*/ 16,
6383 /*GREEN*/ 16,
6384 /*BLUE*/ 16,
6385 /*FOURTH*/ 0,
6386 /*FIFTH*/ 0,
6387 /*ALPHA*/ 16,
6388 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6389 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6390 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6391 /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
6392 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6393 //QImage::Format_RGBX32FPx4:
6394 QPixelFormat(QPixelFormat::RGB,
6395 /*RED*/ 32,
6396 /*GREEN*/ 32,
6397 /*BLUE*/ 32,
6398 /*FOURTH*/ 0,
6399 /*FIFTH*/ 0,
6400 /*ALPHA*/ 32,
6401 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6402 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6403 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6404 /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
6405 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6406 //QImage::Format_RGBA32FPx4:
6407 QPixelFormat(QPixelFormat::RGB,
6408 /*RED*/ 32,
6409 /*GREEN*/ 32,
6410 /*BLUE*/ 32,
6411 /*FOURTH*/ 0,
6412 /*FIFTH*/ 0,
6413 /*ALPHA*/ 32,
6414 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6415 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6416 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6417 /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
6418 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6419 //QImage::Format_RGBA32FPx4_Premultiplied:
6420 QPixelFormat(QPixelFormat::RGB,
6421 /*RED*/ 32,
6422 /*GREEN*/ 32,
6423 /*BLUE*/ 32,
6424 /*FOURTH*/ 0,
6425 /*FIFTH*/ 0,
6426 /*ALPHA*/ 32,
6427 /*ALPHA USAGE*/ QPixelFormat::UsesAlpha,
6428 /*ALPHA POSITION*/ QPixelFormat::AtEnd,
6429 /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
6430 /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
6431 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6432 //QImage::Format_CMYK8888:
6433 QPixelFormat(QPixelFormat::CMYK,
6434 /*RED*/ 8,
6435 /*GREEN*/ 8,
6436 /*BLUE*/ 8,
6437 /*FOURTH*/ 8,
6438 /*FIFTH*/ 0,
6439 /*ALPHA*/ 0,
6440 /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
6441 /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
6442 /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
6443 /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
6444 /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
6445};
6446static_assert(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats);
6447
6448/*!
6449 Returns the QImage::Format as a QPixelFormat
6450*/
6451QPixelFormat QImage::pixelFormat() const noexcept
6452{
6453 return toPixelFormat(format: format());
6454}
6455
6456/*!
6457 Converts \a format into a QPixelFormat
6458*/
6459QPixelFormat QImage::toPixelFormat(QImage::Format format) noexcept
6460{
6461 Q_ASSERT(static_cast<int>(format) < NImageFormats && static_cast<int>(format) >= 0);
6462 return pixelformats[format];
6463}
6464
6465/*!
6466 Converts \a format into a QImage::Format
6467*/
6468QImage::Format QImage::toImageFormat(QPixelFormat format) noexcept
6469{
6470 for (int i = 0; i < NImageFormats; i++) {
6471 if (format == pixelformats[i])
6472 return Format(i);
6473 }
6474 return Format_Invalid;
6475}
6476
6477static inline Qt::Orientations toOrientations(QImageIOHandler::Transformations orient)
6478{
6479 Qt::Orientations orients = {};
6480 if (orient.testFlag(flag: QImageIOHandler::TransformationMirror))
6481 orients |= Qt::Horizontal;
6482 if (orient.testFlag(flag: QImageIOHandler::TransformationFlip))
6483 orients |= Qt::Vertical;
6484 return orients;
6485}
6486
6487Q_GUI_EXPORT void qt_imageTransform(QImage &src, QImageIOHandler::Transformations orient)
6488{
6489 if (orient == QImageIOHandler::TransformationNone)
6490 return;
6491 if (orient == QImageIOHandler::TransformationRotate270) {
6492 src = rotated270(image: src);
6493 } else {
6494 src.flip(orient: toOrientations(orient));
6495 if (orient & QImageIOHandler::TransformationRotate90)
6496 src = rotated90(image: src);
6497 }
6498}
6499
6500QMap<QString, QString> qt_getImageText(const QImage &image, const QString &description)
6501{
6502 QMap<QString, QString> text = qt_getImageTextFromDescription(description);
6503 const auto textKeys = image.textKeys();
6504 for (const QString &key : textKeys) {
6505 if (!key.isEmpty() && !text.contains(key))
6506 text.insert(key, value: image.text(key));
6507 }
6508 return text;
6509}
6510
6511QMap<QString, QString> qt_getImageTextFromDescription(const QString &description)
6512{
6513 QMap<QString, QString> text;
6514 for (const auto &pair : QStringView{description}.tokenize(needle: u"\n\n")) {
6515 int index = pair.indexOf(c: u':');
6516 if (index >= 0 && pair.indexOf(c: u' ') < index) {
6517 if (!pair.trimmed().isEmpty())
6518 text.insert(key: "Description"_L1, value: pair.toString().simplified());
6519 } else {
6520 const auto key = pair.left(n: index);
6521 if (!key.trimmed().isEmpty())
6522 text.insert(key: key.toString(), value: pair.mid(pos: index + 2).toString().simplified());
6523 }
6524 }
6525 return text;
6526}
6527
6528QT_END_NAMESPACE
6529
6530#include "moc_qimage.cpp"
6531

source code of qtbase/src/gui/image/qimage.cpp