summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/image/qimage_conversions.cpp173
-rw-r--r--src/gui/painting/qcolorspace.cpp11
-rw-r--r--src/gui/painting/qcolorspace_p.h1
-rw-r--r--src/gui/painting/qcolortransform.cpp63
-rw-r--r--src/gui/painting/qcolortransform.h1
-rw-r--r--src/gui/painting/qcolortransform_p.h9
-rw-r--r--src/gui/painting/qdrawhelper.cpp98
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp1
-rw-r--r--src/gui/painting/qpaintengine_raster_p.h1
-rw-r--r--tests/auto/gui/image/qimage/tst_qimage.cpp29
-rw-r--r--tests/auto/gui/image/qimagewriter/tst_qimagewriter.cpp8
11 files changed, 358 insertions, 37 deletions
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp
index 84a5a5ab83..7415c86b9b 100644
--- a/src/gui/image/qimage_conversions.cpp
+++ b/src/gui/image/qimage_conversions.cpp
@@ -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,120 @@ static void convert_gray16_to_RGBA64(QImageData *dest, const QImageData *src, Qt
}
}
+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);
+
+ 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;
+
+ 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);
@@ -1297,13 +1408,17 @@ 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;
+
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() && simpleCase; ++i) {
+ if (colors[i] != qRgb(i, i, i))
+ simpleCase = false;
+ }
+ if (simpleCase) {
+ copy_8bit_pixels(dest, src);
+ return;
+ }
+
+ QColorSpace fromCS = src->colorSpace.isValid() ? src->colorSpace : QColorSpace::SRgb;
+ QColorTransform tf = QColorSpacePrivate::get(fromCS)->transformationToXYZ();
for (int i = 0; i < colors.size(); ++i) {
- uchar gray = qGray(colors[i]);
- translate[i] = gray;
- simpleCase = simpleCase && (gray == i);
+ QRgba64 c16 = tf.map(QRgba64::fromArgb32(colors[i]));
+ translate[i] = c16.green8(); // Y from XYZ ends up in the G channel
}
- 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;
- }
+ 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;
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp
index e770d929d3..9007b66601 100644
--- a/src/gui/painting/qcolorspace.cpp
+++ b/src/gui/painting/qcolorspace.cpp
@@ -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.
diff --git a/src/gui/painting/qcolorspace_p.h b/src/gui/painting/qcolorspace_p.h
index b5e5263cae..79b1774d0b 100644
--- a/src/gui/painting/qcolorspace_p.h
+++ b/src/gui/painting/qcolorspace_p.h
@@ -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;
diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp
index 013abf769a..2a1b0ede20 100644
--- a/src/gui/painting/qcolortransform.cpp
+++ b/src/gui/painting/qcolortransform.cpp
@@ -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
diff --git a/src/gui/painting/qcolortransform.h b/src/gui/painting/qcolortransform.h
index 30aceebda3..7ec58c5617 100644
--- a/src/gui/painting/qcolortransform.h
+++ b/src/gui/painting/qcolortransform.h
@@ -75,6 +75,7 @@ public:
private:
friend class QColorSpace;
friend class QColorSpacePrivate;
+ friend class QColorTransformPrivate;
friend class QImage;
QExplicitlySharedDataPointer<QColorTransformPrivate> d;
diff --git a/src/gui/painting/qcolortransform_p.h b/src/gui/painting/qcolortransform_p.h
index 5d7116248d..b9099fa399 100644
--- a/src/gui/painting/qcolortransform_p.h
+++ b/src/gui/painting/qcolortransform_p.h
@@ -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
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index c66268c5f3..161eebb300 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -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
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index de17bff844..bae8695e33 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -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();
diff --git a/src/gui/painting/qpaintengine_raster_p.h b/src/gui/painting/qpaintengine_raster_p.h
index 62cef10d32..d25fe4c3b3 100644
--- a/src/gui/painting/qpaintengine_raster_p.h
+++ b/src/gui/painting/qpaintengine_raster_p.h
@@ -450,6 +450,7 @@ public:
QPainter::CompositionMode compositionMode;
QImage::Format format;
+ QColorSpace colorSpace;
QImage colorizeBitmap(const QImage &image, const QColor &color);
private:
diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp
index 0fb4263024..c3096bd47f 100644
--- a/tests/auto/gui/image/qimage/tst_qimage.cpp
+++ b/tests/auto/gui/image/qimage/tst_qimage.cpp
@@ -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))
diff --git a/tests/auto/gui/image/qimagewriter/tst_qimagewriter.cpp b/tests/auto/gui/image/qimagewriter/tst_qimagewriter.cpp
index 907719259d..49a4504d2a 100644
--- a/tests/auto/gui/image/qimagewriter/tst_qimagewriter.cpp
+++ b/tests/auto/gui/image/qimagewriter/tst_qimagewriter.cpp
@@ -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()