diff options
author | Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> | 2023-10-18 18:42:01 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2024-04-03 18:31:36 +0100 |
commit | 2a54229f90b5abcca04763834aa5b9d48e19131f (patch) | |
tree | e86a2b6dd440f2919c7a1f7dac1a8c71e75f3c14 | |
parent | 9a92e26dcdb40215f2f5d187a09b6b2b73934809 (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.txt | 1 | ||||
-rw-r--r-- | src/gui/image/qimage.cpp | 19 | ||||
-rw-r--r-- | src/gui/image/qimage.h | 1 | ||||
-rw-r--r-- | src/gui/image/qimage_conversions.cpp | 34 | ||||
-rw-r--r-- | src/gui/image/qimage_p.h | 5 | ||||
-rw-r--r-- | src/gui/painting/qcmyk_p.h | 88 | ||||
-rw-r--r-- | src/gui/painting/qdrawhelper.cpp | 73 | ||||
-rw-r--r-- | src/gui/painting/qpainter.cpp | 9 | ||||
-rw-r--r-- | src/gui/painting/qpixellayout.cpp | 106 | ||||
-rw-r--r-- | tests/auto/gui/image/qimage/tst_qimage.cpp | 18 | ||||
-rw-r--r-- | tests/auto/gui/painting/qpainter/tst_qpainter.cpp | 10 | ||||
-rw-r--r-- | tests/baseline/shared/paintcommands.cpp | 1 |
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[] = { |