Add CMYK support to QColorSpace
[ChangeLog][QtGui][QColorSpace] Support for CMYK color spaces has been added. Change-Id: I2c684dbeee8b97fc90ca4e2a892349a7fa465d06 Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
This commit is contained in:
parent
e794894ece
commit
25c96d547b
@ -21,6 +21,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <qpa/qplatformpixmap.h>
|
#include <qpa/qplatformpixmap.h>
|
||||||
|
#include <private/qcolorspace_p.h>
|
||||||
#include <private/qcolortransform_p.h>
|
#include <private/qcolortransform_p.h>
|
||||||
#include <private/qmemrotate_p.h>
|
#include <private/qmemrotate_p.h>
|
||||||
#include <private/qimagescale_p.h>
|
#include <private/qimagescale_p.h>
|
||||||
@ -45,6 +46,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QCmyk32;
|
||||||
|
|
||||||
using namespace Qt::StringLiterals;
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
@ -2635,6 +2637,9 @@ void QImage::setPixel(int x, int y, uint index_or_rgb)
|
|||||||
case Format_A2RGB30_Premultiplied:
|
case Format_A2RGB30_Premultiplied:
|
||||||
((uint *)s)[x] = qConvertArgb32ToA2rgb30<PixelOrderRGB>(index_or_rgb);
|
((uint *)s)[x] = qConvertArgb32ToA2rgb30<PixelOrderRGB>(index_or_rgb);
|
||||||
return;
|
return;
|
||||||
|
case Format_RGBX64:
|
||||||
|
((QRgba64 *)s)[x] = QRgba64::fromArgb32(index_or_rgb | 0xff000000);
|
||||||
|
return;
|
||||||
case Format_RGBA64:
|
case Format_RGBA64:
|
||||||
case Format_RGBA64_Premultiplied:
|
case Format_RGBA64_Premultiplied:
|
||||||
((QRgba64 *)s)[x] = QRgba64::fromArgb32(index_or_rgb);
|
((QRgba64 *)s)[x] = QRgba64::fromArgb32(index_or_rgb);
|
||||||
@ -5035,6 +5040,8 @@ void QImage::convertToColorSpace(const QColorSpace &colorSpace)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
applyColorTransform(d->colorSpace.transformationToColorSpace(colorSpace));
|
applyColorTransform(d->colorSpace.transformationToColorSpace(colorSpace));
|
||||||
|
if (d->ref.loadRelaxed() != 1)
|
||||||
|
detachMetadata(false);
|
||||||
d->colorSpace = colorSpace;
|
d->colorSpace = colorSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5151,6 +5158,13 @@ void QImage::applyColorTransform(const QColorTransform &transform)
|
|||||||
{
|
{
|
||||||
if (transform.isIdentity())
|
if (transform.isIdentity())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!qt_compatibleColorModel(pixelFormat().colorModel(), QColorTransformPrivate::get(transform)->colorSpaceIn->colorModel) ||
|
||||||
|
!qt_compatibleColorModel(pixelFormat().colorModel(), QColorTransformPrivate::get(transform)->colorSpaceOut->colorModel)) {
|
||||||
|
qWarning() << "QImage::applyColorTransform can not apply format switching transform without switching format";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
detach();
|
detach();
|
||||||
if (!d)
|
if (!d)
|
||||||
return;
|
return;
|
||||||
@ -5169,7 +5183,7 @@ void QImage::applyColorTransform(const QColorTransform &transform)
|
|||||||
&& oldFormat != QImage::Format_RGBA64_Premultiplied)
|
&& oldFormat != QImage::Format_RGBA64_Premultiplied)
|
||||||
convertTo(QImage::Format_RGBA64);
|
convertTo(QImage::Format_RGBA64);
|
||||||
} else if (oldFormat != QImage::Format_ARGB32 && oldFormat != QImage::Format_RGB32
|
} else if (oldFormat != QImage::Format_ARGB32 && oldFormat != QImage::Format_RGB32
|
||||||
&& oldFormat != QImage::Format_ARGB32_Premultiplied
|
&& oldFormat != QImage::Format_ARGB32_Premultiplied && oldFormat != QImage::Format_CMYK8888
|
||||||
&& oldFormat != QImage::Format_Grayscale8 && oldFormat != QImage::Format_Grayscale16) {
|
&& oldFormat != QImage::Format_Grayscale8 && oldFormat != QImage::Format_Grayscale16) {
|
||||||
if (hasAlphaChannel())
|
if (hasAlphaChannel())
|
||||||
convertTo(QImage::Format_ARGB32);
|
convertTo(QImage::Format_ARGB32);
|
||||||
@ -5187,6 +5201,7 @@ void QImage::applyColorTransform(const QColorTransform &transform)
|
|||||||
case Format_Grayscale8:
|
case Format_Grayscale8:
|
||||||
case Format_Grayscale16:
|
case Format_Grayscale16:
|
||||||
case Format_RGB32:
|
case Format_RGB32:
|
||||||
|
case Format_CMYK8888:
|
||||||
case Format_RGBX64:
|
case Format_RGBX64:
|
||||||
case Format_RGBX32FPx4:
|
case Format_RGBX32FPx4:
|
||||||
flags = QColorTransformPrivate::InputOpaque;
|
flags = QColorTransformPrivate::InputOpaque;
|
||||||
@ -5229,6 +5244,13 @@ void QImage::applyColorTransform(const QColorTransform &transform)
|
|||||||
QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
|
QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
} else if (oldFormat == QImage::Format_CMYK8888) {
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
|
QCmyk32 *scanline = reinterpret_cast<QCmyk32 *>(d->data + y * d->bytes_per_line);
|
||||||
|
QColorTransformPrivate::get(transform)->apply(scanline, scanline, width(), flags);
|
||||||
|
}
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
transformSegment = [&](int yStart, int yEnd) {
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
for (int y = yStart; y < yEnd; ++y) {
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
@ -5312,6 +5334,8 @@ QImage QImage::colorTransformed(const QColorTransform &transform) const &
|
|||||||
return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
|
return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
|
||||||
case QColorSpace::ColorModel::Gray:
|
case QColorSpace::ColorModel::Gray:
|
||||||
return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
|
return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
|
||||||
|
case QColorSpace::ColorModel::Cmyk:
|
||||||
|
return colorTransformed(transform, QImage::Format_CMYK8888);
|
||||||
case QColorSpace::ColorModel::Undefined:
|
case QColorSpace::ColorModel::Undefined:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -5410,6 +5434,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
|
|||||||
case QImage::Format_RGBA64_Premultiplied:
|
case QImage::Format_RGBA64_Premultiplied:
|
||||||
case QImage::Format_Grayscale8:
|
case QImage::Format_Grayscale8:
|
||||||
case QImage::Format_Grayscale16:
|
case QImage::Format_Grayscale16:
|
||||||
|
case QImage::Format_CMYK8888:
|
||||||
// can be output natively
|
// can be output natively
|
||||||
break;
|
break;
|
||||||
case QImage::Format_RGB16:
|
case QImage::Format_RGB16:
|
||||||
@ -5419,7 +5444,6 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
|
|||||||
case QImage::Format_RGB888:
|
case QImage::Format_RGB888:
|
||||||
case QImage::Format_BGR888:
|
case QImage::Format_BGR888:
|
||||||
case QImage::Format_RGBX8888:
|
case QImage::Format_RGBX8888:
|
||||||
case QImage::Format_CMYK8888:
|
|
||||||
tmpFormat = QImage::Format_RGB32;
|
tmpFormat = QImage::Format_RGB32;
|
||||||
break;
|
break;
|
||||||
case QImage::Format_Mono:
|
case QImage::Format_Mono:
|
||||||
@ -5486,7 +5510,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
|
|||||||
transformSegment = [&](int yStart, int yEnd) {
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
for (int y = yStart; y < yEnd; ++y) {
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
const quint8 *in_scanline = reinterpret_cast<const quint8 *>(d->data + y * d->bytes_per_line);
|
const quint8 *in_scanline = reinterpret_cast<const quint8 *>(d->data + y * d->bytes_per_line);
|
||||||
QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.bits() + y * toImage.bytesPerLine());
|
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)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -5494,7 +5518,26 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
|
|||||||
transformSegment = [&](int yStart, int yEnd) {
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
for (int y = yStart; y < yEnd; ++y) {
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
const quint16 *in_scanline = reinterpret_cast<const quint16 *>(d->data + y * d->bytes_per_line);
|
const quint16 *in_scanline = reinterpret_cast<const quint16 *>(d->data + y * d->bytes_per_line);
|
||||||
QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.bits() + y * toImage.bytesPerLine());
|
QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
|
QColorTransformPrivate::get(transform)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (inColorData == QColorSpace::ColorModel::Gray && outColorData == QColorSpace::ColorModel::Cmyk) {
|
||||||
|
// Gray -> CMYK
|
||||||
|
if (format() == QImage::Format_Grayscale8) {
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
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)->applyGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -5505,8 +5548,8 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
|
|||||||
fromImage.convertTo(QImage::Format_RGB32);
|
fromImage.convertTo(QImage::Format_RGB32);
|
||||||
transformSegment = [&](int yStart, int yEnd) {
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
for (int y = yStart; y < yEnd; ++y) {
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.bits() + y * fromImage.bytesPerLine());
|
const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.bits() + y * toImage.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)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -5514,12 +5557,91 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
|
|||||||
fromImage.convertTo(QImage::Format_RGBX64);
|
fromImage.convertTo(QImage::Format_RGBX64);
|
||||||
transformSegment = [&](int yStart, int yEnd) {
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
for (int y = yStart; y < yEnd; ++y) {
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.bits() + y * fromImage.bytesPerLine());
|
const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.bits() + y * toImage.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)->applyReturnGray(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} else if (inColorData == QColorSpace::ColorModel::Cmyk && outColorData == QColorSpace::ColorModel::Gray) {
|
||||||
|
// CMYK -> Gray
|
||||||
|
if (tmpFormat == QImage::Format_Grayscale8) {
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (inColorData == QColorSpace::ColorModel::Cmyk && outColorData == QColorSpace::ColorModel::Rgb) {
|
||||||
|
// CMYK -> RGB
|
||||||
|
if (isRgb32Data(tmpFormat) ) {
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
|
const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
|
QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (isRgb64Data(tmpFormat)) {
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
|
const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
|
QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(isRgb32fpx4Data(tmpFormat));
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
|
const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
|
QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), QColorTransformPrivate::InputOpaque);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else if (inColorData == QColorSpace::ColorModel::Rgb && outColorData == QColorSpace::ColorModel::Cmyk) {
|
||||||
|
// RGB -> CMYK
|
||||||
|
if (!fromImage.hasAlphaChannel())
|
||||||
|
transFlags = QColorTransformPrivate::InputOpaque;
|
||||||
|
else if (qPixelLayouts[fromImage.format()].premultiplied)
|
||||||
|
transFlags = QColorTransformPrivate::Premultiplied;
|
||||||
|
if (isRgb32Data(fromImage.format()) ) {
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
|
const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
|
QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (isRgb64Data(fromImage.format())) {
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
|
const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
|
QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
Q_ASSERT(isRgb32fpx4Data(fromImage.format()));
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
|
const QRgbaFloat32 *in_scanline = reinterpret_cast<const QRgbaFloat32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
|
QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -5541,7 +5663,7 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
|
|||||||
&& oldFormat != QImage::Format_RGBA64_Premultiplied && oldFormat != QImage::Format_Grayscale16)
|
&& oldFormat != QImage::Format_RGBA64_Premultiplied && oldFormat != QImage::Format_Grayscale16)
|
||||||
fromImage.convertTo(QImage::Format_RGBA64);
|
fromImage.convertTo(QImage::Format_RGBA64);
|
||||||
} else if (oldFormat != QImage::Format_ARGB32 && oldFormat != QImage::Format_RGB32
|
} else if (oldFormat != QImage::Format_ARGB32 && oldFormat != QImage::Format_RGB32
|
||||||
&& oldFormat != QImage::Format_ARGB32_Premultiplied
|
&& oldFormat != QImage::Format_ARGB32_Premultiplied && oldFormat != QImage::Format_CMYK8888
|
||||||
&& oldFormat != QImage::Format_Grayscale8 && oldFormat != QImage::Format_Grayscale16) {
|
&& oldFormat != QImage::Format_Grayscale8 && oldFormat != QImage::Format_Grayscale16) {
|
||||||
if (hasAlphaChannel())
|
if (hasAlphaChannel())
|
||||||
fromImage.convertTo(QImage::Format_ARGB32);
|
fromImage.convertTo(QImage::Format_ARGB32);
|
||||||
@ -5557,13 +5679,13 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
|
|||||||
if (fromImage.format() == Format_Grayscale8) {
|
if (fromImage.format() == Format_Grayscale8) {
|
||||||
transformSegment = [&](int yStart, int yEnd) {
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
for (int y = yStart; y < yEnd; ++y) {
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
const quint8 *in_scanline = reinterpret_cast<const quint8 *>(fromImage.bits() + y * fromImage.bytesPerLine());
|
const quint8 *in_scanline = reinterpret_cast<const quint8 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
if (tmpFormat == Format_Grayscale8) {
|
if (tmpFormat == Format_Grayscale8) {
|
||||||
quint8 *out_scanline = reinterpret_cast<quint8 *>(toImage.bits() + y * toImage.bytesPerLine());
|
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)->applyGray(out_scanline, in_scanline, width(), transFlags);
|
||||||
} else {
|
} else {
|
||||||
Q_ASSERT(tmpFormat == Format_Grayscale16);
|
Q_ASSERT(tmpFormat == Format_Grayscale16);
|
||||||
quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.bits() + y * toImage.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)->applyGray(out_scanline, in_scanline, width(), transFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5571,30 +5693,39 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
|
|||||||
} else if (fromImage.format() == Format_Grayscale16) {
|
} else if (fromImage.format() == Format_Grayscale16) {
|
||||||
transformSegment = [&](int yStart, int yEnd) {
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
for (int y = yStart; y < yEnd; ++y) {
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
const quint16 *in_scanline = reinterpret_cast<const quint16 *>(fromImage.bits() + y * fromImage.bytesPerLine());
|
const quint16 *in_scanline = reinterpret_cast<const quint16 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
quint16 *out_scanline = reinterpret_cast<quint16 *>(toImage.bits() + y * toImage.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)->applyGray(out_scanline, in_scanline, width(), transFlags);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (isRgb32fpx4Data(fromImage.format())) {
|
} else if (fromImage.format() == Format_CMYK8888) {
|
||||||
|
Q_ASSERT(tmpFormat == Format_CMYK8888);
|
||||||
transformSegment = [&](int yStart, int yEnd) {
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
Q_ASSERT(isRgb32fpx4Data(tmpFormat));
|
|
||||||
for (int y = yStart; y < yEnd; ++y) {
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
const QRgbaFloat32 *in_scanline = reinterpret_cast<const QRgbaFloat32 *>(fromImage.bits() + y * fromImage.bytesPerLine());
|
const QCmyk32 *in_scanline = reinterpret_cast<const QCmyk32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.bits() + y * toImage.bytesPerLine());
|
QCmyk32 *out_scanline = reinterpret_cast<QCmyk32 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else if (isRgb32fpx4Data(fromImage.format())) {
|
||||||
|
Q_ASSERT(isRgb32fpx4Data(tmpFormat));
|
||||||
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
|
const QRgbaFloat32 *in_scanline = reinterpret_cast<const QRgbaFloat32 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
|
QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else if (isRgb64Data(fromImage.format())) {
|
} else if (isRgb64Data(fromImage.format())) {
|
||||||
transformSegment = [&](int yStart, int yEnd) {
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
for (int y = yStart; y < yEnd; ++y) {
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.bits() + y * fromImage.bytesPerLine());
|
const QRgba64 *in_scanline = reinterpret_cast<const QRgba64 *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
if (isRgb32fpx4Data(tmpFormat)) {
|
if (isRgb32fpx4Data(tmpFormat)) {
|
||||||
QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.bits() + y * toImage.bytesPerLine());
|
QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
||||||
} else {
|
} else {
|
||||||
Q_ASSERT(isRgb64Data(tmpFormat));
|
Q_ASSERT(isRgb64Data(tmpFormat));
|
||||||
QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.bits() + y * toImage.bytesPerLine());
|
QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5602,16 +5733,16 @@ QImage QImage::colorTransformed(const QColorTransform &transform, QImage::Format
|
|||||||
} else {
|
} else {
|
||||||
transformSegment = [&](int yStart, int yEnd) {
|
transformSegment = [&](int yStart, int yEnd) {
|
||||||
for (int y = yStart; y < yEnd; ++y) {
|
for (int y = yStart; y < yEnd; ++y) {
|
||||||
const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.bits() + y * fromImage.bytesPerLine());
|
const QRgb *in_scanline = reinterpret_cast<const QRgb *>(fromImage.constBits() + y * fromImage.bytesPerLine());
|
||||||
if (isRgb32fpx4Data(tmpFormat)) {
|
if (isRgb32fpx4Data(tmpFormat)) {
|
||||||
QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.bits() + y * toImage.bytesPerLine());
|
QRgbaFloat32 *out_scanline = reinterpret_cast<QRgbaFloat32 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
||||||
} else if (isRgb64Data(tmpFormat)) {
|
} else if (isRgb64Data(tmpFormat)) {
|
||||||
QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.bits() + y * toImage.bytesPerLine());
|
QRgba64 *out_scanline = reinterpret_cast<QRgba64 *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
||||||
} else {
|
} else {
|
||||||
Q_ASSERT(isRgb32Data(tmpFormat));
|
Q_ASSERT(isRgb32Data(tmpFormat));
|
||||||
QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.bits() + y * toImage.bytesPerLine());
|
QRgb *out_scanline = reinterpret_cast<QRgb *>(toImage.d->data + y * toImage.bytesPerLine());
|
||||||
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
QColorTransformPrivate::get(transform)->apply(out_scanline, in_scanline, width(), transFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5671,6 +5802,8 @@ QImage QImage::colorTransformed(const QColorTransform &transform) &&
|
|||||||
return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
|
return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_RGBX64 : QImage::Format_RGB32);
|
||||||
case QColorSpace::ColorModel::Gray:
|
case QColorSpace::ColorModel::Gray:
|
||||||
return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
|
return colorTransformed(transform, qt_highColorPrecision(format(), true) ? QImage::Format_Grayscale16 : QImage::Format_Grayscale8);
|
||||||
|
case QColorSpace::ColorModel::Cmyk:
|
||||||
|
return colorTransformed(transform, QImage::Format_CMYK8888);
|
||||||
case QColorSpace::ColorModel::Undefined:
|
case QColorSpace::ColorModel::Undefined:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -448,6 +448,8 @@ inline QColorSpace::ColorModel qt_csColorData(QPixelFormat::ColorModel format)
|
|||||||
return QColorSpace::ColorModel::Undefined; // No valid colors
|
return QColorSpace::ColorModel::Undefined; // No valid colors
|
||||||
case QPixelFormat::ColorModel::Grayscale:
|
case QPixelFormat::ColorModel::Grayscale:
|
||||||
return QColorSpace::ColorModel::Gray;
|
return QColorSpace::ColorModel::Gray;
|
||||||
|
case QPixelFormat::ColorModel::CMYK:
|
||||||
|
return QColorSpace::ColorModel::Cmyk;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -467,9 +469,8 @@ inline bool qt_compatibleColorModel(QPixelFormat::ColorModel data, QColorSpace::
|
|||||||
if (dataCs == cs)
|
if (dataCs == cs)
|
||||||
return true; // Matching color models
|
return true; // Matching color models
|
||||||
|
|
||||||
if (cs == QColorSpace::ColorModel::Rgb)
|
if (dataCs == QColorSpace::ColorModel::Gray)
|
||||||
// Can apply RGB CS to Gray data
|
return true; // Can apply any CS with white point to Gray data
|
||||||
return dataCs == QColorSpace::ColorModel::Gray;
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
// A 3-dimensional lookup table compatible with ICC lut8, lut16, mAB, and mBA formats.
|
// A 3/4-dimensional lookup table compatible with ICC lut8, lut16, mAB, and mBA formats.
|
||||||
class QColorCLUT
|
class QColorCLUT
|
||||||
{
|
{
|
||||||
inline static QColorVector interpolate(const QColorVector &a, const QColorVector &b, float t)
|
inline static QColorVector interpolate(const QColorVector &a, const QColorVector &b, float t)
|
||||||
@ -32,45 +32,92 @@ class QColorCLUT
|
|||||||
a += (b - a) * t;
|
a += (b - a) * t;
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
qsizetype gridPointsX = 0;
|
uint32_t gridPointsX = 0;
|
||||||
qsizetype gridPointsY = 0;
|
uint32_t gridPointsY = 0;
|
||||||
qsizetype gridPointsZ = 0;
|
uint32_t gridPointsZ = 0;
|
||||||
|
uint32_t gridPointsW = 1;
|
||||||
QList<QColorVector> table;
|
QList<QColorVector> table;
|
||||||
|
|
||||||
bool isEmpty() const { return table.isEmpty(); }
|
bool isEmpty() const { return table.isEmpty(); }
|
||||||
|
|
||||||
QColorVector apply(const QColorVector &v) const
|
QColorVector apply(const QColorVector &v) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(table.size() == gridPointsX * gridPointsY * gridPointsZ);
|
Q_ASSERT(table.size() == gridPointsX * gridPointsY * gridPointsZ * gridPointsW);
|
||||||
|
QColorVector frac;
|
||||||
const float x = std::clamp(v.x, 0.0f, 1.0f) * (gridPointsX - 1);
|
const float x = std::clamp(v.x, 0.0f, 1.0f) * (gridPointsX - 1);
|
||||||
const float y = std::clamp(v.y, 0.0f, 1.0f) * (gridPointsY - 1);
|
const float y = std::clamp(v.y, 0.0f, 1.0f) * (gridPointsY - 1);
|
||||||
const float z = std::clamp(v.z, 0.0f, 1.0f) * (gridPointsZ - 1);
|
const float z = std::clamp(v.z, 0.0f, 1.0f) * (gridPointsZ - 1);
|
||||||
// Variables for trilinear interpolation
|
const float w = std::clamp(v.w, 0.0f, 1.0f) * (gridPointsW - 1);
|
||||||
const qsizetype lox = static_cast<qsizetype>(std::floor(x));
|
const uint32_t lox = static_cast<uint32_t>(std::floor(x));
|
||||||
const qsizetype hix = std::min(lox + 1, gridPointsX - 1);
|
const uint32_t hix = std::min(lox + 1, gridPointsX - 1);
|
||||||
const qsizetype loy = static_cast<qsizetype>(std::floor(y));
|
const uint32_t loy = static_cast<uint32_t>(std::floor(y));
|
||||||
const qsizetype hiy = std::min(loy + 1, gridPointsY - 1);
|
const uint32_t hiy = std::min(loy + 1, gridPointsY - 1);
|
||||||
const qsizetype loz = static_cast<qsizetype>(std::floor(z));
|
const uint32_t loz = static_cast<uint32_t>(std::floor(z));
|
||||||
const qsizetype hiz = std::min(loz + 1, gridPointsZ - 1);
|
const uint32_t hiz = std::min(loz + 1, gridPointsZ - 1);
|
||||||
const float fracx = x - static_cast<float>(lox);
|
const uint32_t low = static_cast<uint32_t>(std::floor(w));
|
||||||
const float fracy = y - static_cast<float>(loy);
|
const uint32_t hiw = std::min(low + 1, gridPointsW - 1);
|
||||||
const float fracz = z - static_cast<float>(loz);
|
frac.x = x - static_cast<float>(lox);
|
||||||
QColorVector tmp[4];
|
frac.y = y - static_cast<float>(loy);
|
||||||
auto index = [&](qsizetype x, qsizetype y, qsizetype z) { return x * gridPointsZ * gridPointsY + y * gridPointsZ + z; };
|
frac.z = z - static_cast<float>(loz);
|
||||||
|
frac.w = w - static_cast<float>(low);
|
||||||
|
if (gridPointsW > 1) {
|
||||||
|
auto index = [&](qsizetype x, qsizetype y, qsizetype z, qsizetype w) -> qsizetype {
|
||||||
|
return x * gridPointsW * gridPointsZ * gridPointsY
|
||||||
|
+ y * gridPointsW * gridPointsZ
|
||||||
|
+ z * gridPointsW
|
||||||
|
+ w;
|
||||||
|
};
|
||||||
|
QColorVector tmp[8];
|
||||||
|
// interpolate over w
|
||||||
|
tmp[0] = interpolate(table[index(lox, loy, loz, low)],
|
||||||
|
table[index(lox, loy, loz, hiw)], frac.w);
|
||||||
|
tmp[1] = interpolate(table[index(lox, loy, hiz, low)],
|
||||||
|
table[index(lox, loy, hiz, hiw)], frac.w);
|
||||||
|
tmp[2] = interpolate(table[index(lox, hiy, loz, low)],
|
||||||
|
table[index(lox, hiy, loz, hiw)], frac.w);
|
||||||
|
tmp[3] = interpolate(table[index(lox, hiy, hiz, low)],
|
||||||
|
table[index(lox, hiy, hiz, hiw)], frac.w);
|
||||||
|
tmp[4] = interpolate(table[index(hix, loy, loz, low)],
|
||||||
|
table[index(hix, loy, loz, hiw)], frac.w);
|
||||||
|
tmp[5] = interpolate(table[index(hix, loy, hiz, low)],
|
||||||
|
table[index(hix, loy, hiz, hiw)], frac.w);
|
||||||
|
tmp[6] = interpolate(table[index(hix, hiy, loz, low)],
|
||||||
|
table[index(hix, hiy, loz, hiw)], frac.w);
|
||||||
|
tmp[7] = interpolate(table[index(hix, hiy, hiz, low)],
|
||||||
|
table[index(hix, hiy, hiz, hiw)], frac.w);
|
||||||
// interpolate over z
|
// interpolate over z
|
||||||
tmp[0] = interpolate(table[index(lox, loy, loz)],
|
for (int i = 0; i < 4; ++i)
|
||||||
table[index(lox, loy, hiz)], fracz);
|
interpolateIn(tmp[i * 2], tmp[i * 2 + 1], frac.z);
|
||||||
tmp[1] = interpolate(table[index(lox, hiy, loz)],
|
|
||||||
table[index(lox, hiy, hiz)], fracz);
|
|
||||||
tmp[2] = interpolate(table[index(hix, loy, loz)],
|
|
||||||
table[index(hix, loy, hiz)], fracz);
|
|
||||||
tmp[3] = interpolate(table[index(hix, hiy, loz)],
|
|
||||||
table[index(hix, hiy, hiz)], fracz);
|
|
||||||
// interpolate over y
|
// interpolate over y
|
||||||
interpolateIn(tmp[0], tmp[1], fracy);
|
for (int i = 0; i < 2; ++i)
|
||||||
interpolateIn(tmp[2], tmp[3], fracy);
|
interpolateIn(tmp[i * 4], tmp[i * 4 + 2], frac.y);
|
||||||
// interpolate over x
|
// interpolate over x
|
||||||
interpolateIn(tmp[0], tmp[2], fracx);
|
interpolateIn(tmp[0], tmp[4], frac.x);
|
||||||
|
return tmp[0];
|
||||||
|
}
|
||||||
|
auto index = [&](qsizetype x, qsizetype y, qsizetype z) -> qsizetype {
|
||||||
|
return x * gridPointsZ * gridPointsY
|
||||||
|
+ y * gridPointsZ
|
||||||
|
+ z;
|
||||||
|
};
|
||||||
|
QColorVector tmp[8] = {
|
||||||
|
table[index(lox, loy, loz)],
|
||||||
|
table[index(lox, loy, hiz)],
|
||||||
|
table[index(lox, hiy, loz)],
|
||||||
|
table[index(lox, hiy, hiz)],
|
||||||
|
table[index(hix, loy, loz)],
|
||||||
|
table[index(hix, loy, hiz)],
|
||||||
|
table[index(hix, hiy, loz)],
|
||||||
|
table[index(hix, hiy, hiz)]
|
||||||
|
};
|
||||||
|
// interpolate over z
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
interpolateIn(tmp[i * 2], tmp[i * 2 + 1], frac.z);
|
||||||
|
// interpolate over y
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
interpolateIn(tmp[i * 4], tmp[i * 4 + 2], frac.y);
|
||||||
|
// interpolate over x
|
||||||
|
interpolateIn(tmp[0], tmp[4], frac.x);
|
||||||
return tmp[0];
|
return tmp[0];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -28,20 +28,20 @@ class QColorVector
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
QColorVector() = default;
|
QColorVector() = default;
|
||||||
constexpr QColorVector(float x, float y, float z) : x(x), y(y), z(z) { }
|
constexpr QColorVector(float x, float y, float z, float w = 0.0f) noexcept : x(x), y(y), z(z), w(w) { }
|
||||||
explicit constexpr QColorVector(const QPointF &chr) // from XY chromaticity
|
explicit constexpr QColorVector(const QPointF &chr) // from XY chromaticity
|
||||||
: x(chr.x() / chr.y())
|
: x(chr.x() / chr.y())
|
||||||
, y(1.0f)
|
, y(1.0f)
|
||||||
, z((1.0f - chr.x() - chr.y()) / chr.y())
|
, z((1.0f - chr.x() - chr.y()) / chr.y())
|
||||||
{ }
|
{ }
|
||||||
float x = 0.0f; // X, x, L, or red
|
float x = 0.0f; // X, x, L, or red/cyan
|
||||||
float y = 0.0f; // Y, y, a, or green
|
float y = 0.0f; // Y, y, a, or green/magenta
|
||||||
float z = 0.0f; // Z, Y, b, or blue
|
float z = 0.0f; // Z, Y, b, or blue/yellow
|
||||||
float _unused = 0.0f;
|
float w = 0.0f; // unused, or black
|
||||||
|
|
||||||
constexpr bool isNull() const noexcept
|
constexpr bool isNull() const noexcept
|
||||||
{
|
{
|
||||||
return !x && !y && !z;
|
return !x && !y && !z && !w;
|
||||||
}
|
}
|
||||||
bool isValid() const noexcept
|
bool isValid() const noexcept
|
||||||
{
|
{
|
||||||
@ -59,10 +59,10 @@ public:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr QColorVector operator*(float f) const { return QColorVector(x * f, y * f, z * f); }
|
constexpr QColorVector operator*(float f) const { return QColorVector(x * f, y * f, z * f, w * f); }
|
||||||
constexpr QColorVector operator+(const QColorVector &v) const { return QColorVector(x + v.x, y + v.y, z + v.z); }
|
constexpr QColorVector operator+(const QColorVector &v) const { return QColorVector(x + v.x, y + v.y, z + v.z, w + v.w); }
|
||||||
constexpr QColorVector operator-(const QColorVector &v) const { return QColorVector(x - v.x, y - v.y, z - v.z); }
|
constexpr QColorVector operator-(const QColorVector &v) const { return QColorVector(x - v.x, y - v.y, z - v.z, w - v.w); }
|
||||||
void operator+=(const QColorVector &v) { x += v.x; y += v.y; z += v.z; }
|
void operator+=(const QColorVector &v) { x += v.x; y += v.y; z += v.z; w += v.w; }
|
||||||
|
|
||||||
QPointF toChromaticity() const
|
QPointF toChromaticity() const
|
||||||
{
|
{
|
||||||
@ -198,7 +198,8 @@ inline bool comparesEqual(const QColorVector &v1, const QColorVector &v2)
|
|||||||
{
|
{
|
||||||
return (std::abs(v1.x - v2.x) < (1.0f / 2048.0f))
|
return (std::abs(v1.x - v2.x) < (1.0f / 2048.0f))
|
||||||
&& (std::abs(v1.y - v2.y) < (1.0f / 2048.0f))
|
&& (std::abs(v1.y - v2.y) < (1.0f / 2048.0f))
|
||||||
&& (std::abs(v1.z - v2.z) < (1.0f / 2048.0f));
|
&& (std::abs(v1.z - v2.z) < (1.0f / 2048.0f))
|
||||||
|
&& (std::abs(v1.w - v2.w) < (1.0f / 2048.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A matrix mapping 3 value colors.
|
// A matrix mapping 3 value colors.
|
||||||
|
@ -468,6 +468,7 @@ void QColorSpacePrivate::clearElementListProcessingForEdit()
|
|||||||
Q_ASSERT(transferFunction == QColorSpace::TransferFunction::Custom);
|
Q_ASSERT(transferFunction == QColorSpace::TransferFunction::Custom);
|
||||||
|
|
||||||
transformModel = QColorSpace::TransformModel::ThreeComponentMatrix;
|
transformModel = QColorSpace::TransformModel::ThreeComponentMatrix;
|
||||||
|
colorModel = QColorSpace::ColorModel::Rgb;
|
||||||
isPcsLab = false;
|
isPcsLab = false;
|
||||||
mAB.clear();
|
mAB.clear();
|
||||||
mBA.clear();
|
mBA.clear();
|
||||||
@ -576,6 +577,8 @@ void QColorSpacePrivate::clearElementListProcessingForEdit()
|
|||||||
\value Undefined No color model
|
\value Undefined No color model
|
||||||
\value Rgb An RGB color model with red, green, and blue colors. Can apply to RGB and grayscale data.
|
\value Rgb An RGB color model with red, green, and blue colors. Can apply to RGB and grayscale data.
|
||||||
\value Gray A gray scale color model. Can only apply to grayscale data.
|
\value Gray A gray scale color model. Can only apply to grayscale data.
|
||||||
|
\value Cmyk Can only represent color data defined with cyan, magenta, yellow, and black colors.
|
||||||
|
In effect only QImage::Format_CMYK32. Note Cmyk color spaces will be TransformModel::ElementListProcessing.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -1151,7 +1154,8 @@ static bool compareElement(const QColorSpacePrivate::TransferElement &element,
|
|||||||
{
|
{
|
||||||
return element.trc[0] == other.trc[0]
|
return element.trc[0] == other.trc[0]
|
||||||
&& element.trc[1] == other.trc[1]
|
&& element.trc[1] == other.trc[1]
|
||||||
&& element.trc[2] == other.trc[2];
|
&& element.trc[2] == other.trc[2]
|
||||||
|
&& element.trc[3] == other.trc[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool compareElement(const QColorMatrix &element,
|
static bool compareElement(const QColorMatrix &element,
|
||||||
@ -1175,6 +1179,8 @@ static bool compareElement(const QColorCLUT &element,
|
|||||||
return false;
|
return false;
|
||||||
if (element.gridPointsZ != other.gridPointsZ)
|
if (element.gridPointsZ != other.gridPointsZ)
|
||||||
return false;
|
return false;
|
||||||
|
if (element.gridPointsW != other.gridPointsW)
|
||||||
|
return false;
|
||||||
if (element.table.size() != other.table.size())
|
if (element.table.size() != other.table.size())
|
||||||
return false;
|
return false;
|
||||||
for (qsizetype i = 0; i < element.table.size(); ++i) {
|
for (qsizetype i = 0; i < element.table.size(); ++i) {
|
||||||
@ -1231,6 +1237,8 @@ bool QColorSpacePrivate::equals(const QColorSpacePrivate *other) const
|
|||||||
if (!isThreeComponentMatrix()) {
|
if (!isThreeComponentMatrix()) {
|
||||||
if (isPcsLab != other->isPcsLab)
|
if (isPcsLab != other->isPcsLab)
|
||||||
return false;
|
return false;
|
||||||
|
if (colorModel != other->colorModel)
|
||||||
|
return false;
|
||||||
if (mAB.count() != other->mAB.count())
|
if (mAB.count() != other->mAB.count())
|
||||||
return false;
|
return false;
|
||||||
if (mBA.count() != other->mBA.count())
|
if (mBA.count() != other->mBA.count())
|
||||||
|
@ -54,6 +54,7 @@ public:
|
|||||||
Undefined = 0,
|
Undefined = 0,
|
||||||
Rgb = 1,
|
Rgb = 1,
|
||||||
Gray = 2,
|
Gray = 2,
|
||||||
|
Cmyk = 3,
|
||||||
};
|
};
|
||||||
Q_ENUM(ColorModel)
|
Q_ENUM(ColorModel)
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ public:
|
|||||||
|
|
||||||
// Element list processing data:
|
// Element list processing data:
|
||||||
struct TransferElement {
|
struct TransferElement {
|
||||||
QColorTrc trc[3];
|
QColorTrc trc[4];
|
||||||
};
|
};
|
||||||
using Element = std::variant<TransferElement, QColorMatrix, QColorVector, QColorCLUT>;
|
using Element = std::variant<TransferElement, QColorMatrix, QColorVector, QColorCLUT>;
|
||||||
bool isPcsLab = false;
|
bool isPcsLab = false;
|
||||||
|
@ -45,7 +45,7 @@ public:
|
|||||||
Q_ASSERT(qsizetype(size) <= table.size());
|
Q_ASSERT(qsizetype(size) <= table.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isEmpty() const
|
bool isEmpty() const noexcept
|
||||||
{
|
{
|
||||||
return m_tableSize == 0;
|
return m_tableSize == 0;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "qcolortransform.h"
|
#include "qcolortransform.h"
|
||||||
#include "qcolortransform_p.h"
|
#include "qcolortransform_p.h"
|
||||||
|
|
||||||
|
#include "qcmyk_p.h"
|
||||||
#include "qcolorclut_p.h"
|
#include "qcolorclut_p.h"
|
||||||
#include "qcolormatrix_p.h"
|
#include "qcolormatrix_p.h"
|
||||||
#include "qcolorspace_p.h"
|
#include "qcolorspace_p.h"
|
||||||
@ -244,38 +245,31 @@ QColor QColorTransform::map(const QColor &color) const
|
|||||||
if (!d)
|
if (!d)
|
||||||
return color;
|
return color;
|
||||||
QColor clr = color;
|
QColor clr = color;
|
||||||
if (color.spec() != QColor::ExtendedRgb || color.spec() != QColor::Rgb)
|
if (d->colorSpaceIn->colorModel == QColorSpace::ColorModel::Rgb) {
|
||||||
|
if (color.spec() != QColor::ExtendedRgb && color.spec() != QColor::Rgb)
|
||||||
clr = clr.toRgb();
|
clr = clr.toRgb();
|
||||||
|
} else if (d->colorSpaceIn->colorModel == QColorSpace::ColorModel::Cmyk) {
|
||||||
|
if (color.spec() != QColor::Cmyk)
|
||||||
|
clr = clr.toCmyk();
|
||||||
|
}
|
||||||
|
|
||||||
|
QColorVector c =
|
||||||
|
(clr.spec() == QColor::Cmyk)
|
||||||
|
? QColorVector(clr.cyanF(), clr.magentaF(), clr.yellowF(), clr.blackF())
|
||||||
|
: QColorVector(clr.redF(), clr.greenF(), clr.blueF());
|
||||||
|
|
||||||
|
c = d->mapExtended(c);
|
||||||
|
|
||||||
QColorVector c = { (float)clr.redF(), (float)clr.greenF(), (float)clr.blueF() };
|
|
||||||
if (clr.spec() == QColor::ExtendedRgb) {
|
|
||||||
c.x = d->colorSpaceIn->trc[0].applyExtended(c.x);
|
|
||||||
c.y = d->colorSpaceIn->trc[1].applyExtended(c.y);
|
|
||||||
c.z = d->colorSpaceIn->trc[2].applyExtended(c.z);
|
|
||||||
} else {
|
|
||||||
c.x = d->colorSpaceIn->trc[0].apply(c.x);
|
|
||||||
c.y = d->colorSpaceIn->trc[1].apply(c.y);
|
|
||||||
c.z = d->colorSpaceIn->trc[2].apply(c.z);
|
|
||||||
}
|
|
||||||
c = d->colorMatrix.map(c);
|
|
||||||
bool inGamut = c.x >= 0.0f && c.x <= 1.0f && c.y >= 0.0f && c.y <= 1.0f && c.z >= 0.0f && c.z <= 1.0f;
|
|
||||||
if (inGamut) {
|
|
||||||
if (d->colorSpaceOut->lut.generated.loadAcquire()) {
|
|
||||||
c.x = d->colorSpaceOut->lut[0]->fromLinear(c.x);
|
|
||||||
c.y = d->colorSpaceOut->lut[1]->fromLinear(c.y);
|
|
||||||
c.z = d->colorSpaceOut->lut[2]->fromLinear(c.z);
|
|
||||||
} else {
|
|
||||||
c.x = d->colorSpaceOut->trc[0].applyInverse(c.x);
|
|
||||||
c.y = d->colorSpaceOut->trc[1].applyInverse(c.y);
|
|
||||||
c.z = d->colorSpaceOut->trc[2].applyInverse(c.z);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
c.x = d->colorSpaceOut->trc[0].applyInverseExtended(c.x);
|
|
||||||
c.y = d->colorSpaceOut->trc[1].applyInverseExtended(c.y);
|
|
||||||
c.z = d->colorSpaceOut->trc[2].applyInverseExtended(c.z);
|
|
||||||
}
|
|
||||||
QColor out;
|
QColor out;
|
||||||
|
if (d->colorSpaceOut->colorModel == QColorSpace::ColorModel::Cmyk) {
|
||||||
|
c.x = std::clamp(c.x, 0.f, 1.f);
|
||||||
|
c.y = std::clamp(c.y, 0.f, 1.f);
|
||||||
|
c.z = std::clamp(c.z, 0.f, 1.f);
|
||||||
|
c.w = std::clamp(c.w, 0.f, 1.f);
|
||||||
|
out.setCmykF(c.x, c.y, c.z, c.w, color.alphaF());
|
||||||
|
} else {
|
||||||
out.setRgbF(c.x, c.y, c.z, color.alphaF());
|
out.setRgbF(c.x, c.y, c.z, color.alphaF());
|
||||||
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,6 +398,10 @@ template<> float getAlphaF(const QRgb &r)
|
|||||||
{
|
{
|
||||||
return qAlpha(r) * (1.f / 255.f);
|
return qAlpha(r) * (1.f / 255.f);
|
||||||
}
|
}
|
||||||
|
template<> float getAlphaF(const QCmyk32 &)
|
||||||
|
{
|
||||||
|
return 1.f;
|
||||||
|
}
|
||||||
template<> float getAlphaF(const QRgba64 &r)
|
template<> float getAlphaF(const QRgba64 &r)
|
||||||
{
|
{
|
||||||
return r.alpha() * (1.f / 65535.f);
|
return r.alpha() * (1.f / 65535.f);
|
||||||
@ -978,8 +976,7 @@ static void storeOpaque(T *dst, const QColorVector *buffer, const qsizetype len,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void storeOpaque<QRgbaFloat32>(QRgbaFloat32 *dst,
|
void storeOpaque(QRgbaFloat32 *dst, const QColorVector *buffer, const qsizetype len,
|
||||||
const QColorVector *buffer, const qsizetype len,
|
|
||||||
const QColorTransformPrivate *d_ptr)
|
const QColorTransformPrivate *d_ptr)
|
||||||
{
|
{
|
||||||
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
const __m128 vTrcRes = _mm_set1_ps(float(QColorTrcLut::Resolution));
|
||||||
@ -1272,6 +1269,18 @@ void loadUnpremultipliedLUT(QColorVector *buffer, const QRgb *src, const qsizety
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadUnpremultipliedLUT(QColorVector *buffer, const QCmyk32 *src, const qsizetype len)
|
||||||
|
{
|
||||||
|
const float f = 1.0f / 255.f;
|
||||||
|
for (qsizetype i = 0; i < len; ++i) {
|
||||||
|
const QCmyk32 p = src[i];
|
||||||
|
buffer[i].x = (p.cyan() * f);
|
||||||
|
buffer[i].y = (p.magenta() * f);
|
||||||
|
buffer[i].z = (p.yellow() * f);
|
||||||
|
buffer[i].w = (p.black() * f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void loadUnpremultipliedLUT(QColorVector *buffer, const QRgba64 *src, const qsizetype len)
|
void loadUnpremultipliedLUT(QColorVector *buffer, const QRgba64 *src, const qsizetype len)
|
||||||
{
|
{
|
||||||
const float f = 1.0f / 65535.f;
|
const float f = 1.0f / 65535.f;
|
||||||
@ -1302,6 +1311,11 @@ void loadPremultipliedLUT(QColorVector *buffer, const QRgb *src, const qsizetype
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadPremultipliedLUT(QColorVector *, const QCmyk32 *, const qsizetype)
|
||||||
|
{
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
void loadPremultipliedLUT(QColorVector *buffer, const QRgba64 *src, const qsizetype len)
|
void loadPremultipliedLUT(QColorVector *buffer, const QRgba64 *src, const qsizetype len)
|
||||||
{
|
{
|
||||||
for (qsizetype i = 0; i < len; ++i) {
|
for (qsizetype i = 0; i < len; ++i) {
|
||||||
@ -1321,7 +1335,6 @@ void loadPremultipliedLUT(QColorVector *buffer, const QRgbaFloat32 *src, const q
|
|||||||
buffer[i].z = src[i].b * f;
|
buffer[i].z = src[i].b * f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void storeUnpremultipliedLUT(QRgb *dst, const T *, const QColorVector *buffer, const qsizetype len)
|
static void storeUnpremultipliedLUT(QRgb *dst, const T *, const QColorVector *buffer, const qsizetype len)
|
||||||
{
|
{
|
||||||
@ -1344,6 +1357,19 @@ void storeUnpremultipliedLUT(QRgb *dst, const QRgb *src, const QColorVector *buf
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void storeUnpremultipliedLUT(QCmyk32 *dst, const T *, const QColorVector *buffer, const qsizetype len)
|
||||||
|
{
|
||||||
|
for (qsizetype i = 0; i < len; ++i) {
|
||||||
|
const int c = buffer[i].x * 255.f;
|
||||||
|
const int m = buffer[i].y * 255.f;
|
||||||
|
const int y = buffer[i].z * 255.f;
|
||||||
|
const int k = buffer[i].w * 255.f;
|
||||||
|
dst[i] = QCmyk32(c, m, y, k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void storeUnpremultipliedLUT(QRgba64 *dst, const T *,
|
static void storeUnpremultipliedLUT(QRgba64 *dst, const T *,
|
||||||
const QColorVector *buffer, const qsizetype len)
|
const QColorVector *buffer, const qsizetype len)
|
||||||
@ -1408,6 +1434,24 @@ 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)
|
||||||
|
{
|
||||||
|
storeUnpremultipliedLUT(dst, src, buffer, len);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void storePremultipliedLUT(QRgba64 *, const T *, const QColorVector *, const qsizetype);
|
static void storePremultipliedLUT(QRgba64 *, const T *, const QColorVector *, const qsizetype);
|
||||||
|
|
||||||
@ -1423,6 +1467,17 @@ 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<>
|
template<>
|
||||||
void storePremultipliedLUT(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len)
|
void storePremultipliedLUT(QRgba64 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len)
|
||||||
{
|
{
|
||||||
@ -1449,10 +1504,13 @@ static void storePremultipliedLUT(QRgbaFloat32 *dst, const T *src, const QColorV
|
|||||||
|
|
||||||
static void visitElement(const QColorSpacePrivate::TransferElement &element, QColorVector *buffer, const qsizetype len)
|
static void visitElement(const QColorSpacePrivate::TransferElement &element, QColorVector *buffer, const qsizetype len)
|
||||||
{
|
{
|
||||||
|
const bool doW = element.trc[3].isValid();
|
||||||
for (qsizetype i = 0; i < len; ++i) {
|
for (qsizetype i = 0; i < len; ++i) {
|
||||||
buffer[i].x = element.trc[0].apply(buffer[i].x);
|
buffer[i].x = element.trc[0].apply(buffer[i].x);
|
||||||
buffer[i].y = element.trc[1].apply(buffer[i].y);
|
buffer[i].y = element.trc[1].apply(buffer[i].y);
|
||||||
buffer[i].z = element.trc[2].apply(buffer[i].z);
|
buffer[i].z = element.trc[2].apply(buffer[i].z);
|
||||||
|
if (doW)
|
||||||
|
buffer[i].w = element.trc[3].apply(buffer[i].w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1573,6 +1631,8 @@ QColorVector QColorTransformPrivate::mapExtended(QColorVector c) const
|
|||||||
template<typename S>
|
template<typename S>
|
||||||
void QColorTransformPrivate::applyConvertIn(const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const
|
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 (colorSpaceIn->isThreeComponentMatrix()) {
|
if (colorSpaceIn->isThreeComponentMatrix()) {
|
||||||
if (flags & InputPremultiplied)
|
if (flags & InputPremultiplied)
|
||||||
loadPremultiplied(buffer, src, len, this);
|
loadPremultiplied(buffer, src, len, this);
|
||||||
@ -1581,7 +1641,10 @@ void QColorTransformPrivate::applyConvertIn(const S *src, QColorVector *buffer,
|
|||||||
|
|
||||||
if (!colorSpaceOut->isThreeComponentMatrix())
|
if (!colorSpaceOut->isThreeComponentMatrix())
|
||||||
applyMatrix<DoClamp>(buffer, len, colorMatrix); // colorMatrix should have the first half only.
|
applyMatrix<DoClamp>(buffer, len, colorMatrix); // colorMatrix should have the first half only.
|
||||||
} else {
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!colorSpaceIn->isThreeComponentMatrix()) {
|
||||||
if (flags & InputPremultiplied)
|
if (flags & InputPremultiplied)
|
||||||
loadPremultipliedLUT(buffer, src, len);
|
loadPremultipliedLUT(buffer, src, len);
|
||||||
else
|
else
|
||||||
@ -1600,16 +1663,25 @@ template<typename D, typename S>
|
|||||||
void QColorTransformPrivate::applyConvertOut(D *dst, const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const
|
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;
|
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()) {
|
if (colorSpaceOut->isThreeComponentMatrix()) {
|
||||||
applyMatrix<doClamp>(buffer, len, colorMatrix); // colorMatrix should have the latter half only.
|
applyMatrix<doClamp>(buffer, len, colorMatrix); // colorMatrix should have the latter half only.
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<S, QCmyk32>) {
|
||||||
|
storeOpaque(dst, buffer, len, this);
|
||||||
|
} else {
|
||||||
if (flags & InputOpaque)
|
if (flags & InputOpaque)
|
||||||
storeOpaque(dst, buffer, len, this);
|
storeOpaque(dst, buffer, len, this);
|
||||||
else if (flags & OutputPremultiplied)
|
else if (flags & OutputPremultiplied)
|
||||||
storePremultiplied(dst, src, buffer, len, this);
|
storePremultiplied(dst, src, buffer, len, this);
|
||||||
else
|
else
|
||||||
storeUnpremultiplied(dst, src, buffer, len, this);
|
storeUnpremultiplied(dst, src, buffer, len, this);
|
||||||
} else {
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!colorSpaceOut->isThreeComponentMatrix()) {
|
||||||
// Do element based conversion
|
// Do element based conversion
|
||||||
for (auto &&element : colorSpaceOut->mBA)
|
for (auto &&element : colorSpaceOut->mBA)
|
||||||
std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
|
std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
|
||||||
@ -1709,9 +1781,10 @@ void QColorTransformPrivate::applyThreeComponentMatrix(D *dst, const S *src, qsi
|
|||||||
template<typename D, typename S>
|
template<typename D, typename S>
|
||||||
void QColorTransformPrivate::apply(D *dst, const S *src, qsizetype count, TransformFlags flags) const
|
void QColorTransformPrivate::apply(D *dst, const S *src, qsizetype count, TransformFlags flags) const
|
||||||
{
|
{
|
||||||
|
if constexpr (!std::is_same_v<D, QCmyk32> && !std::is_same_v<S, QCmyk32>) {
|
||||||
if (isThreeComponentMatrix())
|
if (isThreeComponentMatrix())
|
||||||
applyThreeComponentMatrix<D, S>(dst, src, count, flags);
|
return applyThreeComponentMatrix<D, S>(dst, src, count, flags);
|
||||||
else
|
}
|
||||||
applyElementListTransform<D, S>(dst, src, count, flags);
|
applyElementListTransform<D, S>(dst, src, count, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1724,20 +1797,24 @@ template<typename D, typename S>
|
|||||||
void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const
|
void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(colorSpaceOut->isThreeComponentMatrix());
|
Q_ASSERT(colorSpaceOut->isThreeComponentMatrix());
|
||||||
|
updateLutsOut();
|
||||||
if (!colorSpaceIn->isThreeComponentMatrix()) {
|
if (!colorSpaceIn->isThreeComponentMatrix()) {
|
||||||
QUninitialized<QColorVector, WorkBlockSize> buffer;
|
QUninitialized<QColorVector, WorkBlockSize> buffer;
|
||||||
|
|
||||||
qsizetype i = 0;
|
qsizetype i = 0;
|
||||||
while (i < count) {
|
while (i < count) {
|
||||||
const qsizetype len = qMin(count - i, WorkBlockSize);
|
const qsizetype len = qMin(count - i, WorkBlockSize);
|
||||||
if (flags & InputPremultiplied)
|
|
||||||
loadPremultipliedLUT(buffer, src + i, len);
|
|
||||||
else
|
|
||||||
loadUnpremultipliedLUT(buffer, src + i, len);
|
|
||||||
|
|
||||||
// Do element based conversion
|
applyConvertIn(src, buffer, len, flags);
|
||||||
for (auto &&element : colorSpaceIn->mAB)
|
|
||||||
std::visit([&](auto &&elm) { visitElement(elm, buffer, len); }, element);
|
// 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);
|
applyMatrix<DoClamp>(buffer, len, colorMatrix);
|
||||||
storeOpaque(dst + i, buffer, len, this);
|
storeOpaque(dst + i, buffer, len, this);
|
||||||
@ -1746,12 +1823,11 @@ void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype cou
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if constexpr (!std::is_same_v<S, QCmyk32>) {
|
||||||
if (!colorMatrix.isValid())
|
if (!colorMatrix.isValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
updateLutsIn();
|
updateLutsIn();
|
||||||
updateLutsOut();
|
|
||||||
|
|
||||||
QUninitialized<QColorVector, WorkBlockSize> buffer;
|
QUninitialized<QColorVector, WorkBlockSize> buffer;
|
||||||
|
|
||||||
@ -1769,6 +1845,9 @@ void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype cou
|
|||||||
|
|
||||||
i += len;
|
i += len;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -1778,7 +1857,8 @@ template<typename D, typename S>
|
|||||||
void QColorTransformPrivate::applyGray(D *dst, const S *src, qsizetype count, TransformFlags) const
|
void QColorTransformPrivate::applyGray(D *dst, const S *src, qsizetype count, TransformFlags) const
|
||||||
{
|
{
|
||||||
Q_ASSERT(colorSpaceIn->isThreeComponentMatrix());
|
Q_ASSERT(colorSpaceIn->isThreeComponentMatrix());
|
||||||
if constexpr (std::is_same_v<D, QRgb> || std::is_same_v<D, QRgba64> || std::is_same_v<D, QRgbaFloat32>) {
|
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()) {
|
if (!colorSpaceOut->isThreeComponentMatrix()) {
|
||||||
QUninitialized<QColorVector, WorkBlockSize> buffer;
|
QUninitialized<QColorVector, WorkBlockSize> buffer;
|
||||||
|
|
||||||
@ -1789,6 +1869,15 @@ void QColorTransformPrivate::applyGray(D *dst, const S *src, qsizetype count, Tr
|
|||||||
|
|
||||||
applyMatrix<DoClamp>(buffer, len, colorMatrix);
|
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
|
// Do element based conversion
|
||||||
for (auto &&element : colorSpaceOut->mBA)
|
for (auto &&element : colorSpaceOut->mBA)
|
||||||
std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
|
std::visit([&buffer, len](auto &&elm) { visitElement(elm, buffer, len); }, element);
|
||||||
@ -1803,10 +1892,10 @@ void QColorTransformPrivate::applyGray(D *dst, const S *src, qsizetype count, Tr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Q_ASSERT(colorSpaceOut->isThreeComponentMatrix());
|
Q_ASSERT(colorSpaceOut->isThreeComponentMatrix());
|
||||||
|
if constexpr (!std::is_same_v<D, QCmyk32>) {
|
||||||
if (!colorMatrix.isValid())
|
if (!colorMatrix.isValid())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
updateLutsIn();
|
|
||||||
updateLutsOut();
|
updateLutsOut();
|
||||||
|
|
||||||
QUninitialized<QColorVector, WorkBlockSize> buffer;
|
QUninitialized<QColorVector, WorkBlockSize> buffer;
|
||||||
@ -1821,6 +1910,9 @@ void QColorTransformPrivate::applyGray(D *dst, const S *src, qsizetype count, Tr
|
|||||||
storeOpaque(dst + i, buffer, len, this);
|
storeOpaque(dst + i, buffer, len, this);
|
||||||
i += len;
|
i += len;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Q_UNREACHABLE();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -1853,17 +1945,28 @@ void QColorTransformPrivate::prepare()
|
|||||||
|
|
||||||
// Only allow versions increasing precision
|
// 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, 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::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<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, 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<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<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;
|
template void QColorTransformPrivate::applyGray<QRgba64, quint16>(QRgba64 *dst, const quint16 *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, 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, 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, QRgb>(QRgba64 *dst, const QRgb *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;
|
template void QColorTransformPrivate::apply<QRgba64, QRgba64>(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
|
||||||
template void QColorTransformPrivate::apply<QRgbaFloat32, QRgb>(QRgbaFloat32 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
|
template void QColorTransformPrivate::apply<QRgbaFloat32, QRgb>(QRgbaFloat32 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const;
|
||||||
|
template void QColorTransformPrivate::apply<QRgbaFloat32, QCmyk32>(QRgbaFloat32 *dst, const QCmyk32 *src, qsizetype count, TransformFlags flags) const;
|
||||||
template void QColorTransformPrivate::apply<QRgbaFloat32, QRgba64>(QRgbaFloat32 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
|
template void QColorTransformPrivate::apply<QRgbaFloat32, QRgba64>(QRgbaFloat32 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
|
||||||
template void QColorTransformPrivate::apply<QRgbaFloat32, QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *src, qsizetype count, TransformFlags flags) const;
|
template void QColorTransformPrivate::apply<QRgbaFloat32, QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *src, qsizetype count, TransformFlags flags) const;
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <QtGui/qrgbafloat.h>
|
#include <QtGui/qrgbafloat.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QCmyk32;
|
||||||
|
|
||||||
class QColorTransformPrivate : public QSharedData
|
class QColorTransformPrivate : public QSharedData
|
||||||
{
|
{
|
||||||
|
@ -297,12 +297,13 @@ static bool isValidIccProfile(const ICCProfileHeader &header)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (header.inputColorSpace != uint(ColorSpaceType::Rgb)
|
if (header.inputColorSpace != uint(ColorSpaceType::Rgb)
|
||||||
&& header.inputColorSpace != uint(ColorSpaceType::Gray)) {
|
&& header.inputColorSpace != uint(ColorSpaceType::Gray)
|
||||||
|
&& header.inputColorSpace != uint(ColorSpaceType::Cmyk)) {
|
||||||
qCInfo(lcIcc, "Unsupported ICC input color space 0x%x", quint32(header.inputColorSpace));
|
qCInfo(lcIcc, "Unsupported ICC input color space 0x%x", quint32(header.inputColorSpace));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (header.pcs != uint(Tag::XYZ_) && header.pcs != uint(Tag::Lab_)) {
|
if (header.pcs != uint(Tag::XYZ_) && header.pcs != uint(Tag::Lab_)) {
|
||||||
qCInfo(lcIcc, "Unsupported ICC profile connection space 0x%x", quint32(header.pcs));
|
qCInfo(lcIcc, "Invalid ICC profile connection space 0x%x", quint32(header.pcs));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -679,13 +680,23 @@ static quint32 parseTRC(const QByteArrayView &tagData, QColorTrc &gamma, QColorT
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
static void parseCLUT(const T *tableData, const float f, QColorCLUT *clut)
|
static void parseCLUT(const T *tableData, const float f, QColorCLUT *clut, uchar outputChannels)
|
||||||
{
|
{
|
||||||
|
if (outputChannels == 4) {
|
||||||
|
for (qsizetype index = 0; index < clut->table.size(); ++index) {
|
||||||
|
QColorVector v(tableData[index * 4 + 0] * f,
|
||||||
|
tableData[index * 4 + 1] * f,
|
||||||
|
tableData[index * 4 + 2] * f,
|
||||||
|
tableData[index * 4 + 3] * f);
|
||||||
|
clut->table[index] = v;
|
||||||
|
};
|
||||||
|
} else {
|
||||||
for (qsizetype index = 0; index < clut->table.size(); ++index) {
|
for (qsizetype index = 0; index < clut->table.size(); ++index) {
|
||||||
QColorVector v(tableData[index * 3 + 0] * f,
|
QColorVector v(tableData[index * 3 + 0] * f,
|
||||||
tableData[index * 3 + 1] * f,
|
tableData[index * 3 + 1] * f,
|
||||||
tableData[index * 3 + 2] * f);
|
tableData[index * 3 + 2] * f);
|
||||||
clut->table[index] = v;
|
clut->table[index] = v;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,6 +734,10 @@ static bool parseLutData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
Q_ASSERT(lut.type == quint32(Tag::mft2));
|
Q_ASSERT(lut.type == quint32(Tag::mft2));
|
||||||
inputTableEntries = lut.inputTableEntries;
|
inputTableEntries = lut.inputTableEntries;
|
||||||
outputTableEntries = lut.outputTableEntries;
|
outputTableEntries = lut.outputTableEntries;
|
||||||
|
if (inputTableEntries < 2 || inputTableEntries > 4096)
|
||||||
|
return false;
|
||||||
|
if (outputTableEntries < 2 || outputTableEntries > 4096)
|
||||||
|
return false;
|
||||||
precision = 2;
|
precision = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -746,12 +761,12 @@ static bool parseLutData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lut.inputChannels != 3) {
|
if (lut.inputChannels != 3 && !(isAb && colorSpacePrivate->colorModel == QColorSpace::ColorModel::Cmyk && lut.inputChannels == 4)) {
|
||||||
qCWarning(lcIcc) << "Unsupported lut8/lut16 input channel count" << lut.inputChannels;
|
qCWarning(lcIcc) << "Unsupported lut8/lut16 input channel count" << lut.inputChannels;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lut.outputChannels != 3) {
|
if (lut.outputChannels != 3 && !(!isAb && colorSpacePrivate->colorModel == QColorSpace::ColorModel::Cmyk && lut.outputChannels == 4)) {
|
||||||
qCWarning(lcIcc) << "Unsupported lut8/lut16 output channel count" << lut.outputChannels;
|
qCWarning(lcIcc) << "Unsupported lut8/lut16 output channel count" << lut.outputChannels;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -782,15 +797,18 @@ static bool parseLutData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
|
|
||||||
clutElement.table.resize(clutTableSize);
|
clutElement.table.resize(clutTableSize);
|
||||||
clutElement.gridPointsX = clutElement.gridPointsY = clutElement.gridPointsZ = lut.clutGridPoints;
|
clutElement.gridPointsX = clutElement.gridPointsY = clutElement.gridPointsZ = lut.clutGridPoints;
|
||||||
|
if (lut.inputChannels == 4)
|
||||||
|
clutElement.gridPointsW = lut.clutGridPoints;
|
||||||
|
|
||||||
if constexpr (std::is_same_v<T, Lut8TagData>) {
|
if constexpr (std::is_same_v<T, Lut8TagData>) {
|
||||||
parseCLUT(tableData, 1.f / 255.f, &clutElement);
|
parseCLUT(tableData, 1.f / 255.f, &clutElement, lut.outputChannels);
|
||||||
} else {
|
} else {
|
||||||
float f = 1.0f / 65535.f;
|
float f = 1.0f / 65535.f;
|
||||||
if (colorSpacePrivate->isPcsLab && isAb) // Legacy lut16 conversion to Lab
|
if (colorSpacePrivate->isPcsLab && isAb) // Legacy lut16 conversion to Lab
|
||||||
f = 1.0f / 65280.f;
|
f = 1.0f / 65280.f;
|
||||||
QList<S> clutTable(clutTableSize * lut.outputChannels);
|
QList<S> clutTable(clutTableSize * lut.outputChannels);
|
||||||
qFromBigEndian<S>(tableData, clutTable.size(), clutTable.data());
|
qFromBigEndian<S>(tableData, clutTable.size(), clutTable.data());
|
||||||
parseCLUT(clutTable.constData(), f, &clutElement);
|
parseCLUT(clutTable.constData(), f, &clutElement, lut.outputChannels);
|
||||||
}
|
}
|
||||||
tableData += clutTableSize * lut.outputChannels * precision;
|
tableData += clutTableSize * lut.outputChannels * precision;
|
||||||
|
|
||||||
@ -846,12 +864,12 @@ static bool parseMabData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mab.inputChannels != 3) {
|
if (mab.inputChannels != 3 && !(isAb && colorSpacePrivate->colorModel == QColorSpace::ColorModel::Cmyk && mab.inputChannels == 4)) {
|
||||||
qCWarning(lcIcc) << "Unsupported mAB/mBA input channel count" << mab.inputChannels;
|
qCWarning(lcIcc) << "Unsupported mAB/mBA input channel count" << mab.inputChannels;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mab.outputChannels != 3) {
|
if (mab.outputChannels != 3 && !(!isAb && colorSpacePrivate->colorModel == QColorSpace::ColorModel::Cmyk && mab.outputChannels == 4)) {
|
||||||
qCWarning(lcIcc) << "Unsupported mAB/mBA output channel count" << mab.outputChannels;
|
qCWarning(lcIcc) << "Unsupported mAB/mBA output channel count" << mab.outputChannels;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -901,7 +919,7 @@ static bool parseMabData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
bool bCurvesAreLinear = true, aCurvesAreLinear = true, mCurvesAreLinear = true;
|
bool bCurvesAreLinear = true, aCurvesAreLinear = true, mCurvesAreLinear = true;
|
||||||
|
|
||||||
// B Curves
|
// B Curves
|
||||||
if (!parseCurves(mab.bCurvesOffset, bTableElement.trc, 3)) {
|
if (!parseCurves(mab.bCurvesOffset, bTableElement.trc, isAb ? mab.outputChannels : mab.inputChannels)) {
|
||||||
qCWarning(lcIcc) << "Invalid B curves";
|
qCWarning(lcIcc) << "Invalid B curves";
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -910,7 +928,7 @@ static bool parseMabData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
|
|
||||||
// A Curves
|
// A Curves
|
||||||
if (mab.aCurvesOffset) {
|
if (mab.aCurvesOffset) {
|
||||||
if (!parseCurves(mab.aCurvesOffset, aTableElement.trc, 3)) {
|
if (!parseCurves(mab.aCurvesOffset, aTableElement.trc, isAb ? mab.inputChannels : mab.outputChannels)) {
|
||||||
qCWarning(lcIcc) << "Invalid A curves";
|
qCWarning(lcIcc) << "Invalid A curves";
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -951,9 +969,10 @@ static bool parseMabData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
|
|
||||||
// CLUT
|
// CLUT
|
||||||
if (mab.clutOffset) {
|
if (mab.clutOffset) {
|
||||||
clutElement.gridPointsX = data[tagEntry.offset + mab.clutOffset];
|
clutElement.gridPointsX = uint8_t(data[tagEntry.offset + mab.clutOffset]);
|
||||||
clutElement.gridPointsY = data[tagEntry.offset + mab.clutOffset + 1];
|
clutElement.gridPointsY = uint8_t(data[tagEntry.offset + mab.clutOffset + 1]);
|
||||||
clutElement.gridPointsZ = data[tagEntry.offset + mab.clutOffset + 2];
|
clutElement.gridPointsZ = uint8_t(data[tagEntry.offset + mab.clutOffset + 2]);
|
||||||
|
clutElement.gridPointsW = std::max(uint8_t(data[tagEntry.offset + mab.clutOffset + 3]), uint8_t(1));
|
||||||
const uchar precision = data[tagEntry.offset + mab.clutOffset + 16];
|
const uchar precision = data[tagEntry.offset + mab.clutOffset + 16];
|
||||||
if (precision > 2 || precision < 1) {
|
if (precision > 2 || precision < 1) {
|
||||||
qCWarning(lcIcc) << "Invalid mAB/mBA element CLUT precision";
|
qCWarning(lcIcc) << "Invalid mAB/mBA element CLUT precision";
|
||||||
@ -963,7 +982,7 @@ static bool parseMabData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
qCWarning(lcIcc) << "Empty CLUT";
|
qCWarning(lcIcc) << "Empty CLUT";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const qsizetype clutTableSize = clutElement.gridPointsX * clutElement.gridPointsY * clutElement.gridPointsZ;
|
const qsizetype clutTableSize = clutElement.gridPointsX * clutElement.gridPointsY * clutElement.gridPointsZ * clutElement.gridPointsW;
|
||||||
if ((mab.clutOffset + 20 + clutTableSize * mab.outputChannels * precision) > tagEntry.size) {
|
if ((mab.clutOffset + 20 + clutTableSize * mab.outputChannels * precision) > tagEntry.size) {
|
||||||
qCWarning(lcIcc) << "CLUT oversized for tag";
|
qCWarning(lcIcc) << "CLUT oversized for tag";
|
||||||
return false;
|
return false;
|
||||||
@ -973,10 +992,10 @@ static bool parseMabData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
if (precision == 2) {
|
if (precision == 2) {
|
||||||
QList<uint16_t> clutTable(clutTableSize * mab.outputChannels);
|
QList<uint16_t> clutTable(clutTableSize * mab.outputChannels);
|
||||||
qFromBigEndian<uint16_t>(data.constData() + tagEntry.offset + mab.clutOffset + 20, clutTable.size(), clutTable.data());
|
qFromBigEndian<uint16_t>(data.constData() + tagEntry.offset + mab.clutOffset + 20, clutTable.size(), clutTable.data());
|
||||||
parseCLUT(clutTable.constData(), (1.f/65535.f), &clutElement);
|
parseCLUT(clutTable.constData(), (1.f/65535.f), &clutElement, mab.outputChannels);
|
||||||
} else {
|
} else {
|
||||||
const uint8_t *clutTable = reinterpret_cast<const uint8_t *>(data.constData() + tagEntry.offset + mab.clutOffset + 20);
|
const uint8_t *clutTable = reinterpret_cast<const uint8_t *>(data.constData() + tagEntry.offset + mab.clutOffset + 20);
|
||||||
parseCLUT(clutTable, (1.f/255.f), &clutElement);
|
parseCLUT(clutTable, (1.f/255.f), &clutElement, mab.outputChannels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -987,7 +1006,7 @@ static bool parseMabData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
if (!clutElement.isEmpty())
|
if (!clutElement.isEmpty())
|
||||||
colorSpacePrivate->mAB.append(std::move(clutElement));
|
colorSpacePrivate->mAB.append(std::move(clutElement));
|
||||||
}
|
}
|
||||||
if (mab.mCurvesOffset) {
|
if (mab.mCurvesOffset && mab.outputChannels == 3) {
|
||||||
if (!mCurvesAreLinear)
|
if (!mCurvesAreLinear)
|
||||||
colorSpacePrivate->mAB.append(std::move(mTableElement));
|
colorSpacePrivate->mAB.append(std::move(mTableElement));
|
||||||
if (!matrixElement.isIdentity())
|
if (!matrixElement.isIdentity())
|
||||||
@ -1000,7 +1019,7 @@ static bool parseMabData(const QByteArray &data, const TagEntry &tagEntry, QColo
|
|||||||
} else {
|
} else {
|
||||||
if (!bCurvesAreLinear)
|
if (!bCurvesAreLinear)
|
||||||
colorSpacePrivate->mBA.append(std::move(bTableElement));
|
colorSpacePrivate->mBA.append(std::move(bTableElement));
|
||||||
if (mab.mCurvesOffset) {
|
if (mab.mCurvesOffset && mab.inputChannels == 3) {
|
||||||
if (!matrixElement.isIdentity())
|
if (!matrixElement.isIdentity())
|
||||||
colorSpacePrivate->mBA.append(std::move(matrixElement));
|
colorSpacePrivate->mBA.append(std::move(matrixElement));
|
||||||
if (!offsetElement.isNull())
|
if (!offsetElement.isNull())
|
||||||
@ -1285,11 +1304,11 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
|
|||||||
// Check the profile is three-component matrix based:
|
// Check the profile is three-component matrix based:
|
||||||
if (!tagIndex.contains(Tag::rXYZ) || !tagIndex.contains(Tag::gXYZ) || !tagIndex.contains(Tag::bXYZ) ||
|
if (!tagIndex.contains(Tag::rXYZ) || !tagIndex.contains(Tag::gXYZ) || !tagIndex.contains(Tag::bXYZ) ||
|
||||||
!tagIndex.contains(Tag::rTRC) || !tagIndex.contains(Tag::gTRC) || !tagIndex.contains(Tag::bTRC) ||
|
!tagIndex.contains(Tag::rTRC) || !tagIndex.contains(Tag::gTRC) || !tagIndex.contains(Tag::bTRC) ||
|
||||||
!tagIndex.contains(Tag::wtpt)) {
|
!tagIndex.contains(Tag::wtpt) || header.pcs == uint(Tag::Lab_)) {
|
||||||
threeComponentMatrix = false;
|
threeComponentMatrix = false;
|
||||||
// Check if the profile is valid n-LUT based:
|
// Check if the profile is valid n-LUT based:
|
||||||
if (!tagIndex.contains(Tag::A2B0)) {
|
if (!tagIndex.contains(Tag::A2B0)) {
|
||||||
qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - neither valid three component nor LUT";
|
qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - neither valid three component nor n-LUT";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1298,6 +1317,12 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
|
|||||||
qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - not valid gray scale based";
|
qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - not valid gray scale based";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if (header.inputColorSpace == uint(ColorSpaceType::Cmyk)) {
|
||||||
|
threeComponentMatrix = false;
|
||||||
|
if (!tagIndex.contains(Tag::A2B0)) {
|
||||||
|
qCWarning(lcIcc) << "fromIccProfile: Invalid ICC profile - CMYK, not n-LUT";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Q_UNREACHABLE();
|
Q_UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -1338,6 +1363,9 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
|
|||||||
} else {
|
} else {
|
||||||
colorspaceDPtr->isPcsLab = (header.pcs == uint(Tag::Lab_));
|
colorspaceDPtr->isPcsLab = (header.pcs == uint(Tag::Lab_));
|
||||||
colorspaceDPtr->transformModel = QColorSpace::TransformModel::ElementListProcessing;
|
colorspaceDPtr->transformModel = QColorSpace::TransformModel::ElementListProcessing;
|
||||||
|
if (header.inputColorSpace == uint(ColorSpaceType::Cmyk))
|
||||||
|
colorspaceDPtr->colorModel = QColorSpace::ColorModel::Cmyk;
|
||||||
|
else
|
||||||
colorspaceDPtr->colorModel = QColorSpace::ColorModel::Rgb;
|
colorspaceDPtr->colorModel = QColorSpace::ColorModel::Rgb;
|
||||||
|
|
||||||
// Only parse the default perceptual transform for now
|
// Only parse the default perceptual transform for now
|
||||||
|
BIN
tests/auto/gui/image/qimage/images/CGATS001Compat-v2-micro.icc
Normal file
BIN
tests/auto/gui/image/qimage/images/CGATS001Compat-v2-micro.icc
Normal file
Binary file not shown.
@ -171,6 +171,8 @@ private slots:
|
|||||||
|
|
||||||
void colorSpaceRgbConversion_data();
|
void colorSpaceRgbConversion_data();
|
||||||
void colorSpaceRgbConversion();
|
void colorSpaceRgbConversion();
|
||||||
|
void colorSpaceCmykConversion_data();
|
||||||
|
void colorSpaceCmykConversion();
|
||||||
|
|
||||||
void deepCopyWhenPaintingActive();
|
void deepCopyWhenPaintingActive();
|
||||||
void scaled_QTBUG19157();
|
void scaled_QTBUG19157();
|
||||||
@ -3322,6 +3324,69 @@ void tst_QImage::colorSpaceRgbConversion()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tst_QImage::colorSpaceCmykConversion_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QImage::Format>("toFormat");
|
||||||
|
|
||||||
|
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 toFormat : formats)
|
||||||
|
QTest::addRow("CMYK8888 -> %s", formatToString(toFormat).data()) << toFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QImage::colorSpaceCmykConversion()
|
||||||
|
{
|
||||||
|
QFETCH(QImage::Format, toFormat);
|
||||||
|
|
||||||
|
bool dstGrayscale = toFormat == QImage::Format_Grayscale8 || toFormat == QImage::Format_Grayscale16;
|
||||||
|
|
||||||
|
QImage image(16, 16, QImage::Format_CMYK8888);
|
||||||
|
QFile iccProfile(m_prefix +"CGATS001Compat-v2-micro.icc");
|
||||||
|
iccProfile.open(QIODevice::ReadOnly);
|
||||||
|
image.setColorSpace(QColorSpace::fromIccProfile(iccProfile.readAll()));
|
||||||
|
QVERIFY(image.colorSpace().isValid());
|
||||||
|
|
||||||
|
for (int i = 0; i < image.height(); ++i) {
|
||||||
|
for (int j = 0; j < image.width(); ++j) {
|
||||||
|
if (dstGrayscale)
|
||||||
|
image.setPixel(j, i, qRgb((i + j) * 8, (i + j) * 8, (i + j) * 8));
|
||||||
|
else
|
||||||
|
image.setPixel(j, i, qRgb(j * 16, i * 16, (i + j) * 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage imageConverted = image.convertedToColorSpace(QColorSpace::SRgb, toFormat);
|
||||||
|
QCOMPARE(imageConverted.format(), toFormat);
|
||||||
|
QCOMPARE(imageConverted.size(), image.size());
|
||||||
|
if (dstGrayscale) {
|
||||||
|
int gray = 0;
|
||||||
|
for (int x = 0; x < image.width(); ++x) {
|
||||||
|
int newGray = qGray(imageConverted.pixel(x, 6));
|
||||||
|
QCOMPARE_GE(newGray, gray);
|
||||||
|
gray = newGray;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int red = 0;
|
||||||
|
for (int x = 0; x < image.width(); ++x) {
|
||||||
|
int newRed = qRed(imageConverted.pixel(x, 5));
|
||||||
|
QCOMPARE_GE(newRed, red);
|
||||||
|
red = newRed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QImage::deepCopyWhenPaintingActive()
|
void tst_QImage::deepCopyWhenPaintingActive()
|
||||||
{
|
{
|
||||||
QImage image(64, 64, QImage::Format_ARGB32_Premultiplied);
|
QImage image(64, 64, QImage::Format_ARGB32_Premultiplied);
|
||||||
|
Binary file not shown.
BIN
tests/auto/gui/painting/qcolorspace/resources/sGrey-v4.icc
Normal file
BIN
tests/auto/gui/painting/qcolorspace/resources/sGrey-v4.icc
Normal file
Binary file not shown.
@ -188,21 +188,41 @@ void tst_QColorSpace::fromIccProfile_data()
|
|||||||
QTest::addColumn<QString>("testProfile");
|
QTest::addColumn<QString>("testProfile");
|
||||||
QTest::addColumn<QColorSpace::NamedColorSpace>("namedColorSpace");
|
QTest::addColumn<QColorSpace::NamedColorSpace>("namedColorSpace");
|
||||||
QTest::addColumn<QColorSpace::TransferFunction>("transferFunction");
|
QTest::addColumn<QColorSpace::TransferFunction>("transferFunction");
|
||||||
|
QTest::addColumn<QColorSpace::TransformModel>("transformModel");
|
||||||
|
QTest::addColumn<QColorSpace::ColorModel>("colorModel");
|
||||||
QTest::addColumn<QString>("description");
|
QTest::addColumn<QString>("description");
|
||||||
|
|
||||||
QString prefix = QFINDTESTDATA("resources/");
|
QString prefix = QFINDTESTDATA("resources/");
|
||||||
// Read the official sRGB ICCv2 profile:
|
// Read the official sRGB ICCv2 profile:
|
||||||
QTest::newRow("sRGB2014 (ICCv2)") << prefix + "sRGB2014.icc" << QColorSpace::SRgb
|
QTest::newRow("sRGB2014 (ICCv2)") << prefix + "sRGB2014.icc" << QColorSpace::SRgb
|
||||||
<< QColorSpace::TransferFunction::SRgb << QString("sRGB2014");
|
<< QColorSpace::TransferFunction::SRgb
|
||||||
|
<< QColorSpace::TransformModel::ThreeComponentMatrix
|
||||||
|
<< QColorSpace::ColorModel::Rgb << QString("sRGB2014");
|
||||||
// My monitor's profile:
|
// My monitor's profile:
|
||||||
QTest::newRow("HP ZR30w (ICCv4)") << prefix + "HP_ZR30w.icc" << QColorSpace::NamedColorSpace(0)
|
QTest::newRow("HP ZR30w (ICCv4)") << prefix + "HP_ZR30w.icc" << QColorSpace::NamedColorSpace(0)
|
||||||
<< QColorSpace::TransferFunction::Gamma << QString("HP Z30i");
|
<< QColorSpace::TransferFunction::Gamma
|
||||||
|
<< QColorSpace::TransformModel::ThreeComponentMatrix
|
||||||
|
<< QColorSpace::ColorModel::Rgb << QString("HP Z30i");
|
||||||
// A profile to HD TV
|
// A profile to HD TV
|
||||||
QTest::newRow("VideoHD") << prefix + "VideoHD.icc" << QColorSpace::NamedColorSpace(0)
|
QTest::newRow("VideoHD") << prefix + "VideoHD.icc" << QColorSpace::NamedColorSpace(0)
|
||||||
<< QColorSpace::TransferFunction::Custom << QString("HDTV (Rec. 709)");
|
<< QColorSpace::TransferFunction::Custom
|
||||||
|
<< QColorSpace::TransformModel::ElementListProcessing
|
||||||
|
<< QColorSpace::ColorModel::Rgb << QString("HDTV (Rec. 709)");
|
||||||
// sRGB on PCSLab format
|
// sRGB on PCSLab format
|
||||||
QTest::newRow("sRGB ICCv4 Appearance") << prefix + "sRGB_ICC_v4_Appearance.icc" << QColorSpace::NamedColorSpace(0)
|
QTest::newRow("sRGB ICCv4 Appearance") << prefix + "sRGB_ICC_v4_Appearance.icc" << QColorSpace::NamedColorSpace(0)
|
||||||
<< QColorSpace::TransferFunction::Custom << QString("sRGB_ICC_v4_Appearance.icc");
|
<< QColorSpace::TransferFunction::Custom
|
||||||
|
<< QColorSpace::TransformModel::ElementListProcessing
|
||||||
|
<< QColorSpace::ColorModel::Rgb << QString("sRGB_ICC_v4_Appearance.icc");
|
||||||
|
// Grayscale profile
|
||||||
|
QTest::newRow("sGrey-v4") << prefix + "sGrey-v4.icc" << QColorSpace::NamedColorSpace(0)
|
||||||
|
<< QColorSpace::TransferFunction::SRgb
|
||||||
|
<< QColorSpace::TransformModel::ThreeComponentMatrix
|
||||||
|
<< QColorSpace::ColorModel::Gray << QString("sGry");
|
||||||
|
// CMYK profile
|
||||||
|
QTest::newRow("CGATS compat") << prefix + "CGATS001Compat-v2-micro.icc" << QColorSpace::NamedColorSpace(0)
|
||||||
|
<< QColorSpace::TransferFunction::Custom
|
||||||
|
<< QColorSpace::TransformModel::ElementListProcessing
|
||||||
|
<< QColorSpace::ColorModel::Cmyk << QString("uCMY");
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QColorSpace::fromIccProfile()
|
void tst_QColorSpace::fromIccProfile()
|
||||||
@ -210,6 +230,8 @@ void tst_QColorSpace::fromIccProfile()
|
|||||||
QFETCH(QString, testProfile);
|
QFETCH(QString, testProfile);
|
||||||
QFETCH(QColorSpace::NamedColorSpace, namedColorSpace);
|
QFETCH(QColorSpace::NamedColorSpace, namedColorSpace);
|
||||||
QFETCH(QColorSpace::TransferFunction, transferFunction);
|
QFETCH(QColorSpace::TransferFunction, transferFunction);
|
||||||
|
QFETCH(QColorSpace::TransformModel, transformModel);
|
||||||
|
QFETCH(QColorSpace::ColorModel, colorModel);
|
||||||
QFETCH(QString, description);
|
QFETCH(QString, description);
|
||||||
|
|
||||||
QFile file(testProfile);
|
QFile file(testProfile);
|
||||||
@ -222,6 +244,8 @@ void tst_QColorSpace::fromIccProfile()
|
|||||||
QCOMPARE(fileColorSpace, namedColorSpace);
|
QCOMPARE(fileColorSpace, namedColorSpace);
|
||||||
|
|
||||||
QCOMPARE(fileColorSpace.transferFunction(), transferFunction);
|
QCOMPARE(fileColorSpace.transferFunction(), transferFunction);
|
||||||
|
QCOMPARE(fileColorSpace.transformModel(), transformModel);
|
||||||
|
QCOMPARE(fileColorSpace.colorModel(), colorModel);
|
||||||
QCOMPARE(fileColorSpace.description(), description);
|
QCOMPARE(fileColorSpace.description(), description);
|
||||||
|
|
||||||
QByteArray iccProfile2 = fileColorSpace.iccProfile();
|
QByteArray iccProfile2 = fileColorSpace.iccProfile();
|
||||||
|
@ -4,9 +4,25 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include <QGuiApplication>
|
#include <QGuiApplication>
|
||||||
|
#include <QColor>
|
||||||
#include <QColorSpace>
|
#include <QColorSpace>
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
|
|
||||||
|
static QImage::Format toFormat(QColorSpace::ColorModel model)
|
||||||
|
{
|
||||||
|
switch (model) {
|
||||||
|
case QColorSpace::ColorModel::Rgb:
|
||||||
|
return QImage::Format_RGB32;
|
||||||
|
case QColorSpace::ColorModel::Gray:
|
||||||
|
return QImage::Format_Grayscale16;
|
||||||
|
case QColorSpace::ColorModel::Cmyk:
|
||||||
|
return QImage::Format_CMYK8888;
|
||||||
|
case QColorSpace::ColorModel::Undefined:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return QImage::Format_Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
extern "C" int LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
||||||
// to reduce noise and increase speed
|
// to reduce noise and increase speed
|
||||||
static char quiet[] = "QT_LOGGING_RULES=qt.gui.icc=false";
|
static char quiet[] = "QT_LOGGING_RULES=qt.gui.icc=false";
|
||||||
@ -27,9 +43,9 @@ extern "C" int LLVMFuzzerTestOneInput(const char *data, size_t size) {
|
|||||||
cs2.setDescription("Hello");
|
cs2.setDescription("Hello");
|
||||||
bool b = (cs == cs2);
|
bool b = (cs == cs2);
|
||||||
Q_UNUSED(b);
|
Q_UNUSED(b);
|
||||||
QRgb color = 0xfaf8fa00;
|
QColor color(0xfaf8fa00);
|
||||||
color = trans1.map(color);
|
color = trans1.map(color);
|
||||||
QImage img(16, 2, cs.colorModel() == QColorSpace::ColorModel::Rgb ? QImage::Format_RGB32 : QImage::Format_Grayscale8);
|
QImage img(16, 2, toFormat(cs.colorModel()));
|
||||||
img.setColorSpace(cs);
|
img.setColorSpace(cs);
|
||||||
QImage img2 = img.convertedToColorSpace(QColorSpace::SRgb);
|
QImage img2 = img.convertedToColorSpace(QColorSpace::SRgb);
|
||||||
if (cs.isValidTarget()) {
|
if (cs.isValidTarget()) {
|
||||||
|
@ -79,10 +79,11 @@ bool ImageViewer::loadFile(const QString &fileName)
|
|||||||
|
|
||||||
void ImageViewer::setImage(const QImage &newImage)
|
void ImageViewer::setImage(const QImage &newImage)
|
||||||
{
|
{
|
||||||
|
if (newImage.colorSpace().isValid())
|
||||||
|
image = newImage.convertedToColorSpace(QColorSpace::SRgb);
|
||||||
|
else
|
||||||
image = newImage;
|
image = newImage;
|
||||||
if (image.colorSpace().isValid())
|
imageLabel->setPixmap(QPixmap::fromImage(image, Qt::NoFormatConversion));
|
||||||
image.convertToColorSpace(QColorSpace::SRgb);
|
|
||||||
imageLabel->setPixmap(QPixmap::fromImage(image));
|
|
||||||
//! [4]
|
//! [4]
|
||||||
scaleFactor = 1.0;
|
scaleFactor = 1.0;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user