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:
Allan Sandfeld Jensen 2021-04-28 12:40:11 +02:00
parent b254855aa6
commit f1983dcdf6
11 changed files with 360 additions and 39 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -75,6 +75,7 @@ public:
private:
friend class QColorSpace;
friend class QColorSpacePrivate;
friend class QColorTransformPrivate;
friend class QImage;
QExplicitlySharedDataPointer<QColorTransformPrivate> d;

View File

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

View File

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

View File

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

View File

@ -450,6 +450,7 @@ public:
QPainter::CompositionMode compositionMode;
QImage::Format format;
QColorSpace colorSpace;
QImage colorizeBitmap(const QImage &image, const QColor &color);
private:

View File

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

View File

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