diff options
Diffstat (limited to 'src/gui/painting')
-rw-r--r-- | src/gui/painting/qbackingstore.cpp | 2 | ||||
-rw-r--r-- | src/gui/painting/qcolorspace.cpp | 279 | ||||
-rw-r--r-- | src/gui/painting/qcolorspace.h | 30 | ||||
-rw-r--r-- | src/gui/painting/qcolorspace_p.h | 17 | ||||
-rw-r--r-- | src/gui/painting/qcolortransform.cpp | 27 | ||||
-rw-r--r-- | src/gui/painting/qcolortransform.h | 29 | ||||
-rw-r--r-- | src/gui/painting/qcolortransform_p.h | 4 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper.cpp | 27 | ||||
-rw-r--r-- | src/gui/painting/qicc.cpp | 245 | ||||
-rw-r--r-- | src/gui/painting/qpagedpaintdevice.cpp | 3 | ||||
-rw-r--r-- | src/gui/painting/qpagedpaintdevice.h | 1 | ||||
-rw-r--r-- | src/gui/painting/qpaintengine_raster.cpp | 6 | ||||
-rw-r--r-- | src/gui/painting/qpainterpath.cpp | 9 | ||||
-rw-r--r-- | src/gui/painting/qpainterpath_p.h | 2 | ||||
-rw-r--r-- | src/gui/painting/qpdf.cpp | 12 | ||||
-rw-r--r-- | src/gui/painting/qpdf_p.h | 1 | ||||
-rw-r--r-- | src/gui/painting/qpdfwriter.cpp | 8 | ||||
-rw-r--r-- | src/gui/painting/qtextureglyphcache.cpp | 2 | ||||
-rw-r--r-- | src/gui/painting/qtextureglyphcache_p.h | 8 |
19 files changed, 481 insertions, 231 deletions
diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp index 3fab903c4d..3240b83451 100644 --- a/src/gui/painting/qbackingstore.cpp +++ b/src/gui/painting/qbackingstore.cpp @@ -186,7 +186,7 @@ QPaintDevice *QBackingStore::paintDevice() void QBackingStore::endPaint() { if (paintDevice()->paintingActive()) - qWarning() << "QBackingStore::endPaint() called with active painter on backingstore paint device"; + qWarning("QBackingStore::endPaint() called with active painter; did you forget to destroy it or call QPainter::end() on it?"); handle()->endPaint(); } diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp index 8d3bbbe412..043a951521 100644 --- a/src/gui/painting/qcolorspace.cpp +++ b/src/gui/painting/qcolorspace.cpp @@ -55,34 +55,34 @@ QT_BEGIN_NAMESPACE QBasicMutex QColorSpacePrivate::s_lutWriteLock; -QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Gamut gamut) +QColorSpacePrimaries::QColorSpacePrimaries(QColorSpace::Primaries primaries) { - switch (gamut) { - case QColorSpace::Gamut::SRgb: + switch (primaries) { + case QColorSpace::Primaries::SRgb: redPoint = QPointF(0.640, 0.330); greenPoint = QPointF(0.300, 0.600); bluePoint = QPointF(0.150, 0.060); whitePoint = QColorVector::D65Chromaticity(); break; - case QColorSpace::Gamut::DciP3D65: + case QColorSpace::Primaries::DciP3D65: redPoint = QPointF(0.680, 0.320); greenPoint = QPointF(0.265, 0.690); bluePoint = QPointF(0.150, 0.060); whitePoint = QColorVector::D65Chromaticity(); break; - case QColorSpace::Gamut::Bt2020: + case QColorSpace::Primaries::Bt2020: redPoint = QPointF(0.708, 0.292); greenPoint = QPointF(0.190, 0.797); bluePoint = QPointF(0.131, 0.046); whitePoint = QColorVector::D65Chromaticity(); break; - case QColorSpace::Gamut::AdobeRgb: + case QColorSpace::Primaries::AdobeRgb: redPoint = QPointF(0.640, 0.330); greenPoint = QPointF(0.210, 0.710); bluePoint = QPointF(0.150, 0.060); whitePoint = QColorVector::D65Chromaticity(); break; - case QColorSpace::Gamut::ProPhotoRgb: + case QColorSpace::Primaries::ProPhotoRgb: redPoint = QPointF(0.7347, 0.2653); greenPoint = QPointF(0.1596, 0.8404); bluePoint = QPointF(0.0366, 0.0001); @@ -153,7 +153,7 @@ QColorMatrix QColorSpacePrimaries::toXyzMatrix() const QColorSpacePrivate::QColorSpacePrivate() : id(QColorSpace::Unknown) - , gamut(QColorSpace::Gamut::Custom) + , primaries(QColorSpace::Primaries::Custom) , transferFunction(QColorSpace::TransferFunction::Custom) , gamma(0.0f) , whitePoint(QColorVector::null()) @@ -163,48 +163,43 @@ QColorSpacePrivate::QColorSpacePrivate() QColorSpacePrivate::QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId) : id(colorSpaceId) + , gamma(0.0f) { switch (colorSpaceId) { case QColorSpace::Undefined: - gamut = QColorSpace::Gamut::Custom; + primaries = QColorSpace::Primaries::Custom; transferFunction = QColorSpace::TransferFunction::Custom; - gamma = 0.0f; description = QStringLiteral("Undefined"); break; case QColorSpace::SRgb: - gamut = QColorSpace::Gamut::SRgb; + primaries = QColorSpace::Primaries::SRgb; transferFunction = QColorSpace::TransferFunction::SRgb; - gamma = 2.31f; // ? description = QStringLiteral("sRGB"); break; case QColorSpace::SRgbLinear: - gamut = QColorSpace::Gamut::SRgb; + primaries = QColorSpace::Primaries::SRgb; transferFunction = QColorSpace::TransferFunction::Linear; - gamma = 1.0f; description = QStringLiteral("Linear sRGB"); break; case QColorSpace::AdobeRgb: - gamut = QColorSpace::Gamut::AdobeRgb; + primaries = QColorSpace::Primaries::AdobeRgb; transferFunction = QColorSpace::TransferFunction::Gamma; gamma = 2.19921875f; // Not quite 2.2, see https://www.adobe.com/digitalimag/pdfs/AdobeRGB1998.pdf description = QStringLiteral("Adobe RGB"); break; case QColorSpace::DisplayP3: - gamut = QColorSpace::Gamut::DciP3D65; + primaries = QColorSpace::Primaries::DciP3D65; transferFunction = QColorSpace::TransferFunction::SRgb; - gamma = 2.31f; // ? description = QStringLiteral("Display P3"); break; case QColorSpace::ProPhotoRgb: - gamut = QColorSpace::Gamut::ProPhotoRgb; + primaries = QColorSpace::Primaries::ProPhotoRgb; transferFunction = QColorSpace::TransferFunction::ProPhotoRgb; - gamma = 1.8f; description = QStringLiteral("ProPhoto RGB"); break; case QColorSpace::Bt2020: - gamut = QColorSpace::Gamut::Bt2020; + primaries = QColorSpace::Primaries::Bt2020; transferFunction = QColorSpace::TransferFunction::Bt2020; - gamma = 2.1f; // ? description = QStringLiteral("BT.2020"); break; case QColorSpace::Unknown: @@ -216,8 +211,8 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId) initialize(); } -QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma) - : gamut(gamut) +QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma) + : primaries(primaries) , transferFunction(fun) , gamma(gamma) { @@ -229,74 +224,81 @@ QColorSpacePrivate::QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::Tr QColorSpacePrivate::QColorSpacePrivate(const QColorSpacePrimaries &primaries, QColorSpace::TransferFunction fun, float gamma) - : gamut(QColorSpace::Gamut::Custom) + : primaries(QColorSpace::Primaries::Custom) , transferFunction(fun) , gamma(gamma) { Q_ASSERT(primaries.areValid()); toXyz = primaries.toXyzMatrix(); whitePoint = QColorVector(primaries.whitePoint); - if (!identifyColorSpace()) - id = QColorSpace::Unknown; + identifyColorSpace(); setTransferFunction(); } bool QColorSpacePrivate::identifyColorSpace() { - switch (gamut) { - case QColorSpace::Gamut::SRgb: + switch (primaries) { + case QColorSpace::Primaries::SRgb: if (transferFunction == QColorSpace::TransferFunction::SRgb) { id = QColorSpace::SRgb; - description = QStringLiteral("sRGB"); + if (description.isEmpty()) + description = QStringLiteral("sRGB"); return true; } if (transferFunction == QColorSpace::TransferFunction::Linear) { id = QColorSpace::SRgbLinear; - description = QStringLiteral("Linear sRGB"); + if (description.isEmpty()) + description = QStringLiteral("Linear sRGB"); return true; } break; - case QColorSpace::Gamut::AdobeRgb: + case QColorSpace::Primaries::AdobeRgb: if (transferFunction == QColorSpace::TransferFunction::Gamma) { if (qAbs(gamma - 2.19921875f) < (1/1024.0f)) { id = QColorSpace::AdobeRgb; - description = QStringLiteral("Adobe RGB"); + if (description.isEmpty()) + description = QStringLiteral("Adobe RGB"); return true; } } break; - case QColorSpace::Gamut::DciP3D65: + case QColorSpace::Primaries::DciP3D65: if (transferFunction == QColorSpace::TransferFunction::SRgb) { id = QColorSpace::DisplayP3; - description = QStringLiteral("Display P3"); + if (description.isEmpty()) + description = QStringLiteral("Display P3"); return true; } break; - case QColorSpace::Gamut::ProPhotoRgb: + case QColorSpace::Primaries::ProPhotoRgb: if (transferFunction == QColorSpace::TransferFunction::ProPhotoRgb) { id = QColorSpace::ProPhotoRgb; - description = QStringLiteral("ProPhoto RGB"); + if (description.isEmpty()) + description = QStringLiteral("ProPhoto RGB"); return true; } if (transferFunction == QColorSpace::TransferFunction::Gamma) { // ProPhoto RGB's curve is effectively gamma 1.8 for 8bit precision. if (qAbs(gamma - 1.8f) < (1/1024.0f)) { id = QColorSpace::ProPhotoRgb; - description = QStringLiteral("ProPhoto RGB"); + if (description.isEmpty()) + description = QStringLiteral("ProPhoto RGB"); return true; } } break; - case QColorSpace::Gamut::Bt2020: + case QColorSpace::Primaries::Bt2020: if (transferFunction == QColorSpace::TransferFunction::Bt2020) { id = QColorSpace::Bt2020; - description = QStringLiteral("BT.2020"); + if (description.isEmpty()) + description = QStringLiteral("BT.2020"); return true; } break; default: break; } + id = QColorSpace::Unknown; return false; } @@ -308,14 +310,14 @@ void QColorSpacePrivate::initialize() void QColorSpacePrivate::setToXyzMatrix() { - if (gamut == QColorSpace::Gamut::Custom) { + if (primaries == QColorSpace::Primaries::Custom) { toXyz = QColorMatrix::null(); whitePoint = QColorVector::D50(); return; } - QColorSpacePrimaries primaries(gamut); - toXyz = primaries.toXyzMatrix(); - whitePoint = QColorVector(primaries.whitePoint); + QColorSpacePrimaries colorSpacePrimaries(primaries); + toXyz = colorSpacePrimaries.toXyzMatrix(); + whitePoint = QColorVector(colorSpacePrimaries.whitePoint); } void QColorSpacePrivate::setTransferFunction() @@ -324,6 +326,8 @@ void QColorSpacePrivate::setTransferFunction() case QColorSpace::TransferFunction::Linear: trc[0].m_type = QColorTrc::Type::Function; trc[0].m_fun = QColorTransferFunction(); + if (qFuzzyIsNull(gamma)) + gamma = 1.0f; break; case QColorSpace::TransferFunction::Gamma: trc[0].m_type = QColorTrc::Type::Function; @@ -332,14 +336,20 @@ void QColorSpacePrivate::setTransferFunction() case QColorSpace::TransferFunction::SRgb: trc[0].m_type = QColorTrc::Type::Function; trc[0].m_fun = QColorTransferFunction::fromSRgb(); + if (qFuzzyIsNull(gamma)) + gamma = 2.31f; break; case QColorSpace::TransferFunction::ProPhotoRgb: trc[0].m_type = QColorTrc::Type::Function; trc[0].m_fun = QColorTransferFunction::fromProPhotoRgb(); + if (qFuzzyIsNull(gamma)) + gamma = 1.8f; break; case QColorSpace::TransferFunction::Bt2020: trc[0].m_type = QColorTrc::Type::Function; trc[0].m_fun = QColorTransferFunction::fromBt2020(); + if (qFuzzyIsNull(gamma)) + gamma = 1.961f; break; case QColorSpace::TransferFunction::Custom: break; @@ -355,10 +365,12 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace { Q_ASSERT(out); QColorTransform combined; - combined.d_ptr.reset(new QColorTransformPrivate); - combined.d_ptr->colorSpaceIn = this; - combined.d_ptr->colorSpaceOut = out; - combined.d_ptr->colorMatrix = out->toXyz.inverted() * toXyz; + auto ptr = new QColorTransformPrivate; + combined.d = ptr; + combined.d->ref.ref(); + ptr->colorSpaceIn = this; + ptr->colorSpaceOut = out; + ptr->colorMatrix = out->toXyz.inverted() * toXyz; return combined; } @@ -381,12 +393,14 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace QColorSpace can also represent color spaces defined by ICC profiles or embedded in images, that do not otherwise fit the predefined color spaces. - A color space can generally speaking be conceived as a combination of a transfer - function and a gamut. The gamut defines which colors the color space can represent. - A color space that can represent a wider range of colors is also known as a - wide-gamut color space. The gamut is 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. + 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 + called the gamut, and a color space that can represent a wider range of colors is also + known as a wide-gamut color space. The transfer function or gamma curve determines how each component in the color space is encoded. These are used because human perception does not operate @@ -418,16 +432,16 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace */ /*! - \enum QColorSpace::Gamut + \enum QColorSpace::Primaries - Predefined gamuts, or sets of primary colors. + Predefined sets of primary colors. - \value Custom The gamut is undefined or does not match any predefined sets. - \value SRgb The sRGB gamut - \value AdobeRgb The Adobe RGB gamut - \value DciP3D65 The DCI-P3 gamut with the D65 whitepoint - \value ProPhotoRgb The ProPhoto RGB gamut with the D50 whitepoint - \value Bt2020 The BT.2020 gamut + \value Custom The primaries are undefined or does not match any predefined sets. + \value SRgb The sRGB primaries + \value AdobeRgb The Adobe RGB primaries + \value DciP3D65 The DCI-P3 primaries with the D65 whitepoint + \value ProPhotoRgb The ProPhoto RGB primaries with the D50 whitepoint + \value Bt2020 The BT.2020 primaries */ /*! @@ -463,25 +477,25 @@ QColorSpace::QColorSpace(QColorSpace::ColorSpaceId colorSpaceId) } /*! - Creates a custom color space with the gamut \a gamut, using the transfer function \a fun and + Creates a custom color space with the primaries \a primaries, using the transfer function \a fun and optionally \a gamma. */ -QColorSpace::QColorSpace(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma) - : d_ptr(new QColorSpacePrivate(gamut, fun, gamma)) +QColorSpace::QColorSpace(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma) + : d_ptr(new QColorSpacePrivate(primaries, fun, gamma)) { } /*! - Creates a custom color space with the gamut \a gamut, using a gamma transfer function of + Creates a custom color space with the primaries \a primaries, using a gamma transfer function of \a gamma. */ -QColorSpace::QColorSpace(QColorSpace::Gamut gamut, float gamma) - : d_ptr(new QColorSpacePrivate(gamut, TransferFunction::Gamma, gamma)) +QColorSpace::QColorSpace(QColorSpace::Primaries primaries, float gamma) + : d_ptr(new QColorSpacePrivate(primaries, TransferFunction::Gamma, gamma)) { } /*! - Creates a custom colorspace with a gamut based on the chromaticities of the primary colors \a whitePoint, + 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 fun and optionally \a gamma. */ QColorSpace::QColorSpace(const QPointF &whitePoint, const QPointF &redPoint, @@ -501,7 +515,7 @@ QColorSpace::~QColorSpace() { } -QColorSpace::QColorSpace(QColorSpace &&colorSpace) +QColorSpace::QColorSpace(QColorSpace &&colorSpace) noexcept : d_ptr(std::move(colorSpace.d_ptr)) { } @@ -511,7 +525,7 @@ QColorSpace::QColorSpace(const QColorSpace &colorSpace) { } -QColorSpace &QColorSpace::operator=(QColorSpace &&colorSpace) +QColorSpace &QColorSpace::operator=(QColorSpace &&colorSpace) noexcept { d_ptr = std::move(colorSpace.d_ptr); return *this; @@ -523,6 +537,12 @@ QColorSpace &QColorSpace::operator=(const QColorSpace &colorSpace) return *this; } +/*! \fn void QColorSpace::swap(QColorSpace &other) + + Swaps color space \a other with this color space. This operation is very fast and + never fails. +*/ + /*! Returns the id of the predefined color space this object represents or \c Unknown if it doesn't match any of them. @@ -533,19 +553,21 @@ QColorSpace::ColorSpaceId QColorSpace::colorSpaceId() const noexcept } /*! - Returns the predefined gamut of the color space - or \c Gamut::Custom if it doesn't match any of them. + Returns the predefined primaries of the color space + or \c primaries::Custom if it doesn't match any of them. */ -QColorSpace::Gamut QColorSpace::gamut() const noexcept +QColorSpace::Primaries QColorSpace::primaries() const noexcept { - return d_ptr->gamut; + if (Q_UNLIKELY(!d_ptr)) + return QColorSpace::Primaries::Custom; + return d_ptr->primaries; } /*! Returns the predefined transfer function of the color space or \c TransferFunction::Custom if it doesn't match any of them. - \sa gamma() + \sa gamma(), setTransferFunction(), withTransferFunction() */ QColorSpace::TransferFunction QColorSpace::transferFunction() const noexcept { @@ -565,6 +587,91 @@ float QColorSpace::gamma() const noexcept } /*! + Sets the transfer function to \a transferFunction and \a gamma. + + \note This also changes colorSpaceId(). + + \sa transferFunction(), gamma(), withTransferFunction() +*/ +void QColorSpace::setTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) +{ + if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom) + return; + if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma) + return; + d_ptr.detach(); + d_ptr->description.clear(); + d_ptr->transferFunction = transferFunction; + d_ptr->gamma = gamma; + d_ptr->identifyColorSpace(); + d_ptr->setTransferFunction(); +} + +/*! + Returns a copy of this color space, except using the transfer function + \a transferFunction and \a gamma. + + \sa transferFunction(), gamma(), setTransferFunction() +*/ +QColorSpace QColorSpace::withTransferFunction(QColorSpace::TransferFunction transferFunction, float gamma) const +{ + if (!isValid() || transferFunction == QColorSpace::TransferFunction::Custom) + return *this; + if (d_ptr->transferFunction == transferFunction && d_ptr->gamma == gamma) + return *this; + QColorSpace out(*this); + out.setTransferFunction(transferFunction, gamma); + return out; +} + +/*! + Sets the primaries to those of the \a primariesId set. + + \note This also changes colorSpaceId(). + + \sa primaries() +*/ +void QColorSpace::setPrimaries(QColorSpace::Primaries primariesId) +{ + if (!isValid() || primariesId == QColorSpace::Primaries::Custom) + return; + if (d_ptr->primaries == primariesId) + return; + d_ptr.detach(); + d_ptr->description.clear(); + d_ptr->primaries = primariesId; + d_ptr->identifyColorSpace(); + d_ptr->setToXyzMatrix(); +} + +/*! + Set primaries to the chromaticities of \a whitePoint, \a redPoint, \a greenPoint + and \a bluePoint. + + \note This also changes colorSpaceId(). + + \sa primaries() +*/ +void QColorSpace::setPrimaries(const QPointF &whitePoint, const QPointF &redPoint, + const QPointF &greenPoint, const QPointF &bluePoint) +{ + if (!isValid()) + return; + QColorSpacePrimaries primaries(whitePoint, redPoint, greenPoint, bluePoint); + if (!primaries.areValid()) + return; + QColorMatrix toXyz = primaries.toXyzMatrix(); + if (QColorVector(primaries.whitePoint) == d_ptr->whitePoint && toXyz == d_ptr->toXyz) + return; + d_ptr.detach(); + d_ptr->description.clear(); + d_ptr->primaries = QColorSpace::Primaries::Custom; + d_ptr->toXyz = toXyz; + d_ptr->whitePoint = QColorVector(primaries.whitePoint); + d_ptr->identifyColorSpace(); +} + +/*! Returns an ICC profile representing the color space. If the color space was generated from an ICC profile, that profile @@ -631,8 +738,8 @@ bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2) if (colorSpace1.colorSpaceId() != QColorSpace::Unknown && colorSpace2.colorSpaceId() != QColorSpace::Unknown) return colorSpace1.colorSpaceId() == colorSpace2.colorSpaceId(); - if (colorSpace1.gamut() != QColorSpace::Gamut::Custom && colorSpace2.gamut() != QColorSpace::Gamut::Custom) { - if (colorSpace1.gamut() != colorSpace2.gamut()) + if (colorSpace1.primaries() != QColorSpace::Primaries::Custom && colorSpace2.primaries() != QColorSpace::Primaries::Custom) { + if (colorSpace1.primaries() != colorSpace2.primaries()) return false; } else { if (colorSpace1.d_ptr->toXyz != colorSpace2.d_ptr->toXyz) @@ -644,7 +751,7 @@ bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2) if (colorSpace1.transferFunction() != colorSpace2.transferFunction()) return false; if (colorSpace1.transferFunction() == QColorSpace::TransferFunction::Gamma) - return colorSpace1.gamma() == colorSpace2.gamma(); + return (qAbs(colorSpace1.gamma() - colorSpace2.gamma()) <= (1.0f / 512.0f)); return true; } @@ -676,20 +783,6 @@ QColorTransform QColorSpace::transformationToColorSpace(const QColorSpace &color return d_ptr->transformationToColorSpace(colorspace.d_ptr.constData()); } -/*! - \internal -*/ -QColorSpacePrivate *QColorSpace::d_func() -{ - d_ptr.detach(); - return d_ptr.data(); -} - -/*! - \fn const QColorSpacePrivate* QColorSpacePrivate::d_func() const - \internal -*/ - /***************************************************************************** QColorSpace stream functions *****************************************************************************/ @@ -734,7 +827,7 @@ QDebug operator<<(QDebug dbg, const QColorSpace &colorSpace) QDebugStateSaver saver(dbg); dbg.nospace(); dbg << "QColorSpace("; - dbg << colorSpace.colorSpaceId() << ", " << colorSpace.gamut() << ", " << colorSpace.transferFunction(); + dbg << colorSpace.colorSpaceId() << ", " << colorSpace.primaries() << ", " << colorSpace.transferFunction(); dbg << ", gamma=" << colorSpace.gamma(); dbg << ')'; return dbg; diff --git a/src/gui/painting/qcolorspace.h b/src/gui/painting/qcolorspace.h index 56676826a9..a7c1091911 100644 --- a/src/gui/painting/qcolorspace.h +++ b/src/gui/painting/qcolorspace.h @@ -63,7 +63,7 @@ public: Bt2020, }; Q_ENUM(ColorSpaceId) - enum class Gamut { + enum class Primaries { Custom = 0, SRgb, AdobeRgb, @@ -71,7 +71,7 @@ public: ProPhotoRgb, Bt2020, }; - Q_ENUM(Gamut) + Q_ENUM(Primaries) enum class TransferFunction { Custom = 0, Linear, @@ -83,23 +83,33 @@ public: Q_ENUM(TransferFunction) QColorSpace(ColorSpaceId colorSpaceId = Undefined); - QColorSpace(Gamut gamut, TransferFunction fun, float gamma = 0.0f); - QColorSpace(Gamut gamut, float gamma); + QColorSpace(Primaries primaries, TransferFunction fun, float gamma = 0.0f); + QColorSpace(Primaries primaries, float gamma); QColorSpace(const QPointF &whitePoint, const QPointF &redPoint, const QPointF &greenPoint, const QPointF &bluePoint, TransferFunction fun, float gamma = 0.0f); ~QColorSpace(); - QColorSpace(QColorSpace &&colorSpace); + QColorSpace(QColorSpace &&colorSpace) noexcept; QColorSpace(const QColorSpace &colorSpace); - QColorSpace &operator=(QColorSpace &&colorSpace); + QColorSpace &operator=(QColorSpace &&colorSpace) noexcept; QColorSpace &operator=(const QColorSpace &colorSpace); + void swap(QColorSpace &colorSpace) noexcept + { qSwap(d_ptr, colorSpace.d_ptr); } + ColorSpaceId colorSpaceId() const noexcept; - Gamut gamut() const noexcept; + Primaries primaries() const noexcept; TransferFunction transferFunction() const noexcept; float gamma() const noexcept; + void setTransferFunction(TransferFunction transferFunction, float gamma = 0.0f); + QColorSpace withTransferFunction(TransferFunction transferFunction, float gamma = 0.0f) const; + + void setPrimaries(Primaries primariesId); + void setPrimaries(const QPointF &whitePoint, const QPointF &redPoint, + const QPointF &greenPoint, const QPointF &bluePoint); + bool isValid() const noexcept; friend Q_GUI_EXPORT bool operator==(const QColorSpace &colorSpace1, const QColorSpace &colorSpace2); @@ -110,11 +120,9 @@ public: QColorTransform transformationToColorSpace(const QColorSpace &colorspace) const; - QColorSpacePrivate *d_func(); - inline const QColorSpacePrivate *d_func() const { return d_ptr.constData(); } private: - friend class QColorSpacePrivate; + Q_DECLARE_PRIVATE(QColorSpace) QExplicitlySharedDataPointer<QColorSpacePrivate> d_ptr; }; @@ -124,6 +132,8 @@ inline bool operator!=(const QColorSpace &colorSpace1, const QColorSpace &colorS return !(colorSpace1 == colorSpace2); } +Q_DECLARE_SHARED(QColorSpace) + // QColorSpace stream functions #if !defined(QT_NO_DATASTREAM) Q_GUI_EXPORT QDataStream &operator<<(QDataStream &, const QColorSpace &); diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h index 95e0655d0c..2a40a0cfd8 100644 --- a/src/gui/painting/qcolorspace_p.h +++ b/src/gui/painting/qcolorspace_p.h @@ -66,7 +66,7 @@ class Q_GUI_EXPORT QColorSpacePrimaries { public: QColorSpacePrimaries() = default; - QColorSpacePrimaries(QColorSpace::Gamut gamut); + QColorSpacePrimaries(QColorSpace::Primaries primaries); QColorSpacePrimaries(QPointF whitePoint, QPointF redPoint, QPointF greenPoint, @@ -91,10 +91,21 @@ class QColorSpacePrivate : public QSharedData public: QColorSpacePrivate(); QColorSpacePrivate(QColorSpace::ColorSpaceId colorSpaceId); - QColorSpacePrivate(QColorSpace::Gamut gamut, QColorSpace::TransferFunction fun, float gamma); + QColorSpacePrivate(QColorSpace::Primaries primaries, QColorSpace::TransferFunction fun, float gamma); QColorSpacePrivate(const QColorSpacePrimaries &primaries, QColorSpace::TransferFunction fun, float gamma); QColorSpacePrivate(const QColorSpacePrivate &other) = default; + static QColorSpacePrivate *getWritable(QColorSpace &colorSpace) + { + colorSpace.d_ptr.detach(); + return colorSpace.d_ptr.data(); + } + + static const QColorSpacePrivate *get(const QColorSpace &colorSpace) + { + return colorSpace.d_ptr.data(); + } + void initialize(); void setToXyzMatrix(); void setTransferFunction(); @@ -102,7 +113,7 @@ public: QColorTransform transformationToColorSpace(const QColorSpacePrivate *out) const; QColorSpace::ColorSpaceId id; - QColorSpace::Gamut gamut; + QColorSpace::Primaries primaries; QColorSpace::TransferFunction transferFunction; float gamma; QColorVector whitePoint; diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp index de08bf4221..53fd1dfbaa 100644 --- a/src/gui/painting/qcolortransform.cpp +++ b/src/gui/painting/qcolortransform.cpp @@ -134,8 +134,18 @@ void QColorTransformPrivate::updateLutsOut() const */ -QColorTransform::~QColorTransform() noexcept +QColorTransform::QColorTransform(const QColorTransform &colorTransform) noexcept + : d(colorTransform.d) { + if (d) + d->ref.ref(); +} + + +QColorTransform::~QColorTransform() +{ + if (d && !d->ref.deref()) + delete d; } /*! @@ -143,11 +153,10 @@ QColorTransform::~QColorTransform() noexcept The input should be opaque or unpremultiplied. */ -QRgb QColorTransform::map(const QRgb &argb) const +QRgb QColorTransform::map(QRgb argb) const { - if (!d_ptr) + if (!d) return argb; - Q_D(const QColorTransform); constexpr float f = 1.0f / 255.0f; QColorVector c = { qRed(argb) * f, qGreen(argb) * f, qBlue(argb) * f }; c.x = d->colorSpaceIn->trc[0].apply(c.x); @@ -175,11 +184,10 @@ QRgb QColorTransform::map(const QRgb &argb) const The input should be opaque or unpremultiplied. */ -QRgba64 QColorTransform::map(const QRgba64 &rgba64) const +QRgba64 QColorTransform::map(QRgba64 rgba64) const { - if (!d_ptr) + if (!d) return rgba64; - Q_D(const QColorTransform); constexpr float f = 1.0f / 65535.0f; QColorVector c = { rgba64.red() * f, rgba64.green() * f, rgba64.blue() * f }; c.x = d->colorSpaceIn->trc[0].apply(c.x); @@ -208,9 +216,8 @@ QRgba64 QColorTransform::map(const QRgba64 &rgba64) const */ QColor QColorTransform::map(const QColor &color) const { - if (!d_ptr) + if (!d) return color; - Q_D(const QColorTransform); QColor clr = color; if (color.spec() != QColor::ExtendedRgb || color.spec() != QColor::Rgb) clr = clr.toRgb(); @@ -228,7 +235,7 @@ QColor QColorTransform::map(const QColor &color) const c = d->colorMatrix.map(c); bool inGamut = c.x >= 0.0f && c.x <= 1.0f && c.y >= 0.0f && c.y <= 1.0f && c.z >= 0.0f && c.z <= 1.0f; if (inGamut) { - if (d_ptr->colorSpaceOut->lut.generated.loadAcquire()) { + if (d->colorSpaceOut->lut.generated.loadAcquire()) { c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x); c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y); c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z); diff --git a/src/gui/painting/qcolortransform.h b/src/gui/painting/qcolortransform.h index 9274387b97..5fb51739a7 100644 --- a/src/gui/painting/qcolortransform.h +++ b/src/gui/painting/qcolortransform.h @@ -51,43 +51,42 @@ class QRgba64; class QColorSpacePrivate; class QColorTransformPrivate; -class Q_GUI_EXPORT QColorTransform +class QColorTransform { public: - QColorTransform() noexcept : d_ptr(nullptr) { } - ~QColorTransform() noexcept; - QColorTransform(const QColorTransform &colorTransform) noexcept - : d_ptr(colorTransform.d_ptr) - { } + QColorTransform() noexcept : d(nullptr) { } + Q_GUI_EXPORT ~QColorTransform(); + Q_GUI_EXPORT QColorTransform(const QColorTransform &colorTransform) noexcept; QColorTransform(QColorTransform &&colorTransform) noexcept - : d_ptr(std::move(colorTransform.d_ptr)) + : d{qExchange(colorTransform.d, nullptr)} { } QColorTransform &operator=(const QColorTransform &other) noexcept { - d_ptr = other.d_ptr; + QColorTransform{other}.swap(*this); return *this; } QColorTransform &operator=(QColorTransform &&other) noexcept { - d_ptr = std::move(other.d_ptr); + QColorTransform{std::move(other)}.swap(*this); return *this; } - bool isNull() const { return d_ptr.isNull(); } + void swap(QColorTransform &other) noexcept { qSwap(d, other.d); } - QRgb map(const QRgb &argb) const; - QRgba64 map(const QRgba64 &rgba64) const; - QColor map(const QColor &color) const; + Q_GUI_EXPORT QRgb map(QRgb argb) const; + Q_GUI_EXPORT QRgba64 map(QRgba64 rgba64) const; + Q_GUI_EXPORT QColor map(const QColor &color) const; private: friend class QColorSpace; friend class QColorSpacePrivate; friend class QImage; - Q_DECLARE_PRIVATE(QColorTransform) - QSharedPointer<QColorTransformPrivate> d_ptr; + const QColorTransformPrivate *d; }; +Q_DECLARE_SHARED(QColorTransform) + QT_END_NAMESPACE #endif // QCOLORTRANSFORM_H diff --git a/src/gui/painting/qcolortransform_p.h b/src/gui/painting/qcolortransform_p.h index 74a1e7fe0a..5d7116248d 100644 --- a/src/gui/painting/qcolortransform_p.h +++ b/src/gui/painting/qcolortransform_p.h @@ -54,9 +54,11 @@ #include "qcolormatrix_p.h" #include "qcolorspace_p.h" +#include <QtCore/qshareddata.h> + QT_BEGIN_NAMESPACE -class QColorTransformPrivate +class QColorTransformPrivate : public QSharedData { public: QColorMatrix colorMatrix; diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 006befea22..674f52678d 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -2053,6 +2053,23 @@ inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v) } } +static inline bool canUseFastMatrixPath(const qreal cx, const qreal cy, const qsizetype length, const QSpanData *data) +{ + if (Q_UNLIKELY(!data->fast_matrix)) + return false; + + qreal fx = (data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale; + qreal fy = (data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale; + qreal minc = std::min(fx, fy); + qreal maxc = std::max(fx, fy); + fx += std::trunc(data->m11 * fixed_scale) * length; + fy += std::trunc(data->m12 * fixed_scale) * length; + minc = std::min(minc, std::min(fx, fy)); + maxc = std::max(maxc, std::max(fx, fy)); + + return minc >= std::numeric_limits<int>::min() && maxc <= std::numeric_limits<int>::max(); +} + template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T> static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *data, int y, int x, int length) @@ -2070,7 +2087,7 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat // When templated 'fetch' should be inlined at compile time: const FetchPixelFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : FetchPixelFunc(fetchPixel<bpp>); - if (data->fast_matrix) { + if (canUseFastMatrixPath(cx, cy, length, data)) { // The increment pr x in the scanline int fdx = (int)(data->m11 * fixed_scale); int fdy = (int)(data->m12 * fixed_scale); @@ -3026,7 +3043,7 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c uint *end = buffer + length; uint *b = buffer; - if (data->fast_matrix) { + if (canUseFastMatrixPath(cx, cy, length, data)) { // The increment pr x in the scanline int fdx = (int)(data->m11 * fixed_scale); int fdy = (int)(data->m12 * fixed_scale); @@ -3383,7 +3400,7 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper const qreal cx = x + qreal(0.5); const qreal cy = y + qreal(0.5); - if (data->fast_matrix) { + if (canUseFastMatrixPath(cx, cy, length, data)) { // The increment pr x in the scanline int fdx = (int)(data->m11 * fixed_scale); int fdy = (int)(data->m12 * fixed_scale); @@ -3570,7 +3587,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buf QRgba64 *end = buffer + length; QRgba64 *b = buffer; - if (data->fast_matrix) { + if (canUseFastMatrixPath(cx, cy, length, data)) { // The increment pr x in the scanline const int fdx = (int)(data->m11 * fixed_scale); const int fdy = (int)(data->m12 * fixed_scale); @@ -3728,7 +3745,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buf QRgba64 *end = buffer + length; QRgba64 *b = buffer; - if (data->fast_matrix) { + if (canUseFastMatrixPath(cx, cy, length, data)) { // The increment pr x in the scanline const int fdx = (int)(data->m11 * fixed_scale); const int fdy = (int)(data->m12 * fixed_scale); diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp index d88b005782..6cb7b57493 100644 --- a/src/gui/painting/qicc.cpp +++ b/src/gui/painting/qicc.cpp @@ -42,8 +42,9 @@ #include <qbuffer.h> #include <qbytearray.h> #include <qdatastream.h> -#include <qloggingcategory.h> #include <qendian.h> +#include <qloggingcategory.h> +#include <qstring.h> #include "qcolorspace_p.h" #include "qcolortrc_p.h" @@ -117,6 +118,7 @@ enum class Tag : quint32 { bkpt = IccTag('b', 'k', 'p', 't'), mft1 = IccTag('m', 'f', 't', '1'), mft2 = IccTag('m', 'f', 't', '2'), + mluc = IccTag('m', 'l', 'u', 'c'), mAB_ = IccTag('m', 'A', 'B', ' '), mBA_ = IccTag('m', 'B', 'A', ' '), chad = IccTag('c', 'h', 'a', 'd'), @@ -164,6 +166,25 @@ struct ParaTagData : GenericTagData { quint32_be parameter[1]; }; +struct DescTagData : GenericTagData { + quint32_be asciiDescriptionLength; + char asciiDescription[1]; + // .. we ignore the rest +}; + +struct MlucTagRecord { + quint16_be languageCode; + quint16_be countryCode; + quint32_be size; + quint32_be offset; +}; + +struct MlucTagData : GenericTagData { + quint32_be recordCount; + quint32_be recordSize; // = sizeof(MlucTagRecord) + MlucTagRecord records[1]; +}; + // For both mAB and mBA struct mABTagData : GenericTagData { quint8 inputChannels; @@ -190,23 +211,17 @@ static float fromFixedS1516(int x) return x * (1.0f / 65536.0f); } -QColorVector fromXyzData(const XYZTagData *xyz) -{ - const float x = fromFixedS1516(xyz->fixedX); - const float y = fromFixedS1516(xyz->fixedY); - const float z = fromFixedS1516(xyz->fixedZ); - qCDebug(lcIcc) << "XYZ_ " << x << y << z; - - return QColorVector(x, y, z); -} - static bool isValidIccProfile(const ICCProfileHeader &header) { if (header.signature != uint(Tag::acsp)) { qCWarning(lcIcc, "Failed ICC signature test"); return false; } - if (header.profileSize < (sizeof(ICCProfileHeader) + header.tagCount * sizeof(TagTableEntry))) { + + // Don't overflow 32bit integers: + if (header.tagCount >= INT32_MAX / sizeof(TagTableEntry)) + return false; + if (header.profileSize - sizeof(ICCProfileHeader) < header.tagCount * sizeof(TagTableEntry)) { qCWarning(lcIcc, "Failed basic size sanity"); return false; } @@ -288,7 +303,7 @@ QByteArray toIccProfile(const QColorSpace &space) if (!space.isValid()) return QByteArray(); - const QColorSpacePrivate *spaceDPtr = space.d_func(); + const QColorSpacePrivate *spaceDPtr = QColorSpacePrivate::get(space); constexpr int tagCount = 9; constexpr uint profileDataOffset = 128 + 4 + 12 * tagCount; @@ -413,17 +428,44 @@ QByteArray toIccProfile(const QColorSpace &space) return iccProfile; } -bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma) +struct TagEntry { + quint32 offset; + quint32 size; +}; + +bool parseXyzData(const QByteArray &data, const TagEntry &tagEntry, QColorVector &colorVector) +{ + if (tagEntry.size < sizeof(XYZTagData)) { + qCWarning(lcIcc) << "Undersized XYZ tag"; + return false; + } + const XYZTagData *xyz = reinterpret_cast<const XYZTagData *>(data.constData() + tagEntry.offset); + if (xyz->type != quint32(Tag::XYZ_)) { + qCWarning(lcIcc) << "Bad XYZ content type"; + return false; + } + const float x = fromFixedS1516(xyz->fixedX); + const float y = fromFixedS1516(xyz->fixedY); + const float z = fromFixedS1516(xyz->fixedZ); + + colorVector = QColorVector(x, y, z); + return true; +} + +bool parseTRC(const QByteArray &data, const TagEntry &tagEntry, QColorTrc &gamma) { + const GenericTagData *trcData = reinterpret_cast<const GenericTagData *>(data.constData() + tagEntry.offset); if (trcData->type == quint32(Tag::curv)) { - const CurvTagData *curv = reinterpret_cast<const CurvTagData *>(trcData); - qCDebug(lcIcc) << "curv" << uint(curv->valueCount); + const CurvTagData *curv = static_cast<const CurvTagData *>(trcData); + if (curv->valueCount > (1 << 16)) + return false; + if (tagEntry.size - 12 < 2 * curv->valueCount) + return false; if (curv->valueCount == 0) { gamma.m_type = QColorTrc::Type::Function; gamma.m_fun = QColorTransferFunction(); // Linear } else if (curv->valueCount == 1) { float g = curv->value[0] * (1.0f / 256.0f); - qCDebug(lcIcc) << g; gamma.m_type = QColorTrc::Type::Function; gamma.m_fun = QColorTransferFunction::fromGamma(g); } else { @@ -445,49 +487,54 @@ bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma) return true; } if (trcData->type == quint32(Tag::para)) { - const ParaTagData *para = reinterpret_cast<const ParaTagData *>(trcData); - qCDebug(lcIcc) << "para" << uint(para->curveType); + if (tagEntry.size < sizeof(ParaTagData)) + return false; + const ParaTagData *para = static_cast<const ParaTagData *>(trcData); switch (para->curveType) { case 0: { float g = fromFixedS1516(para->parameter[0]); - qCDebug(lcIcc) << g; gamma.m_type = QColorTrc::Type::Function; gamma.m_fun = QColorTransferFunction::fromGamma(g); break; } case 1: { + if (tagEntry.size < sizeof(ParaTagData) + 2 * 4) + return false; float g = fromFixedS1516(para->parameter[0]); float a = fromFixedS1516(para->parameter[1]); float b = fromFixedS1516(para->parameter[2]); float d = -b / a; - qCDebug(lcIcc) << g << a << b; gamma.m_type = QColorTrc::Type::Function; gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, 0.0f, 0.0f, g); break; } case 2: { + if (tagEntry.size < sizeof(ParaTagData) + 3 * 4) + return false; float g = fromFixedS1516(para->parameter[0]); float a = fromFixedS1516(para->parameter[1]); float b = fromFixedS1516(para->parameter[2]); float c = fromFixedS1516(para->parameter[3]); float d = -b / a; - qCDebug(lcIcc) << g << a << b << c; gamma.m_type = QColorTrc::Type::Function; gamma.m_fun = QColorTransferFunction(a, b, 0.0f, d, c, c, g); break; } case 3: { + if (tagEntry.size < sizeof(ParaTagData) + 4 * 4) + return false; float g = fromFixedS1516(para->parameter[0]); float a = fromFixedS1516(para->parameter[1]); float b = fromFixedS1516(para->parameter[2]); float c = fromFixedS1516(para->parameter[3]); float d = fromFixedS1516(para->parameter[4]); - qCDebug(lcIcc) << g << a << b << c << d; gamma.m_type = QColorTrc::Type::Function; gamma.m_fun = QColorTransferFunction(a, b, c, d, 0.0f, 0.0f, g); break; } case 4: { + if (tagEntry.size < sizeof(ParaTagData) + 6 * 4) + return false; float g = fromFixedS1516(para->parameter[0]); float a = fromFixedS1516(para->parameter[1]); float b = fromFixedS1516(para->parameter[2]); @@ -495,7 +542,6 @@ bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma) float d = fromFixedS1516(para->parameter[4]); float e = fromFixedS1516(para->parameter[5]); float f = fromFixedS1516(para->parameter[6]); - qCDebug(lcIcc) << g << a << b << c << d << e << f; gamma.m_type = QColorTrc::Type::Function; gamma.m_fun = QColorTransferFunction(a, b, c, d, e, f, g); break; @@ -510,6 +556,53 @@ bool parseTRC(const GenericTagData *trcData, QColorTrc &gamma) return false; } +bool parseDesc(const QByteArray &data, const TagEntry &tagEntry, QString &descName) +{ + const GenericTagData *tag = (const GenericTagData *)(data.constData() + tagEntry.offset); + + // Either 'desc' (ICCv2) or 'mluc' (ICCv4) + if (tag->type == quint32(Tag::desc)) { + if (tagEntry.size < sizeof(DescTagData)) + return false; + const DescTagData *desc = (const DescTagData *)(data.constData() + tagEntry.offset); + const quint32 len = desc->asciiDescriptionLength; + if (len < 1) + return false; + if (tagEntry.size - 12 < len) + return false; + if (desc->asciiDescription[len - 1] != '\0') + return false; + descName = QString::fromLatin1(desc->asciiDescription, len - 1); + return true; + } + if (tag->type != quint32(Tag::mluc)) + return false; + + if (tagEntry.size < sizeof(MlucTagData)) + return false; + const MlucTagData *mluc = (const MlucTagData *)(data.constData() + tagEntry.offset); + if (mluc->recordCount < 1) + return false; + if (mluc->recordSize < 12) + return false; + // We just use the primary record regardless of language or country. + const quint32 stringOffset = mluc->records[0].offset; + const quint32 stringSize = mluc->records[0].size; + if (tagEntry.size < stringOffset || tagEntry.size - stringOffset < stringSize ) + return false; + if ((stringSize | stringOffset) & 1) + return false; + quint32 stringLen = stringSize / 2; + const ushort *unicodeString = (const ushort *)(data.constData() + tagEntry.offset + stringOffset); + // The given length shouldn't include 0-termination, but might. + if (stringLen > 1 && unicodeString[stringLen - 1] == 0) + --stringLen; + QVarLengthArray<quint16> utf16hostendian(stringLen); + qFromBigEndian<ushort>(unicodeString, stringLen, utf16hostendian.data()); + descName = QString::fromUtf16(utf16hostendian.data(), stringLen); + return true; +} + bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) { if (data.size() < qsizetype(sizeof(ICCProfileHeader))) { @@ -529,8 +622,12 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) // Read tag index const TagTableEntry *tagTable = (const TagTableEntry *)(data.constData() + sizeof(ICCProfileHeader)); const qsizetype offsetToData = sizeof(ICCProfileHeader) + header->tagCount * sizeof(TagTableEntry); + if (offsetToData > data.size()) { + qCWarning(lcIcc) << "fromIccProfile: failed index size sanity"; + return false; + } - QHash<Tag, quint32> tagIndex; + QHash<Tag, TagEntry> tagIndex; for (uint i = 0; i < header->tagCount; ++i) { // Sanity check tag sizes and offsets: if (qsizetype(tagTable[i].offset) < offsetToData) { @@ -542,15 +639,24 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) qCWarning(lcIcc) << "fromIccProfile: failed tag offset sanity 2"; return false; } - if ((tagTable[i].offset + tagTable[i].size) > header->profileSize) { + if (tagTable[i].size < 12) { + qCWarning(lcIcc) << "fromIccProfile: failed minimal tag size sanity"; + return false; + } + if (tagTable[i].size > header->profileSize - tagTable[i].offset) { qCWarning(lcIcc) << "fromIccProfile: failed tag offset + size sanity"; return false; } + if (tagTable[i].offset & 0x03) { + qCWarning(lcIcc) << "fromIccProfile: invalid tag offset alignment"; + return false; + } // printf("'%4s' %d %d\n", (const char *)&tagTable[i].signature, // quint32(tagTable[i].offset), // quint32(tagTable[i].size)); - tagIndex.insert(Tag(quint32(tagTable[i].signature)), tagTable[i].offset); + tagIndex.insert(Tag(quint32(tagTable[i].signature)), { tagTable[i].offset, tagTable[i].size }); } + // Check the profile is three-component matrix based (what we currently support): if (!tagIndex.contains(Tag::rXYZ) || !tagIndex.contains(Tag::gXYZ) || !tagIndex.contains(Tag::bXYZ) || !tagIndex.contains(Tag::rTRC) || !tagIndex.contains(Tag::gTRC) || !tagIndex.contains(Tag::bTRC) || @@ -559,70 +665,70 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) return false; } + QColorSpacePrivate *colorspaceDPtr = QColorSpacePrivate::getWritable(*colorSpace); + // Parse XYZ tags - const XYZTagData *rXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::rXYZ]); - const XYZTagData *gXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::gXYZ]); - const XYZTagData *bXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::bXYZ]); - const XYZTagData *wXyz = (const XYZTagData *)(data.constData() + tagIndex[Tag::wtpt]); - if (rXyz->type != quint32(Tag::XYZ_) || gXyz->type != quint32(Tag::XYZ_) || - wXyz->type != quint32(Tag::XYZ_) || wXyz->type != quint32(Tag::XYZ_)) { - qCWarning(lcIcc) << "fromIccProfile: Bad XYZ data type"; + if (!parseXyzData(data, tagIndex[Tag::rXYZ], colorspaceDPtr->toXyz.r)) + return false; + if (!parseXyzData(data, tagIndex[Tag::gXYZ], colorspaceDPtr->toXyz.g)) + return false; + if (!parseXyzData(data, tagIndex[Tag::bXYZ], colorspaceDPtr->toXyz.b)) + return false; + if (!parseXyzData(data, tagIndex[Tag::wtpt], colorspaceDPtr->whitePoint)) return false; - } - QColorSpacePrivate *colorspaceDPtr = colorSpace->d_func(); - - colorspaceDPtr->toXyz.r = fromXyzData(rXyz); - colorspaceDPtr->toXyz.g = fromXyzData(gXyz); - colorspaceDPtr->toXyz.b = fromXyzData(bXyz); - QColorVector whitePoint = fromXyzData(wXyz); - colorspaceDPtr->whitePoint = whitePoint; - colorspaceDPtr->gamut = QColorSpace::Gamut::Custom; + colorspaceDPtr->primaries = QColorSpace::Primaries::Custom; if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromSRgb()) { - qCDebug(lcIcc) << "fromIccProfile: sRGB gamut detected"; - colorspaceDPtr->gamut = QColorSpace::Gamut::SRgb; + qCDebug(lcIcc) << "fromIccProfile: sRGB primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::SRgb; } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromAdobeRgb()) { - qCDebug(lcIcc) << "fromIccProfile: Adobe RGB gamut detected"; - colorspaceDPtr->gamut = QColorSpace::Gamut::AdobeRgb; + qCDebug(lcIcc) << "fromIccProfile: Adobe RGB primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::AdobeRgb; } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromDciP3D65()) { - qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 gamut detected"; - colorspaceDPtr->gamut = QColorSpace::Gamut::DciP3D65; + qCDebug(lcIcc) << "fromIccProfile: DCI-P3 D65 primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::DciP3D65; } else if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromBt2020()) { - qCDebug(lcIcc) << "fromIccProfile: BT.2020 gamut detected"; - colorspaceDPtr->gamut = QColorSpace::Gamut::Bt2020; + qCDebug(lcIcc) << "fromIccProfile: BT.2020 primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::Bt2020; } if (colorspaceDPtr->toXyz == QColorMatrix::toXyzFromProPhotoRgb()) { - qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB gamut detected"; - colorspaceDPtr->gamut = QColorSpace::Gamut::ProPhotoRgb; + qCDebug(lcIcc) << "fromIccProfile: ProPhoto RGB primaries detected"; + colorspaceDPtr->primaries = QColorSpace::Primaries::ProPhotoRgb; } // Reset the matrix to our canonical values: - if (colorspaceDPtr->gamut != QColorSpace::Gamut::Custom) + if (colorspaceDPtr->primaries != QColorSpace::Primaries::Custom) colorspaceDPtr->setToXyzMatrix(); // Parse TRC tags - const GenericTagData *rTrc; - const GenericTagData *gTrc; - const GenericTagData *bTrc; + TagEntry rTrc; + TagEntry gTrc; + TagEntry bTrc; if (tagIndex.contains(Tag::aarg) && tagIndex.contains(Tag::aagg) && tagIndex.contains(Tag::aabg)) { // Apple extension for parametric version of TRCs in ICCv2: - rTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aarg]); - gTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aagg]); - bTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::aabg]); + rTrc = tagIndex[Tag::aarg]; + gTrc = tagIndex[Tag::aagg]; + bTrc = tagIndex[Tag::aabg]; } else { - rTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::rTRC]); - gTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::gTRC]); - bTrc = (const GenericTagData *)(data.constData() + tagIndex[Tag::bTRC]); + rTrc = tagIndex[Tag::rTRC]; + gTrc = tagIndex[Tag::gTRC]; + bTrc = tagIndex[Tag::bTRC]; } QColorTrc rCurve; QColorTrc gCurve; QColorTrc bCurve; - if (!parseTRC(rTrc, rCurve)) + if (!parseTRC(data, rTrc, rCurve)) { + qCWarning(lcIcc) << "fromIccProfile: Invalid rTRC"; return false; - if (!parseTRC(gTrc, gCurve)) + } + if (!parseTRC(data, gTrc, gCurve)) { + qCWarning(lcIcc) << "fromIccProfile: Invalid gTRC"; return false; - if (!parseTRC(bTrc, bCurve)) + } + if (!parseTRC(data, bTrc, bCurve)) { + qCWarning(lcIcc) << "fromIccProfile: Invalid bTRC"; return false; + } if (rCurve == gCurve && gCurve == bCurve && rCurve.m_type == QColorTrc::Type::Function) { if (rCurve.m_fun.isLinear()) { qCDebug(lcIcc) << "fromIccProfile: Linear gamma detected"; @@ -652,7 +758,12 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace) colorspaceDPtr->transferFunction = QColorSpace::TransferFunction::Custom; } - // FIXME: try to parse the description.. + if (tagIndex.contains(Tag::desc)) { + if (!parseDesc(data, tagIndex[Tag::desc], colorspaceDPtr->description)) + qCWarning(lcIcc) << "fromIccProfile: Failed to parse description"; + else + qCDebug(lcIcc) << "fromIccProfile: Description" << colorspaceDPtr->description; + } if (!colorspaceDPtr->identifyColorSpace()) colorspaceDPtr->id = QColorSpace::Unknown; diff --git a/src/gui/painting/qpagedpaintdevice.cpp b/src/gui/painting/qpagedpaintdevice.cpp index 72b2834470..3fdd0206b7 100644 --- a/src/gui/painting/qpagedpaintdevice.cpp +++ b/src/gui/painting/qpagedpaintdevice.cpp @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE +// ### Qt 6: remove when the deprecated constructor is removed class QDummyPagedPaintDevicePrivate : public QPagedPaintDevicePrivate { bool setPageLayout(const QPageLayout &newPageLayout) override @@ -85,7 +86,7 @@ QPagedPaintDevicePrivate::~QPagedPaintDevicePrivate() \class QPagedPaintDevice \inmodule QtGui - \brief The QPagedPaintDevice class is a represents a paintdevice that supports + \brief The QPagedPaintDevice class represents a paint device that supports multiple pages. \ingroup painting diff --git a/src/gui/painting/qpagedpaintdevice.h b/src/gui/painting/qpagedpaintdevice.h index 1c37c17fa3..21e23e0eb4 100644 --- a/src/gui/painting/qpagedpaintdevice.h +++ b/src/gui/painting/qpagedpaintdevice.h @@ -213,6 +213,7 @@ public: Envelope10 = Comm10E }; + // keep in sync with QPdfEngine::PdfVersion! enum PdfVersion { PdfVersion_1_4, PdfVersion_A1b, PdfVersion_1_6 }; // ### Qt6 Make these virtual diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 461ad51200..0a440e5b75 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -2902,9 +2902,9 @@ bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, QFontEngine::GlyphFormat glyphFormat = fontEngine->glyphFormat != QFontEngine::Format_None ? fontEngine->glyphFormat : d->glyphCacheFormat; QImageTextureGlyphCache *cache = - static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphFormat, s->matrix)); + static_cast<QImageTextureGlyphCache *>(fontEngine->glyphCache(0, glyphFormat, s->matrix, QColor(s->penData.solidColor))); if (!cache) { - cache = new QImageTextureGlyphCache(glyphFormat, s->matrix); + cache = new QImageTextureGlyphCache(glyphFormat, s->matrix, QColor(s->penData.solidColor)); fontEngine->setGlyphCache(0, cache); } @@ -4295,7 +4295,7 @@ protected: QSharedPointer<const CacheInfo> addCacheElement(quint64 hash_val, const QGradient &gradient, int opacity) { if (cache.size() == maxCacheSize()) { // may remove more than 1, but OK - cache.erase(cache.begin() + QRandomGenerator::global()->bounded(maxCacheSize())); + cache.erase(std::next(cache.begin(), QRandomGenerator::global()->bounded(maxCacheSize()))); } auto cache_entry = QSharedPointer<CacheInfo>::create(gradient.stops(), opacity, gradient.interpolationMode()); generateGradientColorTable(gradient, cache_entry->buffer64, paletteSize(), opacity); diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 8801e66b0f..b1d1f30800 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -661,6 +661,7 @@ void QPainterPath::clear() detach(); d_func()->clear(); + d_func()->elements.append( {0, 0, MoveToElement} ); } /*! @@ -2337,12 +2338,12 @@ bool QPainterPath::operator==(const QPainterPath &path) const { QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func()); QPainterPathData *other_d = path.d_func(); - if (other_d == d) + if (other_d == d) { return true; - else if (!d || !other_d) { - if (!d && other_d->elements.empty() && other_d->fillRule == Qt::OddEvenFill) + } else if (!d || !other_d) { + if (!other_d && isEmpty() && elementAt(0) == QPointF() && d->fillRule == Qt::OddEvenFill) return true; - if (!other_d && d && d->elements.empty() && d->fillRule == Qt::OddEvenFill) + if (!d && path.isEmpty() && path.elementAt(0) == QPointF() && other_d->fillRule == Qt::OddEvenFill) return true; return false; } diff --git a/src/gui/painting/qpainterpath_p.h b/src/gui/painting/qpainterpath_p.h index 22bdbde2a9..a420e0b3d9 100644 --- a/src/gui/painting/qpainterpath_p.h +++ b/src/gui/painting/qpainterpath_p.h @@ -313,7 +313,7 @@ inline void QPainterPathData::clear() elements.clear(); cStart = 0; - + fillRule = Qt::OddEvenFill; bounds = {}; controlBounds = {}; diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 25d488961c..f560e1f0f0 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -1570,12 +1570,14 @@ void QPdfEnginePrivate::writeHeader() { addXrefEntry(0,false); - static const QHash<QPdfEngine::PdfVersion, const char *> mapping { - {QPdfEngine::Version_1_4, "1.4"}, - {QPdfEngine::Version_A1b, "1.4"}, - {QPdfEngine::Version_1_6, "1.6"} + // Keep in sync with QPdfEngine::PdfVersion! + static const char mapping[][4] = { + "1.4", // Version_1_4 + "1.4", // Version_A1b + "1.6", // Version_1_6 }; - const char *verStr = mapping.value(pdfVersion, "1.4"); + static const size_t numMappings = sizeof mapping / sizeof *mapping; + const char *verStr = mapping[size_t(pdfVersion) < numMappings ? pdfVersion : 0]; xprintf("%%PDF-%s\n", verStr); xprintf("%%\303\242\303\243\n"); diff --git a/src/gui/painting/qpdf_p.h b/src/gui/painting/qpdf_p.h index e337c61f64..89e549614a 100644 --- a/src/gui/painting/qpdf_p.h +++ b/src/gui/painting/qpdf_p.h @@ -168,6 +168,7 @@ class Q_GUI_EXPORT QPdfEngine : public QPaintEngine Q_DECLARE_PRIVATE(QPdfEngine) friend class QPdfWriter; public: + // keep in sync with QPagedPaintDevice::PdfVersion and QPdfEnginePrivate::writeHeader()::mapping! enum PdfVersion { Version_1_4, diff --git a/src/gui/painting/qpdfwriter.cpp b/src/gui/painting/qpdfwriter.cpp index 7f18ce42be..bf7e2d3dca 100644 --- a/src/gui/painting/qpdfwriter.cpp +++ b/src/gui/painting/qpdfwriter.cpp @@ -170,17 +170,11 @@ void QPdfWriter::setPdfVersion(PdfVersion version) { Q_D(QPdfWriter); - static const QHash<QPdfWriter::PdfVersion, QPdfEngine::PdfVersion> engineMapping { - {QPdfWriter::PdfVersion_1_4, QPdfEngine::Version_1_4}, - {QPdfWriter::PdfVersion_A1b, QPdfEngine::Version_A1b}, - {QPdfWriter::PdfVersion_1_6, QPdfEngine::Version_1_6} - }; - if (d->pdfVersion == version) return; d->pdfVersion = version; - d->engine->setPdfVersion(engineMapping.value(version, QPdfEngine::Version_1_4)); + d->engine->setPdfVersion(static_cast<QPdfEngine::PdfVersion>(static_cast<int>(version))); } /*! diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index e8c47df21c..7a3dd04965 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -267,7 +267,7 @@ QImage QTextureGlyphCache::textureMapForGlyph(glyph_t g, QFixed subPixelPosition case QFontEngine::Format_A32: return m_current_fontengine->alphaRGBMapForGlyph(g, subPixelPosition, m_transform); case QFontEngine::Format_ARGB: - return m_current_fontengine->bitmapForGlyph(g, subPixelPosition, m_transform); + return m_current_fontengine->bitmapForGlyph(g, subPixelPosition, m_transform, color()); default: return m_current_fontengine->alphaMapForGlyph(g, subPixelPosition, m_transform); } diff --git a/src/gui/painting/qtextureglyphcache_p.h b/src/gui/painting/qtextureglyphcache_p.h index 1e83ab46d1..b6fc7230a8 100644 --- a/src/gui/painting/qtextureglyphcache_p.h +++ b/src/gui/painting/qtextureglyphcache_p.h @@ -74,8 +74,8 @@ class QTextItemInt; class Q_GUI_EXPORT QTextureGlyphCache : public QFontEngineGlyphCache { public: - QTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix) - : QFontEngineGlyphCache(format, matrix), m_current_fontengine(nullptr), + QTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix, const QColor &color = QColor()) + : QFontEngineGlyphCache(format, matrix, color), m_current_fontengine(nullptr), m_w(0), m_h(0), m_cx(0), m_cy(0), m_currentRowHeight(0) { } @@ -165,8 +165,8 @@ inline uint qHash(const QTextureGlyphCache::GlyphAndSubPixelPosition &g) class Q_GUI_EXPORT QImageTextureGlyphCache : public QTextureGlyphCache { public: - QImageTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix) - : QTextureGlyphCache(format, matrix) { } + QImageTextureGlyphCache(QFontEngine::GlyphFormat format, const QTransform &matrix, const QColor &color = QColor()) + : QTextureGlyphCache(format, matrix, color) { } ~QImageTextureGlyphCache(); virtual void createTextureData(int width, int height) override; |