diff options
Diffstat (limited to 'src/gui/image/qpnghandler.cpp')
-rw-r--r-- | src/gui/image/qpnghandler.cpp | 204 |
1 files changed, 156 insertions, 48 deletions
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 53d9a8a49a..8ae03d5d38 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -98,12 +98,13 @@ public: }; QPngHandlerPrivate(QPngHandler *qq) - : gamma(0.0), fileGamma(0.0), quality(2), png_ptr(0), info_ptr(0), end_info(0), state(Ready), q(qq) + : gamma(0.0), fileGamma(0.0), quality(50), compression(50), png_ptr(0), info_ptr(0), end_info(0), state(Ready), q(qq) { } float gamma; float fileGamma; - int quality; + int quality; // quality is used for backward compatibility, maps to compression + int compression; QString description; QSize scaledSize; QStringList readTexts; @@ -160,11 +161,11 @@ public: void setGamma(float); bool writeImage(const QImage& img, int x, int y); - bool writeImage(const QImage& img, volatile int quality, const QString &description, int x, int y); + bool writeImage(const QImage& img, volatile int compression_in, const QString &description, int x, int y); bool writeImage(const QImage& img) { return writeImage(img, 0, 0); } - bool writeImage(const QImage& img, int quality, const QString &description) - { return writeImage(img, quality, description, 0, 0); } + bool writeImage(const QImage& img, int compression, const QString &description) + { return writeImage(img, compression, description, 0, 0); } QIODevice* device() { return dev; } @@ -244,7 +245,7 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal png_set_interlace_handling(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY) { - // Black & White or 8-bit grayscale + // Black & White or 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); @@ -265,19 +266,22 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal else if (g == 1) image.setColor(0, qRgba(255, 255, 255, 0)); } - } 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); + } else if (bit_depth == 16) { + bool hasMask = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS); + if (!hasMask) + png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER); + else + png_set_expand(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); + QImage::Format format = hasMask ? QImage::Format_RGBA64 : QImage::Format_RGBX64; + if (image.size() != QSize(width, height) || image.format() != format) { + image = QImage(width, height, format); if (image.isNull()) return; } - if (QSysInfo::ByteOrder == QSysInfo::BigEndian) - png_set_swap_alpha(png_ptr); - png_read_update_info(png_ptr, info_ptr); + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) + png_set_swap(png_ptr); } else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_expand(png_ptr); if (image.size() != QSize(width, height) || image.format() != QImage::Format_Grayscale8) { @@ -288,9 +292,7 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal png_read_update_info(png_ptr, info_ptr); } else { - if (bit_depth == 16) - png_set_strip_16(png_ptr); - else if (bit_depth < 8) + 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); @@ -351,6 +353,26 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal ); i++; } + // Qt==ARGB==Big(ARGB)==Little(BGRA) + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + png_set_bgr(png_ptr); + } + } else if (bit_depth == 16 && !(color_type & PNG_COLOR_MASK_PALETTE)) { + QImage::Format format = QImage::Format_RGBA64; + if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_filler(png_ptr, 0xffff, PNG_FILLER_AFTER); + format = QImage::Format_RGBX64; + } + if (!(color_type & PNG_COLOR_MASK_COLOR)) + png_set_gray_to_rgb(png_ptr); + 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); + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) + png_set_swap(png_ptr); } else { // 32-bit if (bit_depth == 16) @@ -387,12 +409,12 @@ void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scal if (QSysInfo::ByteOrder == QSysInfo::BigEndian) png_set_swap_alpha(png_ptr); - png_read_update_info(png_ptr, info_ptr); - } + // Qt==ARGB==Big(ARGB)==Little(BGRA) + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + png_set_bgr(png_ptr); + } - // Qt==ARGB==Big(ARGB)==Little(BGRA) - if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { - png_set_bgr(png_ptr); + png_read_update_info(png_ptr, info_ptr); } } @@ -661,11 +683,11 @@ QImage::Format QPngHandlerPrivate::readImageFormat() int num_palette; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0); if (color_type == PNG_COLOR_TYPE_GRAY) { - // Black & White or 8-bit grayscale + // Black & White or grayscale if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) { format = QImage::Format_Mono; - } else if (bit_depth == 16 && png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - format = QImage::Format_ARGB32; + } else if (bit_depth == 16) { + format = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? QImage::Format_RGBA64 : QImage::Format_RGBX64; } else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { format = QImage::Format_Grayscale8; } else { @@ -677,6 +699,10 @@ QImage::Format QPngHandlerPrivate::readImageFormat() { // 1-bit and 8-bit color format = bit_depth == 1 ? QImage::Format_Mono : QImage::Format_Indexed8; + } else if (bit_depth == 16 && !(color_type & PNG_COLOR_MASK_PALETTE)) { + format = QImage::Format_RGBA64; + if (!(color_type & PNG_COLOR_MASK_ALPHA) && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + format = QImage::Format_RGBX64; } else { // 32-bit format = QImage::Format_ARGB32; @@ -788,7 +814,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, int off_x, int off_y) return writeImage(image, -1, QString(), off_x, off_y); } -bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, const QString &description, +bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_in, const QString &description, int off_x_in, int off_y_in) { QPoint offset = image.offset(); @@ -816,13 +842,13 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c return false; } - int quality = quality_in; - if (quality >= 0) { - if (quality > 9) { - qWarning("PNG: Quality %d out of range", quality); - quality = 9; + int compression = compression_in; + if (compression >= 0) { + if (compression > 9) { + qWarning("PNG: Compression %d out of range", compression); + compression = 9; } - png_set_compression_level(png_ptr, quality); + png_set_compression_level(png_ptr, compression); } png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn); @@ -842,8 +868,24 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c else color_type = PNG_COLOR_TYPE_RGB; + int bpc = 0; + switch (image.format()) { + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + bpc = 1; + break; + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + bpc = 16; + break; + default: + bpc = 8; + break; + } + png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), - image.depth() == 1 ? 1 : 8, // per channel + bpc, // per channel color_type, 0, 0, 0); // sets #channels if (gamma != 0.0) { @@ -879,13 +921,31 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c // Swap ARGB to RGBA (normal PNG format) before saving on // BigEndian machines if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { - png_set_swap_alpha(png_ptr); + switch (image.format()) { + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + break; + default: + png_set_swap_alpha(png_ptr); + } } // Qt==ARGB==Big(ARGB)==Little(BGRA). But RGB888 is RGB regardless - if (QSysInfo::ByteOrder == QSysInfo::LittleEndian - && image.format() != QImage::Format_RGB888) { - png_set_bgr(png_ptr); + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + switch (image.format()) { + case QImage::Format_RGB888: + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + break; + default: + png_set_bgr(png_ptr); + } } if (off_x || off_y) { @@ -908,10 +968,32 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c if (image.depth() != 1) png_set_packing(png_ptr); - if (color_type == PNG_COLOR_TYPE_RGB && image.format() != QImage::Format_RGB888) - png_set_filler(png_ptr, 0, - QSysInfo::ByteOrder == QSysInfo::BigEndian ? - PNG_FILLER_BEFORE : PNG_FILLER_AFTER); + if (color_type == PNG_COLOR_TYPE_RGB) { + switch (image.format()) { + case QImage::Format_RGB888: + break; + case QImage::Format_RGBX8888: + case QImage::Format_RGBX64: + png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); + break; + default: + png_set_filler(png_ptr, 0, + QSysInfo::ByteOrder == QSysInfo::BigEndian ? + PNG_FILLER_BEFORE : PNG_FILLER_AFTER); + } + } + + if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) { + switch (image.format()) { + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: + case QImage::Format_RGBA64_Premultiplied: + png_set_swap(png_ptr); + break; + default: + break; + } + } if (looping >= 0 && frames_written == 0) { uchar data[13] = "NETSCAPE2.0"; @@ -939,6 +1021,10 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c case QImage::Format_RGB32: case QImage::Format_ARGB32: case QImage::Format_RGB888: + case QImage::Format_RGBX8888: + case QImage::Format_RGBA8888: + case QImage::Format_RGBX64: + case QImage::Format_RGBA64: { png_bytep* row_pointers = new png_bytep[height]; for (int y=0; y<height; y++) @@ -947,6 +1033,17 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c delete [] row_pointers; } break; + case QImage::Format_RGBA64_Premultiplied: + { + QImage row; + png_bytep row_pointers[1]; + for (int y=0; y<height; y++) { + row = image.copy(0, y, width, 1).convertToFormat(QImage::Format_RGBA64); + row_pointers[0] = const_cast<png_bytep>(row.constScanLine(0)); + png_write_rows(png_ptr, row_pointers, 1); + } + } + break; default: { QImage::Format fmt = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32; @@ -970,15 +1067,21 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c } static bool write_png_image(const QImage &image, QIODevice *device, - int quality, float gamma, const QString &description) + int compression, int quality, float gamma, const QString &description) { + // quality is used for backward compatibility, maps to compression + QPNGImageWriter writer(device); - if (quality >= 0) { - quality = qMin(quality, 100); - quality = (100-quality) * 9 / 91; // map [0,100] -> [9,0] - } + if (compression >= 0) + compression = qMin(compression, 100); + else if (quality >= 0) + compression = 100 - qMin(quality, 100); + + if (compression >= 0) + compression = (compression * 9) / 91; // map [0,100] -> [0,9] + writer.setGamma(gamma); - return writer.writeImage(image, quality, description); + return writer.writeImage(image, compression, description); } QPngHandler::QPngHandler() @@ -1025,7 +1128,7 @@ bool QPngHandler::read(QImage *image) bool QPngHandler::write(const QImage &image) { - return write_png_image(image, device(), d->quality, d->gamma, d->description); + return write_png_image(image, device(), d->compression, d->quality, d->gamma, d->description); } bool QPngHandler::supportsOption(ImageOption option) const @@ -1034,6 +1137,7 @@ bool QPngHandler::supportsOption(ImageOption option) const || option == Description || option == ImageFormat || option == Quality + || option == CompressionRatio || option == Size || option == ScaledSize; } @@ -1049,6 +1153,8 @@ QVariant QPngHandler::option(ImageOption option) const return d->gamma == 0.0 ? d->fileGamma : d->gamma; else if (option == Quality) return d->quality; + else if (option == CompressionRatio) + return d->compression; else if (option == Description) return d->description; else if (option == Size) @@ -1067,6 +1173,8 @@ void QPngHandler::setOption(ImageOption option, const QVariant &value) d->gamma = value.toFloat(); else if (option == Quality) d->quality = value.toInt(); + else if (option == CompressionRatio) + d->compression = value.toInt(); else if (option == Description) d->description = value.toString(); else if (option == ScaledSize) |