Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qpnghandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "private/qpnghandler_p.h"
6
7#ifndef QT_NO_IMAGEFORMAT_PNG
8#include <qcoreapplication.h>
9#include <qdebug.h>
10#include <qiodevice.h>
11#include <qimage.h>
12#include <qloggingcategory.h>
13#include <qvariant.h>
14
15#include <private/qimage_p.h> // for qt_getImageText
16
17#include <qcolorspace.h>
18#include <private/qcolorspace_p.h>
19
20#include <png.h>
21#include <pngconf.h>
22
23#if PNG_LIBPNG_VER >= 10400 && PNG_LIBPNG_VER <= 10502 \
24 && defined(PNG_PEDANTIC_WARNINGS_SUPPORTED)
25/*
26 Versions 1.4.0 to 1.5.2 of libpng declare png_longjmp_ptr to
27 have a noreturn attribute if PNG_PEDANTIC_WARNINGS_SUPPORTED
28 is enabled, but most declarations of longjmp in the wild do
29 not add this attribute. This causes problems when the png_jmpbuf
30 macro expands to calling png_set_longjmp_fn with a mismatched
31 longjmp, as compilers such as Clang will treat this as an error.
32
33 To work around this we override the png_jmpbuf macro to cast
34 longjmp to a png_longjmp_ptr.
35*/
36# undef png_jmpbuf
37# ifdef PNG_SETJMP_SUPPORTED
38# define png_jmpbuf(png_ptr) \
39 (*png_set_longjmp_fn((png_ptr), (png_longjmp_ptr)longjmp, sizeof(jmp_buf)))
40# else
41# define png_jmpbuf(png_ptr) \
42 (LIBPNG_WAS_COMPILED_WITH__PNG_NO_SETJMP)
43# endif
44#endif
45
47
48using namespace Qt::StringLiterals;
49
51
52// avoid going through QImage::scanLine() which calls detach
53#define FAST_SCAN_LINE(data, bpl, y) (data + (y) * bpl)
54
55/*
56 All PNG files load to the minimal QImage equivalent.
57
58 All QImage formats output to reasonably efficient PNG equivalents.
59*/
60
62{
63public:
64 enum State {
68 Error
69 };
70 // Defines the order of how the various ways of setting colorspace overrides each other:
73 GammaChrm = 1, // gAMA+cHRM chunks
74 Srgb = 2, // sRGB chunk
75 Icc = 3 // iCCP chunk
76 };
77
80 { }
81
82 float gamma;
83 float fileGamma;
84 int quality; // quality is used for backward compatibility, maps to compression
91
92 png_struct *png_ptr;
93 png_info *info_ptr;
94 png_info *end_info;
95
96 bool readPngHeader();
98 void readPngTexts(png_info *info);
99
101
105 { }
107 {
108 delete [] row_pointers;
109 row_pointers = nullptr;
110 delete [] accRow;
111 accRow = nullptr;
112 delete [] inRow;
113 inRow = nullptr;
114 delete [] outRow;
115 outRow = nullptr;
116 }
117
118 png_byte **row_pointers;
120 png_byte *inRow;
122 };
123
125
127
129};
130
131
133public:
134 explicit QPNGImageWriter(QIODevice*);
136
139 void setLooping(int loops=0); // 0 == infinity
140 void setFrameDelay(int msecs);
141 void setGamma(float);
142
143 bool writeImage(const QImage& img, int x, int y);
144 bool writeImage(const QImage& img, int compression_in, const QString &description, int x, int y);
145 bool writeImage(const QImage& img)
146 { return writeImage(img, 0, 0); }
147 bool writeImage(const QImage& img, int compression, const QString &description)
148 { return writeImage(img, compression, description, 0, 0); }
149
150 QIODevice* device() { return dev; }
151
152private:
153 QIODevice* dev;
154 int frames_written;
155 DisposalMethod disposal;
156 int looping;
157 int ms_delay;
158 float gamma;
159};
160
161extern "C" {
162static
163void iod_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
164{
165 QPngHandlerPrivate *d = (QPngHandlerPrivate *)png_get_io_ptr(png_ptr);
166 QIODevice *in = d->q->device();
167
168 if (d->state == QPngHandlerPrivate::ReadingEnd && !in->isSequential() && in->size() > 0 && (in->size() - in->pos()) < 4 && length == 4) {
169 // Workaround for certain malformed PNGs that lack the final crc bytes
170 uchar endcrc[4] = { 0xae, 0x42, 0x60, 0x82 };
171 memcpy(data, endcrc, 4);
172 in->seek(in->size());
173 return;
174 }
175
176 while (length) {
177 int nr = in->read((char*)data, length);
178 if (nr <= 0) {
179 png_error(png_ptr, "Read Error");
180 return;
181 }
182 length -= nr;
183 }
184}
185
186
187static
188void qpiw_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
189{
190 QPNGImageWriter* qpiw = (QPNGImageWriter*)png_get_io_ptr(png_ptr);
191 QIODevice* out = qpiw->device();
192
193 uint nr = out->write((char*)data, length);
194 if (nr != length) {
195 png_error(png_ptr, "Write Error");
196 return;
197 }
198}
199
200
201static
202void qpiw_flush_fn(png_structp /* png_ptr */)
203{
204}
205
206}
207
208static
209bool setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scaledSize, bool *doScaledRead)
210{
211 png_uint_32 width = 0;
212 png_uint_32 height = 0;
213 int bit_depth = 0;
214 int color_type = 0;
215 png_bytep trans_alpha = nullptr;
216 png_color_16p trans_color_p = nullptr;
217 int num_trans;
218 png_colorp palette = nullptr;
219 int num_palette;
220 int interlace_method = PNG_INTERLACE_LAST;
221 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_method, nullptr, nullptr);
223 png_set_interlace_handling(png_ptr);
224
225 if (color_type == PNG_COLOR_TYPE_GRAY) {
226 // Black & White or grayscale
227 if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
228 png_set_invert_mono(png_ptr);
229 png_read_update_info(png_ptr, info_ptr);
231 return false;
232 image.setColorCount(2);
233 image.setColor(1, qRgb(0,0,0));
234 image.setColor(0, qRgb(255,255,255));
235 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {
236 const int g = trans_color_p->gray;
237 // the image has white in the first position of the color table,
238 // black in the second. g is 0 for black, 1 for white.
239 if (g == 0)
240 image.setColor(1, qRgba(0, 0, 0, 0));
241 else if (g == 1)
242 image.setColor(0, qRgba(255, 255, 255, 0));
243 }
244 } else if (bit_depth == 16
245 && png_get_channels(png_ptr, info_ptr) == 1
246 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
248 return false;
249 png_read_update_info(png_ptr, info_ptr);
251 png_set_swap(png_ptr);
252 } else if (bit_depth == 16) {
253 bool hasMask = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
254 if (!hasMask)
255 png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER);
256 else
257 png_set_expand(png_ptr);
258 png_set_gray_to_rgb(png_ptr);
261 return false;
262 png_read_update_info(png_ptr, info_ptr);
264 png_set_swap(png_ptr);
265 } else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
266 png_set_expand(png_ptr);
268 return false;
269 png_read_update_info(png_ptr, info_ptr);
270 } else {
271 if (bit_depth < 8)
272 png_set_packing(png_ptr);
273 int ncols = bit_depth < 8 ? 1 << bit_depth : 256;
274 png_read_update_info(png_ptr, info_ptr);
276 return false;
277 image.setColorCount(ncols);
278 for (int i=0; i<ncols; i++) {
279 int c = i*255/(ncols-1);
280 image.setColor(i, qRgba(c,c,c,0xff));
281 }
282 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {
283 const int g = trans_color_p->gray;
284 if (g < ncols) {
285 image.setColor(g, 0);
286 }
287 }
288 }
289 } else if (color_type == PNG_COLOR_TYPE_PALETTE
290 && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
291 && num_palette <= 256)
292 {
293 // 1-bit and 8-bit color
294 if (bit_depth != 1)
295 png_set_packing(png_ptr);
296 png_read_update_info(png_ptr, info_ptr);
297 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr);
301 return false;
302 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
303 image.setColorCount((format == QImage::Format_Mono) ? 2 : num_palette);
304 int i = 0;
305 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) {
306 while (i < num_trans) {
307 image.setColor(i, qRgba(
308 palette[i].red,
309 palette[i].green,
310 palette[i].blue,
311 trans_alpha[i]
312 )
313 );
314 i++;
315 }
316 }
317 while (i < num_palette) {
318 image.setColor(i, qRgba(
319 palette[i].red,
320 palette[i].green,
321 palette[i].blue,
322 0xff
323 )
324 );
325 i++;
326 }
327 // Qt==ARGB==Big(ARGB)==Little(BGRA)
329 png_set_bgr(png_ptr);
330 }
331 } else if (bit_depth == 16 && !(color_type & PNG_COLOR_MASK_PALETTE)) {
333 if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
334 png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER);
336 }
337 if (!(color_type & PNG_COLOR_MASK_COLOR))
338 png_set_gray_to_rgb(png_ptr);
340 return false;
341 png_read_update_info(png_ptr, info_ptr);
343 png_set_swap(png_ptr);
344 } else {
345 // 32-bit
346 if (bit_depth == 16)
347 png_set_strip_16(png_ptr);
348
349 png_set_expand(png_ptr);
350
351 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
352 png_set_gray_to_rgb(png_ptr);
353
355 // Only add filler if no alpha, or we can get 5 channel data.
356 if (!(color_type & PNG_COLOR_MASK_ALPHA)
357 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
358 png_set_filler(png_ptr, 0xff, QSysInfo::ByteOrder == QSysInfo::BigEndian ?
359 PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
360 // We want 4 bytes, but it isn't an alpha channel
362 }
363 QSize outSize(width,height);
364 if (!scaledSize.isEmpty() && quint32(scaledSize.width()) <= width &&
365 quint32(scaledSize.height()) <= height && scaledSize != outSize && interlace_method == PNG_INTERLACE_NONE) {
366 // Do inline downscaling
367 outSize = scaledSize;
368 if (doScaledRead)
369 *doScaledRead = true;
370 }
372 return false;
373
375 png_set_swap_alpha(png_ptr);
376
377 // Qt==ARGB==Big(ARGB)==Little(BGRA)
379 png_set_bgr(png_ptr);
380 }
381
382 png_read_update_info(png_ptr, info_ptr);
383 }
384 return true;
385}
386
387static void read_image_scaled(QImage *outImage, png_structp png_ptr, png_infop info_ptr,
389{
390
391 png_uint_32 width = 0;
392 png_uint_32 height = 0;
393 png_int_32 offset_x = 0;
394 png_int_32 offset_y = 0;
395
396 int bit_depth = 0;
397 int color_type = 0;
398 int unit_type = PNG_OFFSET_PIXEL;
399 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr);
400 png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, &unit_type);
401 uchar *data = outImage->bits();
402 qsizetype bpl = outImage->bytesPerLine();
403
404 if (scaledSize.isEmpty() || !width || !height)
405 return;
406
407 const quint32 iysz = height;
408 const quint32 ixsz = width;
409 const quint32 oysz = scaledSize.height();
410 const quint32 oxsz = scaledSize.width();
411 const quint32 ibw = 4*width;
412 amp.accRow = new quint32[ibw];
413 memset(amp.accRow, 0, ibw*sizeof(quint32));
414 amp.inRow = new png_byte[ibw];
415 memset(amp.inRow, 0, ibw*sizeof(png_byte));
416 amp.outRow = new uchar[ibw];
417 memset(amp.outRow, 0, ibw*sizeof(uchar));
418 qint32 rval = 0;
419 for (quint32 oy=0; oy<oysz; oy++) {
420 // Store the rest of the previous input row, if any
421 for (quint32 i=0; i < ibw; i++)
422 amp.accRow[i] = rval*amp.inRow[i];
423 // Accumulate the next input rows
424 for (rval = iysz-rval; rval > 0; rval-=oysz) {
425 png_read_row(png_ptr, amp.inRow, nullptr);
426 quint32 fact = qMin(oysz, quint32(rval));
427 for (quint32 i=0; i < ibw; i++)
428 amp.accRow[i] += fact*amp.inRow[i];
429 }
430 rval *= -1;
431
432 // We have a full output row, store it
433 for (quint32 i=0; i < ibw; i++)
434 amp.outRow[i] = uchar(amp.accRow[i]/iysz);
435
436 quint32 a[4] = {0, 0, 0, 0};
437 qint32 cval = oxsz;
438 quint32 ix = 0;
439 for (quint32 ox=0; ox<oxsz; ox++) {
440 for (quint32 i=0; i < 4; i++)
441 a[i] = cval * amp.outRow[ix+i];
442 for (cval = ixsz - cval; cval > 0; cval-=oxsz) {
443 ix += 4;
444 if (ix >= ibw)
445 break; // Safety belt, should not happen
446 quint32 fact = qMin(oxsz, quint32(cval));
447 for (quint32 i=0; i < 4; i++)
448 a[i] += fact * amp.outRow[ix+i];
449 }
450 cval *= -1;
451 for (quint32 i=0; i < 4; i++)
452 data[(4*ox)+i] = uchar(a[i]/ixsz);
453 }
454 data += bpl;
455 }
456 amp.deallocate();
457
458 outImage->setDotsPerMeterX((png_get_x_pixels_per_meter(png_ptr,info_ptr)*oxsz)/ixsz);
459 outImage->setDotsPerMeterY((png_get_y_pixels_per_meter(png_ptr,info_ptr)*oysz)/iysz);
460
461 if (unit_type == PNG_OFFSET_PIXEL)
462 outImage->setOffset(QPoint(offset_x*oxsz/ixsz, offset_y*oysz/iysz));
463
464}
465
466extern "C" {
467static void qt_png_warning(png_structp /*png_ptr*/, png_const_charp message)
468{
469 qCInfo(lcImageIo, "libpng warning: %s", message);
470}
471
472}
473
474
476{
477#ifndef QT_NO_IMAGEIO_TEXT_LOADING
478 png_textp text_ptr;
479 int num_text=0;
480 png_get_text(png_ptr, info, &text_ptr, &num_text);
481
482 while (num_text--) {
484 key = QString::fromLatin1(text_ptr->key);
485#if defined(PNG_iTXt_SUPPORTED)
486 if (text_ptr->itxt_length) {
487 value = QString::fromUtf8(text_ptr->text, int(text_ptr->itxt_length));
488 } else
489#endif
490 {
491 value = QString::fromLatin1(text_ptr->text, int(text_ptr->text_length));
492 }
493 if (!description.isEmpty())
494 description += "\n\n"_L1;
495 description += key + ": "_L1 + value.simplified();
496 readTexts.append(key);
497 readTexts.append(value);
498 text_ptr++;
499 }
500#else
501 Q_UNUSED(info);
502#endif
503}
504
505
507{
508 state = Error;
509 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,nullptr,nullptr,nullptr);
510 if (!png_ptr)
511 return false;
512
513 png_set_error_fn(png_ptr, nullptr, nullptr, qt_png_warning);
514
515#if defined(PNG_SET_OPTION_SUPPORTED) && defined(PNG_MAXIMUM_INFLATE_WINDOW)
516 // Trade off a little bit of memory for better compatibility with existing images
517 // Ref. "invalid distance too far back" explanation in libpng-manual.txt
518 png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
519#endif
520
521 info_ptr = png_create_info_struct(png_ptr);
522 if (!info_ptr) {
523 png_destroy_read_struct(&png_ptr, nullptr, nullptr);
524 png_ptr = nullptr;
525 return false;
526 }
527
528 end_info = png_create_info_struct(png_ptr);
529 if (!end_info) {
530 png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
531 png_ptr = nullptr;
532 return false;
533 }
534
535 if (setjmp(png_jmpbuf(png_ptr))) {
536 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
537 png_ptr = nullptr;
538 return false;
539 }
540
541 png_set_read_fn(png_ptr, this, iod_read_fn);
542 png_read_info(png_ptr, info_ptr);
543
545
546#ifdef PNG_iCCP_SUPPORTED
547 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
548 png_charp name = nullptr;
549 int compressionType = 0;
550#if (PNG_LIBPNG_VER < 10500)
551 png_charp profileData = nullptr;
552#else
553 png_bytep profileData = nullptr;
554#endif
555 png_uint_32 profLen;
556 png_get_iCCP(png_ptr, info_ptr, &name, &compressionType, &profileData, &profLen);
557 colorSpace = QColorSpace::fromIccProfile(QByteArray((const char *)profileData, profLen));
558 if (!colorSpace.isValid()) {
559 qCDebug(lcImageIo) << "QPngHandler: Failed to parse ICC profile";
560 } else {
562 if (csD->description.isEmpty())
563 csD->description = QString::fromLatin1((const char *)name);
565 }
566 }
567#endif
568 if (colorSpaceState <= Srgb && png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
569 int rendering_intent = -1;
570 png_get_sRGB(png_ptr, info_ptr, &rendering_intent);
571 // We don't actually care about the rendering_intent, just that it is valid
572 if (rendering_intent >= 0 && rendering_intent <= 3) {
575 }
576 }
577 if (colorSpaceState <= GammaChrm && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
578 double file_gamma = 0.0;
579 png_get_gAMA(png_ptr, info_ptr, &file_gamma);
580 fileGamma = file_gamma;
581 if (fileGamma > 0.0f) {
582 QColorSpacePrimaries primaries;
583 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) {
584 double white_x, white_y, red_x, red_y;
585 double green_x, green_y, blue_x, blue_y;
586 png_get_cHRM(png_ptr, info_ptr,
587 &white_x, &white_y, &red_x, &red_y,
588 &green_x, &green_y, &blue_x, &blue_y);
589 primaries.whitePoint = QPointF(white_x, white_y);
590 primaries.redPoint = QPointF(red_x, red_y);
591 primaries.greenPoint = QPointF(green_x, green_y);
592 primaries.bluePoint = QPointF(blue_x, blue_y);
593 }
594 if (primaries.areValid()) {
595 colorSpace = QColorSpace(primaries.whitePoint, primaries.redPoint, primaries.greenPoint, primaries.bluePoint,
597 } else {
600 }
602 }
603 }
604
606 return true;
607}
608
610{
611 if (state == Error)
612 return false;
613
614 if (state == Ready && !readPngHeader()) {
615 state = Error;
616 return false;
617 }
618
619 if (setjmp(png_jmpbuf(png_ptr))) {
620 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
621 png_ptr = nullptr;
622 amp.deallocate();
623 state = Error;
624 return false;
625 }
626
627 if (gamma != 0.0 && fileGamma != 0.0) {
628 // This configuration forces gamma correction and
629 // thus changes the output colorspace
630 png_set_gamma(png_ptr, 1.0f / gamma, fileGamma);
633 }
634
635 bool doScaledRead = false;
636 if (!setup_qt(*outImage, png_ptr, info_ptr, scaledSize, &doScaledRead)) {
637 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
638 png_ptr = nullptr;
639 amp.deallocate();
640 state = Error;
641 return false;
642 }
643
644 if (doScaledRead) {
646 } else {
647 png_uint_32 width = 0;
648 png_uint_32 height = 0;
649 png_int_32 offset_x = 0;
650 png_int_32 offset_y = 0;
651
652 int bit_depth = 0;
653 int color_type = 0;
654 int unit_type = PNG_OFFSET_PIXEL;
655 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr);
656 png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, &unit_type);
657 uchar *data = outImage->bits();
658 qsizetype bpl = outImage->bytesPerLine();
659 amp.row_pointers = new png_bytep[height];
660
661 for (uint y = 0; y < height; y++)
662 amp.row_pointers[y] = data + y * bpl;
663
664 png_read_image(png_ptr, amp.row_pointers);
665 amp.deallocate();
666
667 outImage->setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr));
668 outImage->setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr));
669
670 if (unit_type == PNG_OFFSET_PIXEL)
671 outImage->setOffset(QPoint(offset_x, offset_y));
672
673 // sanity check palette entries
674 if (color_type == PNG_COLOR_TYPE_PALETTE && outImage->format() == QImage::Format_Indexed8) {
675 int color_table_size = outImage->colorCount();
676 for (int y=0; y<(int)height; ++y) {
677 uchar *p = FAST_SCAN_LINE(data, bpl, y);
678 uchar *end = p + width;
679 while (p < end) {
680 if (*p >= color_table_size)
681 *p = 0;
682 ++p;
683 }
684 }
685 }
686 }
687
689 png_read_end(png_ptr, end_info);
690
692 for (int i = 0; i < readTexts.size()-1; i+=2)
693 outImage->setText(readTexts.at(i), readTexts.at(i+1));
694
695 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
696 png_ptr = nullptr;
697 amp.deallocate();
698 state = Ready;
699
700 if (scaledSize.isValid() && outImage->size() != scaledSize)
702
704 outImage->setColorSpace(colorSpace);
705
706 return true;
707}
708
710{
712 png_uint_32 width = 0, height = 0;
713 int bit_depth = 0, color_type = 0;
714 png_colorp palette;
715 int num_palette;
716 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr);
717 if (color_type == PNG_COLOR_TYPE_GRAY) {
718 // Black & White or grayscale
719 if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
721 } else if (bit_depth == 16) {
722 format = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? QImage::Format_RGBA64 : QImage::Format_Grayscale16;
723 } else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
725 } else {
727 }
728 } else if (color_type == PNG_COLOR_TYPE_PALETTE
729 && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
730 && num_palette <= 256)
731 {
732 // 1-bit and 8-bit color
734 } else if (bit_depth == 16 && !(color_type & PNG_COLOR_MASK_PALETTE)) {
736 if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
738 } else {
739 // 32-bit
741 // Only add filler if no alpha, or we can get 5 channel data.
742 if (!(color_type & PNG_COLOR_MASK_ALPHA)
743 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
744 // We want 4 bytes, but it isn't an alpha channel
746 }
747 }
748
749 return format;
750}
751
753 dev(iod),
754 frames_written(0),
755 disposal(Unspecified),
756 looping(-1),
757 ms_delay(-1),
758 gamma(0.0)
759{
760}
761
763{
764}
765
767{
768 disposal = dm;
769}
770
772{
773 looping = loops;
774}
775
777{
778 ms_delay = msecs;
779}
780
782{
783 gamma = g;
784}
785
786static void set_text(const QImage &image, png_structp png_ptr, png_infop info_ptr,
787 const QString &description)
788{
789 const QMap<QString, QString> text = qt_getImageText(image, description);
790
791 if (text.isEmpty())
792 return;
793
794 png_textp text_ptr = new png_text[text.size()];
795 memset(text_ptr, 0, text.size() * sizeof(png_text));
796
798 int i = 0;
799 while (it != text.constEnd()) {
800 text_ptr[i].key = qstrdup(QStringView{it.key()}.left(79).toLatin1().constData());
801 bool noCompress = (it.value().size() < 40);
802
803#ifdef PNG_iTXt_SUPPORTED
804 bool needsItxt = false;
805 for (QChar c : it.value()) {
806 uchar ch = c.cell();
807 if (c.row() || (ch < 0x20 && ch != '\n') || (ch > 0x7e && ch < 0xa0)) {
808 needsItxt = true;
809 break;
810 }
811 }
812
813 if (needsItxt) {
814 text_ptr[i].compression = noCompress ? PNG_ITXT_COMPRESSION_NONE : PNG_ITXT_COMPRESSION_zTXt;
815 QByteArray value = it.value().toUtf8();
816 text_ptr[i].text = qstrdup(value.constData());
817 text_ptr[i].itxt_length = value.size();
818 text_ptr[i].lang = const_cast<char*>("UTF-8");
819 text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData());
820 }
821 else
822#endif
823 {
824 text_ptr[i].compression = noCompress ? PNG_TEXT_COMPRESSION_NONE : PNG_TEXT_COMPRESSION_zTXt;
825 QByteArray value = it.value().toLatin1();
826 text_ptr[i].text = qstrdup(value.constData());
827 text_ptr[i].text_length = value.size();
828 }
829 ++i;
830 ++it;
831 }
832
833 png_set_text(png_ptr, info_ptr, text_ptr, i);
834 for (i = 0; i < text.size(); ++i) {
835 delete [] text_ptr[i].key;
836 delete [] text_ptr[i].text;
837#ifdef PNG_iTXt_SUPPORTED
838 delete [] text_ptr[i].lang_key;
839#endif
840 }
841 delete [] text_ptr;
842}
843
844bool QPNGImageWriter::writeImage(const QImage& image, int off_x, int off_y)
845{
846 return writeImage(image, -1, QString(), off_x, off_y);
847}
848
849bool QPNGImageWriter::writeImage(const QImage& image, int compression_in, const QString &description,
850 int off_x_in, int off_y_in)
851{
852 QPoint offset = image.offset();
853 int off_x = off_x_in + offset.x();
854 int off_y = off_y_in + offset.y();
855
856 png_structp png_ptr;
857 png_infop info_ptr;
858
859 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,nullptr,nullptr,nullptr);
860 if (!png_ptr) {
861 return false;
862 }
863
864 png_set_error_fn(png_ptr, nullptr, nullptr, qt_png_warning);
865#ifdef PNG_BENIGN_ERRORS_SUPPORTED
866 png_set_benign_errors(png_ptr, 1);
867#endif
868
869 info_ptr = png_create_info_struct(png_ptr);
870 if (!info_ptr) {
871 png_destroy_write_struct(&png_ptr, nullptr);
872 return false;
873 }
874
875 if (setjmp(png_jmpbuf(png_ptr))) {
876 png_destroy_write_struct(&png_ptr, &info_ptr);
877 return false;
878 }
879
880 int compression = compression_in;
881 if (compression >= 0) {
882 if (compression > 9) {
883 qCWarning(lcImageIo, "PNG: Compression %d out of range", compression);
884 compression = 9;
885 }
886 png_set_compression_level(png_ptr, compression);
887 }
888
889 png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn);
890
891
892 int color_type = 0;
893 if (image.format() <= QImage::Format_Indexed8) {
894 if (image.isGrayscale())
895 color_type = PNG_COLOR_TYPE_GRAY;
896 else
897 color_type = PNG_COLOR_TYPE_PALETTE;
898 }
899 else if (image.format() == QImage::Format_Grayscale8
900 || image.format() == QImage::Format_Grayscale16)
901 color_type = PNG_COLOR_TYPE_GRAY;
902 else if (image.hasAlphaChannel())
903 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
904 else
905 color_type = PNG_COLOR_TYPE_RGB;
906
907 int bpc = 0;
908 switch (image.format()) {
911 bpc = 1;
912 break;
917 bpc = 16;
918 break;
919 default:
920 bpc = 8;
921 break;
922 }
923
924 png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(),
925 bpc, // per channel
926 color_type, 0, 0, 0); // sets #channels
927
928#ifdef PNG_iCCP_SUPPORTED
929 if (image.colorSpace().isValid()) {
930 QColorSpace cs = image.colorSpace();
931 // Support the old gamma making it override transferfunction.
932 if (gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma))
934 QByteArray iccProfileName = cs.description().toLatin1();
935 if (iccProfileName.isEmpty())
936 iccProfileName = QByteArrayLiteral("Custom");
937 QByteArray iccProfile = cs.iccProfile();
938 png_set_iCCP(png_ptr, info_ptr,
939 #if PNG_LIBPNG_VER < 10500
940 iccProfileName.data(), PNG_COMPRESSION_TYPE_BASE, iccProfile.data(),
941 #else
942 iccProfileName.constData(), PNG_COMPRESSION_TYPE_BASE,
943 (png_const_bytep)iccProfile.constData(),
944 #endif
945 iccProfile.size());
946 } else
947#endif
948 if (gamma != 0.0) {
949 png_set_gAMA(png_ptr, info_ptr, 1.0/gamma);
950 }
951
952 if (image.format() == QImage::Format_MonoLSB)
953 png_set_packswap(png_ptr);
954
955 if (color_type == PNG_COLOR_TYPE_PALETTE) {
956 // Paletted
957 int num_palette = qMin(256, image.colorCount());
958 png_color palette[256];
959 png_byte trans[256];
960 int num_trans = 0;
961 for (int i=0; i<num_palette; i++) {
962 QRgb rgba=image.color(i);
963 palette[i].red = qRed(rgba);
964 palette[i].green = qGreen(rgba);
965 palette[i].blue = qBlue(rgba);
966 trans[i] = qAlpha(rgba);
967 if (trans[i] < 255) {
968 num_trans = i+1;
969 }
970 }
971 png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
972
973 if (num_trans) {
974 png_set_tRNS(png_ptr, info_ptr, trans, num_trans, nullptr);
975 }
976 }
977
978 // Swap ARGB to RGBA (normal PNG format) before saving on
979 // BigEndian machines
981 switch (image.format()) {
987 break;
988 default:
989 png_set_swap_alpha(png_ptr);
990 }
991 }
992
993 // Qt==ARGB==Big(ARGB)==Little(BGRA). But RGB888 is RGB regardless
995 switch (image.format()) {
1002 break;
1003 default:
1004 png_set_bgr(png_ptr);
1005 }
1006 }
1007
1008 if (off_x || off_y) {
1009 png_set_oFFs(png_ptr, info_ptr, off_x, off_y, PNG_OFFSET_PIXEL);
1010 }
1011
1012 if (frames_written > 0)
1013 png_set_sig_bytes(png_ptr, 8);
1014
1015 if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) {
1016 png_set_pHYs(png_ptr, info_ptr,
1017 image.dotsPerMeterX(), image.dotsPerMeterY(),
1018 PNG_RESOLUTION_METER);
1019 }
1020
1021 set_text(image, png_ptr, info_ptr, description);
1022
1023 png_write_info(png_ptr, info_ptr);
1024
1025 if (image.depth() != 1)
1026 png_set_packing(png_ptr);
1027
1028 if (color_type == PNG_COLOR_TYPE_RGB) {
1029 switch (image.format()) {
1032 break;
1035 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
1036 break;
1037 default:
1038 png_set_filler(png_ptr, 0,
1040 PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
1041 }
1042 }
1043
1045 switch (image.format()) {
1050 png_set_swap(png_ptr);
1051 break;
1052 default:
1053 break;
1054 }
1055 }
1056
1057 if (looping >= 0 && frames_written == 0) {
1058 uchar data[13] = "NETSCAPE2.0";
1059 // 0123456789aBC
1060 data[0xB] = looping%0x100;
1061 data[0xC] = looping/0x100;
1062 png_write_chunk(png_ptr, const_cast<png_bytep>((const png_byte *)"gIFx"), data, 13);
1063 }
1064 if (ms_delay >= 0 || disposal!=Unspecified) {
1065 uchar data[4];
1066 data[0] = disposal;
1067 data[1] = 0;
1068 data[2] = (ms_delay/10)/0x100; // hundredths
1069 data[3] = (ms_delay/10)%0x100;
1070 png_write_chunk(png_ptr, const_cast<png_bytep>((const png_byte *)"gIFg"), data, 4);
1071 }
1072
1073 int height = image.height();
1074 int width = image.width();
1075 switch (image.format()) {
1089 {
1090 png_bytep* row_pointers = new png_bytep[height];
1091 for (int y=0; y<height; y++)
1092 row_pointers[y] = const_cast<png_bytep>(image.constScanLine(y));
1093 png_write_image(png_ptr, row_pointers);
1094 delete [] row_pointers;
1095 }
1096 break;
1098 {
1099 QImage row;
1100 png_bytep row_pointers[1];
1101 for (int y=0; y<height; y++) {
1102 row = image.copy(0, y, width, 1).convertToFormat(QImage::Format_RGBA64);
1103 row_pointers[0] = const_cast<png_bytep>(row.constScanLine(0));
1104 png_write_rows(png_ptr, row_pointers, 1);
1105 }
1106 }
1107 break;
1108 default:
1109 {
1111 QImage row;
1112 png_bytep row_pointers[1];
1113 for (int y=0; y<height; y++) {
1114 row = image.copy(0, y, width, 1).convertToFormat(fmt);
1115 row_pointers[0] = const_cast<png_bytep>(row.constScanLine(0));
1116 png_write_rows(png_ptr, row_pointers, 1);
1117 }
1118 }
1119 break;
1120 }
1121
1122 png_write_end(png_ptr, info_ptr);
1123 frames_written++;
1124
1125 png_destroy_write_struct(&png_ptr, &info_ptr);
1126
1127 return true;
1128}
1129
1131 int compression, int quality, float gamma, const QString &description)
1132{
1133 // quality is used for backward compatibility, maps to compression
1134
1135 QPNGImageWriter writer(device);
1136 if (compression >= 0)
1137 compression = qMin(compression, 100);
1138 else if (quality >= 0)
1139 compression = 100 - qMin(quality, 100);
1140
1141 if (compression >= 0)
1142 compression = (compression * 9) / 91; // map [0,100] -> [0,9]
1143
1144 writer.setGamma(gamma);
1145 return writer.writeImage(image, compression, description);
1146}
1147
1149 : d(new QPngHandlerPrivate(this))
1150{
1151}
1152
1154{
1155 if (d->png_ptr)
1156 png_destroy_read_struct(&d->png_ptr, &d->info_ptr, &d->end_info);
1157 delete d;
1158}
1159
1161{
1163 return false;
1164
1165 if (d->state != QPngHandlerPrivate::Error) {
1166 setFormat("png");
1167 return true;
1168 }
1169
1170 return false;
1171}
1172
1174{
1175 if (!device) {
1176 qCWarning(lcImageIo, "QPngHandler::canRead() called with no device");
1177 return false;
1178 }
1179
1180 return device->peek(8) == "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
1181}
1182
1184{
1185 if (!canRead())
1186 return false;
1187 return d->readPngImage(image);
1188}
1189
1191{
1192 return write_png_image(image, device(), d->compression, d->quality, d->gamma, d->description);
1193}
1194
1196{
1197 return option == Gamma
1198 || option == Description
1199 || option == ImageFormat
1200 || option == Quality
1202 || option == Size
1203 || option == ScaledSize;
1204}
1205
1207{
1209 return QVariant();
1211 return QVariant();
1212
1213 if (option == Gamma)
1214 return d->gamma == 0.0 ? d->fileGamma : d->gamma;
1215 else if (option == Quality)
1216 return d->quality;
1217 else if (option == CompressionRatio)
1218 return d->compression;
1219 else if (option == Description)
1220 return d->description;
1221 else if (option == Size)
1222 return QSize(png_get_image_width(d->png_ptr, d->info_ptr),
1223 png_get_image_height(d->png_ptr, d->info_ptr));
1224 else if (option == ScaledSize)
1225 return d->scaledSize;
1226 else if (option == ImageFormat)
1227 return d->readImageFormat();
1228 return QVariant();
1229}
1230
1232{
1233 if (option == Gamma)
1234 d->gamma = value.toFloat();
1235 else if (option == Quality)
1236 d->quality = value.toInt();
1237 else if (option == CompressionRatio)
1238 d->compression = value.toInt();
1239 else if (option == Description)
1240 d->description = value.toString();
1241 else if (option == ScaledSize)
1242 d->scaledSize = value.toSize();
1243}
1244
1246
1247#endif // QT_NO_IMAGEFORMAT_PNG
IOBluetoothDevice * device
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
\inmodule QtCore
Definition qchar.h:48
bool areValid() const
static const QColorSpacePrivate * get(const QColorSpace &colorSpace)
The QColorSpace class provides a color space abstraction.
Definition qcolorspace.h:21
bool isValid() const noexcept
Returns true if the color space is valid.
void setTransferFunction(TransferFunction transferFunction, float gamma=0.0f)
Sets the transfer function to transferFunction and gamma.
float gamma() const noexcept
Returns the gamma value of color spaces with TransferFunction::Gamma, an approximate gamma value for ...
static QColorSpace fromIccProfile(const QByteArray &iccProfile)
Creates a QColorSpace from ICC profile iccProfile.
QColorSpace withTransferFunction(TransferFunction transferFunction, float gamma=0.0f) const
Returns a copy of this color space, except using the transfer function transferFunction and gamma.
QString description() const noexcept
Returns the name or short description.
QByteArray iccProfile() const
Returns an ICC profile representing the color space.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
qint64 peek(char *data, qint64 maxlen)
ImageOption
This enum describes the different options supported by QImageIOHandler.
static bool allocateImage(QSize size, QImage::Format format, QImage *image)
QIODevice * device() const
Returns the device currently assigned to the QImageIOHandler.
void setFormat(const QByteArray &format)
Sets the format of the QImageIOHandler to format.
\inmodule QtGui
Definition qimage.h:37
void setDotsPerMeterY(int)
Sets the number of pixels that fit vertically in a physical meter, to y.
Definition qimage.cpp:4120
QImage scaled(int w, int h, Qt::AspectRatioMode aspectMode=Qt::IgnoreAspectRatio, Qt::TransformationMode mode=Qt::FastTransformation) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qimage.h:208
void setText(const QString &key, const QString &value)
Sets the image text to the given text and associate it with the given key.
Definition qimage.cpp:4222
qsizetype bytesPerLine() const
Returns the number of bytes per image scanline.
Definition qimage.cpp:1538
QSize size() const
Returns the size of the image, i.e.
uchar * bits()
Returns a pointer to the first pixel data.
Definition qimage.cpp:1677
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_Grayscale16
Definition qimage.h:70
@ Format_RGBA8888
Definition qimage.h:59
@ Format_RGB888
Definition qimage.h:55
@ Format_RGB32
Definition qimage.h:46
@ Format_Invalid
Definition qimage.h:42
@ Format_RGBA64_Premultiplied
Definition qimage.h:69
@ Format_MonoLSB
Definition qimage.h:44
@ Format_RGBA64
Definition qimage.h:68
@ Format_Mono
Definition qimage.h:43
@ Format_RGBX64
Definition qimage.h:67
@ Format_Indexed8
Definition qimage.h:45
@ Format_BGR888
Definition qimage.h:71
@ Format_ARGB32
Definition qimage.h:47
@ Format_RGBX8888
Definition qimage.h:58
@ Format_Grayscale8
Definition qimage.h:66
Format format() const
Returns the format of the image.
Definition qimage.cpp:2146
void setDotsPerMeterX(int)
Sets the number of pixels that fit horizontally in a physical meter, to x.
Definition qimage.cpp:4098
int colorCount() const
Returns the depth of the image.
void setColorSpace(const QColorSpace &)
Definition qimage.cpp:4978
void setOffset(const QPoint &)
Sets the number of pixels by which the image is intended to be offset by when positioning relative to...
Definition qimage.cpp:4152
Definition qmap.h:186
void setLooping(int loops=0)
void setDisposalMethod(DisposalMethod)
QIODevice * device()
bool writeImage(const QImage &img)
void setGamma(float)
void setFrameDelay(int msecs)
QPNGImageWriter(QIODevice *)
bool writeImage(const QImage &img, int compression, const QString &description)
bool writeImage(const QImage &img, int x, int y)
void readPngTexts(png_info *info)
QPngHandlerPrivate(QPngHandler *qq)
ColorSpaceState colorSpaceState
AllocatedMemoryPointers amp
png_struct * png_ptr
QColorSpace colorSpace
QStringList readTexts
QImage::Format readImageFormat()
bool readPngImage(QImage *image)
void setOption(ImageOption option, const QVariant &value) override
Sets the option option with the value value.
bool write(const QImage &image) override
Writes the image image to the assigned device.
bool supportsOption(ImageOption option) const override
Returns true if the QImageIOHandler supports the option option; otherwise returns false.
QVariant option(ImageOption option) const override
Returns the value assigned to option as a QVariant.
bool read(QImage *image) override
Read an image from the device, and stores it in image.
bool canRead() const override
Returns true if an image can be read from the device (i.e., the image format is supported,...
\inmodule QtCore\reentrant
Definition qpoint.h:214
\inmodule QtCore\reentrant
Definition qpoint.h:23
qsizetype size() const
Definition qset.h:50
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:123
constexpr bool isValid() const noexcept
Returns true if both the width and height is equal to or greater than 0; otherwise returns false.
Definition qsize.h:126
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toLatin1() const &
Definition qstring.h:559
const_iterator constEnd() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing just after the last character in...
Definition qstring.h:1211
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString simplified() const &
Definition qstring.h:384
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
const_iterator constBegin() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first character in the st...
Definition qstring.h:1203
@ BigEndian
Definition qsysinfo.h:29
@ ByteOrder
Definition qsysinfo.h:34
@ LittleEndian
Definition qsysinfo.h:30
\inmodule QtCore
Definition qvariant.h:64
#define this
Definition dialogs.cpp:9
QString text
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ SmoothTransformation
@ IgnoreAspectRatio
Definition image.cpp:4
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
Q_CORE_EXPORT char * qstrdup(const char *)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
QMap< QString, QString > qt_getImageText(const QImage &image, const QString &description)
Definition qimage.cpp:5753
#define qCInfo(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLint GLsizei width
GLuint GLsizei const GLchar * message
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint GLintptr offset
GLboolean GLboolean g
GLuint name
GLint GLsizei GLsizei GLenum format
GLint y
GLbyte GLbyte blue
Definition qopenglext.h:385
const GLubyte * c
GLint void * img
Definition qopenglext.h:233
GLuint in
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLenum GLenum GLsizei void * row
GLfloat GLfloat p
[1]
GLuint GLenum option
GLbyte green
Definition qopenglext.h:385
static void qpiw_write_fn(png_structp png_ptr, png_bytep data, png_size_t length)
static void set_text(const QImage &image, png_structp png_ptr, png_infop info_ptr, const QString &description)
static bool setup_qt(QImage &image, png_structp png_ptr, png_infop info_ptr, QSize scaledSize, bool *doScaledRead)
static void qpiw_flush_fn(png_structp)
static void qt_png_warning(png_structp, png_const_charp message)
#define FAST_SCAN_LINE(data, bpl, y)
static void read_image_scaled(QImage *outImage, png_structp png_ptr, png_infop info_ptr, QPngHandlerPrivate::AllocatedMemoryPointers &amp, QSize scaledSize)
static bool write_png_image(const QImage &image, QIODevice *device, int compression, int quality, float gamma, const QString &description)
static void iod_read_fn(png_structp png_ptr, png_bytep data, png_size_t length)
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
constexpr QRgb qRgb(int r, int g, int b)
Definition qrgb.h:30
constexpr int qRed(QRgb rgb)
Definition qrgb.h:18
constexpr int qGreen(QRgb rgb)
Definition qrgb.h:21
constexpr QRgb qRgba(int r, int g, int b, int a)
Definition qrgb.h:33
constexpr int qBlue(QRgb rgb)
Definition qrgb.h:24
constexpr int qAlpha(QRgb rgb)
Definition qrgb.h:27
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:45
unsigned char uchar
Definition qtypes.h:27
int qint32
Definition qtypes.h:44
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
QVideoFrameFormat::PixelFormat fmt
QFileInfo info(fileName)
[8]
QTextStream out(stdout)
[7]
QObject::connect nullptr
QGraphicsSvgItem * red