summaryrefslogtreecommitdiffstats
path: root/src/gui/image/qpnghandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/image/qpnghandler.cpp')
-rw-r--r--src/gui/image/qpnghandler.cpp157
1 files changed, 123 insertions, 34 deletions
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp
index 242d8cd63b..39d0807606 100644
--- a/src/gui/image/qpnghandler.cpp
+++ b/src/gui/image/qpnghandler.cpp
@@ -42,15 +42,19 @@
#ifndef QT_NO_IMAGEFORMAT_PNG
#include <qcoreapplication.h>
+#include <qdebug.h>
#include <qiodevice.h>
#include <qimage.h>
#include <qlist.h>
-#include <qtextcodec.h>
#include <qvariant.h>
#include <qvector.h>
#include <private/qimage_p.h> // for qt_getImageText
+#include <qcolorspace.h>
+#include <private/qcolorspace_p.h>
+#include <private/qicc_p.h>
+
#include <png.h>
#include <pngconf.h>
@@ -97,17 +101,27 @@ public:
ReadingEnd,
Error
};
+ // Defines the order of how the various ways of setting colorspace overrides eachother:
+ enum ColorSpaceState {
+ Undefined = 0,
+ GammaChrm = 1, // gAMA+cHRM chunks
+ Srgb = 2, // sRGB chunk
+ Icc = 3 // iCCP chunk
+ };
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), colorSpaceState(Undefined), 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;
+ QColorSpace colorSpace;
+ ColorSpaceState colorSpaceState;
png_struct *png_ptr;
png_info *info_ptr;
@@ -161,11 +175,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; }
@@ -226,21 +240,18 @@ void qpiw_flush_fn(png_structp /* png_ptr */)
}
static
-void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scaledSize, bool *doScaledRead, float screen_gamma=0.0, float file_gamma=0.0)
+void setup_qt(QImage& image, png_structp png_ptr, png_infop info_ptr, QSize scaledSize, bool *doScaledRead)
{
- if (screen_gamma != 0.0 && file_gamma != 0.0)
- png_set_gamma(png_ptr, 1.0f / screen_gamma, file_gamma);
-
- png_uint_32 width;
- png_uint_32 height;
- int bit_depth;
- int color_type;
+ png_uint_32 width = 0;
+ png_uint_32 height = 0;
+ int bit_depth = 0;
+ int color_type = 0;
png_bytep trans_alpha = 0;
png_color_16p trans_color_p = 0;
int num_trans;
png_colorp palette = 0;
int num_palette;
- int interlace_method;
+ int interlace_method = PNG_INTERLACE_LAST;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_method, 0, 0);
png_set_interlace_handling(png_ptr);
@@ -266,6 +277,18 @@ 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_channels(png_ptr, info_ptr) == 1
+ && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ if (image.size() != QSize(width, height) || image.format() != QImage::Format_Grayscale16) {
+ image = QImage(width, height, QImage::Format_Grayscale16);
+ if (image.isNull())
+ return;
+ }
+
+ png_read_update_info(png_ptr, info_ptr);
+ if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
+ png_set_swap(png_ptr);
} else if (bit_depth == 16) {
bool hasMask = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS);
if (!hasMask)
@@ -573,10 +596,45 @@ bool QPngHandlerPrivate::readPngHeader()
readPngTexts(info_ptr);
+#ifdef PNG_iCCP_SUPPORTED
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) {
+ png_charp name = nullptr;
+ int compressionType = 0;
+#if (PNG_LIBPNG_VER < 10500)
+ png_charp profileData = nullptr;
+#else
+ png_bytep profileData = nullptr;
+#endif
+ png_uint_32 profLen;
+ png_get_iCCP(png_ptr, info_ptr, &name, &compressionType, &profileData, &profLen);
+ if (!QIcc::fromIccProfile(QByteArray::fromRawData((const char *)profileData, profLen), &colorSpace)) {
+ qWarning() << "QPngHandler: Failed to parse ICC profile";
+ } else {
+ colorSpaceState = Icc;
+ }
+ }
+#endif
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) {
+ int rendering_intent = -1;
+ png_get_sRGB(png_ptr, info_ptr, &rendering_intent);
+ // We don't actually care about the rendering_intent, just that it is valid
+ if (rendering_intent >= 0 && rendering_intent <= 3 && colorSpaceState <= Srgb) {
+ colorSpace = QColorSpace::SRgb;
+ colorSpaceState = Srgb;
+ }
+ }
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) {
double file_gamma = 0.0;
png_get_gAMA(png_ptr, info_ptr, &file_gamma);
fileGamma = file_gamma;
+ if (fileGamma > 0.0f && colorSpaceState <= GammaChrm) {
+ QColorSpacePrivate *csPrivate = colorSpace.d_func();
+ csPrivate->gamut = QColorSpace::Gamut::SRgb;
+ csPrivate->transferFunction = QColorSpace::TransferFunction::Gamma;
+ csPrivate->gamma = fileGamma;
+ csPrivate->initialize();
+ colorSpaceState = GammaChrm;
+ }
}
state = ReadHeader;
@@ -601,8 +659,19 @@ bool QPngHandlerPrivate::readPngImage(QImage *outImage)
return false;
}
+ if (gamma != 0.0 && fileGamma != 0.0) {
+ // This configuration forces gamma correction and
+ // thus changes the output colorspace
+ png_set_gamma(png_ptr, 1.0f / gamma, fileGamma);
+ QColorSpacePrivate *csPrivate = colorSpace.d_func();
+ csPrivate->transferFunction = QColorSpace::TransferFunction::Gamma;
+ csPrivate->gamma = gamma;
+ csPrivate->initialize();
+ colorSpaceState = GammaChrm;
+ }
+
bool doScaledRead = false;
- setup_qt(*outImage, png_ptr, info_ptr, scaledSize, &doScaledRead, gamma, fileGamma);
+ setup_qt(*outImage, png_ptr, info_ptr, scaledSize, &doScaledRead);
if (outImage->isNull()) {
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
@@ -671,14 +740,17 @@ bool QPngHandlerPrivate::readPngImage(QImage *outImage)
if (scaledSize.isValid() && outImage->size() != scaledSize)
*outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ if (colorSpaceState > Undefined && colorSpace.isValid())
+ outImage->setColorSpace(colorSpace);
+
return true;
}
QImage::Format QPngHandlerPrivate::readImageFormat()
{
QImage::Format format = QImage::Format_Invalid;
- png_uint_32 width, height;
- int bit_depth, color_type;
+ png_uint_32 width = 0, height = 0;
+ int bit_depth = 0, color_type = 0;
png_colorp palette;
int num_palette;
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, 0, 0, 0);
@@ -687,7 +759,7 @@ QImage::Format QPngHandlerPrivate::readImageFormat()
if (bit_depth == 1 && png_get_channels(png_ptr, info_ptr) == 1) {
format = QImage::Format_Mono;
} else if (bit_depth == 16) {
- format = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? QImage::Format_RGBA64 : QImage::Format_RGBX64;
+ format = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? QImage::Format_RGBA64 : QImage::Format_Grayscale16;
} else if (bit_depth == 8 && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
format = QImage::Format_Grayscale8;
} else {
@@ -814,7 +886,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();
@@ -842,13 +914,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);
@@ -861,7 +933,8 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
else
color_type = PNG_COLOR_TYPE_PALETTE;
}
- else if (image.format() == QImage::Format_Grayscale8)
+ else if (image.format() == QImage::Format_Grayscale8
+ || image.format() == QImage::Format_Grayscale16)
color_type = PNG_COLOR_TYPE_GRAY;
else if (image.hasAlphaChannel())
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
@@ -877,6 +950,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
case QImage::Format_RGBX64:
case QImage::Format_RGBA64:
case QImage::Format_RGBA64_Premultiplied:
+ case QImage::Format_Grayscale16:
bpc = 16;
break;
default:
@@ -988,6 +1062,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
case QImage::Format_RGBX64:
case QImage::Format_RGBA64:
case QImage::Format_RGBA64_Premultiplied:
+ case QImage::Format_Grayscale16:
png_set_swap(png_ptr);
break;
default:
@@ -1018,6 +1093,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, c
case QImage::Format_MonoLSB:
case QImage::Format_Indexed8:
case QImage::Format_Grayscale8:
+ case QImage::Format_Grayscale16:
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
case QImage::Format_RGB888:
@@ -1067,15 +1143,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()
@@ -1122,7 +1204,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
@@ -1131,6 +1213,7 @@ bool QPngHandler::supportsOption(ImageOption option) const
|| option == Description
|| option == ImageFormat
|| option == Quality
+ || option == CompressionRatio
|| option == Size
|| option == ScaledSize;
}
@@ -1146,6 +1229,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)
@@ -1164,16 +1249,20 @@ 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)
d->scaledSize = value.toSize();
}
+#if QT_DEPRECATED_SINCE(5, 13)
QByteArray QPngHandler::name() const
{
return "png";
}
+#endif
QT_END_NAMESPACE