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:
parent
70dd53e3d3
commit
e673e5a257
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()) {
|
||||
|
BIN
tests/auto/gui/image/qimage/images/VideoHD.icc
Normal file
BIN
tests/auto/gui/image/qimage/images/VideoHD.icc
Normal file
Binary file not shown.
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user