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 <allan.jensen@qt.io>
This commit is contained in:
Allan Sandfeld Jensen 2024-04-25 11:57:58 +02:00
parent 70dd53e3d3
commit e673e5a257
10 changed files with 366 additions and 246 deletions

View File

@ -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:

View File

@ -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;
}

View File

@ -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
}
if (dataCs == QColorSpace::ColorModel::Gray)
return true; // Can apply any CS with white point to Gray data
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 (data == QPixelFormat::ColorModel::Grayscale && tm == QColorSpace::TransformModel::ThreeComponentMatrix)
return true; // Can apply three-component matrix CS to gray output
return false;
}

View File

@ -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;
}

View File

@ -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)
@ -1469,17 +1529,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)
{
@ -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>) {
if (colorSpaceOut->isThreeComponentMatrix()) {
applyMatrix<doClamp>(buffer, len, colorMatrix);
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;
}
if constexpr (std::is_same_v<S, QCmyk32>) {
applyMatrix<doClamp>(buffer, len, grayMatrix);
storeOpaque(dst, buffer, len, this);
return;
} else if constexpr (CanUseThreeComponent<D>) {
if (colorSpaceOut->isThreeComponentMatrix()) {
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 (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
{
@ -1745,133 +1858,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
@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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()) {

Binary file not shown.

View File

@ -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);