Correct RGB to Grayscale conversion
The existing conversions weren't handling gamma correctly and used an ad-hoc definition of gray instead of based on true luminance. [ChangeLog][QtGui] RGB conversions to grayscale formats are now gamma-corrected and produce color-space luminance values Change-Id: I88ab870c8f5e502ddb053e6a14a75102239a26f2 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
parent
b254855aa6
commit
f1983dcdf6
@ -38,6 +38,7 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include <private/qguiapplication_p.h>
|
||||
#include <private/qcolortransform_p.h>
|
||||
#include <private/qcolortrclut_p.h>
|
||||
#include <private/qdrawhelper_p.h>
|
||||
#include <private/qendian_p.h>
|
||||
@ -1284,10 +1285,81 @@ static void convert_gray16_to_RGBA64(QImageData *dest, const QImageData *src, Qt
|
||||
}
|
||||
}
|
||||
|
||||
static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
|
||||
template<bool Premultiplied>
|
||||
static void convert_ARGB_to_gray8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
|
||||
{
|
||||
Q_ASSERT(dest->format == QImage::Format_Grayscale8);
|
||||
Q_ASSERT(src->format == QImage::Format_RGB32 ||
|
||||
src->format == QImage::Format_ARGB32 ||
|
||||
src->format == QImage::Format_ARGB32_Premultiplied);
|
||||
Q_ASSERT(src->width == dest->width);
|
||||
Q_ASSERT(src->height == dest->height);
|
||||
|
||||
const qsizetype sbpl = src->bytes_per_line;
|
||||
const qsizetype dbpl = dest->bytes_per_line;
|
||||
const uchar *src_data = src->data;
|
||||
uchar *dest_data = dest->data;
|
||||
|
||||
QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
|
||||
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
|
||||
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
|
||||
QColorTransformPrivate::TransformFlags flags = Premultiplied
|
||||
? QColorTransformPrivate::InputPremultiplied
|
||||
: QColorTransformPrivate::Unpremultiplied;
|
||||
|
||||
for (int i = 0; i < src->height; ++i) {
|
||||
const QRgb *src_line = reinterpret_cast<const QRgb *>(src_data);
|
||||
tfd->apply(dest_data, src_line, src->width, flags);
|
||||
src_data += sbpl;
|
||||
dest_data += dbpl;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Premultiplied>
|
||||
static void convert_ARGB_to_gray16(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
|
||||
{
|
||||
Q_ASSERT(dest->format == QImage::Format_Grayscale16);
|
||||
Q_ASSERT(src->format == QImage::Format_RGB32 ||
|
||||
src->format == QImage::Format_ARGB32 ||
|
||||
src->format == QImage::Format_ARGB32_Premultiplied);
|
||||
Q_ASSERT(src->width == dest->width);
|
||||
Q_ASSERT(src->height == dest->height);
|
||||
|
||||
const qsizetype sbpl = src->bytes_per_line;
|
||||
const qsizetype dbpl = dest->bytes_per_line;
|
||||
const uchar *src_data = src->data;
|
||||
uchar *dest_data = dest->data;
|
||||
|
||||
QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
|
||||
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
|
||||
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
|
||||
QColorTransformPrivate::TransformFlags flags = Premultiplied
|
||||
? QColorTransformPrivate::InputPremultiplied
|
||||
: QColorTransformPrivate::Unpremultiplied;
|
||||
|
||||
QRgba64 tmp_line[BufferSize];
|
||||
for (int i = 0; i < src->height; ++i) {
|
||||
const QRgb *src_line = reinterpret_cast<const QRgb *>(src_data);
|
||||
quint16 *dest_line = reinterpret_cast<quint16 *>(dest_data);
|
||||
int j = 0;
|
||||
while (j < src->width) {
|
||||
const int len = std::min(src->width - j, BufferSize);
|
||||
for (int k = 0; k < len; ++k)
|
||||
tmp_line[k] = QRgba64::fromArgb32(src_line[j + k]);
|
||||
tfd->apply(dest_line + j, tmp_line, len, flags);
|
||||
j += len;
|
||||
}
|
||||
src_data += sbpl;
|
||||
dest_data += dbpl;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Premultiplied>
|
||||
static void convert_RGBA64_to_gray8(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
|
||||
{
|
||||
Q_ASSERT(dest->format == QImage::Format_Grayscale8);
|
||||
Q_ASSERT(src->format == QImage::Format_RGBX64 ||
|
||||
src->format == QImage::Format_RGBA64 ||
|
||||
src->format == QImage::Format_RGBA64_Premultiplied);
|
||||
Q_ASSERT(src->width == dest->width);
|
||||
Q_ASSERT(src->height == dest->height);
|
||||
@ -1297,13 +1369,56 @@ static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt
|
||||
const uchar *src_data = src->data;
|
||||
uchar *dest_data = dest->data;
|
||||
|
||||
QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
|
||||
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
|
||||
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
|
||||
QColorTransformPrivate::TransformFlags flags = Premultiplied
|
||||
? QColorTransformPrivate::InputPremultiplied
|
||||
: QColorTransformPrivate::Unpremultiplied;
|
||||
|
||||
quint16 gray_line[BufferSize];
|
||||
for (int i = 0; i < src->height; ++i) {
|
||||
const QRgba64 *src_line = reinterpret_cast<const QRgba64 *>(src_data);
|
||||
uchar *dest_line = dest_data;
|
||||
int j = 0;
|
||||
while (j < src->width) {
|
||||
const int len = std::min(src->width - j, BufferSize);
|
||||
tfd->apply(gray_line, src_line + j, len, flags);
|
||||
for (int k = 0; k < len; ++k)
|
||||
dest_line[j + k] = qt_div_257(gray_line[k]);
|
||||
j += len;
|
||||
}
|
||||
src_data += sbpl;
|
||||
dest_data += dbpl;
|
||||
}
|
||||
}
|
||||
|
||||
template<bool Premultiplied>
|
||||
static void convert_RGBA64_to_gray16(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
|
||||
{
|
||||
Q_ASSERT(dest->format == QImage::Format_Grayscale16);
|
||||
Q_ASSERT(src->format == QImage::Format_RGBX64 ||
|
||||
src->format == QImage::Format_RGBA64 ||
|
||||
src->format == QImage::Format_RGBA64_Premultiplied);
|
||||
Q_ASSERT(src->width == dest->width);
|
||||
Q_ASSERT(src->height == dest->height);
|
||||
|
||||
const qsizetype sbpl = src->bytes_per_line;
|
||||
const qsizetype dbpl = dest->bytes_per_line;
|
||||
const uchar *src_data = src->data;
|
||||
uchar *dest_data = dest->data;
|
||||
|
||||
QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
|
||||
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
|
||||
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
|
||||
QColorTransformPrivate::TransformFlags flags = Premultiplied
|
||||
? QColorTransformPrivate::InputPremultiplied
|
||||
: QColorTransformPrivate::Unpremultiplied;
|
||||
|
||||
for (int i = 0; i < src->height; ++i) {
|
||||
const QRgba64 *src_line = reinterpret_cast<const QRgba64 *>(src_data);
|
||||
quint16 *dest_line = reinterpret_cast<quint16 *>(dest_data);
|
||||
for (int j = 0; j < src->width; ++j) {
|
||||
QRgba64 s = src_line[j].unpremultiplied();
|
||||
dest_line[j] = qGray(s.red(), s.green(), s.blue());
|
||||
}
|
||||
tfd->apply(dest_line, src_line, src->width, flags);
|
||||
src_data += sbpl;
|
||||
dest_data += dbpl;
|
||||
}
|
||||
@ -2072,23 +2187,29 @@ static void convert_Indexed8_to_Grayscale8(QImageData *dest, const QImageData *s
|
||||
uchar translate[256];
|
||||
const QList<QRgb> &colors = src->colortable;
|
||||
bool simpleCase = (colors.size() == 256);
|
||||
for (int i = 0; i < colors.size(); ++i) {
|
||||
uchar gray = qGray(colors[i]);
|
||||
translate[i] = gray;
|
||||
simpleCase = simpleCase && (gray == i);
|
||||
for (int i = 0; i < colors.size() && simpleCase; ++i) {
|
||||
if (colors[i] != qRgb(i, i, i))
|
||||
simpleCase = false;
|
||||
}
|
||||
if (simpleCase) {
|
||||
copy_8bit_pixels(dest, src);
|
||||
return;
|
||||
}
|
||||
|
||||
if (simpleCase)
|
||||
copy_8bit_pixels(dest, src);
|
||||
else {
|
||||
const uchar *sdata = src->data;
|
||||
uchar *ddata = dest->data;
|
||||
for (int y = 0; y < src->height; ++y) {
|
||||
for (int x = 0; x < src->width; ++x)
|
||||
ddata[x] = translate[sdata[x]];
|
||||
sdata += src->bytes_per_line;
|
||||
ddata += dest->bytes_per_line;
|
||||
}
|
||||
QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
|
||||
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
|
||||
for (int i = 0; i < colors.size(); ++i) {
|
||||
QRgba64 c16 = tf.map(QRgba64::fromArgb32(colors[i]));
|
||||
translate[i] = c16.green8(); // Y from XYZ ends up in the G channel
|
||||
}
|
||||
|
||||
const uchar *sdata = src->data;
|
||||
uchar *ddata = dest->data;
|
||||
for (int y = 0; y < src->height; ++y) {
|
||||
for (int x = 0; x < src->width; ++x)
|
||||
ddata[x] = translate[sdata[x]];
|
||||
sdata += src->bytes_per_line;
|
||||
ddata += dest->bytes_per_line;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2120,7 +2241,7 @@ static bool convert_Indexed8_to_Grayscale8_inplace(QImageData *data, Qt::ImageCo
|
||||
if (colors.size() != 256)
|
||||
return false;
|
||||
for (int i = 0; i < colors.size(); ++i) {
|
||||
if (i != qGray(colors[i]))
|
||||
if (colors[i] != qRgb(i, i, i))
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2206,6 +2327,8 @@ static void qInitImageConversions()
|
||||
qimage_converter_map[QImage::Format_RGB32][QImage::Format_Indexed8] = convert_RGB_to_Indexed8;
|
||||
qimage_converter_map[QImage::Format_RGB32][QImage::Format_ARGB32] = mask_alpha_converter;
|
||||
qimage_converter_map[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = mask_alpha_converter;
|
||||
qimage_converter_map[QImage::Format_RGB32][QImage::Format_Grayscale8] = convert_ARGB_to_gray8<false>;
|
||||
qimage_converter_map[QImage::Format_RGB32][QImage::Format_Grayscale16] = convert_ARGB_to_gray16<false>;
|
||||
|
||||
qimage_converter_map[QImage::Format_ARGB32][QImage::Format_Mono] = convert_X_to_Mono;
|
||||
qimage_converter_map[QImage::Format_ARGB32][QImage::Format_MonoLSB] = convert_X_to_Mono;
|
||||
@ -2217,11 +2340,15 @@ static void qInitImageConversions()
|
||||
qimage_converter_map[QImage::Format_ARGB32][QImage::Format_A2BGR30_Premultiplied] = convert_ARGB_to_A2RGB30<PixelOrderBGR, false>;
|
||||
qimage_converter_map[QImage::Format_ARGB32][QImage::Format_A2RGB30_Premultiplied] = convert_ARGB_to_A2RGB30<PixelOrderRGB, false>;
|
||||
qimage_converter_map[QImage::Format_ARGB32][QImage::Format_RGBA64] = convert_ARGB32_to_RGBA64<false>;
|
||||
qimage_converter_map[QImage::Format_ARGB32][QImage::Format_Grayscale8] = convert_ARGB_to_gray8<false>;
|
||||
qimage_converter_map[QImage::Format_ARGB32][QImage::Format_Grayscale16] = convert_ARGB_to_gray16<false>;
|
||||
|
||||
qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_Mono] = convert_ARGB_PM_to_Mono;
|
||||
qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_MonoLSB] = convert_ARGB_PM_to_Mono;
|
||||
qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_Indexed8] = convert_ARGB_PM_to_Indexed8;
|
||||
qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGBA8888_Premultiplied] = convert_ARGB_to_RGBA;
|
||||
qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_Grayscale8] = convert_ARGB_to_gray8<true>;
|
||||
qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_Grayscale16] = convert_ARGB_to_gray16<true>;
|
||||
|
||||
qimage_converter_map[QImage::Format_RGB888][QImage::Format_RGB32] = convert_RGB888_to_RGB<false>;
|
||||
qimage_converter_map[QImage::Format_RGB888][QImage::Format_ARGB32] = convert_RGB888_to_RGB<false>;
|
||||
@ -2271,13 +2398,17 @@ static void qInitImageConversions()
|
||||
|
||||
qimage_converter_map[QImage::Format_RGBX64][QImage::Format_RGBA64] = convert_passthrough;
|
||||
qimage_converter_map[QImage::Format_RGBX64][QImage::Format_RGBA64_Premultiplied] = convert_passthrough;
|
||||
qimage_converter_map[QImage::Format_RGBX64][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16;
|
||||
qimage_converter_map[QImage::Format_RGBX64][QImage::Format_Grayscale8] = convert_RGBA64_to_gray8<false>;
|
||||
qimage_converter_map[QImage::Format_RGBX64][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16<false>;
|
||||
|
||||
qimage_converter_map[QImage::Format_RGBA64][QImage::Format_ARGB32] = convert_RGBA64_to_ARGB32<false>;
|
||||
qimage_converter_map[QImage::Format_RGBA64][QImage::Format_RGBA8888] = convert_RGBA64_to_ARGB32<true>;
|
||||
qimage_converter_map[QImage::Format_RGBA64][QImage::Format_RGBX64] = convert_RGBA64_to_RGBx64;
|
||||
qimage_converter_map[QImage::Format_RGBA64][QImage::Format_Grayscale8] = convert_RGBA64_to_gray8<false>;
|
||||
qimage_converter_map[QImage::Format_RGBA64][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16<false>;
|
||||
|
||||
qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16;
|
||||
qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_Grayscale8] = convert_RGBA64_to_gray8<true>;
|
||||
qimage_converter_map[QImage::Format_RGBA64_Premultiplied][QImage::Format_Grayscale16] = convert_RGBA64_to_gray16<true>;
|
||||
|
||||
qimage_converter_map[QImage::Format_Grayscale16][QImage::Format_RGBX64] = convert_gray16_to_RGBA64;
|
||||
qimage_converter_map[QImage::Format_Grayscale16][QImage::Format_RGBA64] = convert_gray16_to_RGBA64;
|
||||
|
@ -454,6 +454,17 @@ QColorTransform QColorSpacePrivate::transformationToColorSpace(const QColorSpace
|
||||
return combined;
|
||||
}
|
||||
|
||||
QColorTransform QColorSpacePrivate::transformationToXYZ() const
|
||||
{
|
||||
QColorTransform transform;
|
||||
auto ptr = new QColorTransformPrivate;
|
||||
transform.d = ptr;
|
||||
ptr->colorSpaceIn = this;
|
||||
ptr->colorSpaceOut = this;
|
||||
ptr->colorMatrix = toXyz;
|
||||
return transform;
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QColorSpace
|
||||
\brief The QColorSpace class provides a color space abstraction.
|
||||
|
@ -120,6 +120,7 @@ public:
|
||||
const QList<uint16_t> &greenTransferFunctionTable,
|
||||
const QList<uint16_t> &blueTransferFunctionTable);
|
||||
QColorTransform transformationToColorSpace(const QColorSpacePrivate *out) const;
|
||||
QColorTransform transformationToXYZ() const;
|
||||
|
||||
static constexpr QColorSpace::NamedColorSpace Unknown = QColorSpace::NamedColorSpace(0);
|
||||
QColorSpace::NamedColorSpace namedColorSpace = Unknown;
|
||||
|
@ -602,6 +602,22 @@ static void storeOpaque(QRgba64 *dst, const QRgba64 *src, const QColorVector *bu
|
||||
}
|
||||
}
|
||||
|
||||
static void storeGray(quint8 *dst, const QRgb *src, const QColorVector *buffer, const qsizetype len,
|
||||
const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
Q_UNUSED(src);
|
||||
for (qsizetype i = 0; i < len; ++i)
|
||||
dst[i] = d_ptr->colorSpaceOut->lut[1]->u8FromLinearF32(buffer[i].y);
|
||||
}
|
||||
|
||||
static void storeGray(quint16 *dst, const QRgba64 *src, const QColorVector *buffer, const qsizetype len,
|
||||
const QColorTransformPrivate *d_ptr)
|
||||
{
|
||||
Q_UNUSED(src);
|
||||
for (qsizetype i = 0; i < len; ++i)
|
||||
dst[i] = d_ptr->colorSpaceOut->lut[1]->u16FromLinearF32(buffer[i].y);
|
||||
}
|
||||
|
||||
static constexpr qsizetype WorkBlockSize = 256;
|
||||
|
||||
template <typename T, int Count = 1>
|
||||
@ -648,6 +664,33 @@ void QColorTransformPrivate::apply(T *dst, const T *src, qsizetype count, Transf
|
||||
}
|
||||
}
|
||||
|
||||
template<typename D, typename S>
|
||||
void QColorTransformPrivate::applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const
|
||||
{
|
||||
if (!colorMatrix.isValid())
|
||||
return;
|
||||
|
||||
updateLutsIn();
|
||||
updateLutsOut();
|
||||
|
||||
QUninitialized<QColorVector, WorkBlockSize> buffer;
|
||||
|
||||
qsizetype i = 0;
|
||||
while (i < count) {
|
||||
const qsizetype len = qMin(count - i, WorkBlockSize);
|
||||
if (flags & InputPremultiplied)
|
||||
loadPremultiplied(buffer, src + i, len, this);
|
||||
else
|
||||
loadUnpremultiplied(buffer, src + i, len, this);
|
||||
|
||||
applyMatrix(buffer, len, colorMatrix);
|
||||
|
||||
storeGray(dst + i, src + i, buffer, len, this);
|
||||
|
||||
i += len;
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\enum QColorTransformPrivate::TransformFlag
|
||||
@ -708,5 +751,25 @@ void QColorTransformPrivate::apply(QRgba64 *dst, const QRgba64 *src, qsizetype c
|
||||
apply<QRgba64>(dst, src, count, flags);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Is to be called on a color-transform to XYZ, returns only luminance values.
|
||||
|
||||
*/
|
||||
void QColorTransformPrivate::apply(quint8 *dst, const QRgb *src, qsizetype count, TransformFlags flags) const
|
||||
{
|
||||
applyReturnGray<quint8, QRgb>(dst, src, count, flags);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Is to be called on a color-transform to XYZ, returns only luminance values.
|
||||
|
||||
*/
|
||||
void QColorTransformPrivate::apply(quint16 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const
|
||||
{
|
||||
applyReturnGray<quint16, QRgba64>(dst, src, count, flags);
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -75,6 +75,7 @@ public:
|
||||
private:
|
||||
friend class QColorSpace;
|
||||
friend class QColorSpacePrivate;
|
||||
friend class QColorTransformPrivate;
|
||||
friend class QImage;
|
||||
|
||||
QExplicitlySharedDataPointer<QColorTransformPrivate> d;
|
||||
|
@ -65,6 +65,9 @@ public:
|
||||
QExplicitlySharedDataPointer<const QColorSpacePrivate> colorSpaceIn;
|
||||
QExplicitlySharedDataPointer<const QColorSpacePrivate> colorSpaceOut;
|
||||
|
||||
static QColorTransformPrivate *get(const QColorTransform &q)
|
||||
{ return q.d.data(); }
|
||||
|
||||
void updateLutsIn() const;
|
||||
void updateLutsOut() const;
|
||||
bool simpleGammaCorrection() const;
|
||||
@ -81,9 +84,15 @@ public:
|
||||
|
||||
void apply(QRgb *dst, const QRgb *src, qsizetype count, TransformFlags flags = Unpremultiplied) const;
|
||||
void apply(QRgba64 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags = Unpremultiplied) const;
|
||||
void apply(quint8 *dst, const QRgb *src, qsizetype count, TransformFlags flags = Unpremultiplied) const;
|
||||
void apply(quint16 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags = Unpremultiplied) const;
|
||||
|
||||
template<typename T>
|
||||
void apply(T *dst, const T *src, qsizetype count, TransformFlags flags) const;
|
||||
|
||||
template<typename D, typename S>
|
||||
void applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
|
||||
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include <qstylehints.h>
|
||||
#include <qguiapplication.h>
|
||||
#include <qatomic.h>
|
||||
#include <private/qcolortransform_p.h>
|
||||
#include <private/qcolortrclut_p.h>
|
||||
#include <private/qdrawhelper_p.h>
|
||||
#include <private/qdrawhelper_x86_p.h>
|
||||
@ -334,6 +335,51 @@ static void QT_FASTCALL destStore(QRasterBuffer *rasterBuffer, int x, int y, con
|
||||
store(dest, buffer, x, length, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static void QT_FASTCALL destStoreGray8(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
|
||||
{
|
||||
uchar *data = rasterBuffer->scanLine(y) + x;
|
||||
|
||||
bool failed = false;
|
||||
for (int k = 0; k < length; ++k) {
|
||||
if (!qIsGray(buffer[k])) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
data[k] = qRed(buffer[k]);
|
||||
}
|
||||
if (failed) { // Non-gray colors
|
||||
QColorSpace fromCS = rasterBuffer->colorSpace.isValid() ? rasterBuffer->colorSpace : QColorSpace::SRgb;
|
||||
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
|
||||
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
|
||||
|
||||
tfd->apply(data, buffer, length, QColorTransformPrivate::InputPremultiplied);
|
||||
}
|
||||
}
|
||||
|
||||
static void QT_FASTCALL destStoreGray16(QRasterBuffer *rasterBuffer, int x, int y, const uint *buffer, int length)
|
||||
{
|
||||
quint16 *data = reinterpret_cast<quint16 *>(rasterBuffer->scanLine(y)) + x;
|
||||
|
||||
bool failed = false;
|
||||
for (int k = 0; k < length; ++k) {
|
||||
if (!qIsGray(buffer[k])) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
data[k] = qRed(buffer[k]) * 257;
|
||||
}
|
||||
if (failed) { // Non-gray colors
|
||||
QColorSpace fromCS = rasterBuffer->colorSpace.isValid() ? rasterBuffer->colorSpace : QColorSpace::SRgb;
|
||||
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
|
||||
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
|
||||
|
||||
QRgba64 tmp_line[BufferSize];
|
||||
for (int k = 0; k < length; ++k)
|
||||
tmp_line[k] = QRgba64::fromArgb32(buffer[k]);
|
||||
tfd->apply(data, tmp_line, length, QColorTransformPrivate::InputPremultiplied);
|
||||
}
|
||||
}
|
||||
|
||||
static DestStoreProc destStoreProc[QImage::NImageFormats] =
|
||||
{
|
||||
nullptr, // Format_Invalid
|
||||
@ -360,11 +406,11 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] =
|
||||
destStore, // Format_RGB30
|
||||
destStore, // Format_A2RGB30_Premultiplied
|
||||
destStore, // Format_Alpha8
|
||||
destStore, // Format_Grayscale8
|
||||
destStoreGray8, // Format_Grayscale8
|
||||
destStore, // Format_RGBX64
|
||||
destStore, // Format_RGBA64
|
||||
destStore, // Format_RGBA64_Premultiplied
|
||||
destStore, // Format_Grayscale16
|
||||
destStoreGray16, // Format_Grayscale16
|
||||
destStore, // Format_BGR888
|
||||
};
|
||||
|
||||
@ -384,6 +430,50 @@ static void QT_FASTCALL destStore64RGBA64(QRasterBuffer *rasterBuffer, int x, in
|
||||
}
|
||||
}
|
||||
|
||||
static void QT_FASTCALL destStore64Gray8(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
|
||||
{
|
||||
uchar *data = rasterBuffer->scanLine(y) + x;
|
||||
|
||||
bool failed = false;
|
||||
for (int k = 0; k < length; ++k) {
|
||||
if (buffer[k].red() != buffer[k].green() || buffer[k].red() != buffer[k].blue()) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
data[k] = buffer[k].red8();
|
||||
}
|
||||
if (failed) { // Non-gray colors
|
||||
QColorSpace fromCS = rasterBuffer->colorSpace.isValid() ? rasterBuffer->colorSpace : QColorSpace::SRgb;
|
||||
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
|
||||
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
|
||||
|
||||
quint16 gray_line[BufferSize];
|
||||
tfd->apply(gray_line, buffer, length, QColorTransformPrivate::InputPremultiplied);
|
||||
for (int k = 0; k < length; ++k)
|
||||
data[k] = qt_div_257(gray_line[k]);
|
||||
}
|
||||
}
|
||||
|
||||
static void QT_FASTCALL destStore64Gray16(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
|
||||
{
|
||||
quint16 *data = reinterpret_cast<quint16 *>(rasterBuffer->scanLine(y)) + x;
|
||||
|
||||
bool failed = false;
|
||||
for (int k = 0; k < length; ++k) {
|
||||
if (buffer[k].red() != buffer[k].green() || buffer[k].red() != buffer[k].blue()) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
data[k] = buffer[k].red();
|
||||
}
|
||||
if (failed) { // Non-gray colors
|
||||
QColorSpace fromCS = rasterBuffer->colorSpace.isValid() ? rasterBuffer->colorSpace : QColorSpace::SRgb;
|
||||
QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
|
||||
QColorTransformPrivate *tfd = QColorTransformPrivate::get(tf);
|
||||
tfd->apply(data, buffer, length, QColorTransformPrivate::InputPremultiplied);
|
||||
}
|
||||
}
|
||||
|
||||
static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
|
||||
{
|
||||
nullptr, // Format_Invalid
|
||||
@ -410,11 +500,11 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
|
||||
destStore64, // Format_RGB30
|
||||
destStore64, // Format_A2RGB30_Premultiplied
|
||||
destStore64, // Format_Alpha8
|
||||
destStore64, // Format_Grayscale8
|
||||
destStore64Gray8, // Format_Grayscale8
|
||||
nullptr, // Format_RGBX64
|
||||
destStore64RGBA64, // Format_RGBA64
|
||||
nullptr, // Format_RGBA64_Premultiplied
|
||||
destStore64, // Format_Grayscale16
|
||||
destStore64Gray16, // Format_Grayscale16
|
||||
destStore64, // Format_BGR888
|
||||
};
|
||||
#endif
|
||||
|
@ -3788,6 +3788,7 @@ QImage::Format QRasterBuffer::prepare(QImage *image)
|
||||
bytes_per_line = image->bytesPerLine();
|
||||
|
||||
format = image->format();
|
||||
colorSpace = image->colorSpace();
|
||||
if (image->depth() == 1 && image->colorTable().size() == 2) {
|
||||
monoDestinationWithClut = true;
|
||||
const QList<QRgb> colorTable = image->colorTable();
|
||||
|
@ -450,6 +450,7 @@ public:
|
||||
|
||||
QPainter::CompositionMode compositionMode;
|
||||
QImage::Format format;
|
||||
QColorSpace colorSpace;
|
||||
QImage colorizeBitmap(const QImage &image, const QColor &color);
|
||||
|
||||
private:
|
||||
|
@ -2951,21 +2951,29 @@ void tst_QImage::genericRgbConversion()
|
||||
|
||||
QImage image(16, 16, format);
|
||||
|
||||
for (int i = 0; i < image.height(); ++i)
|
||||
for (int j = 0; j < image.width(); ++j)
|
||||
image.setPixel(j, i, qRgb(j*16, i*16, 0));
|
||||
for (int i = 0; i < image.height(); ++i) {
|
||||
for (int j = 0; j < image.width(); ++j) {
|
||||
if (srcGrayscale || 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.convertToFormat(dest_format);
|
||||
uint mask = std::min(image.depth(), imageConverted.depth()) < 32 ? 0xFFF0F0F0 : 0xFFFFFFFF;
|
||||
if (srcGrayscale || dstGrayscale)
|
||||
mask = std::max(image.depth(), imageConverted.depth()) < 32 ? 0xFFF0F0F0 : 0xFFFFFFFF;
|
||||
if (srcGrayscale && dstGrayscale)
|
||||
mask = 0xFFFFFFFF;
|
||||
QCOMPARE(imageConverted.format(), dest_format);
|
||||
for (int i = 0; i < imageConverted.height(); ++i) {
|
||||
for (int j = 0; j < imageConverted.width(); ++j) {
|
||||
QRgb convertedColor = imageConverted.pixel(j,i);
|
||||
if (srcGrayscale || dstGrayscale) {
|
||||
QVERIFY(qAbs(qGray(convertedColor) - qGray(qRgb(j*16, i*16, 0))) < 15);
|
||||
} else {
|
||||
QCOMPARE(qRed(convertedColor) & 0xF0, j * 16);
|
||||
QCOMPARE(qGreen(convertedColor) & 0xF0, i * 16);
|
||||
}
|
||||
if (srcGrayscale || dstGrayscale)
|
||||
QCOMPARE(convertedColor & mask, qRgb((i + j) * 8, (i + j) * 8, (i + j) * 8) & mask);
|
||||
else
|
||||
QCOMPARE(convertedColor & mask, qRgb(j * 16, i * 16, (i + j) * 8) & mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3014,8 +3022,7 @@ void tst_QImage::inplaceRgbConversion()
|
||||
for (int i = 0; i < imageConverted.height(); ++i) {
|
||||
for (int j = 0; j < imageConverted.width(); ++j) {
|
||||
QRgb convertedColor = imageConverted.pixel(j,i);
|
||||
QCOMPARE(qRed(convertedColor) & 0xF0, j * 16);
|
||||
QCOMPARE(qGreen(convertedColor) & 0xF0, i * 16);
|
||||
QCOMPARE(convertedColor & 0xFFF0F0F0, qRgb(j * 16, i * 16, 0));
|
||||
}
|
||||
}
|
||||
if (qt_depthForFormat(format) == qt_depthForFormat(dest_format))
|
||||
|
@ -291,7 +291,13 @@ void tst_QImageWriter::writeImage2()
|
||||
QVERIFY(reader.read(&written));
|
||||
}
|
||||
|
||||
written = written.convertToFormat(image.format());
|
||||
// The 8-bit input value might have turned into a fraction in 16-bit grayscale
|
||||
// which can't be preserved in file formats that doesn't support 16bpc.
|
||||
if (image.format() == QImage::Format_Grayscale16 &&
|
||||
written.format() != QImage::Format_Grayscale16 && written.depth() <= 32)
|
||||
image.convertTo(QImage::Format_Grayscale8);
|
||||
|
||||
written.convertTo(image.format());
|
||||
if (!equalImageContents(written, image)) {
|
||||
qDebug() << "image" << image.format() << image.width()
|
||||
<< image.height() << image.depth()
|
||||
|
Loading…
Reference in New Issue
Block a user