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