From 0eb836043d3f1af4bb6b92bf45bde5cd2a8ccb38 Mon Sep 17 00:00:00 2001 From: Ivailo Monev Date: Fri, 17 Dec 2021 06:33:06 +0200 Subject: [PATCH] effectively revert 2337455e9459f1edd9159bdfc7ca1b481fdb9556 and 6815cf1ce9c95663cc3d976353a24b77bec4066f Signed-off-by: Ivailo Monev --- src/gui/image/qpnghandler.cpp | 253 +++++++++++++++++++++++++++++++++--------- 1 file changed, 203 insertions(+), 50 deletions(-) diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index b807f0d50..adcdb678a 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -88,41 +88,133 @@ static void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr) png_uint_32 height; int bit_depth; int color_type; + png_bytep trans_alpha = 0; + png_color_16p trans_color_p = 0; + int num_trans; + png_colorp palette = 0; + int num_palette; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); png_set_interlace_handling(png_ptr); - // 32-bit - if (bit_depth == 16) { - png_set_strip_16(png_ptr); - } - - png_set_expand(png_ptr); - - if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { - png_set_gray_to_rgb(png_ptr); - } else if (color_type == PNG_COLOR_TYPE_PALETTE) { - png_set_palette_to_rgb(png_ptr); - } + if (color_type == PNG_COLOR_TYPE_GRAY) { + // Black & White or 8-bit grayscale + if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) { + png_set_invert_mono(png_ptr); + png_read_update_info(png_ptr, info_ptr); + if (image.size() != QSize(width, height) || image.format() != QImage::Format_Mono) { + image = QImage(width, height, QImage::Format_Mono); + if (image.isNull()) + return; + } + image.setColorTable(monoColorTable()); + } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_expand(png_ptr); + png_set_strip_16(png_ptr); + png_set_gray_to_rgb(png_ptr); + if (image.size() != QSize(width, height) || image.format() != QImage::Format_ARGB32) { + image = QImage(width, height, QImage::Format_ARGB32); + if (image.isNull()) + return; + } +#if Q_BYTE_ORDER == Q_BIG_ENDIAN + png_set_swap_alpha(png_ptr); +#endif - QImage::Format format = QImage::Format_ARGB32; - // Only add filler if no alpha, or we can get 5 channel data. - if (!(color_type & PNG_COLOR_MASK_ALPHA) - && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - png_set_filler(png_ptr, 0xff, QFILLER_ORDER); - // We want 4 bytes, but it isn't an alpha channel - format = QImage::Format_RGB32; - } - if (image.size() != QSize(width, height) || image.format() != format) { - image = QImage(width, height, format); - if (image.isNull()) - return; - } + png_read_update_info(png_ptr, info_ptr); + } else { + if (bit_depth == 16) + png_set_strip_16(png_ptr); + else if (bit_depth < 8) + png_set_packing(png_ptr); + int ncols = bit_depth < 8 ? 1 << bit_depth : 256; + png_read_update_info(png_ptr, info_ptr); + if (image.size() != QSize(width, height) || image.format() != QImage::Format_Indexed8) { + image = QImage(width, height, QImage::Format_Indexed8); + if (image.isNull()) + return; + } + image.setColorCount(ncols); + for (int i=0; igray; + if (g < ncols) { + image.setColor(g, 0); + } + } + } + } else if (color_type == PNG_COLOR_TYPE_PALETTE + && png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette) + && num_palette <= 256) + { + // 1-bit and 8-bit color + if (bit_depth != 1) + png_set_packing(png_ptr); + png_read_update_info(png_ptr, info_ptr); + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); + QImage::Format format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8; + if (image.size() != QSize(width, height) || image.format() != format) { + image = QImage(width, height, format); + if (image.isNull()) + return; + } + png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); + image.setColorCount(num_palette); + int i = 0; + if (png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color_p) && trans_alpha) { + while (i < num_trans) { + image.setColor(i, qRgba( + palette[i].red, + palette[i].green, + palette[i].blue, + trans_alpha[i] + ) + ); + i++; + } + } + while (i < num_palette) { + image.setColor(i, qRgba( + palette[i].red, + palette[i].green, + palette[i].blue, + 0xff + ) + ); + i++; + } + } else { + // 32-bit + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + png_set_expand(png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + QImage::Format format = QImage::Format_ARGB32; + // Only add filler if no alpha, or we can get 5 channel data. + if (!(color_type & PNG_COLOR_MASK_ALPHA) + && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_filler(png_ptr, 0xff, QFILLER_ORDER); + // We want 4 bytes, but it isn't an alpha channel + format = QImage::Format_RGB32; + } + if (image.size() != QSize(width, height) || image.format() != format) { + image = QImage(width, height, format); + if (image.isNull()) + return; + } #if Q_BYTE_ORDER == Q_BIG_ENDIAN - png_set_swap_alpha(png_ptr); + png_set_swap_alpha(png_ptr); #endif - png_read_update_info(png_ptr, info_ptr); + png_read_update_info(png_ptr, info_ptr); + } // Qt==ARGB==Big(ARGB)==Little(BGRA) #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN @@ -160,9 +252,8 @@ bool QPngHandler::canRead(QIODevice *device) bool QPngHandler::read(QImage *image) { - if (!canRead()) { + if (!canRead()) return false; - } png_struct *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { @@ -205,13 +296,17 @@ bool QPngHandler::read(QImage *image) png_uint_32 width = 0; png_uint_32 height = 0; - png_get_IHDR(png_ptr, info_ptr, &width, &height, 0, 0, 0, 0, 0); + int bit_depth = 0; + int color_type = 0; + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + 0, 0, 0); uchar *data = image->bits(); - const int bpl = image->bytesPerLine(); + int bpl = image->bytesPerLine(); png_byte **row_pointers = new png_bytep[height]; + for (uint y = 0; y < height; y++) { - row_pointers[y] = QFAST_SCAN_LINE(data, bpl, y); + row_pointers[y] = data + y * bpl; } png_read_image(png_ptr, row_pointers); @@ -224,14 +319,27 @@ bool QPngHandler::read(QImage *image) png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); delete [] row_pointers; + // sanity check palette entries + if (color_type == PNG_COLOR_TYPE_PALETTE + && image->format() == QImage::Format_Indexed8) { + int color_table_size = image->colorCount(); + for (int y=0; y<(int)height; ++y) { + uchar *p = QFAST_SCAN_LINE(data, bpl, y); + uchar *end = p + width; + while (p < end) { + if (*p >= color_table_size) { + *p = 0; + } + ++p; + } + } + } + return true; } bool QPngHandler::write(const QImage &image) { - QImage copy = image.convertToFormat(image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32); - const int height = copy.height(); - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { return false; @@ -253,23 +361,51 @@ bool QPngHandler::write(const QImage &image) png_set_write_fn(png_ptr, (void*)this, qt_png_write, qt_png_flush); int color_type = 0; - if (copy.hasAlphaChannel()) { + if (image.colorCount()) { + color_type = PNG_COLOR_TYPE_PALETTE; + } else if (image.hasAlphaChannel()) { color_type = PNG_COLOR_TYPE_RGB_ALPHA; } else { color_type = PNG_COLOR_TYPE_RGB; } - png_set_IHDR(png_ptr, info_ptr, copy.width(), height, - 8, // per channel + png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), + image.depth() == 1 ? 1 : 8, // per channel color_type, 0, 0, 0); // sets #channels png_color_8 sig_bit; sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; - sig_bit.alpha = copy.hasAlphaChannel() ? 8 : 0; + sig_bit.alpha = image.hasAlphaChannel() ? 8 : 0; png_set_sBIT(png_ptr, info_ptr, &sig_bit); + if (image.format() == QImage::Format_MonoLSB) + png_set_packswap(png_ptr); + + if (image.colorCount()) { + // Paletted + int num_palette = qMin(256, image.colorCount()); + png_color palette[256]; + png_byte trans[256]; + int num_trans = 0; + for (int i=0; i 0 || copy.dotsPerMeterY() > 0) { + if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) { png_set_pHYs(png_ptr, info_ptr, - copy.dotsPerMeterX(), copy.dotsPerMeterY(), + image.dotsPerMeterX(), image.dotsPerMeterY(), PNG_RESOLUTION_METER); } png_write_info(png_ptr, info_ptr); - png_set_packing(png_ptr); + if (image.depth() != 1) + png_set_packing(png_ptr); - if (color_type == PNG_COLOR_TYPE_RGB) { + if (color_type == PNG_COLOR_TYPE_RGB) png_set_filler(png_ptr, 0, QFILLER_ORDER); - } - png_bytep* row_pointers = new png_bytep[height]; - for (int y=0; y