185bool setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr)
187 png_uint_32 width = 0;
188 png_uint_32 height = 0;
191 png_bytep trans_alpha =
nullptr;
192 png_color_16p trans_color_p =
nullptr;
194 png_colorp palette =
nullptr;
196 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
nullptr,
nullptr,
nullptr);
197 QSize size(width, height);
198 png_set_interlace_handling(png_ptr);
200 if (color_type == PNG_COLOR_TYPE_GRAY) {
202 if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
203 png_set_invert_mono(png_ptr);
204 png_read_update_info(png_ptr, info_ptr);
205 if (!QImageIOHandler::allocateImage(size, QImage::Format_Mono, &image))
207 image.setColorCount(2);
208 image.setColor(1, qRgb(0,0,0));
209 image.setColor(0, qRgb(255,255,255));
210 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {
211 const int g = trans_color_p->gray;
215 image.setColor(1, qRgba(0, 0, 0, 0));
217 image.setColor(0, qRgba(255, 255, 255, 0));
219 }
else if (bit_depth == 16
220 && png_get_channels(png_ptr, info_ptr) == 1
221 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
222 if (!QImageIOHandler::allocateImage(size, QImage::Format_Grayscale16, &image))
224 png_read_update_info(png_ptr, info_ptr);
225 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
226 png_set_swap(png_ptr);
227 }
else if (bit_depth == 16) {
228 bool hasMask = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
230 png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER);
232 png_set_expand(png_ptr);
233 png_set_gray_to_rgb(png_ptr);
234 QImage::Format format = hasMask ? QImage::Format_RGBA64 : QImage::Format_RGBX64;
235 if (!QImageIOHandler::allocateImage(size, format, &image))
237 png_read_update_info(png_ptr, info_ptr);
238 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
239 png_set_swap(png_ptr);
240 }
else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
241 png_set_expand(png_ptr);
242 if (!QImageIOHandler::allocateImage(size, QImage::Format_Grayscale8, &image))
244 png_read_update_info(png_ptr, info_ptr);
247 png_set_packing(png_ptr);
248 int ncols = bit_depth < 8 ? 1 << bit_depth : 256;
249 png_read_update_info(png_ptr, info_ptr);
250 if (!QImageIOHandler::allocateImage(size, QImage::Format_Indexed8, &image))
252 image.setColorCount(ncols);
253 for (
int i=0; i<ncols; i++) {
254 int c = i*255/(ncols-1);
255 image.setColor(i, qRgba(c,c,c,0xff));
257 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_color_p) {
258 const int g = trans_color_p->gray;
260 image.setColor(g, 0);
264 }
else if (color_type == PNG_COLOR_TYPE_PALETTE
265 && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
266 && num_palette <= 256)
270 png_set_packing(png_ptr);
271 png_read_update_info(png_ptr, info_ptr);
272 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
nullptr,
nullptr,
nullptr);
273 size = QSize(width, height);
274 QImage::Format format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
275 if (!QImageIOHandler::allocateImage(size, format, &image))
277 png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
278 image.setColorCount((format == QImage::Format_Mono) ? 2 : num_palette);
280 if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) {
281 while (i < num_trans) {
282 image.setColor(i, qRgba(
292 while (i < num_palette) {
293 image.setColor(i, qRgba(
303 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
304 png_set_bgr(png_ptr);
306 }
else if (bit_depth == 16 && !(color_type & PNG_COLOR_MASK_PALETTE)) {
307 QImage::Format format = QImage::Format_RGBA64;
308 if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
309 png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER);
310 format = QImage::Format_RGBX64;
312 if (!(color_type & PNG_COLOR_MASK_COLOR))
313 png_set_gray_to_rgb(png_ptr);
314 if (!QImageIOHandler::allocateImage(size, format, &image))
316 png_read_update_info(png_ptr, info_ptr);
317 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
318 png_set_swap(png_ptr);
322 png_set_strip_16(png_ptr);
324 png_set_expand(png_ptr);
326 if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
327 png_set_gray_to_rgb(png_ptr);
329 QImage::Format format = QImage::Format_ARGB32;
331 if (!(color_type & PNG_COLOR_MASK_ALPHA)
332 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
333 png_set_filler(png_ptr, 0xff, QSysInfo::ByteOrder == QSysInfo::BigEndian ?
334 PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
336 format = QImage::Format_RGB32;
338 if (!QImageIOHandler::allocateImage(size, format, &image))
341 if (QSysInfo::ByteOrder == QSysInfo::BigEndian)
342 png_set_swap_alpha(png_ptr);
345 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
346 png_set_bgr(png_ptr);
349 png_read_update_info(png_ptr, info_ptr);
397 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
403#if defined(PNG_SET_OPTION_SUPPORTED
) && defined(PNG_MAXIMUM_INFLATE_WINDOW
)
406 png_set_option(
png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
411 png_destroy_read_struct(&
png_ptr,
nullptr,
nullptr);
423 if (setjmp(png_jmpbuf(png_ptr))) {
434#ifdef PNG_iCCP_SUPPORTED
436 png_charp name =
nullptr;
437 int compressionType = 0;
438#if (PNG_LIBPNG_VER
< 10500
)
439 png_charp profileData =
nullptr;
441 png_bytep profileData =
nullptr;
444 png_get_iCCP(
png_ptr,
info_ptr, &name, &compressionType, &profileData, &profLen);
446 Q_UNUSED(compressionType);
448 colorSpace = QColorSpace::fromIccProfile(QByteArray((
const char *)profileData, profLen));
450 if (csD->description.isEmpty())
451 csD->description = QString::fromLatin1((
const char *)name);
456 if (colorSpaceState <= Srgb && png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
457 int rendering_intent = -1;
460 if (rendering_intent >= 0 && rendering_intent <= 3) {
461 colorSpace = QColorSpace::SRgb;
465 if (colorSpaceState <= GammaChrm && png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
466 double file_gamma = 0.0;
470 QColorSpace::PrimaryPoints primaries;
472 double white_x, white_y, red_x, red_y;
473 double green_x, green_y, blue_x, blue_y;
475 &white_x, &white_y, &red_x, &red_y,
476 &green_x, &green_y, &blue_x, &blue_y);
477 primaries.whitePoint = QPointF(white_x, white_y);
478 primaries.redPoint = QPointF(red_x, red_y);
479 primaries.greenPoint = QPointF(green_x, green_y);
480 primaries.bluePoint = QPointF(blue_x, blue_y);
482 if (primaries.isValid()) {
483 colorSpace = QColorSpace(primaries.whitePoint, primaries.redPoint, primaries.greenPoint, primaries.bluePoint,
484 QColorSpace::TransferFunction::Gamma, 1.0f / fileGamma);
486 colorSpace = QColorSpace(QColorSpace::Primaries::SRgb,
487 QColorSpace::TransferFunction::Gamma, 1.0f / fileGamma);
502 if (state == Ready && !readPngHeader()) {
507 row_pointers =
nullptr;
508 if (setjmp(png_jmpbuf(png_ptr))) {
509 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
511 delete[] row_pointers;
516 if (gamma != 0.0 && fileGamma != 0.0) {
519 png_set_gamma(png_ptr, 1.0f / gamma, fileGamma);
520 colorSpace.setTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma);
521 colorSpaceState = GammaChrm;
524 if (!setup_qt(*outImage, png_ptr, info_ptr)) {
525 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
527 delete[] row_pointers;
532 png_uint_32 width = 0;
533 png_uint_32 height = 0;
534 png_int_32 offset_x = 0;
535 png_int_32 offset_y = 0;
539 int unit_type = PNG_OFFSET_PIXEL;
540 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
nullptr,
nullptr,
nullptr);
541 png_get_oFFs(png_ptr, info_ptr, &offset_x, &offset_y, &unit_type);
542 uchar *data = outImage->bits();
543 qsizetype bpl = outImage->bytesPerLine();
544 row_pointers =
new png_bytep[height];
546 for (uint y = 0; y < height; y++)
547 row_pointers[y] = data + y * bpl;
549 png_read_image(png_ptr, row_pointers);
551 outImage->setDotsPerMeterX(png_get_x_pixels_per_meter(png_ptr,info_ptr));
552 outImage->setDotsPerMeterY(png_get_y_pixels_per_meter(png_ptr,info_ptr));
554 if (unit_type == PNG_OFFSET_PIXEL)
555 outImage->setOffset(QPoint(offset_x, offset_y));
558 if (color_type == PNG_COLOR_TYPE_PALETTE && outImage->format() == QImage::Format_Indexed8) {
559 int color_table_size = outImage->colorCount();
560 for (
int y=0; y<(
int)height; ++y) {
562 uchar *end = p + width;
564 if (*p >= color_table_size)
572 png_read_end(png_ptr, end_info);
574 readPngTexts(end_info);
575 for (
int i = 0; i < readTexts.size()-1; i+=2)
576 outImage->setText(readTexts.at(i), readTexts.at(i+1));
578 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
580 delete[] row_pointers;
581 row_pointers =
nullptr;
584 if (colorSpaceState > Undefined && colorSpace.isValid())
585 outImage->setColorSpace(colorSpace);
592 QImage::Format format = QImage::Format_Invalid;
593 png_uint_32 width = 0, height = 0;
594 int bit_depth = 0, color_type = 0;
597 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
nullptr,
nullptr,
nullptr);
598 if (color_type == PNG_COLOR_TYPE_GRAY) {
600 if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
601 format = QImage::Format_Mono;
602 }
else if (bit_depth == 16) {
603 format = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? QImage::Format_RGBA64 : QImage::Format_Grayscale16;
604 }
else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
605 format = QImage::Format_Grayscale8;
607 format = QImage::Format_Indexed8;
609 }
else if (color_type == PNG_COLOR_TYPE_PALETTE
610 && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)
611 && num_palette <= 256)
614 format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8;
615 }
else if (bit_depth == 16 && !(color_type & PNG_COLOR_MASK_PALETTE)) {
616 format = QImage::Format_RGBA64;
617 if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
618 format = QImage::Format_RGBX64;
621 format = QImage::Format_ARGB32;
623 if (!(color_type & PNG_COLOR_MASK_ALPHA)
624 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
626 format = QImage::Format_RGB32;
667static void set_text(
const QImage &image, png_structp png_ptr, png_infop info_ptr,
668 const QString &description)
670 const QMap<QString, QString> text = qt_getImageText(image, description);
675 png_textp text_ptr =
new png_text[text.size()];
676 memset(text_ptr, 0, text.size() *
sizeof(png_text));
678 QMap<QString, QString>::ConstIterator it = text.constBegin();
680 while (it != text.constEnd()) {
681 text_ptr[i].key = qstrdup(QStringView{it.key()}.left(79).toLatin1().constData());
682 bool noCompress = (it.value().size() < 40);
684#ifdef PNG_iTXt_SUPPORTED
685 bool needsItxt =
false;
686 for (QChar c : it.value()) {
688 if (c.row() || (ch < 0x20 && ch !=
'\n') || (ch > 0x7e && ch < 0xa0)) {
695 text_ptr[i].compression = noCompress ? PNG_ITXT_COMPRESSION_NONE : PNG_ITXT_COMPRESSION_zTXt;
697 text_ptr[i].text = qstrdup(value.constData());
698 text_ptr[i].itxt_length = value.size();
699 text_ptr[i].lang =
const_cast<
char*>(
"UTF-8");
700 text_ptr[i].lang_key = qstrdup(it.key().toUtf8().constData());
705 text_ptr[i].compression = noCompress ? PNG_TEXT_COMPRESSION_NONE : PNG_TEXT_COMPRESSION_zTXt;
707 text_ptr[i].text = qstrdup(value.constData());
708 text_ptr[i].text_length = value.size();
714 png_set_text(png_ptr, info_ptr, text_ptr, i);
715 for (i = 0; i < text.size(); ++i) {
716 delete [] text_ptr[i].key;
717 delete [] text_ptr[i].text;
718#ifdef PNG_iTXt_SUPPORTED
719 delete [] text_ptr[i].lang_key;
731 int off_x_in,
int off_y_in)
733 QPoint offset = image.offset();
734 int off_x = off_x_in + offset.x();
735 int off_y = off_y_in + offset.y();
740 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
nullptr,
nullptr);
746#ifdef PNG_BENIGN_ERRORS_SUPPORTED
747 png_set_benign_errors(png_ptr, 1);
750 info_ptr = png_create_info_struct(png_ptr);
752 png_destroy_write_struct(&png_ptr,
nullptr);
756 if (setjmp(png_jmpbuf(png_ptr))) {
757 png_destroy_write_struct(&png_ptr, &info_ptr);
761 int compression = compression_in;
762 if (compression >= 0) {
763 if (compression > 9) {
764 qCWarning(lcImageIo,
"PNG: Compression %d out of range", compression);
767 png_set_compression_level(png_ptr, compression);
774 if (image.format() <= QImage::Format_Indexed8) {
775 if (image.isGrayscale())
776 color_type = PNG_COLOR_TYPE_GRAY;
778 color_type = PNG_COLOR_TYPE_PALETTE;
780 else if (image.format() == QImage::Format_Grayscale8
781 || image.format() == QImage::Format_Grayscale16)
782 color_type = PNG_COLOR_TYPE_GRAY;
783 else if (image.hasAlphaChannel())
784 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
786 color_type = PNG_COLOR_TYPE_RGB;
789 switch (image.format()) {
790 case QImage::Format_Mono:
791 case QImage::Format_MonoLSB:
794 case QImage::Format_RGBX64:
795 case QImage::Format_RGBA64:
796 case QImage::Format_RGBA64_Premultiplied:
797 case QImage::Format_Grayscale16:
805 png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(),
807 color_type, 0, 0, 0);
809#ifdef PNG_iCCP_SUPPORTED
810 QColorSpace cs = image.colorSpace();
812 if (cs.isValid() && gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma))
813 cs = cs.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma);
815 if (!iccProfile.isEmpty()) {
816 QByteArray iccProfileName = cs.description().toLatin1();
817 if (iccProfileName.isEmpty())
818 iccProfileName = QByteArrayLiteral(
"Custom");
819 png_set_iCCP(png_ptr, info_ptr,
820 #if PNG_LIBPNG_VER
< 10500
821 iccProfileName.data(), PNG_COMPRESSION_TYPE_BASE, iccProfile.data(),
823 iccProfileName.constData(), PNG_COMPRESSION_TYPE_BASE,
824 (png_const_bytep)iccProfile.constData(),
830 png_set_gAMA(png_ptr, info_ptr, 1.0/gamma);
833 if (image.format() == QImage::Format_MonoLSB)
834 png_set_packswap(png_ptr);
836 if (color_type == PNG_COLOR_TYPE_PALETTE) {
838 int num_palette = qMin(256, image.colorCount());
839 png_color palette[256];
842 for (
int i=0; i<num_palette; i++) {
843 QRgb rgba=image.color(i);
844 palette[i].red = qRed(rgba);
845 palette[i].green = qGreen(rgba);
846 palette[i].blue = qBlue(rgba);
847 trans[i] = qAlpha(rgba);
848 if (trans[i] < 255) {
852 png_set_PLTE(png_ptr, info_ptr, palette, num_palette);
855 png_set_tRNS(png_ptr, info_ptr, trans, num_trans,
nullptr);
861 if (QSysInfo::ByteOrder == QSysInfo::BigEndian) {
862 switch (image.format()) {
863 case QImage::Format_RGBX8888:
864 case QImage::Format_RGBA8888:
865 case QImage::Format_RGBX64:
866 case QImage::Format_RGBA64:
867 case QImage::Format_RGBA64_Premultiplied:
870 png_set_swap_alpha(png_ptr);
875 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
876 switch (image.format()) {
877 case QImage::Format_RGB888:
878 case QImage::Format_RGBX8888:
879 case QImage::Format_RGBA8888:
880 case QImage::Format_RGBX64:
881 case QImage::Format_RGBA64:
882 case QImage::Format_RGBA64_Premultiplied:
885 png_set_bgr(png_ptr);
889 if (off_x || off_y) {
890 png_set_oFFs(png_ptr, info_ptr, off_x, off_y, PNG_OFFSET_PIXEL);
893 if (frames_written > 0)
894 png_set_sig_bytes(png_ptr, 8);
896 if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) {
897 png_set_pHYs(png_ptr, info_ptr,
898 image.dotsPerMeterX(), image.dotsPerMeterY(),
899 PNG_RESOLUTION_METER);
902 set_text(image, png_ptr, info_ptr, description);
904 png_write_info(png_ptr, info_ptr);
906 if (image.depth() != 1)
907 png_set_packing(png_ptr);
909 if (color_type == PNG_COLOR_TYPE_RGB) {
910 switch (image.format()) {
911 case QImage::Format_RGB888:
912 case QImage::Format_BGR888:
914 case QImage::Format_RGBX8888:
915 case QImage::Format_RGBX64:
916 png_set_filler(png_ptr, 0, PNG_FILLER_AFTER);
919 png_set_filler(png_ptr, 0,
920 QSysInfo::ByteOrder == QSysInfo::BigEndian ?
921 PNG_FILLER_BEFORE : PNG_FILLER_AFTER);
925 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) {
926 switch (image.format()) {
927 case QImage::Format_RGBX64:
928 case QImage::Format_RGBA64:
929 case QImage::Format_RGBA64_Premultiplied:
930 case QImage::Format_Grayscale16:
931 png_set_swap(png_ptr);
938 if (looping >= 0 && frames_written == 0) {
939 uchar data[13] =
"NETSCAPE2.0";
941 data[0xB] = looping%0x100;
942 data[0xC] = looping/0x100;
943 png_write_chunk(png_ptr,
const_cast<png_bytep>((
const png_byte *)
"gIFx"), data, 13);
949 data[2] = (ms_delay/10)/0x100;
950 data[3] = (ms_delay/10)%0x100;
951 png_write_chunk(png_ptr,
const_cast<png_bytep>((
const png_byte *)
"gIFg"), data, 4);
954 int height = image.height();
955 int width = image.width();
956 switch (image.format()) {
957 case QImage::Format_Mono:
958 case QImage::Format_MonoLSB:
959 case QImage::Format_Indexed8:
960 case QImage::Format_Grayscale8:
961 case QImage::Format_Grayscale16:
962 case QImage::Format_RGB32:
963 case QImage::Format_ARGB32:
964 case QImage::Format_RGB888:
965 case QImage::Format_BGR888:
966 case QImage::Format_RGBX8888:
967 case QImage::Format_RGBA8888:
968 case QImage::Format_RGBX64:
969 case QImage::Format_RGBA64:
971 png_bytep* row_pointers =
new png_bytep[height];
972 for (
int y=0; y<height; y++)
973 row_pointers[y] =
const_cast<png_bytep>(image.constScanLine(y));
974 png_write_image(png_ptr, row_pointers);
975 delete [] row_pointers;
978 case QImage::Format_RGBA64_Premultiplied:
981 png_bytep row_pointers[1];
982 for (
int y=0; y<height; y++) {
983 row = image.copy(0, y, width, 1).convertToFormat(QImage::Format_RGBA64);
984 row_pointers[0] =
const_cast<png_bytep>(row.constScanLine(0));
985 png_write_rows(png_ptr, row_pointers, 1);
991 QImage::Format fmt = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32;
993 png_bytep row_pointers[1];
994 for (
int y=0; y<height; y++) {
995 row = image.copy(0, y, width, 1).convertToFormat(fmt);
996 row_pointers[0] =
const_cast<png_bytep>(row.constScanLine(0));
997 png_write_rows(png_ptr, row_pointers, 1);
1003 png_write_end(png_ptr, info_ptr);
1006 png_destroy_write_struct(&png_ptr, &info_ptr);