diff options
Diffstat (limited to 'src/gui/image')
-rw-r--r-- | src/gui/image/qimage.cpp | 134 | ||||
-rw-r--r-- | src/gui/image/qimage.h | 10 | ||||
-rw-r--r-- | src/gui/image/qimage_conversions.cpp | 4 | ||||
-rw-r--r-- | src/gui/image/qimage_p.h | 6 | ||||
-rw-r--r-- | src/gui/image/qpixmapcache_p.h | 2 | ||||
-rw-r--r-- | src/gui/image/qpnghandler.cpp | 72 | ||||
-rw-r--r-- | src/gui/image/qppmhandler_p.h | 2 |
7 files changed, 216 insertions, 14 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index ebd9037e44..79a3e35935 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtGui module of the Qt Toolkit. @@ -38,8 +38,10 @@ ****************************************************************************/ #include "qimage.h" -#include "qdatastream.h" + #include "qbuffer.h" +#include "qdatastream.h" +#include "qcolortransform.h" #include "qmap.h" #include "qmatrix.h" #include "qtransform.h" @@ -54,6 +56,7 @@ #include <stdlib.h> #include <limits.h> #include <qpa/qplatformpixmap.h> +#include <private/qcolortransform_p.h> #include <private/qdrawhelper_p.h> #include <private/qmemrotate_p.h> #include <private/qimagescale_p.h> @@ -1103,6 +1106,7 @@ static void copyMetadata(QImageData *dst, const QImageData *src) // Doesn't copy colortable and alpha_clut, or offset. copyPhysicalMetadata(dst, src); dst->text = src->text; + dst->colorSpace = src->colorSpace; } static void copyMetadata(QImage *dst, const QImage &src) @@ -4949,6 +4953,132 @@ QTransform QImage::trueMatrix(const QTransform &matrix, int w, int h) return matrix * QTransform().translate(-delta.x(), -delta.y()); } +/*! + \since 5.14 + + Sets the image color space to \a colorSpace without performing any conversions on image data. + + \sa colorSpace() +*/ +void QImage::setColorSpace(const QColorSpace &colorSpace) +{ + if (!d) + return; + if (d->colorSpace == colorSpace) + return; + if (!isDetached()) // Detach only if shared, not for read-only data. + detach(); + d->colorSpace = colorSpace; +} + +/*! + \since 5.14 + + Converts the image to \a colorSpace. + + If the image has no valid color space, the method does nothing. + + \sa convertedToColorSpace(), setColorSpace() +*/ +void QImage::convertToColorSpace(const QColorSpace &colorSpace) +{ + if (!d) + return; + if (!d->colorSpace.isValid()) + return; + if (!colorSpace.isValid()) { + qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid"; + return; + } + detach(); + applyColorTransform(d->colorSpace.transformationToColorSpace(colorSpace)); + d->colorSpace = colorSpace; +} + +/*! + \since 5.14 + + Returns the image converted to \a colorSpace. + + If the image has no valid color space, a null QImage is returned. + + \sa convertToColorSpace() +*/ +QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace) const +{ + if (!d || !d->colorSpace.isValid() || !colorSpace.isValid()) + return QImage(); + QImage image = copy(); + image.convertToColorSpace(colorSpace); + return image; +} + +/*! + \since 5.14 + + Returns the color space of the image if a color space is defined. +*/ +QColorSpace QImage::colorSpace() const +{ + if (!d) + return QColorSpace::Undefined; + return d->colorSpace; +} + +/*! + \since 5.14 + + Applies the color transformation \a transform to all pixels in the image. +*/ +void QImage::applyColorTransform(const QColorTransform &transform) +{ + QImage::Format oldFormat = format(); + if (depth() > 32) { + if (format() != QImage::Format_RGBX64 && format() != QImage::Format_RGBA64 + && format() != QImage::Format_RGBA64_Premultiplied) + *this = std::move(*this).convertToFormat(QImage::Format_RGBA64); + } else if (format() != QImage::Format_ARGB32 && format() != QImage::Format_RGB32 + && format() != QImage::Format_ARGB32_Premultiplied) { + if (hasAlphaChannel()) + *this = std::move(*this).convertToFormat(QImage::Format_ARGB32); + else + *this = std::move(*this).convertToFormat(QImage::Format_RGB32); + } + + QColorTransformPrivate::TransformFlags flags = QColorTransformPrivate::Unpremultiplied; + switch (format()) { + case Format_ARGB32_Premultiplied: + case Format_RGBA64_Premultiplied: + flags = QColorTransformPrivate::Premultiplied; + break; + case Format_RGB32: + case Format_RGBX64: + flags = QColorTransformPrivate::InputOpaque; + break; + case Format_ARGB32: + case Format_RGBA64: + break; + default: + Q_UNREACHABLE(); + } + + if (depth() > 32) { + for (int i = 0; i < height(); ++i) { + QRgba64 *scanline = reinterpret_cast<QRgba64 *>(scanLine(i)); + transform.d_func()->apply(scanline, scanline, width(), flags); + } + } else { + for (int i = 0; i < height(); ++i) { + QRgb *scanline = reinterpret_cast<QRgb *>(scanLine(i)); + transform.d_func()->apply(scanline, scanline, width(), flags); + } + } + + if (oldFormat != format()) + *this = std::move(*this).convertToFormat(oldFormat); +} + + bool QImageData::convertInPlace(QImage::Format newFormat, Qt::ImageConversionFlags flags) { if (format == newFormat) diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h index 8335e117f2..af7e6988cb 100644 --- a/src/gui/image/qimage.h +++ b/src/gui/image/qimage.h @@ -61,9 +61,11 @@ Q_FORWARD_DECLARE_MUTABLE_CG_TYPE(CGImage); QT_BEGIN_NAMESPACE +class QColorSpace; +class QColorTransform; class QIODevice; -class QStringList; class QMatrix; +class QStringList; class QTransform; class QVariant; template <class T> class QList; @@ -300,6 +302,12 @@ public: #endif void invertPixels(InvertMode = InvertRgb); + QColorSpace colorSpace() const; + QImage convertedToColorSpace(const QColorSpace &) const; + void convertToColorSpace(const QColorSpace &); + void setColorSpace(const QColorSpace &); + + void applyColorTransform(const QColorTransform &transform); bool load(QIODevice *device, const char* format); bool load(const QString &fileName, const char *format = nullptr); diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 82ffb8af8b..837ac88470 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -39,7 +39,7 @@ #include <private/qdrawhelper_p.h> #include <private/qguiapplication_p.h> -#include <private/qcolorprofile_p.h> +#include <private/qcolortrclut_p.h> #include <private/qendian_p.h> #include <private/qsimd_p.h> #include <private/qimage_p.h> @@ -100,7 +100,7 @@ const uchar *qt_get_bitflip_array() void qGamma_correct_back_to_linear_cs(QImage *image) { - const QColorProfile *cp = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); + const QColorTrcLut *cp = QGuiApplicationPrivate::instance()->colorProfileForA32Text(); if (!cp) return; // gamma correct the pixels back to linear color space... diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h index d88ad2d1d2..9da6acd0a7 100644 --- a/src/gui/image/qimage_p.h +++ b/src/gui/image/qimage_p.h @@ -51,7 +51,9 @@ // We mean it. // +#include <QtGui/qcolorspace.h> #include <QtGui/private/qtguiglobal_p.h> +#include <QtGui/qimage.h> #include <QtCore/private/qnumeric_p.h> #include <QMap> @@ -65,7 +67,7 @@ struct Q_GUI_EXPORT QImageData { // internal image data QImageData(); ~QImageData(); static QImageData *create(const QSize &size, QImage::Format format); - static QImageData *create(uchar *data, int w, int h, int bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction = 0, void *cleanupInfo = 0); + static QImageData *create(uchar *data, int w, int h, int bpl, QImage::Format format, bool readOnly, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr); QAtomicInt ref; @@ -106,6 +108,8 @@ struct Q_GUI_EXPORT QImageData { // internal image data QPaintEngine *paintEngine; + QColorSpace colorSpace; + struct ImageSizeParameters { qsizetype bytesPerLine; qsizetype totalSize; diff --git a/src/gui/image/qpixmapcache_p.h b/src/gui/image/qpixmapcache_p.h index 3c57367514..ab8e2b7558 100644 --- a/src/gui/image/qpixmapcache_p.h +++ b/src/gui/image/qpixmapcache_p.h @@ -87,7 +87,7 @@ public: && !d->image.d->paintEngine->isActive()) { delete d->image.d->paintEngine; - d->image.d->paintEngine = 0; + d->image.d->paintEngine = nullptr; } } } diff --git a/src/gui/image/qpnghandler.cpp b/src/gui/image/qpnghandler.cpp index 140196004b..93635a051d 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,10 @@ #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> @@ -96,9 +101,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 +120,8 @@ public: QString description; QSize scaledSize; QStringList readTexts; + QColorSpace colorSpace; + ColorSpaceState colorSpaceState; png_struct *png_ptr; png_info *info_ptr; @@ -226,11 +240,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; png_uint_32 height; int bit_depth = 0; @@ -585,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; @@ -613,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); @@ -683,6 +740,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; } diff --git a/src/gui/image/qppmhandler_p.h b/src/gui/image/qppmhandler_p.h index f3c9d0f139..2f3811b759 100644 --- a/src/gui/image/qppmhandler_p.h +++ b/src/gui/image/qppmhandler_p.h @@ -71,7 +71,7 @@ public: QByteArray name() const override; #endif - static bool canRead(QIODevice *device, QByteArray *subType = 0); + static bool canRead(QIODevice *device, QByteArray *subType = nullptr); QVariant option(ImageOption option) const override; void setOption(ImageOption option, const QVariant &value) override; |