diff options
Diffstat (limited to 'src/gui/painting/qcolorspace.cpp')
-rw-r--r-- | src/gui/painting/qcolorspace.cpp | 600 |
1 files changed, 487 insertions, 113 deletions
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp index e1f8d0b63b..7a1d34a408 100644 --- a/src/gui/painting/qcolorspace.cpp +++ b/src/gui/painting/qcolorspace.cpp @@ -1,46 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qcolorspace.h" #include "qcolorspace_p.h" #include "qcolortransform.h" +#include "qcolorclut_p.h" #include "qcolormatrix_p.h" #include "qcolortransferfunction_p.h" #include "qcolortransform_p.h" @@ -54,9 +19,9 @@ QT_BEGIN_NAMESPACE -QBasicMutex QColorSpacePrivate::s_lutWriteLock; +Q_CONSTINIT QBasicMutex QColorSpacePrivate::s_lutWriteLock; -static QAtomicPointer<QColorSpacePrivate> s_predefinedColorspacePrivates[QColorSpace::ProPhotoRgb] = {}; +Q_CONSTINIT static QAtomicPointer<QColorSpacePrivate> s_predefinedColorspacePrivates[QColorSpace::ProPhotoRgb] = {}; static void cleanupPredefinedColorspaces() { for (QAtomicPointer<QColorSpacePrivate> &ptr : s_predefinedColorspacePrivates) { @@ -116,45 +81,19 @@ bool QColorSpacePrimaries::areValid() const QColorMatrix QColorSpacePrimaries::toXyzMatrix() const { // This converts to XYZ in some undefined scale. - QColorMatrix toXyz = { QColorVector(redPoint), - QColorVector(greenPoint), - QColorVector(bluePoint) }; + QColorMatrix toXyz = { QColorVector::fromXYChromaticity(redPoint), + QColorVector::fromXYChromaticity(greenPoint), + QColorVector::fromXYChromaticity(bluePoint) }; // Since the white point should be (1.0, 1.0, 1.0) in the // input, we can figure out the scale by using the // inverse conversion on the white point. - QColorVector wXyz(whitePoint); + const auto wXyz = QColorVector::fromXYChromaticity(whitePoint); QColorVector whiteScale = toXyz.inverted().map(wXyz); // Now we have scaled conversion to XYZ relative to the given whitepoint toXyz = toXyz * QColorMatrix::fromScale(whiteScale); - // But we want a conversion to XYZ relative to D50 - QColorVector wXyzD50 = QColorVector::D50(); - - if (wXyz != wXyzD50) { - // Do chromatic adaptation to map our white point to XYZ D50. - - // The Bradford method chromatic adaptation matrix: - QColorMatrix abrad = { { 0.8951f, -0.7502f, 0.0389f }, - { 0.2664f, 1.7135f, -0.0685f }, - { -0.1614f, 0.0367f, 1.0296f } }; - QColorMatrix abradinv = { { 0.9869929f, 0.4323053f, -0.0085287f }, - { -0.1470543f, 0.5183603f, 0.0400428f }, - { 0.1599627f, 0.0492912f, 0.9684867f } }; - - QColorVector srcCone = abrad.map(wXyz); - QColorVector dstCone = abrad.map(wXyzD50); - - QColorMatrix wToD50 = { { dstCone.x / srcCone.x, 0, 0 }, - { 0, dstCone.y / srcCone.y, 0 }, - { 0, 0, dstCone.z / srcCone.z } }; - - - QColorMatrix chromaticAdaptation = abradinv * (wToD50 * abrad); - toXyz = chromaticAdaptation * toXyz; - } - return toXyz; } @@ -164,6 +103,7 @@ QColorSpacePrivate::QColorSpacePrivate() QColorSpacePrivate::QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSpace) : namedColorSpace(namedColorSpace) + , colorModel(QColorSpace::ColorModel::Rgb) { switch (namedColorSpace) { case QColorSpace::SRgb: @@ -201,6 +141,7 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::NamedColorSpace namedColorSp QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction transferFunction, float gamma) : primaries(primaries) , transferFunction(transferFunction) + , colorModel(QColorSpace::ColorModel::Rgb) , gamma(gamma) { identifyColorSpace(); @@ -212,18 +153,50 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, float gamma) : primaries(QColorSpace::Primaries::Custom) , transferFunction(transferFunction) + , colorModel(QColorSpace::ColorModel::Rgb) , gamma(gamma) + , whitePoint(QColorVector::fromXYChromaticity(primaries.whitePoint)) { Q_ASSERT(primaries.areValid()); toXyz = primaries.toXyzMatrix(); - whitePoint = QColorVector(primaries.whitePoint); + chad = QColorMatrix::chromaticAdaptation(whitePoint); + toXyz = chad * toXyz; + identifyColorSpace(); setTransferFunction(); } +QColorSpacePrivate::QColorSpacePrivate(const QPointF &whitePoint, + QColorSpace::TransferFunction transferFunction, + float gamma) + : primaries(QColorSpace::Primaries::Custom) + , transferFunction(transferFunction) + , colorModel(QColorSpace::ColorModel::Gray) + , gamma(gamma) + , whitePoint(QColorVector::fromXYChromaticity(whitePoint)) +{ + chad = QColorMatrix::chromaticAdaptation(this->whitePoint); + toXyz = chad; + setTransferFunction(); +} + +QColorSpacePrivate::QColorSpacePrivate(const QPointF &whitePoint, const QList<uint16_t> &transferFunctionTable) + : primaries(QColorSpace::Primaries::Custom) + , transferFunction(QColorSpace::TransferFunction::Custom) + , colorModel(QColorSpace::ColorModel::Gray) + , gamma(0) + , whitePoint(QColorVector::fromXYChromaticity(whitePoint)) +{ + chad = QColorMatrix::chromaticAdaptation(this->whitePoint); + toXyz = chad; + setTransferFunctionTable(transferFunctionTable); + setTransferFunction(); +} + QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, const QList<uint16_t> &transferFunctionTable) : primaries(primaries) , transferFunction(QColorSpace::TransferFunction::Custom) + , colorModel(QColorSpace::ColorModel::Rgb) , gamma(0) { setTransferFunctionTable(transferFunctionTable); @@ -234,11 +207,14 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, const Q QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, const QList<uint16_t> &transferFunctionTable) : primaries(QColorSpace::Primaries::Custom) , transferFunction(QColorSpace::TransferFunction::Custom) + , colorModel(QColorSpace::ColorModel::Rgb) , gamma(0) + , whitePoint(QColorVector::fromXYChromaticity(primaries.whitePoint)) { Q_ASSERT(primaries.areValid()); toXyz = primaries.toXyzMatrix(); - whitePoint = QColorVector(primaries.whitePoint); + chad = QColorMatrix::chromaticAdaptation(whitePoint); + toXyz = chad * toXyz; setTransferFunctionTable(transferFunctionTable); identifyColorSpace(); initialize(); @@ -250,16 +226,18 @@ QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, const QList<uint16_t> &blueTransferFunctionTable) : primaries(QColorSpace::Primaries::Custom) , transferFunction(QColorSpace::TransferFunction::Custom) + , colorModel(QColorSpace::ColorModel::Rgb) , gamma(0) { Q_ASSERT(primaries.areValid()); toXyz = primaries.toXyzMatrix(); - whitePoint = QColorVector(primaries.whitePoint); + whitePoint = QColorVector::fromXYChromaticity(primaries.whitePoint); + chad = QColorMatrix::chromaticAdaptation(whitePoint); + toXyz = chad * toXyz; setTransferFunctionTables(redTransferFunctionTable, greenTransferFunctionTable, blueTransferFunctionTable); identifyColorSpace(); - setToXyzMatrix(); } void QColorSpacePrivate::identifyColorSpace() @@ -336,7 +314,9 @@ void QColorSpacePrivate::setToXyzMatrix() } QColorSpacePrimaries colorSpacePrimaries(primaries); toXyz = colorSpacePrimaries.toXyzMatrix(); - whitePoint = QColorVector(colorSpacePrimaries.whitePoint); + whitePoint = QColorVector::fromXYChromaticity(colorSpacePrimaries.whitePoint); + chad = QColorMatrix::chromaticAdaptation(whitePoint); + toXyz = chad * toXyz; } void QColorSpacePrivate::setTransferFunctionTable(const QList<uint16_t> &transferFunctionTable) @@ -351,7 +331,7 @@ void QColorSpacePrivate::setTransferFunctionTable(const QList<uint16_t> &transfe QColorTransferFunction curve; if (table.asColorTransferFunction(&curve)) { // Table recognized as a specific curve - if (curve.isLinear()) { + if (curve.isIdentity()) { transferFunction = QColorSpace::TransferFunction::Linear; gamma = 1.0f; } else if (curve.isSRgb()) { @@ -450,10 +430,50 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace combined.d = ptr; ptr->colorSpaceIn = this; ptr->colorSpaceOut = out; - ptr->colorMatrix = out->toXyz.inverted() * toXyz; + if (isThreeComponentMatrix()) + ptr->colorMatrix = toXyz; + else + ptr->colorMatrix = QColorMatrix::identity(); + if (out->isThreeComponentMatrix()) + ptr->colorMatrix = out->toXyz.inverted() * ptr->colorMatrix; + if (ptr->isIdentity()) + return QColorTransform(); return combined; } +QColorTransform QColorSpacePrivate::transformationToXYZ() const +{ + QColorTransform transform; + auto ptr = new QColorTransformPrivate; + transform.d = ptr; + ptr->colorSpaceIn = this; + ptr->colorSpaceOut = this; + // Convert to XYZ relative to our white point, not the regular D50 white point. + if (isThreeComponentMatrix()) + ptr->colorMatrix = QColorMatrix::chromaticAdaptation(whitePoint).inverted() * toXyz; + else + ptr->colorMatrix = QColorMatrix::identity(); + return transform; +} + +bool QColorSpacePrivate::isThreeComponentMatrix() const +{ + return transformModel == QColorSpace::TransformModel::ThreeComponentMatrix; +} + +void QColorSpacePrivate::clearElementListProcessingForEdit() +{ + Q_ASSERT(transformModel == QColorSpace::TransformModel::ElementListProcessing); + Q_ASSERT(primaries == QColorSpace::Primaries::Custom); + Q_ASSERT(transferFunction == QColorSpace::TransferFunction::Custom); + + transformModel = QColorSpace::TransformModel::ThreeComponentMatrix; + colorModel = QColorSpace::ColorModel::Rgb; + isPcsLab = false; + mAB.clear(); + mBA.clear(); +} + /*! \class QColorSpace \brief The QColorSpace class provides a color space abstraction. @@ -476,9 +496,10 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace A color space can generally speaking be conceived as a combination of set of primary colors and a transfer function. The primaries defines the axes of the color space, and the transfer function how values are mapped on the axes. - The primaries are defined by three primary colors that represent exactly how red, green, - and blue look in this particular color space, and a white color that represents where - and how bright pure white is. The range of colors expressable by the primary colors is + The primaries are for ColorModel::Rgb color spaces defined by three primary colors that + represent exactly how red, green, and blue look in this particular color space, and a white + color that represents where and how bright pure white is. For grayscale color spaces, only + a single white primary is needed. The range of colors expressible by the primary colors is called the gamut, and a color space that can represent a wider range of colors is also known as a wide-gamut color space. @@ -532,6 +553,35 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace */ /*! + \enum QColorSpace::TransformModel + \since 6.8 + + Defines the processing model used for color space transforms. + + \value ThreeComponentMatrix The transform consist of a matrix calculated from primaries and set of transfer functions + for each color channel. This is very fast and used by all predefined color spaces. Any color space on this form is + reversible and always both valid sources and targets. + \value ElementListProcessing The transforms are one or two lists of processing elements that can do many things, + each list only process either to the connection color space or from it. This is very flexible, but rather + slow, and can only be set by reading ICC profiles (See \l fromIccProfile()). Since the two lists are + separate a color space on this form can be a valid source, but not necessarily also a valid target. When changing + either primaries or transfer function on a color space on this type it will reset to an empty ThreeComponentMatrix form. +*/ + +/*! + \enum QColorSpace::ColorModel + \since 6.8 + + Defines the color model used by the color space data. + + \value Undefined No color model + \value Rgb An RGB color model with red, green, and blue colors. Can apply to RGB and grayscale data. + \value Gray A gray scale color model. Can only apply to grayscale data. + \value Cmyk Can only represent color data defined with cyan, magenta, yellow, and black colors. + In effect only QImage::Format_CMYK32. Note Cmyk color spaces will be TransformModel::ElementListProcessing. +*/ + +/*! \fn QColorSpace::QColorSpace() Creates a new colorspace object that represents an undefined and invalid colorspace. @@ -594,6 +644,28 @@ QColorSpace::QColorSpace(QColorSpace::Primaries gamut, const QList<uint16_t> &tr } /*! + Creates a custom grayscale color space with the white point \a whitePoint, using the transfer function \a transferFunction and + optionally \a gamma. + + \since 6.8 +*/ +QColorSpace::QColorSpace(const QPointF &whitePoint, TransferFunction transferFunction, float gamma) + : d_ptr(new QColorSpacePrivate(whitePoint, transferFunction, gamma)) +{ +} + +/*! + Creates a custom grayscale color space with white point \a whitePoint, and using the custom transfer function described by + \a transferFunctionTable. + + \since 6.8 +*/ +QColorSpace::QColorSpace(const QPointF &whitePoint, const QList<uint16_t> &transferFunctionTable) + : d_ptr(new QColorSpacePrivate(whitePoint, transferFunctionTable)) +{ +} + +/*! Creates a custom colorspace with a primaries based on the chromaticities of the primary colors \a whitePoint, \a redPoint, \a greenPoint and \a bluePoint, and using the transfer function \a transferFunction and optionally \a gamma. */ @@ -708,7 +780,10 @@ void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunc if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma) return; detach(); - d_ptr->description.clear(); + if (d_ptr->transformModel == TransformModel::ElementListProcessing) + d_ptr->clearElementListProcessingForEdit(); + d_ptr->iccProfile = {}; + d_ptr->description = QString(); d_ptr->transferFunction = transferFunction; d_ptr->gamma = gamma; d_ptr->identifyColorSpace(); @@ -729,7 +804,10 @@ void QColorSpace::setTransferFunction(const QList<uint16_t> &transferFunctionTab return; } detach(); - d_ptr->description.clear(); + if (d_ptr->transformModel == TransformModel::ElementListProcessing) + d_ptr->clearElementListProcessingForEdit(); + d_ptr->iccProfile = {}; + d_ptr->description = QString(); d_ptr->setTransferFunctionTable(transferFunctionTable); d_ptr->gamma = 0; d_ptr->identifyColorSpace(); @@ -756,7 +834,10 @@ void QColorSpace::setTransferFunctions(const QList<uint16_t> &redTransferFunctio return; } detach(); - d_ptr->description.clear(); + if (d_ptr->transformModel == TransformModel::ElementListProcessing) + d_ptr->clearElementListProcessingForEdit(); + d_ptr->iccProfile = {}; + d_ptr->description = QString(); d_ptr->setTransferFunctionTables(redTransferFunctionTable, greenTransferFunctionTable, blueTransferFunctionTable); @@ -772,7 +853,7 @@ void QColorSpace::setTransferFunctions(const QList<uint16_t> &redTransferFunctio */ QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) const { - if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom) + if (!isValid() || transferFunction == TransferFunction::Custom) return *this; if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma) return *this; @@ -832,8 +913,12 @@ void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId) if (d_ptr->primaries == primariesId) return; detach(); - d_ptr->description.clear(); + if (d_ptr->transformModel == TransformModel::ElementListProcessing) + d_ptr->clearElementListProcessingForEdit(); + d_ptr->iccProfile = {}; + d_ptr->description = QString(); d_ptr->primaries = primariesId; + d_ptr->colorModel = QColorSpace::ColorModel::Rgb; d_ptr->identifyColorSpace(); d_ptr->setToXyzMatrix(); } @@ -855,17 +940,100 @@ void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoin return; } QColorMatrix toXyz = primaries.toXyzMatrix(); - if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz) + QColorMatrix chad = QColorMatrix::chromaticAdaptation(QColorVector::fromXYChromaticity(whitePoint)); + toXyz = chad * toXyz; + if (QColorVector::fromXYChromaticity(primaries.whitePoint) == d_ptr->whitePoint + && toXyz == d_ptr->toXyz && chad == d_ptr->chad) return; detach(); - d_ptr->description.clear(); + if (d_ptr->transformModel == TransformModel::ElementListProcessing) + d_ptr->clearElementListProcessingForEdit(); + d_ptr->iccProfile = {}; + d_ptr->description = QString(); d_ptr->primaries = QColorSpace::Primaries::Custom; + d_ptr->colorModel = QColorSpace::ColorModel::Rgb; d_ptr->toXyz = toXyz; - d_ptr->whitePoint = QColorVector(primaries.whitePoint); + d_ptr->chad = chad; + d_ptr->whitePoint = QColorVector::fromXYChromaticity(primaries.whitePoint); d_ptr->identifyColorSpace(); } /*! + Returns the white point used for this color space. Returns a null QPointF if not defined. + + \since 6.8 +*/ +QPointF QColorSpace::whitePoint() const +{ + if (Q_UNLIKELY(!d_ptr)) + return QPointF(); + return d_ptr->whitePoint.toChromaticity(); +} + +/*! + Sets the white point to used for this color space to \a whitePoint. + + \since 6.8 +*/ +void QColorSpace::setWhitePoint(const QPointF &whitePoint) +{ + if (Q_UNLIKELY(!d_ptr)) { + d_ptr = new QColorSpacePrivate(whitePoint, TransferFunction::Custom, 0.0f); + return; + } + if (QColorVector::fromXYChromaticity(whitePoint) == d_ptr->whitePoint) + return; + detach(); + if (d_ptr->transformModel == TransformModel::ElementListProcessing) + d_ptr->clearElementListProcessingForEdit(); + d_ptr->iccProfile = {}; + d_ptr->description = QString(); + d_ptr->primaries = QColorSpace::Primaries::Custom; + // An RGB color model stays RGB, a gray stays gray, but an undefined one can now be considered gray + if (d_ptr->colorModel == QColorSpace::ColorModel::Undefined) + d_ptr->colorModel = QColorSpace::ColorModel::Gray; + QColorVector wXyz(QColorVector::fromXYChromaticity(whitePoint)); + if (d_ptr->transformModel == QColorSpace::TransformModel::ThreeComponentMatrix) { + if (d_ptr->colorModel == QColorSpace::ColorModel::Rgb) { + // Rescale toXyz to new whitepoint + QColorMatrix rawToXyz = d_ptr->chad.inverted() * d_ptr->toXyz; + QColorVector whiteScale = rawToXyz.inverted().map(wXyz); + rawToXyz = rawToXyz * QColorMatrix::fromScale(whiteScale); + d_ptr->chad = QColorMatrix::chromaticAdaptation(wXyz); + d_ptr->toXyz = d_ptr->chad * rawToXyz; + } else if (d_ptr->colorModel == QColorSpace::ColorModel::Gray) { + d_ptr->chad = d_ptr->toXyz = QColorMatrix::chromaticAdaptation(wXyz); + } + } + d_ptr->whitePoint = wXyz; + d_ptr->identifyColorSpace(); +} + +/*! + Returns the transfrom processing model used for this color space. + + \since 6.8 +*/ +QColorSpace::TransformModel QColorSpace::transformModel() const noexcept +{ + if (Q_UNLIKELY(!d_ptr)) + return QColorSpace::TransformModel::ThreeComponentMatrix; + return d_ptr->transformModel; +} + +/*! + Returns the color model this color space can represent + + \since 6.8 +*/ +QColorSpace::ColorModel QColorSpace::colorModel() const noexcept +{ + if (Q_UNLIKELY(!d_ptr)) + return QColorSpace::ColorModel::Undefined; + return d_ptr->colorModel; +} + +/*! \internal */ void QColorSpace::detach() @@ -903,7 +1071,7 @@ QByteArray QColorSpace::iccProfile() const Creates a QColorSpace from ICC profile \a iccProfile. \note Not all ICC profiles are supported. QColorSpace only supports - RGB-XYZ ICC profiles that are three-component matrix-based. + RGB or Gray ICC profiles. If the ICC profile is not supported an invalid QColorSpace is returned where you can still read the original ICC profile using iccProfile(). @@ -921,13 +1089,51 @@ QColorSpace QColorSpace::fromIccProfile(const QByteArray &iccProfile) } /*! - Returns \c true if the color space is valid. + Returns \c true if the color space is valid. For a color space with \c TransformModel::ThreeComponentMatrix + that means both primaries and transfer functions set, and implies isValidTarget(). + For a color space with \c TransformModel::ElementListProcessing it means it has a valid source transform, to + check if it also a valid target color space use isValidTarget(). + + \sa isValidTarget() */ bool QColorSpace::isValid() const noexcept { - return d_ptr - && d_ptr->toXyz.isValid() - && d_ptr->trc[0].isValid() && d_ptr->trc[1].isValid() && d_ptr->trc[2].isValid(); + if (!d_ptr) + return false; + return d_ptr->isValid(); +} + +/*! + \since 6.8 + + Returns \c true if the color space is a valid target color space. +*/ +bool QColorSpace::isValidTarget() const noexcept +{ + if (!d_ptr) + return false; + if (!d_ptr->isThreeComponentMatrix()) + return !d_ptr->mBA.isEmpty(); + return d_ptr->isValid(); +} + +/*! + \internal +*/ +bool QColorSpacePrivate::isValid() const noexcept +{ + if (!isThreeComponentMatrix()) + return !mAB.isEmpty(); + if (!toXyz.isValid()) + return false; + if (colorModel == QColorSpace::ColorModel::Gray) { + if (!trc[0].isValid()) + return false; + } else { + if (!trc[0].isValid() || !trc[1].isValid() || !trc[2].isValid()) + return false; + } + return true; } /*! @@ -944,6 +1150,53 @@ bool QColorSpace::isValid() const noexcept otherwise returns \c false */ +static bool compareElement(const QColorSpacePrivate::TransferElement &element, + const QColorSpacePrivate::TransferElement &other) +{ + return element.trc[0] == other.trc[0] + && element.trc[1] == other.trc[1] + && element.trc[2] == other.trc[2] + && element.trc[3] == other.trc[3]; +} + +static bool compareElement(const QColorMatrix &element, + const QColorMatrix &other) +{ + return element == other; +} + +static bool compareElement(const QColorVector &element, + const QColorVector &other) +{ + return element == other; +} + +static bool compareElement(const QColorCLUT &element, + const QColorCLUT &other) +{ + if (element.gridPointsX != other.gridPointsX) + return false; + if (element.gridPointsY != other.gridPointsY) + return false; + if (element.gridPointsZ != other.gridPointsZ) + return false; + if (element.gridPointsW != other.gridPointsW) + return false; + if (element.table.size() != other.table.size()) + return false; + for (qsizetype i = 0; i < element.table.size(); ++i) { + if (element.table[i] != other.table[i]) + return false; + } + return true; +} + +template<typename T> +static bool compareElements(const T &element, const QColorSpacePrivate::Element &other) +{ + return compareElement(element, std::get<T>(other)); +} + /*! \internal */ @@ -951,43 +1204,89 @@ bool QColorSpace::equals(const QColorSpace &other) const { if (d_ptr == other.d_ptr) return true; - if (!d_ptr || !other.d_ptr) + if (!d_ptr) + return false; + return d_ptr->equals(other.d_ptr.constData()); +} + +/*! + \internal +*/ +bool QColorSpacePrivate::equals(const QColorSpacePrivate *other) const +{ + if (!other) return false; - if (d_ptr->namedColorSpace && other.d_ptr->namedColorSpace) - return d_ptr->namedColorSpace == other.d_ptr->namedColorSpace; + if (namedColorSpace && other->namedColorSpace) + return namedColorSpace == other->namedColorSpace; const bool valid1 = isValid(); - const bool valid2 = other.isValid(); + const bool valid2 = other->isValid(); if (valid1 != valid2) return false; if (!valid1 && !valid2) { - if (!d_ptr->iccProfile.isEmpty() || !other.d_ptr->iccProfile.isEmpty()) - return d_ptr->iccProfile == other.d_ptr->iccProfile; + if (!iccProfile.isEmpty() || !other->iccProfile.isEmpty()) + return iccProfile == other->iccProfile; + return false; } // At this point one or both color spaces are unknown, and must be compared in detail instead - if (primaries() != QColorSpace::Primaries::Custom && other.primaries() != QColorSpace::Primaries::Custom) { - if (primaries() != other.primaries()) + if (transformModel != other->transformModel) + return false; + + if (!isThreeComponentMatrix()) { + if (isPcsLab != other->isPcsLab) + return false; + if (colorModel != other->colorModel) + return false; + if (mAB.count() != other->mAB.count()) + return false; + if (mBA.count() != other->mBA.count()) + return false; + + // Compare element types + for (qsizetype i = 0; i < mAB.count(); ++i) { + if (mAB[i].index() != other->mAB[i].index()) + return false; + } + for (qsizetype i = 0; i < mBA.count(); ++i) { + if (mBA[i].index() != other->mBA[i].index()) + return false; + } + + // Compare element contents + for (qsizetype i = 0; i < mAB.count(); ++i) { + if (!std::visit([&](auto &&elm) { return compareElements(elm, other->mAB[i]); }, mAB[i])) + return false; + } + for (qsizetype i = 0; i < mBA.count(); ++i) { + if (!std::visit([&](auto &&elm) { return compareElements(elm, other->mBA[i]); }, mBA[i])) + return false; + } + + return true; + } + + if (primaries != QColorSpace::Primaries::Custom && other->primaries != QColorSpace::Primaries::Custom) { + if (primaries != other->primaries) return false; } else { - if (d_ptr->toXyz != other.d_ptr->toXyz) + if (toXyz != other->toXyz) return false; } - if (transferFunction() != QColorSpace::TransferFunction::Custom && - other.transferFunction() != QColorSpace::TransferFunction::Custom) { - if (transferFunction() != other.transferFunction()) + if (transferFunction != QColorSpace::TransferFunction::Custom && other->transferFunction != QColorSpace::TransferFunction::Custom) { + if (transferFunction != other->transferFunction) return false; - if (transferFunction() == QColorSpace::TransferFunction::Gamma) - return (qAbs(gamma() - other.gamma()) <= (1.0f / 512.0f)); + if (transferFunction == QColorSpace::TransferFunction::Gamma) + return (qAbs(gamma - other->gamma) <= (1.0f / 512.0f)); return true; } - if (d_ptr->trc[0] != other.d_ptr->trc[0] || - d_ptr->trc[1] != other.d_ptr->trc[1] || - d_ptr->trc[2] != other.d_ptr->trc[2]) + if (trc[0] != other->trc[0] || + trc[1] != other->trc[1] || + trc[2] != other->trc[2]) return false; return true; @@ -999,8 +1298,15 @@ bool QColorSpace::equals(const QColorSpace &other) const */ QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &colorspace) const { - if (!isValid() || !colorspace.isValid()) + if (!isValid()) + return QColorTransform(); + + if (*this == colorspace) return QColorTransform(); + if (!colorspace.isValidTarget()) { + qWarning() << "QColorSpace::transformationToColorSpace: colorspace not a valid target"; + return QColorTransform(); + } return d_ptr->transformationToColorSpace(colorspace.d_ptr.get()); } @@ -1014,6 +1320,36 @@ QColorSpace::operator QVariant() const return QVariant::fromValue(*this); } +/*! + Returns the name or short description. If a description hasn't been given + in setDescription(), the original name of the profile is returned if the + profile is unmodified, a guessed name is returned if the profile has been + recognized as a known color space, otherwise an empty string is returned. + + \since 6.2 +*/ +QString QColorSpace::description() const noexcept +{ + if (d_ptr) + return d_ptr->userDescription.isEmpty() ? d_ptr->description : d_ptr->userDescription; + return QString(); +} + +/*! + Sets the name or short description of the color space to \a description. + + If set to empty description() will return original or guessed descriptions + instead. + + \since 6.2 +*/ +void QColorSpace::setDescription(const QString &description) +{ + detach(); + d_ptr->iccProfile = {}; + d_ptr->userDescription = description; +} + /***************************************************************************** QColorSpace stream functions *****************************************************************************/ @@ -1053,6 +1389,28 @@ QDataStream &operator>>(QDataStream &s, QColorSpace &colorSpace) #endif // QT_NO_DATASTREAM #ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QColorSpacePrivate::TransferElement &) +{ + return dbg << ":Transfer"; +} +QDebug operator<<(QDebug dbg, const QColorMatrix &) +{ + return dbg << ":Matrix"; +} +QDebug operator<<(QDebug dbg, const QColorVector &) +{ + return dbg << ":Offset"; +} +QDebug operator<<(QDebug dbg, const QColorCLUT &) +{ + return dbg << ":CLUT"; +} +QDebug operator<<(QDebug dbg, const QList<QColorSpacePrivate::Element> &elements) +{ + for (auto &&element : elements) + std::visit([&](auto &&elm) { dbg << elm; }, element); + return dbg; +} QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace) { QDebugStateSaver saver(dbg); @@ -1061,8 +1419,22 @@ QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace) if (colorSpace.d_ptr) { if (colorSpace.d_ptr->namedColorSpace) dbg << colorSpace.d_ptr->namedColorSpace << ", "; - dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction(); - dbg << ", gamma=" << colorSpace.gamma(); + if (!colorSpace.isValid()) { + dbg << "Invalid"; + if (!colorSpace.d_ptr->iccProfile.isEmpty()) + dbg << " with profile data"; + } else if (colorSpace.d_ptr->isThreeComponentMatrix()) { + dbg << colorSpace.primaries() << ", " << colorSpace.transferFunction(); + dbg << ", gamma=" << colorSpace.gamma(); + } else { + if (colorSpace.d_ptr->isPcsLab) + dbg << "PCSLab, "; + else + dbg << "PCSXYZ, "; + dbg << "A2B" << colorSpace.d_ptr->mAB; + if (!colorSpace.d_ptr->mBA.isEmpty()) + dbg << ", B2A" << colorSpace.d_ptr->mBA; + } } dbg << ')'; return dbg; @@ -1070,3 +1442,5 @@ QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace) #endif QT_END_NAMESPACE + +#include "moc_qcolorspace.cpp" |