diff options
Diffstat (limited to 'src/gui/image/qpnghandler.cpp')
-rw-r--r-- | src/gui/image/qpnghandler.cpp | 108 |
1 files changed, 102 insertions, 6 deletions
diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index f1b25f07a7..4ab45337b0 100644 --- a/src/gui/image/qpnghandler.cpp +++ b/src/gui/image/qpnghandler.cpp @@ -42,6 +42,7 @@ #ifndef QT_NO_IMAGEFORMAT_PNG #include <qcoreapplication.h> +#include <qdebug.h> #include <qiodevice.h> #include <qimage.h> #include <qlist.h> @@ -50,6 +51,9 @@ #include <private/qimage_p.h> // for qt_getImageText +#include <qcolorspace.h> +#include <private/qcolorspace_p.h> + #include <png.h> #include <pngconf.h> @@ -96,9 +100,16 @@ 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(50), compression(50), 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; @@ -108,6 +119,8 @@ public: QString description; QSize scaledSize; QStringList readTexts; + QColorSpace colorSpace; + ColorSpaceState colorSpaceState; png_struct *png_ptr; png_info *info_ptr; @@ -226,11 +239,8 @@ 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 = 0; png_uint_32 height = 0; int bit_depth = 0; @@ -585,10 +595,63 @@ 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); + colorSpace = QColorSpace::fromIccProfile(QByteArray::fromRawData((const char *)profileData, profLen)); + if (!colorSpace.isValid()) { + qWarning() << "QPngHandler: Failed to parse ICC profile"; + } else { + QColorSpacePrivate *csD = QColorSpacePrivate::getWritable(colorSpace); + if (csD->description.isEmpty()) + csD->description = QString::fromLatin1((const char *)name); + 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) { + QColorSpacePrimaries primaries; + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_cHRM)) { + double white_x, white_y, red_x, red_y; + double green_x, green_y, blue_x, blue_y; + png_get_cHRM(png_ptr, info_ptr, + &white_x, &white_y, &red_x, &red_y, + &green_x, &green_y, &blue_x, &blue_y); + primaries.whitePoint = QPointF(white_x, white_y); + primaries.redPoint = QPointF(red_x, red_y); + primaries.greenPoint = QPointF(green_x, green_y); + primaries.bluePoint = QPointF(blue_x, blue_y); + } + if (primaries.areValid()) { + colorSpace = QColorSpace(primaries.whitePoint, primaries.redPoint, primaries.greenPoint, primaries.bluePoint, + QColorSpace::TransferFunction::Gamma, fileGamma); + } else { + colorSpace = QColorSpace(QColorSpace::Primaries::SRgb, + QColorSpace::TransferFunction::Gamma, fileGamma); + } + colorSpaceState = GammaChrm; + } } state = ReadHeader; @@ -613,8 +676,16 @@ 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); + colorSpace = colorSpace.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma); + 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); @@ -683,6 +754,9 @@ 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; } @@ -902,6 +976,26 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_i bpc, // per channel color_type, 0, 0, 0); // sets #channels +#ifdef PNG_iCCP_SUPPORTED + if (image.colorSpace().isValid()) { + QColorSpace cs = image.colorSpace(); + // Support the old gamma making it override transferfunction. + if (gamma != 0.0 && !qFuzzyCompare(cs.gamma(), 1.0f / gamma)) + cs = cs.withTransferFunction(QColorSpace::TransferFunction::Gamma, 1.0f / gamma); + QByteArray iccProfileName = QColorSpacePrivate::get(cs)->description.toLatin1(); + if (iccProfileName.isEmpty()) + iccProfileName = QByteArrayLiteral("Custom"); + QByteArray iccProfile = cs.iccProfile(); + png_set_iCCP(png_ptr, info_ptr, + #if PNG_LIBPNG_VER < 10500 + iccProfileName.data(), PNG_COMPRESSION_TYPE_BASE, iccProfile.data(), + #else + iccProfileName.constData(), PNG_COMPRESSION_TYPE_BASE, + (png_const_bytep)iccProfile.constData(), + #endif + iccProfile.length()); + } else +#endif if (gamma != 0.0) { png_set_gAMA(png_ptr, info_ptr, 1.0/gamma); } @@ -985,6 +1079,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_i if (color_type == PNG_COLOR_TYPE_RGB) { switch (image.format()) { case QImage::Format_RGB888: + case QImage::Format_BGR888: break; case QImage::Format_RGBX8888: case QImage::Format_RGBX64: @@ -1037,6 +1132,7 @@ bool QPNGImageWriter::writeImage(const QImage& image, volatile int compression_i case QImage::Format_RGB32: case QImage::Format_ARGB32: case QImage::Format_RGB888: + case QImage::Format_BGR888: case QImage::Format_RGBX8888: case QImage::Format_RGBA8888: case QImage::Format_RGBX64: |