summaryrefslogtreecommitdiffstats
path: root/src/gui/painting
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/painting')
-rw-r--r--src/gui/painting/qbackingstore.cpp2
-rw-r--r--src/gui/painting/qcolorspace.cpp279
-rw-r--r--src/gui/painting/qcolorspace.h30
-rw-r--r--src/gui/painting/qcolorspace_p.h17
-rw-r--r--src/gui/painting/qcolortransform.cpp27
-rw-r--r--src/gui/painting/qcolortransform.h29
-rw-r--r--src/gui/painting/qcolortransform_p.h4
-rw-r--r--src/gui/painting/qdrawhelper.cpp27
-rw-r--r--src/gui/painting/qicc.cpp245
-rw-r--r--src/gui/painting/qpagedpaintdevice.cpp3
-rw-r--r--src/gui/painting/qpagedpaintdevice.h1
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp6
-rw-r--r--src/gui/painting/qpainterpath.cpp9
-rw-r--r--src/gui/painting/qpainterpath_p.h2
-rw-r--r--src/gui/painting/qpdf.cpp12
-rw-r--r--src/gui/painting/qpdf_p.h1
-rw-r--r--src/gui/painting/qpdfwriter.cpp8
-rw-r--r--src/gui/painting/qtextureglyphcache.cpp2
-rw-r--r--src/gui/painting/qtextureglyphcache_p.h8
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;