summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGiuseppe D'Angelo <giuseppe.dangelo@kdab.com>2023-10-18 18:42:01 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2024-04-03 18:31:36 +0100
commit2a54229f90b5abcca04763834aa5b9d48e19131f (patch)
treee86a2b6dd440f2919c7a1f7dac1a8c71e75f3c14
parent9a92e26dcdb40215f2f5d187a09b6b2b73934809 (diff)
GUI: add CMYK painting support
This commit adds a CMYK 8bpp format to QImage. The idea is to enable the transport of CMYK images inside Qt, for instance to be loaded/saved from files or painted on CMYK capable paint devices (e.g. PDF). Also, rasterization support *from* a CMYK image is added (on top of a RGB surface), as well as CMYK image scaling/conversion. Conversion and rasterization between CMYK and RGB isn't particularly optimized nor it honors any colorspaces yet. The overall idea is to match 1:1 the existing behavior of CMYK QColor (which get naively changed to RGB; there isn't colorspace support in QPainter yet). There are no plans to add rasterization *towards* CMYK. Image save/load in native CMYK formats will be added in future commits. This work has been kindly sponsored by the QGIS project (https://qgis.org/). [ChangeLog][QtGui] Support for 8-bit CMYK images has been added. Change-Id: I4b024cd4c15119c669b6ddd450418a9e425587f8 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--src/gui/CMakeLists.txt1
-rw-r--r--src/gui/image/qimage.cpp19
-rw-r--r--src/gui/image/qimage.h1
-rw-r--r--src/gui/image/qimage_conversions.cpp34
-rw-r--r--src/gui/image/qimage_p.h5
-rw-r--r--src/gui/painting/qcmyk_p.h88
-rw-r--r--src/gui/painting/qdrawhelper.cpp73
-rw-r--r--src/gui/painting/qpainter.cpp9
-rw-r--r--src/gui/painting/qpixellayout.cpp106
-rw-r--r--tests/auto/gui/image/qimage/tst_qimage.cpp18
-rw-r--r--tests/auto/gui/painting/qpainter/tst_qpainter.cpp10
-rw-r--r--tests/baseline/shared/paintcommands.cpp1
12 files changed, 335 insertions, 30 deletions
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index dc6bf27834..5f5ed6d66b 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -170,6 +170,7 @@ qt_internal_add_module(Gui
painting/qcolortrclut.cpp painting/qcolortrclut_p.h
painting/qcompositionfunctions.cpp
painting/qcosmeticstroker.cpp painting/qcosmeticstroker_p.h
+ painting/qcmyk_p.h
painting/qdatabuffer_p.h
painting/qdrawhelper_p.h
painting/qdrawhelper_x86_p.h
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 4f91fbb8b9..ccc54fae59 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -304,6 +304,7 @@ bool QImageData::checkForAlphaPixels() const
case QImage::Format_RGBX64:
case QImage::Format_RGBX16FPx4:
case QImage::Format_RGBX32FPx4:
+ case QImage::Format_CMYK32:
break;
case QImage::Format_Invalid:
case QImage::NImageFormats:
@@ -360,7 +361,7 @@ bool QImageData::checkForAlphaPixels() const
refer to the \l{How to Create Qt Plugins}{Plugin HowTo}.
\warning Painting on a QImage with the format
- QImage::Format_Indexed8 is not supported.
+ QImage::Format_Indexed8 or QImage::Format_CMYK32 is not supported.
\tableofcontents
@@ -742,8 +743,9 @@ bool QImageData::checkForAlphaPixels() const
\value Format_RGBA32FPx4 The image is stored using a 4 32-bit floating point RGBA format (32FP-32FP-32FP-32FP). (added in Qt 6.2)
\value Format_RGBA32FPx4_Premultiplied The image is stored using a premultiplied 4 32-bit floating point
RGBA format (32FP-32FP-32FP-32FP). (added in Qt 6.2)
+ \value Format_CMYK32 The image is stored using a 32 bit CMYK format (0xCCMMYYKK). (added in Qt 6.8)
- \note Drawing into a QImage with QImage::Format_Indexed8 is not
+ \note Drawing into a QImage with format QImage::Format_Indexed8 or QImage::Format_CMYK32 is not
supported.
\note Avoid most rendering directly to most of these formats using QPainter. Rendering
@@ -5727,6 +5729,19 @@ static constexpr QPixelFormat pixelformats[] = {
/*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
/*INTERPRETATION*/ QPixelFormat::FloatingPoint,
/*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
+ //QImage::Format_CMYK32:
+ QPixelFormat(QPixelFormat::CMYK,
+ /*RED*/ 8,
+ /*GREEN*/ 8,
+ /*BLUE*/ 8,
+ /*FOURTH*/ 8,
+ /*FIFTH*/ 0,
+ /*ALPHA*/ 0,
+ /*ALPHA USAGE*/ QPixelFormat::IgnoresAlpha,
+ /*ALPHA POSITION*/ QPixelFormat::AtBeginning,
+ /*PREMULTIPLIED*/ QPixelFormat::NotPremultiplied,
+ /*INTERPRETATION*/ QPixelFormat::UnsignedInteger,
+ /*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian),
};
static_assert(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats);
diff --git a/src/gui/image/qimage.h b/src/gui/image/qimage.h
index 86c49ac28a..605a1084ed 100644
--- a/src/gui/image/qimage.h
+++ b/src/gui/image/qimage.h
@@ -75,6 +75,7 @@ public:
Format_RGBX32FPx4,
Format_RGBA32FPx4,
Format_RGBA32FPx4_Premultiplied,
+ Format_CMYK32,
#ifndef Q_QDOC
NImageFormats
#endif
diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp
index 3599002123..a35f4097e6 100644
--- a/src/gui/image/qimage_conversions.cpp
+++ b/src/gui/image/qimage_conversions.cpp
@@ -4,6 +4,7 @@
#include <private/qguiapplication_p.h>
#include <private/qcolortransform_p.h>
#include <private/qcolortrclut_p.h>
+#include <private/qcmyk_p.h>
#include <private/qdrawhelper_p.h>
#include <private/qendian_p.h>
#include <private/qpixellayout_p.h>
@@ -2454,6 +2455,34 @@ static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageCo
return true;
}
+template <bool SourceIsPremultiplied>
+static void convert_ARGB32_to_CMYK32(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags)
+{
+ Q_ASSERT(src->format == QImage::Format_RGB32 ||
+ src->format == QImage::Format_ARGB32 ||
+ src->format == QImage::Format_ARGB32_Premultiplied);
+ Q_ASSERT(dest->format == QImage::Format_CMYK32);
+ Q_ASSERT(src->width == dest->width);
+ Q_ASSERT(src->height == dest->height);
+
+ const uchar *src_data = src->data;
+ uchar *dest_data = dest->data;
+ for (int y = 0; y < src->height; ++y) {
+ const QRgb *srcRgba = reinterpret_cast<const QRgb *>(src_data);
+ uint *destCmyk = reinterpret_cast<uint *>(dest_data);
+
+ for (int x = 0; x < src->width; ++x) {
+ QRgb sourcePixel = srcRgba[x];
+ if constexpr (SourceIsPremultiplied)
+ sourcePixel = qUnpremultiply(sourcePixel);
+
+ destCmyk[x] = QCmyk32::fromRgba(sourcePixel).toUint();
+ }
+
+ src_data += src->bytes_per_line;;
+ dest_data += dest->bytes_per_line;
+ }
+}
// first index source, second dest
Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = {};
@@ -2590,6 +2619,11 @@ static void qInitImageConversions()
qimage_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4] = convert_passthrough;
qimage_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4_Premultiplied] = convert_passthrough;
+ qimage_converter_map[QImage::Format_CMYK32][QImage::Format_CMYK32] = convert_passthrough;
+ qimage_converter_map[QImage::Format_RGB32][QImage::Format_CMYK32] = convert_ARGB32_to_CMYK32<false>;
+ qimage_converter_map[QImage::Format_ARGB32][QImage::Format_CMYK32] = convert_ARGB32_to_CMYK32<false>;
+ qimage_converter_map[QImage::Format_ARGB32_Premultiplied][QImage::Format_CMYK32] = convert_ARGB32_to_CMYK32<true>;
+
// Inline converters:
qimage_inplace_converter_map[QImage::Format_Indexed8][QImage::Format_Grayscale8] =
convert_Indexed8_to_Grayscale8_inplace;
diff --git a/src/gui/image/qimage_p.h b/src/gui/image/qimage_p.h
index d355bccace..89fb66aedc 100644
--- a/src/gui/image/qimage_p.h
+++ b/src/gui/image/qimage_p.h
@@ -195,6 +195,9 @@ inline int qt_depthForFormat(QImage::Format format)
case QImage::Format_RGBA32FPx4_Premultiplied:
depth = 128;
break;
+ case QImage::Format_CMYK32:
+ depth = 32;
+ break;
}
return depth;
}
@@ -248,6 +251,7 @@ inline QImage::Format qt_opaqueVersion(QImage::Format format)
case QImage::Format_RGBX32FPx4:
case QImage::Format_Grayscale8:
case QImage::Format_Grayscale16:
+ case QImage::Format_CMYK32:
return format;
case QImage::Format_Mono:
case QImage::Format_MonoLSB:
@@ -311,6 +315,7 @@ inline QImage::Format qt_alphaVersion(QImage::Format format)
case QImage::Format_Alpha8:
case QImage::Format_Grayscale8:
case QImage::Format_Invalid:
+ case QImage::Format_CMYK32:
case QImage::NImageFormats:
break;
}
diff --git a/src/gui/painting/qcmyk_p.h b/src/gui/painting/qcmyk_p.h
new file mode 100644
index 0000000000..d00a4b5a6e
--- /dev/null
+++ b/src/gui/painting/qcmyk_p.h
@@ -0,0 +1,88 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCMYK_P_H
+#define QCMYK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtGui/private/qtguiglobal_p.h>
+#include <QtGui/qcolor.h>
+
+QT_BEGIN_NAMESPACE
+
+class QCmyk32
+{
+private:
+ uint m_cmyk = 0;
+
+public:
+ QCmyk32() = default;
+
+ constexpr QCmyk32(int cyan, int magenta, int yellow, int black) :
+#if QT_BYTE_ORDER == Q_BIG_ENDIAN
+ m_cmyk(cyan << 24 | magenta << 16 | yellow << 8 | black)
+#else
+ m_cmyk(cyan | magenta << 8 | yellow << 16 | black << 24)
+#endif
+ {
+ }
+
+#if QT_BYTE_ORDER == Q_BIG_ENDIAN
+ constexpr int cyan() const noexcept { return (m_cmyk >> 24) & 0xff; }
+ constexpr int magenta() const noexcept { return (m_cmyk >> 16) & 0xff; }
+ constexpr int yellow() const noexcept { return (m_cmyk >> 8) & 0xff; }
+ constexpr int black() const noexcept { return (m_cmyk ) & 0xff; }
+#else
+ constexpr int cyan() const noexcept { return (m_cmyk ) & 0xff; }
+ constexpr int magenta() const noexcept { return (m_cmyk >> 8) & 0xff; }
+ constexpr int yellow() const noexcept { return (m_cmyk >> 16) & 0xff; }
+ constexpr int black() const noexcept { return (m_cmyk >> 24) & 0xff; }
+#endif
+
+ QColor toColor() const noexcept
+ {
+ return QColor::fromCmyk(cyan(), magenta(), yellow(), black());
+ }
+
+ constexpr uint toUint() const noexcept
+ {
+ return m_cmyk;
+ }
+
+ constexpr static QCmyk32 fromCmyk32(uint cmyk) noexcept
+ {
+ QCmyk32 result;
+ result.m_cmyk = cmyk;
+ return result;
+ }
+
+ static QCmyk32 fromRgba(QRgb rgba) noexcept
+ {
+ const QColor c = QColor(rgba).toCmyk();
+ return QCmyk32(c.cyan(), c.magenta(), c.yellow(), c.black());
+ }
+
+ static QCmyk32 fromColor(const QColor &color) noexcept
+ {
+ QColor c = color.toCmyk();
+ return QCmyk32(c.cyan(), c.magenta(), c.yellow(), c.black());
+ }
+};
+
+static_assert(sizeof(QCmyk32) == sizeof(int));
+static_assert(alignof(QCmyk32) == alignof(int));
+static_assert(std::is_standard_layout_v<QCmyk32>);
+
+QT_END_NAMESPACE
+
+#endif // QCMYK_P_H
diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
index 218c9d1656..1ffe88e459 100644
--- a/src/gui/painting/qdrawhelper.cpp
+++ b/src/gui/painting/qdrawhelper.cpp
@@ -176,7 +176,7 @@ static void QT_FASTCALL convertRGBA32FPMToRGBA64PM(QRgba64 *buffer, int count)
}
}
-static Convert64Func convert64ToRGBA64PM[QImage::NImageFormats] = {
+static Convert64Func convert64ToRGBA64PM[] = {
nullptr,
nullptr,
nullptr,
@@ -213,7 +213,10 @@ static Convert64Func convert64ToRGBA64PM[QImage::NImageFormats] = {
convertRGBA32FPMToRGBA64PM,
convertRGBA32FToRGBA64PM,
convertRGBA32FPMToRGBA64PM,
+ nullptr,
};
+
+static_assert(std::size(convert64ToRGBA64PM) == QImage::NImageFormats);
#endif
#if QT_CONFIG(raster_fp)
@@ -247,7 +250,7 @@ static void QT_FASTCALL convertRGBA16FToRGBA32F(QRgbaFloat32 *buffer, const quin
qFloatFromFloat16((float *)buffer, (const qfloat16 *)src, count * 4);
}
-static Convert64ToFPFunc convert64ToRGBA32F[QImage::NImageFormats] = {
+static Convert64ToFPFunc convert64ToRGBA32F[] = {
nullptr,
nullptr,
nullptr,
@@ -284,8 +287,11 @@ static Convert64ToFPFunc convert64ToRGBA32F[QImage::NImageFormats] = {
nullptr,
nullptr,
nullptr,
+ nullptr,
};
+static_assert(std::size(convert64ToRGBA32F) == QImage::NImageFormats);
+
static void convertRGBA32FToRGBA32FPM(QRgbaFloat32 *buffer, int count)
{
for (int i = 0; i < count; ++i)
@@ -353,7 +359,7 @@ static uint *QT_FASTCALL destFetchUndefined(uint *buffer, QRasterBuffer *, int,
return buffer;
}
-static DestFetchProc destFetchProc[QImage::NImageFormats] =
+static DestFetchProc destFetchProc[] =
{
nullptr, // Format_Invalid
destFetchMono, // Format_Mono,
@@ -391,8 +397,11 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] =
destFetch, // Format_RGBX32FPx4
destFetch, // Format_RGBA32FPx4
destFetch, // Format_RGBA32FPx4_Premultiplied
+ destFetch, // Format_CMYK32
};
+static_assert(std::size(destFetchProc) == QImage::NImageFormats);
+
#if QT_CONFIG(raster_64bit)
static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length)
{
@@ -410,7 +419,7 @@ static QRgba64 * QT_FASTCALL destFetch64Undefined(QRgba64 *buffer, QRasterBuffer
return buffer;
}
-static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
+static DestFetchProc64 destFetchProc64[] =
{
nullptr, // Format_Invalid
nullptr, // Format_Mono,
@@ -448,7 +457,10 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
destFetch64, // Format_RGBX32FPx4
destFetch64, // Format_RGBA32FPx4
destFetch64, // Format_RGBA32FPx4_Premultiplied
+ destFetch64, // Format_CMYK32
};
+
+static_assert(std::size(destFetchProc64) == QImage::NImageFormats);
#endif
#if QT_CONFIG(raster_fp)
@@ -466,7 +478,7 @@ static QRgbaFloat32 *QT_FASTCALL destFetchFPUndefined(QRgbaFloat32 *buffer, QRas
{
return buffer;
}
-static DestFetchProcFP destFetchProcFP[QImage::NImageFormats] =
+static DestFetchProcFP destFetchProcFP[] =
{
nullptr, // Format_Invalid
nullptr, // Format_Mono,
@@ -504,7 +516,10 @@ static DestFetchProcFP destFetchProcFP[QImage::NImageFormats] =
destFetchRGBFP, // Format_RGBX32FPx4
destFetchFP, // Format_RGBA32FPx4
destFetchRGBFP, // Format_RGBA32FPx4_Premultiplied
+ destFetchFP, // Format_CMYK32
};
+
+static_assert(std::size(destFetchProcFP) == QImage::NImageFormats);
#endif
/*
@@ -657,7 +672,7 @@ static void QT_FASTCALL destStoreGray16(QRasterBuffer *rasterBuffer, int x, int
}
}
-static DestStoreProc destStoreProc[QImage::NImageFormats] =
+static DestStoreProc destStoreProc[] =
{
nullptr, // Format_Invalid
destStoreMono, // Format_Mono,
@@ -695,8 +710,11 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] =
destStore, // Format_RGBX32FPx4
destStore, // Format_RGBA32FPx4
destStore, // Format_RGBA32FPx4_Premultiplied
+ destStore, // Format_CMYK32
};
+static_assert(std::size(destStoreProc) == QImage::NImageFormats);
+
#if QT_CONFIG(raster_64bit)
static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length)
{
@@ -757,7 +775,7 @@ static void QT_FASTCALL destStore64Gray16(QRasterBuffer *rasterBuffer, int x, in
}
}
-static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
+static DestStoreProc64 destStoreProc64[] =
{
nullptr, // Format_Invalid
nullptr, // Format_Mono,
@@ -795,7 +813,10 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
destStore64, // Format_RGBX32FPx4
destStore64, // Format_RGBA32FPx4
destStore64, // Format_RGBA32FPx4_Premultiplied
+ destStore64, // Format_CMYK32
};
+
+static_assert(std::size(destStoreProc64) == QImage::NImageFormats);
#endif
#if QT_CONFIG(raster_fp)
@@ -3070,7 +3091,7 @@ static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP(QRgbaFloat32 *
#endif // QT_CONFIG(raster_fp)
// FetchUntransformed can have more specialized methods added depending on SIMD features.
-static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = {
+static SourceFetchProc sourceFetchUntransformed[] = {
nullptr, // Invalid
fetchUntransformed, // Mono
fetchUntransformed, // MonoLsb
@@ -3107,9 +3128,12 @@ static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = {
fetchUntransformed, // RGBX32Px4
fetchUntransformed, // RGBA32FPx4
fetchUntransformed, // RGBA32FPx4_Premultiplied
+ fetchUntransformed, // CMYK32
};
-static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = {
+static_assert(std::size(sourceFetchUntransformed) == QImage::NImageFormats);
+
+static const SourceFetchProc sourceFetchGeneric[] = {
fetchUntransformed, // Untransformed
fetchUntransformed, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPPNone>, // Transformed
@@ -3118,7 +3142,9 @@ static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = {
fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPPNone> // TransformedBilinearTiled
};
-static SourceFetchProc sourceFetchARGB32PM[NBlendTypes] = {
+static_assert(std::size(sourceFetchGeneric) == NBlendTypes);
+
+static SourceFetchProc sourceFetchARGB32PM[] = {
fetchUntransformedARGB32PM, // Untransformed
fetchUntransformedARGB32PM, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
@@ -3127,7 +3153,9 @@ static SourceFetchProc sourceFetchARGB32PM[NBlendTypes] = {
fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled> // BilinearTiled
};
-static SourceFetchProc sourceFetchAny16[NBlendTypes] = {
+static_assert(std::size(sourceFetchARGB32PM) == NBlendTypes);
+
+static SourceFetchProc sourceFetchAny16[] = {
fetchUntransformed, // Untransformed
fetchUntransformed, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPP16>, // Transformed
@@ -3136,7 +3164,9 @@ static SourceFetchProc sourceFetchAny16[NBlendTypes] = {
fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP16> // TransformedBilinearTiled
};
-static SourceFetchProc sourceFetchAny32[NBlendTypes] = {
+static_assert(std::size(sourceFetchAny16) == NBlendTypes);
+
+static SourceFetchProc sourceFetchAny32[] = {
fetchUntransformed, // Untransformed
fetchUntransformed, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
@@ -3145,6 +3175,8 @@ static SourceFetchProc sourceFetchAny32[NBlendTypes] = {
fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP32> // TransformedBilinearTiled
};
+static_assert(std::size(sourceFetchAny32) == NBlendTypes);
+
static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format)
{
if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied)
@@ -3159,7 +3191,7 @@ static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage:
}
#if QT_CONFIG(raster_64bit)
-static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = {
+static const SourceFetchProc64 sourceFetchGeneric64[] = {
fetchUntransformed64, // Untransformed
fetchUntransformed64, // Tiled
fetchTransformed64<BlendTransformed>, // Transformed
@@ -3168,7 +3200,9 @@ static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = {
fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
};
-static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = {
+static_assert(std::size(sourceFetchGeneric64) == NBlendTypes);
+
+static const SourceFetchProc64 sourceFetchRGBA64PM[] = {
fetchUntransformedRGBA64PM, // Untransformed
fetchUntransformedRGBA64PM, // Tiled
fetchTransformed64<BlendTransformed>, // Transformed
@@ -3177,6 +3211,8 @@ static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = {
fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
};
+static_assert(std::size(sourceFetchRGBA64PM) == NBlendTypes);
+
static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format)
{
if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64_Premultiplied)
@@ -3186,7 +3222,7 @@ static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QIm
#endif
#if QT_CONFIG(raster_fp)
-static const SourceFetchProcFP sourceFetchGenericFP[NBlendTypes] = {
+static const SourceFetchProcFP sourceFetchGenericFP[] = {
fetchUntransformedFP, // Untransformed
fetchUntransformedFP, // Tiled
fetchTransformedFP<BlendTransformed>, // Transformed
@@ -3195,6 +3231,8 @@ static const SourceFetchProcFP sourceFetchGenericFP[NBlendTypes] = {
fetchTransformedBilinearFP<BlendTransformedBilinearTiled> // BilinearTiled
};
+static_assert(std::size(sourceFetchGenericFP) == NBlendTypes);
+
static inline SourceFetchProcFP getSourceFetchFP(TextureBlendType blendType, QImage::Format /*format*/)
{
return sourceFetchGenericFP[blendType];
@@ -3612,7 +3650,6 @@ static inline Operator getOperator(const QSpanData *data, const QT_FT_Span *span
{
Operator op;
bool solidSource = false;
-
switch(data->type) {
case QSpanData::Solid:
solidSource = data->solidColor.alphaF() >= 1.0f;
@@ -5962,7 +5999,7 @@ static void qt_rectfill_fp32x4(QRasterBuffer *rasterBuffer,
// Map table for destination image format. Contains function pointers
// for blends of various types unto the destination
-DrawHelper qDrawHelper[QImage::NImageFormats] =
+DrawHelper qDrawHelper[] =
{
// Format_Invalid,
{ nullptr, nullptr, nullptr, nullptr, nullptr },
@@ -6239,6 +6276,8 @@ DrawHelper qDrawHelper[QImage::NImageFormats] =
},
};
+static_assert(std::size(qDrawHelper) == QImage::NImageFormats);
+
#if !defined(Q_PROCESSOR_X86)
void qt_memfill64(quint64 *dest, quint64 color, qsizetype count)
{
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index 5b4a6b05ea..148cc6c5dd 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -1765,9 +1765,12 @@ bool QPainter::begin(QPaintDevice *pd)
qWarning("QPainter::begin: Cannot paint on a null image");
qt_cleanup_painter_state(d);
return false;
- } else if (img->format() == QImage::Format_Indexed8) {
- // Painting on indexed8 images is not supported.
- qWarning("QPainter::begin: Cannot paint on an image with the QImage::Format_Indexed8 format");
+ } else if (img->format() == QImage::Format_Indexed8 ||
+ img->format() == QImage::Format_CMYK32) {
+ // Painting on these formats is not supported.
+ qWarning() << "QPainter::begin: Cannot paint on an image with the"
+ << img->format()
+ << "format";
qt_cleanup_painter_state(d);
return false;
}
diff --git a/src/gui/painting/qpixellayout.cpp b/src/gui/painting/qpixellayout.cpp
index e6dc774b2d..e94fbbff15 100644
--- a/src/gui/painting/qpixellayout.cpp
+++ b/src/gui/painting/qpixellayout.cpp
@@ -7,6 +7,7 @@
#include "qpixellayout_p.h"
#include "qrgba64_p.h"
#include <QtCore/private/qsimd_p.h>
+#include <QtGui/private/qcmyk_p.h>
QT_BEGIN_NAMESPACE
@@ -1657,6 +1658,66 @@ static const QRgba64 *QT_FASTCALL fetchRGBA32FPMToRGBA64PM(QRgba64 *buffer, cons
return buffer;
}
+inline const uint *qt_convertCMYK32ToARGB32PM(uint *buffer, const uint *src, int count)
+{
+ UNALIASED_CONVERSION_LOOP(buffer, src, count, [](uint s) {
+ const QColor color = QCmyk32::fromCmyk32(s).toColor();
+ return color.rgba();
+ });
+ return buffer;
+}
+
+static void QT_FASTCALL convertCMYK32ToARGB32PM(uint *buffer, int count, const QList<QRgb> *)
+{
+ qt_convertCMYK32ToARGB32PM(buffer, buffer, count);
+}
+
+static const QRgba64 *QT_FASTCALL convertCMYK32ToToRGBA64PM(QRgba64 *buffer, const uint *src, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i)
+ buffer[i] = qPremultiply(QCmyk32::fromCmyk32(src[i]).toColor().rgba64());
+ return buffer;
+}
+
+static const uint *QT_FASTCALL fetchCMYK32ToARGB32PM(uint *buffer, const uchar *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ const uint *s = reinterpret_cast<const uint *>(src) + index;
+ for (int i = 0; i < count; ++i)
+ buffer[i] = qPremultiply(QCmyk32::fromCmyk32(s[i]).toColor().rgba());
+ return buffer;
+}
+
+static const QRgba64 *QT_FASTCALL fetchCMYK32ToRGBA64PM(QRgba64 *buffer, const uchar *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ const uint *s = reinterpret_cast<const uint *>(src) + index;
+ for (int i = 0; i < count; ++i)
+ buffer[i] = qPremultiply(QCmyk32::fromCmyk32(s[i]).toColor().rgba64());
+ return buffer;
+}
+
+static void QT_FASTCALL storeCMYKFromARGB32PM(uchar *dest, const uint *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ for (int i = 0; i < count; ++i) {
+ QColor c = qUnpremultiply(src[i]);
+ d[i] = QCmyk32::fromColor(c).toUint();
+ }
+}
+
+static void QT_FASTCALL storeCMYKFromRGB32(uchar *dest, const uint *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ for (int i = 0; i < count; ++i) {
+ QColor c = src[i];
+ d[i] = QCmyk32::fromColor(c).toUint();
+ }
+}
+
// Note:
// convertToArgb32() assumes that no color channel is less than 4 bits.
// storeRGBFromARGB32PM() assumes that no color channel is more than 8 bits.
@@ -1779,6 +1840,10 @@ QPixelLayout qPixelLayouts[] = {
convertPassThrough, nullptr,
fetchRGB32FToRGB32, fetchRGBA32FPMToRGBA64PM,
storeRGB32FFromRGB32, storeRGB32FFromRGB32 }, // Format_RGBA32FPx4_Premultiplied
+ { false, false, QPixelLayout::BPP32, nullptr,
+ convertCMYK32ToARGB32PM, convertCMYK32ToToRGBA64PM,
+ fetchCMYK32ToARGB32PM, fetchCMYK32ToRGBA64PM,
+ storeCMYKFromARGB32PM, storeCMYKFromRGB32 }, // Format_CMYK32
};
static_assert(std::size(qPixelLayouts) == QImage::NImageFormats);
@@ -1916,6 +1981,14 @@ static void QT_FASTCALL storeRGBA32FPMFromRGBA64PM(uchar *dest, const QRgba64 *s
d[i] = qConvertRgb64ToRgbaF32(src[i]);
}
+static void QT_FASTCALL storeCMYKFromRGBA64PM(uchar *dest, const QRgba64 *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ for (int i = 0; i < count; ++i)
+ d[i] = QCmyk32::fromColor(QColor(src[i])).toUint();
+}
+
ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[] = {
nullptr,
nullptr,
@@ -1953,6 +2026,7 @@ ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[] = {
storeRGBX32FFromRGBA64PM,
storeRGBA32FFromRGBA64PM,
storeRGBA32FPMFromRGBA64PM,
+ storeCMYKFromRGBA64PM,
};
static_assert(std::size(qStoreFromRGBA64PM) == QImage::NImageFormats);
@@ -2002,6 +2076,15 @@ static const QRgbaFloat32 * QT_FASTCALL convertRGB30ToRGBA32F(QRgbaFloat32 *buff
return buffer;
}
+static const QRgbaFloat32 * QT_FASTCALL convertCMYKToRGBA32F(QRgbaFloat32 *buffer, const uint *src, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ for (int i = 0; i < count; ++i)
+ QRgbaFloat32::fromArgb32(QCmyk32::fromCmyk32(src[i]).toColor().rgba());
+
+ return buffer;
+}
+
ConvertToFPFunc qConvertToRGBA32F[] = {
nullptr,
convertIndexedTo<QRgbaFloat32>,
@@ -2039,6 +2122,7 @@ ConvertToFPFunc qConvertToRGBA32F[] = {
nullptr,
nullptr,
nullptr,
+ convertCMYKToRGBA32F,
};
static_assert(std::size(qConvertToRGBA32F) == QImage::NImageFormats);
@@ -2107,6 +2191,16 @@ static const QRgbaFloat32 *QT_FASTCALL fetchRGBA32F(QRgbaFloat32 *, const uchar
return s;
}
+static const QRgbaFloat32 *QT_FASTCALL fetchCMYKToRGBA32F(QRgbaFloat32 *buffer, const uchar *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ const uint *s = reinterpret_cast<const uint *>(src) + index;
+ for (int i = 0; i < count; ++i)
+ buffer[i] = QRgbaFloat32::fromArgb32(QCmyk32::fromCmyk32(s[i]).toColor().rgba());
+
+ return buffer;
+}
+
FetchAndConvertPixelsFuncFP qFetchToRGBA32F[] = {
nullptr,
fetchIndexedToRGBA32F<QPixelLayout::BPP1MSB>,
@@ -2144,6 +2238,7 @@ FetchAndConvertPixelsFuncFP qFetchToRGBA32F[] = {
fetchRGBA32F,
fetchRGBA32FToRGBA32F,
fetchRGBA32F,
+ fetchCMYKToRGBA32F,
};
static_assert(std::size(qFetchToRGBA32F) == QImage::NImageFormats);
@@ -2284,6 +2379,16 @@ static void QT_FASTCALL storeRGBA32FPMFromRGBA32F(uchar *dest, const QRgbaFloat3
}
}
+static void QT_FASTCALL storeCMYKFromRGBA32F(uchar *dest, const QRgbaFloat32 *src, int index, int count,
+ const QList<QRgb> *, QDitherInfo *)
+{
+ uint *d = reinterpret_cast<uint *>(dest) + index;
+ for (int i = 0; i < count; ++i) {
+ // Yikes, this really needs enablers in QColor and friends
+ d[i] = QCmyk32::fromColor(QColor(src[i].toArgb32())).toUint();
+ }
+}
+
ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[] = {
nullptr,
nullptr,
@@ -2321,6 +2426,7 @@ ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[] = {
storeRGBX32FFromRGBA32F,
storeRGBA32FFromRGBA32F,
storeRGBA32FPMFromRGBA32F,
+ storeCMYKFromRGBA32F,
};
static_assert(std::size(qStoreFromRGBA32F) == QImage::NImageFormats);
diff --git a/tests/auto/gui/image/qimage/tst_qimage.cpp b/tests/auto/gui/image/qimage/tst_qimage.cpp
index 59cc3a2129..d7f70f29a1 100644
--- a/tests/auto/gui/image/qimage/tst_qimage.cpp
+++ b/tests/auto/gui/image/qimage/tst_qimage.cpp
@@ -321,7 +321,9 @@ static QLatin1String formatToString(QImage::Format format)
return QLatin1String("RGBA32FPx4");
case QImage::Format_RGBA32FPx4_Premultiplied:
return QLatin1String("RGBA32FPx4pm");
- default:
+ case QImage::Format_CMYK32:
+ return QLatin1String("CMYK32");
+ case QImage::NImageFormats:
break;
};
Q_UNREACHABLE();
@@ -1507,6 +1509,8 @@ void tst_QImage::setPixelWithAlpha_data()
continue;
if (c == QImage::Format_Alpha8)
continue;
+ if (c == QImage::Format_CMYK32)
+ continue;
QTest::newRow(qPrintable(formatToString(QImage::Format(c)))) << QImage::Format(c);
}
}
@@ -2568,7 +2572,8 @@ void tst_QImage::rgbSwapped_data()
for (int i = QImage::Format_Indexed8; i < QImage::NImageFormats; ++i) {
if (i == QImage::Format_Alpha8
|| i == QImage::Format_Grayscale8
- || i == QImage::Format_Grayscale16) {
+ || i == QImage::Format_Grayscale16
+ || i == QImage::Format_CMYK32) {
continue;
}
QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i);
@@ -3050,13 +3055,15 @@ void tst_QImage::inplaceRgbConversion_data()
for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) {
if (i == QImage::Format_Alpha8
|| i == QImage::Format_Grayscale8
- || i == QImage::Format_Grayscale16) {
+ || i == QImage::Format_Grayscale16
+ || i == QImage::Format_CMYK32) {
continue;
}
for (int j = QImage::Format_RGB32; j < QImage::NImageFormats; ++j) {
if (j == QImage::Format_Alpha8
|| j == QImage::Format_Grayscale8
- || j == QImage::Format_Grayscale16) {
+ || j == QImage::Format_Grayscale16
+ || j == QImage::Format_CMYK32) {
continue;
}
if (i == j)
@@ -3345,7 +3352,8 @@ void tst_QImage::invertPixelsRGB_data()
for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) {
if (i == QImage::Format_Alpha8
|| i == QImage::Format_Grayscale8
- || i == QImage::Format_Grayscale16) {
+ || i == QImage::Format_Grayscale16
+ || i == QImage::Format_CMYK32) {
continue;
}
QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i);
diff --git a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp
index 15735aecf7..96f92105d1 100644
--- a/tests/auto/gui/painting/qpainter/tst_qpainter.cpp
+++ b/tests/auto/gui/painting/qpainter/tst_qpainter.cpp
@@ -2784,7 +2784,7 @@ void tst_QPainter::monoImages()
for (int i = 1; i < QImage::NImageFormats; ++i) {
for (int j = 0; j < numColorPairs; ++j) {
const QImage::Format format = QImage::Format(i);
- if (format == QImage::Format_Indexed8)
+ if (format == QImage::Format_Indexed8 || format == QImage::Format_CMYK32)
continue;
QImage img(2, 2, format);
@@ -3554,9 +3554,13 @@ void tst_QPainter::drawImage_data()
for (int srcFormat = QImage::Format_Mono; srcFormat < QImage::NImageFormats; ++srcFormat) {
for (int dstFormat = QImage::Format_Mono; dstFormat < QImage::NImageFormats; ++dstFormat) {
- // Indexed8 can't be painted to, and Alpha8 can't hold a color.
- if (dstFormat == QImage::Format_Indexed8 || dstFormat == QImage::Format_Alpha8)
+ // Indexed8 and CMYK32 can't be painted to, and Alpha8 can't hold a color.
+ if (dstFormat == QImage::Format_Indexed8 ||
+ dstFormat == QImage::Format_CMYK32 ||
+ dstFormat == QImage::Format_Alpha8) {
continue;
+ }
+
for (int odd_x = 0; odd_x <= 1; ++odd_x) {
for (int odd_width = 0; odd_width <= 1; ++odd_width) {
QTest::addRow("srcFormat %d, dstFormat %d, odd x: %d, odd width: %d",
diff --git a/tests/baseline/shared/paintcommands.cpp b/tests/baseline/shared/paintcommands.cpp
index a1d2b9e497..20201b66b0 100644
--- a/tests/baseline/shared/paintcommands.cpp
+++ b/tests/baseline/shared/paintcommands.cpp
@@ -173,6 +173,7 @@ const char *PaintCommands::imageFormatTable[] = {
"RGBx32FPx4",
"RGBA32FPx4",
"RGBA32FPx4_Premultiplied",
+ "CMYK32",
};
const char *PaintCommands::renderHintTable[] = {