summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <[email protected]>2024-04-25 11:57:58 +0200
committerAllan Sandfeld Jensen <[email protected]>2024-06-01 16:27:05 +0200
commite673e5a257569eaa816c6acd31dd754efd9f8c75 (patch)
tree3460f30024a09164cf486b39fca832d4ca798851
parent70dd53e3d30dc85b6963bb2412535d037f4edf04 (diff)
Merge applyGray, applyReturnGray, and apply methods
Combine all into a single flexible apply method. Also fixes a few issues with RGB colorspace on gray input. Blocking CMYK colorspace on Grayscale and ElementListProcessing RGB on grayscale out. Fixes: QTBUG-125303 Change-Id: I3987010062fbb5aa708aeb1cc239f3ce9413e34f Reviewed-by: Allan Sandfeld Jensen <[email protected]>
-rw-r--r--src/gui/image/qimage.cpp67
-rw-r--r--src/gui/image/qimage_conversions.cpp8
-rw-r--r--src/gui/image/qimage_p.h26
-rw-r--r--src/gui/painting/qcolorspace.cpp2
-rw-r--r--src/gui/painting/qcolortransform.cpp369
-rw-r--r--src/gui/painting/qcolortransform_p.h6
-rw-r--r--src/gui/painting/qdrawhelper.cpp8
-rw-r--r--src/gui/painting/qicc.cpp6
-rw-r--r--tests/auto/gui/image/qimage/images/VideoHD.iccbin0 -> 786 bytes
-rw-r--r--tests/auto/gui/image/qimage/tst_qimage.cpp118
10 files changed, 365 insertions, 245 deletions
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 5a2af0dbb01..175ff8cc499 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -5027,7 +5027,7 @@ void QImage::setColorSpace(const QColorSpace &colorSpace)
return;
if (d->colorSpace == colorSpace)
return;
- if (colorSpace.isValid() && !qt_compatibleColorModel(pixelFormat().colorModel(), colorSpace.colorModel()))
+ if (colorSpace.isValid() && !qt_compatibleColorModelSource(pixelFormat().colorModel(), colorSpace.colorModel()))
return;
detachMetadata(false);
@@ -5057,7 +5057,7 @@ void QImage::convertToColorSpace(const QColorSpace &colorSpace)
}
if (d->colorSpace == colorSpace)
return;
- if (!qt_compatibleColorModel(pixelFormat().colorModel(), colorSpace.colorModel())) {
+ if (!qt_compatibleColorModelTarget(pixelFormat().colorModel(), colorSpace.colorModel(), colorSpace.transformModel())) {
*this = convertedToColorSpace(colorSpace);
return;
}
@@ -5088,7 +5088,7 @@ void QImage::convertToColorSpace(const QColorSpace &colorSpace, QImage::Format f
qWarning() << "QImage::convertToColorSpace: Output colorspace is not valid";
return;
}
- if (!qt_compatibleColorModel(toPixelFormat(format).colorModel(), colorSpace.colorModel())) {
+ if (!qt_compatibleColorModelTarget(toPixelFormat(format).colorModel(), colorSpace.colorModel(), colorSpace.transformModel())) {
qWarning() << "QImage::convertToColorSpace: Color space is not compatible with format";
return;
}
@@ -5148,7 +5148,7 @@ QImage QImage::convertedToColorSpace(const QColorSpace &colorSpace, QImage::Form
qWarning() << "QImage::convertedToColorSpace: Output colorspace is not valid";
return QImage();
}
- if (!qt_compatibleColorModel(toPixelFormat(format).colorModel(), colorSpace.colorModel())) {
+ if (!qt_compatibleColorModelTarget(toPixelFormat(format).colorModel(), colorSpace.colorModel(), colorSpace.transformModel())) {
qWarning() << "QImage::convertedToColorSpace: Color space is not compatible with format";
return QImage();
}
@@ -5181,8 +5181,9 @@ void QImage::applyColorTransform(const QColorTransform &transform)
if (transform.isIdentity())
return;
- if (!qt_compatibleColorModel(pixelFormat().colorModel(), QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel) ||
- !qt_compatibleColorModel(pixelFormat().colorModel(), QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel)) {
+ if (!qt_compatibleColorModelSource(pixelFormat().colorModel(), QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel) ||
+ !qt_compatibleColorModelTarget(pixelFormat().colorModel(), QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel,
+ QColorTransformPrivate::get(transform)->colorSpaceOut->transformModel)) {
qWarning() << "QImage::applyColorTransform can not apply format switching transform without switching format";
return;
}
@@ -5242,14 +5243,14 @@ void QImage::applyColorTransform(const QColorTransform &transform)
transformSegment = [&](int yStart, int yEnd) {
for (int y = yStart; y < yEnd; ++y) {
uint8_t *scanline = reinterpret_cast<uint8_t *>(d->data + y * d->bytes_per_line);
- QColorTransformPrivate::get(transform)->applyGray(scanline, scanline, width(), flags);
+ QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
}
};
} else if (format() == Format_Grayscale16) {
transformSegment = [&](int yStart, int yEnd) {
for (int y = yStart; y < yEnd; ++y) {
uint16_t *scanline = reinterpret_cast<uint16_t *>(d->data + y * d->bytes_per_line);
- QColorTransformPrivate::get(transform)->applyGray(scanline, scanline, width(), flags);
+ QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
}
};
} else if (qt_fpColorPrecision(format())) {
@@ -5343,15 +5344,15 @@ QImage QImage::colorTransformed(const QColorTransform &transform) const &
if (transform.isIdentity())
return *this;
- QColorSpace::ColorModel inColorModel = QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel;
- QColorSpace::ColorModel outColorModel = QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel;
- if (!qt_compatibleColorModel(pixelFormat().colorModel(), inColorModel)) {
+ const QColorSpacePrivate *inColorSpace = QColorTransformPrivate::get(transform)->colorSpaceIn.constData();
+ const QColorSpacePrivate *outColorSpace = QColorTransformPrivate::get(transform)->colorSpaceOut.constData();
+ if (!qt_compatibleColorModelSource(pixelFormat().colorModel(), inColorSpace->colorModel)) {
qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
return QImage();
}
- if (!qt_compatibleColorModel(pixelFormat().colorModel(), outColorModel)) {
+ if (!qt_compatibleColorModelTarget(pixelFormat().colorModel(), outColorSpace->colorModel, outColorSpace->transformModel)) {
// All model switching transforms are opaque in at least one end.
- switch (outColorModel) {
+ switch (outColorSpace->colorModel) {
case QColorSpace::ColorModel::Rgb:
return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
case QColorSpace::ColorModel::Gray:
@@ -5430,13 +5431,13 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
if (transform.isIdentity())
return convertedTo(toFormat, flags);
- QColorSpace::ColorModel inColorModel = QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel;
- QColorSpace::ColorModel outColorModel = QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel;
- if (!qt_compatibleColorModel(pixelFormat().colorModel(), inColorModel)) {
+ const QColorSpacePrivate *inColorSpace = QColorTransformPrivate::get(transform)->colorSpaceIn.constData();
+ const QColorSpacePrivate *outColorSpace = QColorTransformPrivate::get(transform)->colorSpaceOut.constData();
+ if (!qt_compatibleColorModelSource(pixelFormat().colorModel(), inColorSpace->colorModel)) {
qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
return QImage();
}
- if (!qt_compatibleColorModel(toPixelFormat(toFormat).colorModel(), outColorModel)) {
+ if (!qt_compatibleColorModelTarget(toPixelFormat(toFormat).colorModel(), outColorSpace->colorModel, outColorSpace->transformModel)) {
qWarning() << "QImage::colorTransformed: Invalid output color space for transform";
return QImage();
}
@@ -5533,7 +5534,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
for (int y = yStart; y < yEnd; ++y) {
const quint8 *in_scanline = reinterpret_cast<const quint8 *>(d->data + y * d->bytes_per_line);
QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
}
};
} else {
@@ -5541,7 +5542,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
for (int y = yStart; y < yEnd; ++y) {
const quint16 *in_scanline = reinterpret_cast<const quint16 *>(d->data + y * d->bytes_per_line);
QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
}
};
}
@@ -5552,7 +5553,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
for (int y = yStart; y < yEnd; ++y) {
const quint8 *in_scanline = reinterpret_cast<const quint8 *>(d->data + y * d->bytes_per_line);
QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
}
};
} else {
@@ -5560,7 +5561,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
for (int y = yStart; y < yEnd; ++y) {
const quint16 *in_scanline = reinterpret_cast<const quint16 *>(d->data + y * d->bytes_per_line);
QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
}
};
}
@@ -5572,7 +5573,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
for (int y = yStart; y < yEnd; ++y) {
const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
}
};
} else {
@@ -5581,7 +5582,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
for (int y = yStart; y < yEnd; ++y) {
const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
}
};
}
@@ -5592,7 +5593,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
for (int y = yStart; y < yEnd; ++y) {
const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
}
};
} else {
@@ -5600,7 +5601,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
for (int y = yStart; y < yEnd; ++y) {
const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
}
};
}
@@ -5704,11 +5705,11 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
const quint8 *in_scanline = reinterpret_cast<const quint8 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
if (tmpFormat == Format_Grayscale8) {
quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), transFlags);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
} else {
Q_ASSERT(tmpFormat == Format_Grayscale16);
quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), transFlags);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
}
}
};
@@ -5717,7 +5718,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
for (int y = yStart; y < yEnd; ++y) {
const quint16 *in_scanline = reinterpret_cast<const quint16 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.d->data + y * toImage.bytesPerLine());
- QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), transFlags);
+ QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
}
};
} else if (fromImage.format() == Format_CMYK8888) {
@@ -5811,15 +5812,15 @@ QImage QImage::colorTransformed(const QColorTransform &transform) &&
if (!d)
return QImage();
- QColorSpace::ColorModel inColorModel = QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel;
- QColorSpace::ColorModel outColorModel = QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel;
- if (!qt_compatibleColorModel(pixelFormat().colorModel(), inColorModel)) {
+ const QColorSpacePrivate *inColorSpace = QColorTransformPrivate::get(transform)->colorSpaceIn.constData();
+ const QColorSpacePrivate *outColorSpace = QColorTransformPrivate::get(transform)->colorSpaceOut.constData();
+ if (!qt_compatibleColorModelSource(pixelFormat().colorModel(), inColorSpace->colorModel)) {
qWarning() << "QImage::colorTransformed: Invalid input color space for transform";
return QImage();
}
- if (!qt_compatibleColorModel(pixelFormat().colorModel(), outColorModel)) {
+ if (!qt_compatibleColorModelTarget(pixelFormat().colorModel(), outColorSpace->colorModel, outColorSpace->transformModel)) {
// There is currently no inplace conversion of both colorspace and format, so just use the normal version.
- switch (outColorModel) {
+ switch (outColorSpace->colorModel) {
case QColorSpace::ColorModel::Rgb:
return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
case QColorSpace::ColorModel::Gray:
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp
index a806954df2c..8d9e0b87e29 100644
--- a/src/gui/image/qimage_conversions.cpp
+++ b/src/gui/image/qimage_conversions.cpp
@@ -1424,7 +1424,7 @@ static void convert_ARGB_to_gray8(QImageData *dest, const QImageData *src, Qt::I
for (int i = 0; i < src->height; ++i) {
const QRgb *src_line = reinterpret_cast<const QRgb *>(src_data);
- tfd->applyReturnGray(dest_data, src_line, src->width, flags);
+ tfd->apply(dest_data, src_line, src->width, flags);
src_data += sbpl;
dest_data += dbpl;
}
@@ -1461,7 +1461,7 @@ static void convert_ARGB_to_gray16(QImageData *dest, const QImageData *src, Qt::
const int len = std::min(src->width - j, BufferSize);
for (int k = 0; k < len; ++k)
tmp_line[k] = QRgba64::fromArgb32(src_line[j + k]);
- tfd->applyReturnGray(dest_line + j, tmp_line, len, flags);
+ tfd->apply(dest_line + j, tmp_line, len, flags);
j += len;
}
src_data += sbpl;
@@ -1498,7 +1498,7 @@ static void convert_RGBA64_to_gray8(QImageData *dest, const QImageData *src, Qt:
int j = 0;
while (j < src->width) {
const int len = std::min(src->width - j, BufferSize);
- tfd->applyReturnGray(gray_line, src_line + j, len, flags);
+ tfd->apply(gray_line, src_line + j, len, flags);
for (int k = 0; k < len; ++k)
dest_line[j + k] = qt_div_257(gray_line[k]);
j += len;
@@ -1533,7 +1533,7 @@ static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt
for (int i = 0; i < src->height; ++i) {
const QRgba64 *src_line = reinterpret_cast<const QRgba64 *>(src_data);
quint16 *dest_line = reinterpret_cast<quint16 *>(dest_data);
- tfd->applyReturnGray(dest_line, src_line, src->width, flags);
+ tfd->apply(dest_line, src_line, src->width, flags);
src_data += sbpl;
dest_data += dbpl;
}
diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h
index 0d42f942533..43b03ada7c2 100644
--- a/src/gui/image/qimage_p.h
+++ b/src/gui/image/qimage_p.h
@@ -456,7 +456,7 @@ inline QColorSpace::ColorModel qt_csColorData(QPixelFormat::ColorModel format)
return QColorSpace::ColorModel::Undefined;
}
-inline bool qt_compatibleColorModel(QPixelFormat::ColorModel data, QColorSpace::ColorModel cs)
+inline bool qt_compatibleColorModelBase(QPixelFormat::ColorModel data, QColorSpace::ColorModel cs)
{
QColorSpace::ColorModel dataCs = qt_csColorData(data);
@@ -466,11 +466,27 @@ inline bool qt_compatibleColorModel(QPixelFormat::ColorModel data, QColorSpace::
if (cs == QColorSpace::ColorModel::Undefined || dataCs == QColorSpace::ColorModel::Undefined)
return false;
- if (dataCs == cs)
- return true; // Matching color models
+ return (dataCs == cs); // Matching color models
+}
+
+inline bool qt_compatibleColorModelSource(QPixelFormat::ColorModel data, QColorSpace::ColorModel cs)
+{
+ if (qt_compatibleColorModelBase(data, cs))
+ return true;
+
+ if (data == QPixelFormat::ColorModel::Grayscale && cs == QColorSpace::ColorModel::Rgb)
+ return true; // Can apply Rgb CS to Gray input data
+
+ return false;
+}
+
+inline bool qt_compatibleColorModelTarget(QPixelFormat::ColorModel data, QColorSpace::ColorModel cs, QColorSpace::TransformModel tm)
+{
+ if (qt_compatibleColorModelBase(data, cs))
+ return true;
- if (dataCs == QColorSpace::ColorModel::Gray)
- return true; // Can apply any CS with white point to Gray data
+ if (data == QPixelFormat::ColorModel::Grayscale && tm == QColorSpace::TransformModel::ThreeComponentMatrix)
+ return true; // Can apply three-component matrix CS to gray output
return false;
}
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp
index fce2ae4c85f..3d763d577c9 100644
--- a/src/gui/painting/qcolorspace.cpp
+++ b/src/gui/painting/qcolorspace.cpp
@@ -503,8 +503,6 @@ QColorTransform QColorSpacePrivate::transformationToXYZ() const
// Convert to XYZ relative to our white point, not the regular D50 white point.
if (!chad.isNull())
ptr->colorMatrix = chad.inverted() * ptr->colorMatrix;
- else if (!whitePoint.isNull())
- ptr->colorMatrix = QColorMatrix::chromaticAdaptation(whitePoint).inverted() * ptr->colorMatrix;
return transform;
}
diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp
index cffcc4485de..f54dac9f268 100644
--- a/src/gui/painting/qcolortransform.cpp
+++ b/src/gui/painting/qcolortransform.cpp
@@ -1221,17 +1221,41 @@ static void storeOpaque(QRgbaFloat32 *dst, const QColorVector *buffer, const qsi
static void loadGray(QColorVector *buffer, const quint8 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
{
- for (qsizetype i = 0; i < len; ++i) {
- const float y = d_ptr->colorSpaceIn->lut[0]->u8ToLinearF32(src[i]);
- buffer[i] = d_ptr->colorSpaceIn->whitePoint * y;
+ if (d_ptr->colorSpaceIn->colorModel == QColorSpace::ColorModel::Gray ||
+ (d_ptr->colorSpaceIn->lut[0] == d_ptr->colorSpaceIn->lut[1] &&
+ d_ptr->colorSpaceIn->lut[0] == d_ptr->colorSpaceIn->lut[2])) {
+ for (qsizetype i = 0; i < len; ++i) {
+ const float y = d_ptr->colorSpaceIn->lut[0]->u8ToLinearF32(src[i]);
+ buffer[i] = d_ptr->colorSpaceIn->whitePoint * y;
+ }
+ } else {
+ for (qsizetype i = 0; i < len; ++i) {
+ QColorVector v;
+ v.x = d_ptr->colorSpaceIn->lut[0]->u8ToLinearF32(src[i]);
+ v.y = d_ptr->colorSpaceIn->lut[1]->u8ToLinearF32(src[i]);
+ v.z = d_ptr->colorSpaceIn->lut[2]->u8ToLinearF32(src[i]);
+ buffer[i] = d_ptr->colorSpaceIn->toXyz.map(v);
+ }
}
}
static void loadGray(QColorVector *buffer, const quint16 *src, const qsizetype len, const QColorTransformPrivate *d_ptr)
{
- for (qsizetype i = 0; i < len; ++i) {
- const float y = d_ptr->colorSpaceIn->lut[0]->u16ToLinearF32(src[i]);
- buffer[i] = d_ptr->colorSpaceIn->whitePoint * y;
+ if (d_ptr->colorSpaceIn->colorModel == QColorSpace::ColorModel::Gray ||
+ (d_ptr->colorSpaceIn->lut[0] == d_ptr->colorSpaceIn->lut[1] &&
+ d_ptr->colorSpaceIn->lut[0] == d_ptr->colorSpaceIn->lut[2])) {
+ for (qsizetype i = 0; i < len; ++i) {
+ const float y = d_ptr->colorSpaceIn->lut[0]->u16ToLinearF32(src[i]);
+ buffer[i] = d_ptr->colorSpaceIn->whitePoint * y;
+ }
+ } else {
+ for (qsizetype i = 0; i < len; ++i) {
+ QColorVector v;
+ v.x = d_ptr->colorSpaceIn->lut[0]->u16ToLinearF32(src[i]);
+ v.y = d_ptr->colorSpaceIn->lut[1]->u16ToLinearF32(src[i]);
+ v.z = d_ptr->colorSpaceIn->lut[2]->u16ToLinearF32(src[i]);
+ buffer[i] = d_ptr->colorSpaceIn->toXyz.map(v);
+ }
}
}
@@ -1260,6 +1284,28 @@ private:
alignas(T) char data[sizeof(T) * Count];
};
+void loadUnpremultipliedLUT(QColorVector *buffer, const uchar *src, const qsizetype len)
+{
+ const float f = 1.0f / 255.f;
+ for (qsizetype i = 0; i < len; ++i) {
+ const float p = src[i] * f;
+ buffer[i].x = p;
+ buffer[i].y = p;
+ buffer[i].z = p;
+ }
+}
+
+void loadUnpremultipliedLUT(QColorVector *buffer, const quint16 *src, const qsizetype len)
+{
+ const float f = 1.0f / 65535.f;
+ for (qsizetype i = 0; i < len; ++i) {
+ const float p = src[i] * f;
+ buffer[i].x = p;
+ buffer[i].y = p;
+ buffer[i].z = p;
+ }
+}
+
void loadUnpremultipliedLUT(QColorVector *buffer, const QRgb *src, const qsizetype len)
{
const float f = 1.0f / 255.f;
@@ -1302,6 +1348,16 @@ void loadUnpremultipliedLUT(QColorVector *buffer, const QRgbaFloat32 *src, const
}
}
+void loadPremultipliedLUT(QColorVector *, const uchar *, const qsizetype)
+{
+ Q_UNREACHABLE();
+}
+
+void loadPremultipliedLUT(QColorVector *, const quint16 *, const qsizetype)
+{
+ Q_UNREACHABLE();
+}
+
void loadPremultipliedLUT(QColorVector *buffer, const QRgb *src, const qsizetype len)
{
for (qsizetype i = 0; i < len; ++i) {
@@ -1422,7 +1478,15 @@ static void storeUnpremultipliedLUT(QRgbaFloat32 *dst, const T *src,
}
template<typename T>
-static void storePremultipliedLUT(QRgb *, const T *, const QColorVector *, const qsizetype);
+static void storePremultipliedLUT(QRgb *dst, const T *, const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int r = buffer[i].x * 255.f;
+ const int g = buffer[i].y * 255.f;
+ const int b = buffer[i].z * 255.f;
+ dst[i] = 0xff000000 | (r << 16) | (g << 8) | (b << 0);
+ }
+}
template<>
void storePremultipliedLUT(QRgb *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len)
@@ -1436,18 +1500,6 @@ void storePremultipliedLUT(QRgb *dst, const QRgb *src, const QColorVector *buffe
}
}
-template<>
-void storePremultipliedLUT(QRgb *dst, const QCmyk32 *, const QColorVector *buffer, const qsizetype len)
-{
- for (qsizetype i = 0; i < len; ++i) {
- const int r = buffer[i].x * 255.f;
- const int g = buffer[i].y * 255.f;
- const int b = buffer[i].z * 255.f;
- dst[i] = 0xff000000 | (r << 16) | (g << 8) | (b << 0);
- }
-}
-
-
template<typename T>
static void storePremultipliedLUT(QCmyk32 *dst, const T *src, const QColorVector *buffer, const qsizetype len)
{
@@ -1455,7 +1507,15 @@ static void storePremultipliedLUT(QCmyk32 *dst, const T *src, const QColorVector
}
template<typename T>
-static void storePremultipliedLUT(QRgba64 *, const T *, const QColorVector *, const qsizetype);
+static void storePremultipliedLUT(QRgba64 *dst, const T *, const QColorVector *buffer, const qsizetype len)
+{
+ for (qsizetype i = 0; i < len; ++i) {
+ const int r = buffer[i].x * 65535.f;
+ const int g = buffer[i].y * 65535.f;
+ const int b = buffer[i].z * 65535.f;
+ dst[i] = qRgba64(r, g, b, 65535);
+ }
+}
template<>
void storePremultipliedLUT(QRgba64 *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len)
@@ -1470,17 +1530,6 @@ void storePremultipliedLUT(QRgba64 *dst, const QRgb *src, const QColorVector *bu
}
template<>
-void storePremultipliedLUT(QRgba64 *dst, const QCmyk32 *, const QColorVector *buffer, const qsizetype len)
-{
- for (qsizetype i = 0; i < len; ++i) {
- const int r = buffer[i].x * 65535.f;
- const int g = buffer[i].y * 65535.f;
- const int b = buffer[i].z * 65535.f;
- dst[i] = qRgba64(r, g, b, 65535);
- }
-}
-
-template<>
void storePremultipliedLUT(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len)
{
for (qsizetype i = 0; i < len; ++i) {
@@ -1630,11 +1679,43 @@ QColorVector QColorTransformPrivate::mapExtended(QColorVector c) const
return c;
}
+template<typename T>
+constexpr bool IsGrayscale = std::is_same_v<T, uchar> || std::is_same_v<T, quint16>;
+template<typename T>
+constexpr bool IsAlwaysOpaque = std::is_same_v<T, QCmyk32> || IsGrayscale<T>;
+template<typename T>
+constexpr bool CanUseThreeComponent = !std::is_same_v<T, QCmyk32>;
+template<typename T>
+constexpr bool UnclampedValues = std::is_same_v<T, QRgbaFloat16> || std::is_same_v<T, QRgbaFloat32>;
+
+// Possible combos for data and color spaces:
+// DataCM ColorSpaceCM ColorSpacePM Notes
+// Gray Gray ThreeMatrix
+// Gray Rgb ThreeMatrix Invalid colorMatrix
+// Rgb Rgb ThreeMatrix
+// Rgb Rgb ElementProc
+// Gray Rgb ElementProc Only possible for input data
+// Cmyk Cmyk ElementProc
+//
+// Gray data can be uchar, quint16, and is always Opaque
+// Rgb data can be QRgb, QRgba64, or QRgbaFloat32, and is Unpremultiplied, Premultiplied, or Opaque
+// Cmyk data can be Cmyk32, and is always Opaque
+//
+// colorMatrix as setup for Gray on Gray or Rgb on Rgb, but not Gray data on Rgb colorspace.
+
template<typename S>
void QColorTransformPrivate::applyConvertIn(const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const
{
- // Avoid compiling this part for S=QCmyk32:
- if constexpr (!std::is_same_v<S, QCmyk32>) {
+ if constexpr (IsGrayscale<S>) {
+ if (colorSpaceIn->isThreeComponentMatrix()) {
+ loadGray(buffer, src, len, this);
+ if (!colorSpaceOut->isThreeComponentMatrix() || colorSpaceIn->colorModel != QColorSpace::ColorModel::Gray) {
+ if (!colorSpaceIn->chad.isNull())
+ applyMatrix<DoClamp>(buffer, len, colorSpaceIn->chad);
+ }
+ return;
+ }
+ } else if constexpr (CanUseThreeComponent<S>) {
if (colorSpaceIn->isThreeComponentMatrix()) {
if (flags & InputPremultiplied)
loadPremultiplied(buffer, src, len, this);
@@ -1642,7 +1723,7 @@ void QColorTransformPrivate::applyConvertIn(const S *src, QColorVector *buffer,
loadUnpremultiplied(buffer, src, len, this);
if (!colorSpaceOut->isThreeComponentMatrix())
- applyMatrix<DoClamp>(buffer, len, colorSpaceIn->toXyz);
+ applyMatrix<DoClamp>(buffer, len, colorMatrix);
return;
}
}
@@ -1653,9 +1734,6 @@ void QColorTransformPrivate::applyConvertIn(const S *src, QColorVector *buffer,
else
loadUnpremultipliedLUT(buffer, src, len);
- if constexpr (std::is_same_v<S, QRgbaFloat16> || std::is_same_v<S, QRgbaFloat32>)
- clampIfNeeded<DoClamp>(buffer, len);
-
// Do element based conversion
for (auto &&element : colorSpaceIn->mAB)
std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
@@ -1664,13 +1742,44 @@ void QColorTransformPrivate::applyConvertIn(const S *src, QColorVector *buffer,
template<typename D, typename S>
void QColorTransformPrivate::applyConvertOut(D *dst, const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const
{
- constexpr ApplyMatrixForm doClamp = (std::is_same_v<D, QRgbaFloat16> || std::is_same_v<D, QRgbaFloat32>) ? DoNotClamp : DoClamp;
- // Avoid compiling this part for D=QCmyk32:
- if constexpr (!std::is_same_v<D, QCmyk32>) {
+ constexpr ApplyMatrixForm doClamp = UnclampedValues<D> ? DoNotClamp : DoClamp;
+ if constexpr (IsGrayscale<D>) {
+ Q_UNUSED(src); // dealing with buggy warnings in gcc 9
+ Q_UNUSED(flags);
+ // Calculate the matrix for grayscale conversion
+ QColorMatrix grayMatrix;
+ if (colorSpaceIn == colorSpaceOut ||
+ (colorSpaceIn->colorModel == QColorSpace::ColorModel::Gray &&
+ colorSpaceOut->colorModel == QColorSpace::ColorModel::Gray)) {
+ // colorMatrix already has the right form
+ grayMatrix = colorMatrix;
+ } else {
+ if constexpr (IsGrayscale<S>) {
+ if (colorSpaceIn->colorModel == QColorSpace::ColorModel::Gray)
+ grayMatrix = colorSpaceIn->chad;
+ else
+ grayMatrix = QColorMatrix::identity(); // Otherwise already handled in applyConvertIn
+ } else {
+ if (colorSpaceIn->isThreeComponentMatrix())
+ grayMatrix = colorSpaceIn->toXyz;
+ else
+ grayMatrix = QColorMatrix::identity();
+ }
+ if (!colorSpaceOut->chad.isNull())
+ grayMatrix = colorSpaceOut->chad.inverted() * grayMatrix;
+ }
+
+ applyMatrix<doClamp>(buffer, len, grayMatrix);
+ storeOpaque(dst, buffer, len, this);
+ return;
+ } else if constexpr (CanUseThreeComponent<D>) {
if (colorSpaceOut->isThreeComponentMatrix()) {
- applyMatrix<doClamp>(buffer, len, colorMatrix);
+ if (IsGrayscale<S> && colorSpaceIn->colorModel != QColorSpace::ColorModel::Gray)
+ applyMatrix<doClamp>(buffer, len, colorSpaceOut->toXyz.inverted()); // colorMatrix wasnt prepared for gray input
+ else
+ applyMatrix<doClamp>(buffer, len, colorMatrix);
- if constexpr (std::is_same_v<S, QCmyk32>) {
+ if constexpr (IsAlwaysOpaque<S>) {
storeOpaque(dst, buffer, len, this);
} else {
if (flags & InputOpaque)
@@ -1683,23 +1792,27 @@ void QColorTransformPrivate::applyConvertOut(D *dst, const S *src, QColorVector
return;
}
}
- Q_ASSERT(!colorSpaceOut->isThreeComponentMatrix());
+ if constexpr (!IsGrayscale<D>) {
+ Q_ASSERT(!colorSpaceOut->isThreeComponentMatrix());
- // Do element based conversion
- for (auto &&element : colorSpaceOut->mBA)
- std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
+ // Do element based conversion
+ for (auto &&element : colorSpaceOut->mBA)
+ std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
- clampIfNeeded<doClamp>(buffer, len);
+ clampIfNeeded<doClamp>(buffer, len);
- if (flags & OutputPremultiplied)
- storePremultipliedLUT(dst, src, buffer, len);
- else
- storeUnpremultipliedLUT(dst, src, buffer, len);
+ if (flags & OutputPremultiplied)
+ storePremultipliedLUT(dst, src, buffer, len);
+ else
+ storeUnpremultipliedLUT(dst, src, buffer, len);
+ } else {
+ Q_UNREACHABLE();
+ }
}
/*!
\internal
- Adapt Profile Connecting Color spaces.
+ Adapt Profile Connection Spaces.
*/
void QColorTransformPrivate::pcsAdapt(QColorVector *buffer, qsizetype count) const
{
@@ -1747,133 +1860,6 @@ void QColorTransformPrivate::apply(D *dst, const S *src, qsizetype count, Transf
/*!
\internal
- Is to be called on a color-transform to XYZ, returns only luminance values.
-
- */
-template<typename D, typename S>
-void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const
-{
- Q_ASSERT(colorSpaceOut->isThreeComponentMatrix());
- updateLutsOut();
- if (!colorSpaceIn->isThreeComponentMatrix()) {
- QUninitialized<QColorVector, WorkBlockSize> buffer;
-
- qsizetype i = 0;
- while (i < count) {
- const qsizetype len = qMin(count - i, WorkBlockSize);
-
- applyConvertIn(src, buffer, len, flags);
-
- // Match Profile Connection Spaces (PCS):
- if (colorSpaceOut->isPcsLab && !colorSpaceIn->isPcsLab) {
- for (qsizetype j = 0; j < len; ++j)
- buffer[j] = buffer[j].xyzToLab();
- } else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab) {
- for (qsizetype j = 0; j < len; ++j)
- buffer[j] = buffer[j].labToXyz();
- }
-
- applyMatrix<DoClamp>(buffer, len, colorMatrix);
- storeOpaque(dst + i, buffer, len, this);
-
- i += len;
- }
- return;
- }
- if constexpr (!std::is_same_v<S, QCmyk32>) {
- if (!colorMatrix.isValid())
- return;
-
- updateLutsIn();
-
- QUninitialized<QColorVector, WorkBlockSize> buffer;
-
- qsizetype i = 0;
- while (i < count) {
- const qsizetype len = qMin(count - i, WorkBlockSize);
- if (flags & InputPremultiplied)
- loadPremultiplied(buffer, src + i, len, this);
- else
- loadUnpremultiplied(buffer, src + i, len, this);
-
- applyMatrix<DoClamp>(buffer, len, colorMatrix);
-
- storeOpaque(dst + i, buffer, len, this);
-
- i += len;
- }
- } else {
- Q_UNREACHABLE();
- }
-}
-
-/*!
- \internal
-*/
-template<typename D, typename S>
-void QColorTransformPrivate::applyGray(D *dst, const S *src, qsizetype count, TransformFlags) const
-{
- Q_ASSERT(colorSpaceIn->isThreeComponentMatrix());
- updateLutsIn();
- if constexpr (std::is_same_v<D, QRgb> || std::is_same_v<D, QRgba64> || std::is_same_v<D, QRgbaFloat32> || std::is_same_v<D, QCmyk32>) {
- if (!colorSpaceOut->isThreeComponentMatrix()) {
- QUninitialized<QColorVector, WorkBlockSize> buffer;
-
- qsizetype i = 0;
- while (i < count) {
- const qsizetype len = qMin(count - i, WorkBlockSize);
- loadGray(buffer, src + i, len, this);
-
- applyMatrix<DoClamp>(buffer, len, colorMatrix);
-
- // Match Profile Connection Spaces (PCS):
- if (colorSpaceOut->isPcsLab && !colorSpaceIn->isPcsLab) {
- for (qsizetype j = 0; j < len; ++j)
- buffer[j] = buffer[j].xyzToLab();
- } else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab) {
- for (qsizetype j = 0; j < len; ++j)
- buffer[j] = buffer[j].labToXyz();
- }
-
- // Do element based conversion
- for (auto &&element : colorSpaceOut->mBA)
- std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
-
- clampIfNeeded<DoClamp>(buffer, len);
-
- storeUnpremultipliedLUT(dst, src, buffer, len); // input is always opaque
-
- i += len;
- }
- return;
- }
- }
- Q_ASSERT(colorSpaceOut->isThreeComponentMatrix());
- if constexpr (!std::is_same_v<D, QCmyk32>) {
- if (!colorMatrix.isValid())
- return;
-
- updateLutsOut();
-
- QUninitialized<QColorVector, WorkBlockSize> buffer;
-
- qsizetype i = 0;
- while (i < count) {
- const qsizetype len = qMin(count - i, WorkBlockSize);
- loadGray(buffer, src + i, len, this);
-
- applyMatrix<DoClamp>(buffer, len, colorMatrix);
-
- storeOpaque(dst + i, buffer, len, this);
- i += len;
- }
- } else {
- Q_UNREACHABLE();
- }
-}
-
-/*!
- \internal
\enum QColorTransformPrivate::TransformFlag
Defines how the transform should handle alpha values.
@@ -1900,25 +1886,24 @@ void QColorTransformPrivate::prepare()
updateLutsOut();
}
-// Only allow versions increasing precision
-template void QColorTransformPrivate::applyReturnGray<quint8, QRgb>(quint8 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
-template void QColorTransformPrivate::applyReturnGray<quint8, QCmyk32>(quint8 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
-template void QColorTransformPrivate::applyReturnGray<quint16, QCmyk32>(quint16 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
-template void QColorTransformPrivate::applyReturnGray<quint16, QRgba64>(quint16 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
-template void QColorTransformPrivate::applyGray<quint8, quint8>(quint8 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
-template void QColorTransformPrivate::applyGray<quint16, quint8>(quint16 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
-template void QColorTransformPrivate::applyGray<quint16, quint16>(quint16 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
-template void QColorTransformPrivate::applyGray<QRgb, quint8>(QRgb *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
-template void QColorTransformPrivate::applyGray<QCmyk32, quint8>(QCmyk32 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
-template void QColorTransformPrivate::applyGray<QCmyk32, quint16>(QCmyk32 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
-template void QColorTransformPrivate::applyGray<QRgba64, quint16>(QRgba64 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
-
+// Only some versions increasing precision 14/36 combos
+template void QColorTransformPrivate::apply<quint8, quint8>(quint8 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<quint8, QRgb>(quint8 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<quint8, QCmyk32>(quint8 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<quint16, quint8>(quint16 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<quint16, quint16>(quint16 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<quint16, QCmyk32>(quint16 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<quint16, QRgba64>(quint16 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QRgb, quint8>(QRgb *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
template void QColorTransformPrivate::apply<QRgb, QRgb>(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
template void QColorTransformPrivate::apply<QRgb, QCmyk32>(QRgb *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QCmyk32, quint8>(QCmyk32 *dst, const quint8 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QCmyk32, quint16>(QCmyk32 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
template void QColorTransformPrivate::apply<QCmyk32, QRgb>(QCmyk32 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
template void QColorTransformPrivate::apply<QCmyk32, QCmyk32>(QCmyk32 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
template void QColorTransformPrivate::apply<QCmyk32, QRgba64>(QCmyk32 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
template void QColorTransformPrivate::apply<QCmyk32, QRgbaFloat32>(QCmyk32 *dst, const QRgbaFloat32 *src, qsizetype count, TransformFlags flags) const;
+template void QColorTransformPrivate::apply<QRgba64, quint16>(QRgba64 *dst, const quint16 *src, qsizetype count, TransformFlags flags) const;
template void QColorTransformPrivate::apply<QRgba64, QRgb>(QRgba64 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
template void QColorTransformPrivate::apply<QRgba64, QCmyk32>(QRgba64 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
template void QColorTransformPrivate::apply<QRgba64, QRgba64>(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
diff --git a/src/gui/painting/qcolortransform_p.h b/src/gui/painting/qcolortransform_p.h
index c74fe100eb1..3f116034200 100644
--- a/src/gui/painting/qcolortransform_p.h
+++ b/src/gui/painting/qcolortransform_p.h
@@ -27,7 +27,7 @@ class QCmyk32;
class QColorTransformPrivate : public QSharedData
{
public:
- QColorMatrix colorMatrix;
+ QColorMatrix colorMatrix; // Combined colorSpaceIn->toXyz and colorSpaceOut->toXyz.inverted()
QExplicitlySharedDataPointer<const QColorSpacePrivate> colorSpaceIn;
QExplicitlySharedDataPointer<const QColorSpacePrivate> colorSpaceOut;
@@ -53,10 +53,6 @@ public:
template<typename D, typename S>
void apply(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
- template<typename D, typename S>
- void applyGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
- template<typename D, typename S>
- void applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
private:
void pcsAdapt(QColorVector *buffer, qsizetype len) const;
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index b7a943be38e..aaf57e19d1f 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -644,7 +644,7 @@ static void QT_FASTCALL destStoreGray8(QRasterBuffer *rasterBuffer, int x, int y
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
- tfd->applyReturnGray(data, buffer, length, QColorTransformPrivate::InputPremultiplied);
+ tfd->apply(data, buffer, length, QColorTransformPrivate::InputPremultiplied);
}
}
@@ -668,7 +668,7 @@ static void QT_FASTCALL destStoreGray16(QRasterBuffer *rasterBuffer, int x, int
QRgba64 tmp_line[BufferSize];
for (int k = 0; k < length; ++k)
tmp_line[k] = QRgba64::fromArgb32(buffer[k]);
- tfd->applyReturnGray(data, tmp_line, length, QColorTransformPrivate::InputPremultiplied);
+ tfd->apply(data, tmp_line, length, QColorTransformPrivate::InputPremultiplied);
}
}
@@ -749,7 +749,7 @@ static void QT_FASTCALL destStore64Gray8(QRasterBuffer *rasterBuffer, int x, int
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
quint16 gray_line[BufferSize];
- tfd->applyReturnGray(gray_line, buffer, length, QColorTransformPrivate::InputPremultiplied);
+ tfd->apply(gray_line, buffer, length, QColorTransformPrivate::InputPremultiplied);
for (int k = 0; k < length; ++k)
data[k] = qt_div_257(gray_line[k]);
}
@@ -771,7 +771,7 @@ static void QT_FASTCALL destStore64Gray16(QRasterBuffer *rasterBuffer, int x, in
QColorSpace fromCS = rasterBuffer->colorSpace.isValid() ? rasterBuffer->colorSpace : QColorSpace::SRgb;
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
- tfd->applyReturnGray(data, buffer, length, QColorTransformPrivate::InputPremultiplied);
+ tfd->apply(data, buffer, length, QColorTransformPrivate::InputPremultiplied);
}
}
diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp
index 574911788da..0323dd11959 100644
--- a/src/gui/painting/qicc.cpp
+++ b/src/gui/painting/qicc.cpp
@@ -1993,6 +1993,12 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
if (!parseXyzData(data, it.value(), colorspaceDPtr->whitePoint))
return false;
}
+ if (auto it = tagIndex.constFind(Tag::chad); it != tagIndex.constEnd()) {
+ if (!parseChad(data, it.value(), colorspaceDPtr))
+ return false;
+ } else if (!colorspaceDPtr->whitePoint.isNull()) {
+ colorspaceDPtr->chad = QColorMatrix::chromaticAdaptation(colorspaceDPtr->whitePoint);
+ }
}
if (auto it = tagIndex.constFind(Tag::desc); it != tagIndex.constEnd()) {
diff --git a/tests/auto/gui/image/qimage/images/VideoHD.icc b/tests/auto/gui/image/qimage/images/VideoHD.icc
new file mode 100644
index 00000000000..b96eb68136e
--- /dev/null
+++ b/tests/auto/gui/image/qimage/images/VideoHD.icc
Binary files differ
diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp
index 9d3311c13c9..8086ffcc28e 100644
--- a/tests/auto/gui/image/qimage/tst_qimage.cpp
+++ b/tests/auto/gui/image/qimage/tst_qimage.cpp
@@ -176,6 +176,10 @@ private slots:
void colorSpaceRgbConversion();
void colorSpaceCmykConversion_data();
void colorSpaceCmykConversion();
+ void colorSpaceFromGrayConversion_data();
+ void colorSpaceFromGrayConversion();
+ void colorSpaceToGrayConversion_data();
+ void colorSpaceToGrayConversion();
void deepCopyWhenPaintingActive();
void scaled_QTBUG19157();
@@ -3460,6 +3464,120 @@ void tst_QImage::colorSpaceCmykConversion()
}
}
+void tst_QImage::colorSpaceFromGrayConversion_data()
+{
+ QTest::addColumn<QImage::Format>("fromFormat");
+ QTest::addColumn<QColorSpace>("fromCS");
+ QTest::addColumn<QColorSpace>("toCS");
+
+ QImage::Format formats[] = {
+ QImage::Format_Grayscale8,
+ QImage::Format_Grayscale16,
+ };
+
+ QList<QColorSpace> colorSpaces = {
+ QColorSpace::SRgbLinear,
+ QColorSpace::DisplayP3,
+ QColorSpace(QPointF(0.31271, 0.32902), QColorSpace::TransferFunction::SRgb),
+ QColorSpace(QPointF(0.30, 0.33), QColorSpace::TransferFunction::Linear)
+ };
+ std::string names[] = {
+ "sRgbLinear",
+ "displayP3",
+ "graySRgb",
+ "grayOther",
+ "videoHD(A2B)"
+ };
+
+ QFile iccProfile(m_prefix + "VideoHD.icc");
+ iccProfile.open(QIODevice::ReadOnly);
+ colorSpaces.append(QColorSpace::fromIccProfile(iccProfile.readAll()));
+
+ for (auto fromFormat : formats) {
+ for (int from = 0; from < 5; ++from) {
+ for (int to = 0; to < 4; ++to) {
+ QTest::addRow("%s: %s -> %s", formatToString(fromFormat).data(), names[from].c_str(), names[to].c_str())
+ << fromFormat << colorSpaces[from] << colorSpaces[to];
+ }
+ }
+ }
+}
+
+void tst_QImage::colorSpaceFromGrayConversion()
+{
+ QFETCH(QImage::Format, fromFormat);
+ QFETCH(QColorSpace, fromCS);
+ QFETCH(QColorSpace, toCS);
+
+ QImage image(16, 16, fromFormat);
+ image.setColorSpace(fromCS);
+ QVERIFY(image.colorSpace().isValid());
+
+ for (int i = 0; i < image.height(); ++i) {
+ for (int j = 0; j < image.width(); ++j) {
+ image.setPixel(j, i, qRgb((i + j) * 8, (i + j) * 8, (i + j) * 8));
+ }
+ }
+ QImage imageConverted = image.convertedToColorSpace(toCS);
+ QCOMPARE(imageConverted.format(), fromFormat);
+ QCOMPARE(imageConverted.size(), image.size());
+ int gray = 0;
+ for (int x = 0; x < image.width(); ++x) {
+ int newGray = qGray(imageConverted.pixel(x, 3));
+ QCOMPARE_GE(newGray, gray);
+ gray = newGray;
+ }
+}
+
+void tst_QImage::colorSpaceToGrayConversion_data()
+{
+ QTest::addColumn<QImage::Format>("fromFormat");
+
+ QImage::Format formats[] = {
+ QImage::Format_RGB32,
+ QImage::Format_ARGB32,
+ QImage::Format_ARGB32_Premultiplied,
+ QImage::Format_RGBX64,
+ QImage::Format_RGBA64,
+ QImage::Format_RGBA64_Premultiplied,
+ QImage::Format_RGBX32FPx4,
+ QImage::Format_RGBA32FPx4,
+ QImage::Format_RGBA32FPx4_Premultiplied,
+ QImage::Format_Grayscale8,
+ QImage::Format_Grayscale16,
+ };
+
+ for (auto fromFormat : formats)
+ QTest::addRow("%s -> Gray", formatToString(fromFormat).data()) << fromFormat;
+}
+
+void tst_QImage::colorSpaceToGrayConversion()
+{
+ QFETCH(QImage::Format, fromFormat);
+
+ QImage image(16, 16, fromFormat);
+ image.setColorSpace(QColorSpace::DisplayP3);
+ QVERIFY(image.colorSpace().isValid());
+
+ for (int i = 0; i < image.height(); ++i) {
+ for (int j = 0; j < image.width(); ++j) {
+ image.setPixel(j, i, qRgb((i + j) * 8, (i + j) * 8, (i + j) * 8));
+ }
+ }
+
+ QColorSpace grayColorSpace(QPointF(0.31271, 0.32902), QColorSpace::TransferFunction::SRgb);
+
+ QImage imageConverted = image.convertedToColorSpace(grayColorSpace);
+ QVERIFY(imageConverted.format() == QImage::Format_Grayscale8 || imageConverted.format() == QImage::Format_Grayscale16);
+ QCOMPARE(imageConverted.size(), image.size());
+ int gray = 0;
+ for (int x = 0; x < image.width(); ++x) {
+ int newGray = qGray(imageConverted.pixel(x, 11));
+ QCOMPARE_GE(newGray, gray);
+ gray = newGray;
+ }
+}
+
void tst_QImage::deepCopyWhenPaintingActive()
{
QImage image(64, 64, QImage::Format_ARGB32_Premultiplied);