187bool setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr)
189 png_uint_32 width = 0;
190 png_uint_32 height = 0;
193 png_bytep trans_alpha =
nullptr;
194 png_color_16p trans_color_p =
nullptr;
196 png_colorp palette =
nullptr;
198 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
nullptr,
nullptr,
nullptr);
199 QSize size(width, height);
200 png_set_interlace_handling(png_ptr);
202 if (color_type == PNG_COLOR_TYPE_GRAY) {
204 if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
205 png_set_invert_mono(png_ptr);
206 png_read_update_info(png_ptr, info_ptr);
207 if (!QImageIOHandler::allocateImage(size, QImage::Format_Mono, &image))
209 image.setColorCount(2);
210 image.setColor(1, qRgb(0,0,0));
211 image.setColor(0, qRgb(255,255,255));
212 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {
213 const int g = trans_color_p->gray;
217 image.setColor(1, qRgba(0, 0, 0, 0));
219 image.setColor(0, qRgba(255, 255, 255, 0));
221 }
else if (bit_depth == 16
222 && png_get_channels(png_ptr, info_ptr) == 1
223 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
224 if (!QImageIOHandler::allocateImage(size, QImage::Format_Grayscale16, &image))
226 png_read_update_info(png_ptr, info_ptr);
227 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
228 png_set_swap(png_ptr);
229 }
else if (bit_depth == 16) {
230 bool hasMask = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
232 png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER);
234 png_set_expand(png_ptr);
235 png_set_gray_to_rgb(png_ptr);
236 QImage::Format format = hasMask ? QImage::Format_RGBA64 : QImage::Format_RGBX64;
237 if (!QImageIOHandler::allocateImage(size, format, &image))
239 png_read_update_info(png_ptr, info_ptr);
240 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
241 png_set_swap(png_ptr);
242 }
else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
243 png_set_expand(png_ptr);
244 if (!QImageIOHandler::allocateImage(size, QImage::Format_Grayscale8, &image))
246 png_read_update_info(png_ptr, info_ptr);
249 png_set_packing(png_ptr);
250 int ncols = bit_depth < 8 ? 1 << bit_depth : 256;
251 png_read_update_info(png_ptr, info_ptr);
252 if (!QImageIOHandler::allocateImage(size, QImage::Format_Indexed8, &image))
254 image.setColorCount(ncols);
255 for (
int i=0; i<ncols; i++) {
256 int c = i*255/(ncols-1);
257 image.setColor(i, qRgba(c,c,c,0xff));
259 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {
260 const int g = trans_color_p->gray;
262 image.setColor(g, 0);
266 }
else if (color_type == PNG_COLOR_TYPE_PALETTE
267 && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
268 && num_palette <= 256)
272 png_set_packing(png_ptr);
273 png_read_update_info(png_ptr, info_ptr);
274 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
nullptr,
nullptr,
nullptr);
275 size = QSize(width, height);
276 QImage::Format format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
277 if (!QImageIOHandler::allocateImage(size, format, &image))
279 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
280 image.setColorCount((format == QImage::Format_Mono) ? 2 : num_palette);
282 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) {
283 while (i < num_trans) {
284 image.setColor(i, qRgba(
294 while (i < num_palette) {
295 image.setColor(i, qRgba(
305 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
306 png_set_bgr(png_ptr);
308 }
else if (bit_depth == 16 && !(color_type & PNG_COLOR_MASK_PALETTE)) {
309 QImage::Format format = QImage::Format_RGBA64;
310 if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
311 png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER);
312 format = QImage::Format_RGBX64;
314 if (!(color_type & PNG_COLOR_MASK_COLOR))
315 png_set_gray_to_rgb(png_ptr);
316 if (!QImageIOHandler::allocateImage(size, format, &image))
318 png_read_update_info(png_ptr, info_ptr);
319 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
320 png_set_swap(png_ptr);
324 png_set_strip_16(png_ptr);
326 png_set_expand(png_ptr);
328 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
329 png_set_gray_to_rgb(png_ptr);
331 QImage::Format format = QImage::Format_ARGB32;
333 if (!(color_type & PNG_COLOR_MASK_ALPHA)
334 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
335 png_set_filler(png_ptr, 0xff, QSysInfo::ByteOrder == QSysInfo::BigEndian ?
336 PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
338 format = QImage::Format_RGB32;
340 if (!QImageIOHandler::allocateImage(size, format, &image))
343 if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
344 png_set_swap_alpha(png_ptr);
347 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
348 png_set_bgr(png_ptr);
351 png_read_update_info(png_ptr, info_ptr);
409 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
415#if defined(PNG_SET_OPTION_SUPPORTED
) && defined(PNG_MAXIMUM_INFLATE_WINDOW
)
418 png_set_option(
png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
421#ifdef PNG_SET_USER_LIMITS_SUPPORTED
423 if (QImageReader::allocationLimit() > 0) {
424 constexpr png_uint_32 maxChunkCache = 100;
425 const png_uint_32 chunkCache = png_get_chunk_cache_max(png_ptr);
426 if (chunkCache == 0 || chunkCache > maxChunkCache)
427 png_set_chunk_cache_max(
png_ptr, maxChunkCache);
433 png_destroy_read_struct(&
png_ptr,
nullptr,
nullptr);
445 if (setjmp(png_jmpbuf(png_ptr))) {
456#ifdef PNG_iCCP_SUPPORTED
458 png_charp name =
nullptr;
459 int compressionType = 0;
460#if (PNG_LIBPNG_VER
< 10500
)
461 png_charp profileData =
nullptr;
463 png_bytep profileData =
nullptr;
466 png_get_iCCP(
png_ptr,
info_ptr, &name, &compressionType, &profileData, &profLen);
468 Q_UNUSED(compressionType);
470 colorSpace = QColorSpace::fromIccProfile(QByteArray((
const char *)profileData, profLen));
472 if (csD->description.isEmpty())
473 csD->description = QString::fromLatin1((
const char *)name);
478 if (colorSpaceState <= Srgb && png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
479 int rendering_intent = -1;
482 if (rendering_intent >= 0 && rendering_intent <= 3) {
483 colorSpace = QColorSpace::SRgb;
487 if (colorSpaceState <= GammaChrm && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
488 double file_gamma = 0.0;
492 QColorSpace::PrimaryPoints primaries;
494 double white_x, white_y, red_x, red_y;
495 double green_x, green_y, blue_x, blue_y;
497 &white_x, &white_y, &red_x, &red_y,
498 &green_x, &green_y, &blue_x, &blue_y);
499 primaries.whitePoint = QPointF(white_x, white_y);
500 primaries.redPoint = QPointF(red_x, red_y);
501 primaries.greenPoint = QPointF(green_x, green_y);
502 primaries.bluePoint = QPointF(blue_x, blue_y);
504 if (primaries.isValid()) {
505 colorSpace = QColorSpace(primaries.whitePoint, primaries.redPoint, primaries.greenPoint, primaries.bluePoint,
506 QColorSpace::TransferFunction::Gamma, 1.0f / fileGamma);
508 colorSpace = QColorSpace(QColorSpace::Primaries::SRgb,
509 QColorSpace::TransferFunction::Gamma, 1.0f / fileGamma);
524 if (state == Ready && !readPngHeader()) {
529 row_pointers =
nullptr;
530 if (setjmp(png_jmpbuf(png_ptr))) {
531 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
533 delete[] row_pointers;
538 if (gamma != 0.0 && fileGamma != 0.0) {
541 png_set_gamma(png_ptr, 1.0f / gamma, fileGamma);
542 colorSpace.setTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma);
543 colorSpaceState = GammaChrm;
546 if (!setup_qt(*outImage, png_ptr, info_ptr)) {
547 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
549 delete[] row_pointers;
554 png_uint_32 width = 0;
555 png_uint_32 height = 0;
556 png_int_32 offset_x = 0;
557 png_int_32 offset_y = 0;
561 int unit_type = PNG_OFFSET_PIXEL;
562 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
nullptr,
nullptr,
nullptr);
563 png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, &unit_type);
564 uchar *data = outImage->bits();
565 qsizetype bpl = outImage->bytesPerLine();
566 row_pointers =
new png_bytep[height];
568 for (uint y = 0; y < height; y++)
569 row_pointers[y] = data + y * bpl;
571 png_read_image(png_ptr, row_pointers);
573 outImage->setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr));
574 outImage->setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr));
576 if (unit_type == PNG_OFFSET_PIXEL)
577 outImage->setOffset(QPoint(offset_x, offset_y));
580 if (color_type == PNG_COLOR_TYPE_PALETTE && outImage->format() == QImage::Format_Indexed8) {
581 int color_table_size = outImage->colorCount();
582 for (
int y=0; y<(
int)height; ++y) {
584 uchar *end = p + width;
586 if (*p >= color_table_size)
594 png_read_end(png_ptr, end_info);
596 readPngTexts(end_info);
597 for (
int i = 0; i < readTexts.size()-1; i+=2)
598 outImage->setText(readTexts.at(i), readTexts.at(i+1));
600 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
602 delete[] row_pointers;
603 row_pointers =
nullptr;
606 if (colorSpaceState > Undefined)
607 outImage->setColorSpace(colorSpace);
614 QImage::Format format = QImage::Format_Invalid;
615 png_uint_32 width = 0, height = 0;
616 int bit_depth = 0, color_type = 0;
619 png_get_IHDR(
png_ptr,
info_ptr, &width, &height, &bit_depth, &color_type,
nullptr,
nullptr,
nullptr);
620 if (color_type == PNG_COLOR_TYPE_GRAY) {
622 if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
623 format = QImage::Format_Mono;
624 }
else if (bit_depth == 16) {
625 format = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? QImage::Format_RGBA64 : QImage::Format_Grayscale16;
626 }
else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
627 format = QImage::Format_Grayscale8;
629 format = QImage::Format_Indexed8;
631 }
else if (color_type == PNG_COLOR_TYPE_PALETTE
632 && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
633 && num_palette <= 256)
636 format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
637 }
else if (bit_depth == 16 && !(color_type & PNG_COLOR_MASK_PALETTE)) {
638 format = QImage::Format_RGBA64;
639 if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
640 format = QImage::Format_RGBX64;
643 format = QImage::Format_ARGB32;
645 if (!(color_type & PNG_COLOR_MASK_ALPHA)
646 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
648 format = QImage::Format_RGB32;
689static void set_text(
const QImage &image, png_structp png_ptr, png_infop info_ptr,
690 const QString &description)
692 const QMap<QString, QString> text = qt_getImageText(image, description);
697 png_textp text_ptr =
new png_text[text.size()];
698 memset(text_ptr, 0, text.size() *
sizeof(png_text));
700 QMap<QString, QString>::ConstIterator it = text.constBegin();
702 while (it != text.constEnd()) {
703 text_ptr[i].key = qstrdup(QStringView{it.key()}.left(79).toLatin1().constData());
704 bool noCompress = (it.value().size() < 40);
706#ifdef PNG_iTXt_SUPPORTED
707 bool needsItxt =
false;
708 for (QChar c : it.value()) {
710 if (c.row() || (ch < 0x20 && ch !=
'\n') || (ch > 0x7e && ch < 0xa0)) {
717 text_ptr[i].compression = noCompress ? PNG_ITXT_COMPRESSION_NONE : PNG_ITXT_COMPRESSION_zTXt;
719 text_ptr[i].text = qstrdup(value.constData());
720 text_ptr[i].itxt_length = value.size();
721 text_ptr[i].lang =
const_cast<
char*>(
"UTF-8");
722 text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData());
727 text_ptr[i].compression = noCompress ? PNG_TEXT_COMPRESSION_NONE : PNG_TEXT_COMPRESSION_zTXt;
729 text_ptr[i].text = qstrdup(value.constData());
730 text_ptr[i].text_length = value.size();
736 png_set_text(png_ptr, info_ptr, text_ptr, i);
737 for (i = 0; i < text.size(); ++i) {
738 delete [] text_ptr[i].key;
739 delete [] text_ptr[i].text;
740#ifdef PNG_iTXt_SUPPORTED
741 delete [] text_ptr[i].lang_key;
753 int off_x_in,
int off_y_in)
755 QPoint offset = image.offset();
756 int off_x = off_x_in + offset.x();
757 int off_y = off_y_in + offset.y();
762 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
768#ifdef PNG_BENIGN_ERRORS_SUPPORTED
769 png_set_benign_errors(png_ptr, 1);
772 info_ptr = png_create_info_struct(png_ptr);
774 png_destroy_write_struct(&png_ptr,
nullptr);
778 if (setjmp(png_jmpbuf(png_ptr))) {
779 png_destroy_write_struct(&png_ptr, &info_ptr);
783 int compression = compression_in;
784 if (compression >= 0) {
785 if (compression > 9) {
786 qCWarning(lcImageIo,
"PNG: Compression %d out of range", compression);
789 png_set_compression_level(png_ptr, compression);
796 if (image.format() <= QImage::Format_Indexed8) {
797 if (image.isGrayscale())
798 color_type = PNG_COLOR_TYPE_GRAY;
800 color_type = PNG_COLOR_TYPE_PALETTE;
802 else if (image.format() == QImage::Format_Grayscale8
803 || image.format() == QImage::Format_Grayscale16)
804 color_type = PNG_COLOR_TYPE_GRAY;
805 else if (image.hasAlphaChannel())
806 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
808 color_type = PNG_COLOR_TYPE_RGB;
811 switch (image.format()) {
812 case QImage::Format_Mono:
813 case QImage::Format_MonoLSB:
816 case QImage::Format_RGBX64:
817 case QImage::Format_RGBA64:
818 case QImage::Format_RGBA64_Premultiplied:
819 case QImage::Format_Grayscale16:
827 png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(),
829 color_type, 0, 0, 0);
831#ifdef PNG_iCCP_SUPPORTED
832 QColorSpace cs = image.colorSpace();
834 if (cs.isValid() && gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma))
835 cs = cs.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma);
837 if (!iccProfile.isEmpty()) {
838 QByteArray iccProfileName = cs.description().toLatin1();
839 if (iccProfileName.isEmpty())
840 iccProfileName = QByteArrayLiteral(
"Custom");
841 png_set_iCCP(png_ptr, info_ptr,
842 #if PNG_LIBPNG_VER
< 10500
843 iccProfileName.data(), PNG_COMPRESSION_TYPE_BASE, iccProfile.data(),
845 iccProfileName.constData(), PNG_COMPRESSION_TYPE_BASE,
846 (png_const_bytep)iccProfile.constData(),
852 png_set_gAMA(png_ptr, info_ptr, 1.0/gamma);
855 if (image.format() == QImage::Format_MonoLSB)
856 png_set_packswap(png_ptr);
858 if (color_type == PNG_COLOR_TYPE_PALETTE) {
860 int num_palette = qMin(256, image.colorCount());
861 png_color palette[256];
864 for (
int i=0; i<num_palette; i++) {
865 QRgb rgba=image.color(i);
866 palette[i].red = qRed(rgba);
867 palette[i].green = qGreen(rgba);
868 palette[i].blue = qBlue(rgba);
869 trans[i] = qAlpha(rgba);
870 if (trans[i] < 255) {
874 png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
877 png_set_tRNS(png_ptr, info_ptr, trans, num_trans,
nullptr);
883 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
884 switch (image.format()) {
885 case QImage::Format_RGBX8888:
886 case QImage::Format_RGBA8888:
887 case QImage::Format_RGBX64:
888 case QImage::Format_RGBA64:
889 case QImage::Format_RGBA64_Premultiplied:
892 png_set_swap_alpha(png_ptr);
897 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
898 switch (image.format()) {
899 case QImage::Format_RGB888:
900 case QImage::Format_RGBX8888:
901 case QImage::Format_RGBA8888:
902 case QImage::Format_RGBX64:
903 case QImage::Format_RGBA64:
904 case QImage::Format_RGBA64_Premultiplied:
907 png_set_bgr(png_ptr);
911 if (off_x || off_y) {
912 png_set_oFFs(png_ptr, info_ptr, off_x, off_y, PNG_OFFSET_PIXEL);
915 if (frames_written > 0)
916 png_set_sig_bytes(png_ptr, 8);
918 if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) {
919 png_set_pHYs(png_ptr, info_ptr,
920 image.dotsPerMeterX(), image.dotsPerMeterY(),
921 PNG_RESOLUTION_METER);
924 set_text(image, png_ptr, info_ptr, description);
926 png_write_info(png_ptr, info_ptr);
928 if (image.depth() != 1)
929 png_set_packing(png_ptr);
931 if (color_type == PNG_COLOR_TYPE_RGB) {
932 switch (image.format()) {
933 case QImage::Format_RGB888:
934 case QImage::Format_BGR888:
936 case QImage::Format_RGBX8888:
937 case QImage::Format_RGBX64:
938 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
941 png_set_filler(png_ptr, 0,
942 QSysInfo::ByteOrder == QSysInfo::BigEndian ?
943 PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
947 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
948 switch (image.format()) {
949 case QImage::Format_RGBX64:
950 case QImage::Format_RGBA64:
951 case QImage::Format_RGBA64_Premultiplied:
952 case QImage::Format_Grayscale16:
953 png_set_swap(png_ptr);
960 if (looping >= 0 && frames_written == 0) {
961 uchar data[13] =
"NETSCAPE2.0";
963 data[0xB] = looping%0x100;
964 data[0xC] = looping/0x100;
965 png_write_chunk(png_ptr,
const_cast<png_bytep>((
const png_byte *)
"gIFx"), data, 13);
971 data[2] = (ms_delay/10)/0x100;
972 data[3] = (ms_delay/10)%0x100;
973 png_write_chunk(png_ptr,
const_cast<png_bytep>((
const png_byte *)
"gIFg"), data, 4);
976 int height = image.height();
977 int width = image.width();
978 switch (image.format()) {
979 case QImage::Format_Mono:
980 case QImage::Format_MonoLSB:
981 case QImage::Format_Indexed8:
982 case QImage::Format_Grayscale8:
983 case QImage::Format_Grayscale16:
984 case QImage::Format_RGB32:
985 case QImage::Format_ARGB32:
986 case QImage::Format_RGB888:
987 case QImage::Format_BGR888:
988 case QImage::Format_RGBX8888:
989 case QImage::Format_RGBA8888:
990 case QImage::Format_RGBX64:
991 case QImage::Format_RGBA64:
993 png_bytep* row_pointers =
new png_bytep[height];
994 for (
int y=0; y<height; y++)
995 row_pointers[y] =
const_cast<png_bytep>(image.constScanLine(y));
996 png_write_image(png_ptr, row_pointers);
997 delete [] row_pointers;
1000 case QImage::Format_RGBA64_Premultiplied:
1003 png_bytep row_pointers[1];
1004 for (
int y=0; y<height; y++) {
1005 row = image.copy(0, y, width, 1).convertToFormat(QImage::Format_RGBA64);
1006 row_pointers[0] =
const_cast<png_bytep>(row.constScanLine(0));
1007 png_write_rows(png_ptr, row_pointers, 1);
1013 QImage::Format fmt = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32;
1015 png_bytep row_pointers[1];
1016 for (
int y=0; y<height; y++) {
1017 row = image.copy(0, y, width, 1).convertToFormat(fmt);
1018 row_pointers[0] =
const_cast<png_bytep>(row.constScanLine(0));
1019 png_write_rows(png_ptr, row_pointers, 1);
1025 png_write_end(png_ptr, info_ptr);
1028 png_destroy_write_struct(&png_ptr, &info_ptr);