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>
This commit is contained in:
Giuseppe D'Angelo 2023-10-18 18:42:01 +02:00 committed by Allan Sandfeld Jensen
parent 9a92e26dcd
commit 2a54229f90
12 changed files with 335 additions and 30 deletions

View File

@ -170,6 +170,7 @@ qt_internal_add_module(Gui
painting/qcolortrclut.cpp painting/qcolortrclut_p.h painting/qcolortrclut.cpp painting/qcolortrclut_p.h
painting/qcompositionfunctions.cpp painting/qcompositionfunctions.cpp
painting/qcosmeticstroker.cpp painting/qcosmeticstroker_p.h painting/qcosmeticstroker.cpp painting/qcosmeticstroker_p.h
painting/qcmyk_p.h
painting/qdatabuffer_p.h painting/qdatabuffer_p.h
painting/qdrawhelper_p.h painting/qdrawhelper_p.h
painting/qdrawhelper_x86_p.h painting/qdrawhelper_x86_p.h

View File

@ -304,6 +304,7 @@ bool QImageData::checkForAlphaPixels() const
case QImage::Format_RGBX64: case QImage::Format_RGBX64:
case QImage::Format_RGBX16FPx4: case QImage::Format_RGBX16FPx4:
case QImage::Format_RGBX32FPx4: case QImage::Format_RGBX32FPx4:
case QImage::Format_CMYK32:
break; break;
case QImage::Format_Invalid: case QImage::Format_Invalid:
case QImage::NImageFormats: case QImage::NImageFormats:
@ -360,7 +361,7 @@ bool QImageData::checkForAlphaPixels() const
refer to the \l{How to Create Qt Plugins}{Plugin HowTo}. refer to the \l{How to Create Qt Plugins}{Plugin HowTo}.
\warning Painting on a QImage with the format \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 \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 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 \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) 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. supported.
\note Avoid most rendering directly to most of these formats using QPainter. Rendering \note Avoid most rendering directly to most of these formats using QPainter. Rendering
@ -5727,6 +5729,19 @@ static constexpr QPixelFormat pixelformats[] = {
/*PREMULTIPLIED*/ QPixelFormat::Premultiplied, /*PREMULTIPLIED*/ QPixelFormat::Premultiplied,
/*INTERPRETATION*/ QPixelFormat::FloatingPoint, /*INTERPRETATION*/ QPixelFormat::FloatingPoint,
/*BYTE ORDER*/ QPixelFormat::CurrentSystemEndian), /*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); static_assert(sizeof(pixelformats) / sizeof(*pixelformats) == QImage::NImageFormats);

View File

@ -75,6 +75,7 @@ public:
Format_RGBX32FPx4, Format_RGBX32FPx4,
Format_RGBA32FPx4, Format_RGBA32FPx4,
Format_RGBA32FPx4_Premultiplied, Format_RGBA32FPx4_Premultiplied,
Format_CMYK32,
#ifndef Q_QDOC #ifndef Q_QDOC
NImageFormats NImageFormats
#endif #endif

View File

@ -4,6 +4,7 @@
#include <private/qguiapplication_p.h> #include <private/qguiapplication_p.h>
#include <private/qcolortransform_p.h> #include <private/qcolortransform_p.h>
#include <private/qcolortrclut_p.h> #include <private/qcolortrclut_p.h>
#include <private/qcmyk_p.h>
#include <private/qdrawhelper_p.h> #include <private/qdrawhelper_p.h>
#include <private/qendian_p.h> #include <private/qendian_p.h>
#include <private/qpixellayout_p.h> #include <private/qpixellayout_p.h>
@ -2454,6 +2455,34 @@ static bool convert_Grayscale8_to_Indexed8_inplace(QImageData *data, Qt::ImageCo
return true; 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 // first index source, second dest
Image_Converter qimage_converter_map[QImage::NImageFormats][QImage::NImageFormats] = {}; 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] = convert_passthrough;
qimage_converter_map[QImage::Format_RGBX32FPx4][QImage::Format_RGBA32FPx4_Premultiplied] = 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: // Inline converters:
qimage_inplace_converter_map[QImage::Format_Indexed8][QImage::Format_Grayscale8] = qimage_inplace_converter_map[QImage::Format_Indexed8][QImage::Format_Grayscale8] =
convert_Indexed8_to_Grayscale8_inplace; convert_Indexed8_to_Grayscale8_inplace;

View File

@ -195,6 +195,9 @@ inline int qt_depthForFormat(QImage::Format format)
case QImage::Format_RGBA32FPx4_Premultiplied: case QImage::Format_RGBA32FPx4_Premultiplied:
depth = 128; depth = 128;
break; break;
case QImage::Format_CMYK32:
depth = 32;
break;
} }
return depth; return depth;
} }
@ -248,6 +251,7 @@ inline QImage::Format qt_opaqueVersion(QImage::Format format)
case QImage::Format_RGBX32FPx4: case QImage::Format_RGBX32FPx4:
case QImage::Format_Grayscale8: case QImage::Format_Grayscale8:
case QImage::Format_Grayscale16: case QImage::Format_Grayscale16:
case QImage::Format_CMYK32:
return format; return format;
case QImage::Format_Mono: case QImage::Format_Mono:
case QImage::Format_MonoLSB: case QImage::Format_MonoLSB:
@ -311,6 +315,7 @@ inline QImage::Format qt_alphaVersion(QImage::Format format)
case QImage::Format_Alpha8: case QImage::Format_Alpha8:
case QImage::Format_Grayscale8: case QImage::Format_Grayscale8:
case QImage::Format_Invalid: case QImage::Format_Invalid:
case QImage::Format_CMYK32:
case QImage::NImageFormats: case QImage::NImageFormats:
break; break;
} }

View File

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

View File

@ -176,7 +176,7 @@ static void QT_FASTCALL convertRGBA32FPMToRGBA64PM(QRgba64 *buffer, int count)
} }
} }
static Convert64Func convert64ToRGBA64PM[QImage::NImageFormats] = { static Convert64Func convert64ToRGBA64PM[] = {
nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr,
@ -213,7 +213,10 @@ static Convert64Func convert64ToRGBA64PM[QImage::NImageFormats] = {
convertRGBA32FPMToRGBA64PM, convertRGBA32FPMToRGBA64PM,
convertRGBA32FToRGBA64PM, convertRGBA32FToRGBA64PM,
convertRGBA32FPMToRGBA64PM, convertRGBA32FPMToRGBA64PM,
nullptr,
}; };
static_assert(std::size(convert64ToRGBA64PM) == QImage::NImageFormats);
#endif #endif
#if QT_CONFIG(raster_fp) #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); qFloatFromFloat16((float *)buffer, (const qfloat16 *)src, count * 4);
} }
static Convert64ToFPFunc convert64ToRGBA32F[QImage::NImageFormats] = { static Convert64ToFPFunc convert64ToRGBA32F[] = {
nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr,
@ -284,8 +287,11 @@ static Convert64ToFPFunc convert64ToRGBA32F[QImage::NImageFormats] = {
nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr,
nullptr,
}; };
static_assert(std::size(convert64ToRGBA32F) == QImage::NImageFormats);
static void convertRGBA32FToRGBA32FPM(QRgbaFloat32 *buffer, int count) static void convertRGBA32FToRGBA32FPM(QRgbaFloat32 *buffer, int count)
{ {
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
@ -353,7 +359,7 @@ static uint *QT_FASTCALL destFetchUndefined(uint *buffer, QRasterBuffer *, int,
return buffer; return buffer;
} }
static DestFetchProc destFetchProc[QImage::NImageFormats] = static DestFetchProc destFetchProc[] =
{ {
nullptr, // Format_Invalid nullptr, // Format_Invalid
destFetchMono, // Format_Mono, destFetchMono, // Format_Mono,
@ -391,8 +397,11 @@ static DestFetchProc destFetchProc[QImage::NImageFormats] =
destFetch, // Format_RGBX32FPx4 destFetch, // Format_RGBX32FPx4
destFetch, // Format_RGBA32FPx4 destFetch, // Format_RGBA32FPx4
destFetch, // Format_RGBA32FPx4_Premultiplied destFetch, // Format_RGBA32FPx4_Premultiplied
destFetch, // Format_CMYK32
}; };
static_assert(std::size(destFetchProc) == QImage::NImageFormats);
#if QT_CONFIG(raster_64bit) #if QT_CONFIG(raster_64bit)
static QRgba64 *QT_FASTCALL destFetch64(QRgba64 *buffer, QRasterBuffer *rasterBuffer, int x, int y, int length) 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; return buffer;
} }
static DestFetchProc64 destFetchProc64[QImage::NImageFormats] = static DestFetchProc64 destFetchProc64[] =
{ {
nullptr, // Format_Invalid nullptr, // Format_Invalid
nullptr, // Format_Mono, nullptr, // Format_Mono,
@ -448,7 +457,10 @@ static DestFetchProc64 destFetchProc64[QImage::NImageFormats] =
destFetch64, // Format_RGBX32FPx4 destFetch64, // Format_RGBX32FPx4
destFetch64, // Format_RGBA32FPx4 destFetch64, // Format_RGBA32FPx4
destFetch64, // Format_RGBA32FPx4_Premultiplied destFetch64, // Format_RGBA32FPx4_Premultiplied
destFetch64, // Format_CMYK32
}; };
static_assert(std::size(destFetchProc64) == QImage::NImageFormats);
#endif #endif
#if QT_CONFIG(raster_fp) #if QT_CONFIG(raster_fp)
@ -466,7 +478,7 @@ static QRgbaFloat32 *QT_FASTCALL destFetchFPUndefined(QRgbaFloat32 *buffer, QRas
{ {
return buffer; return buffer;
} }
static DestFetchProcFP destFetchProcFP[QImage::NImageFormats] = static DestFetchProcFP destFetchProcFP[] =
{ {
nullptr, // Format_Invalid nullptr, // Format_Invalid
nullptr, // Format_Mono, nullptr, // Format_Mono,
@ -504,7 +516,10 @@ static DestFetchProcFP destFetchProcFP[QImage::NImageFormats] =
destFetchRGBFP, // Format_RGBX32FPx4 destFetchRGBFP, // Format_RGBX32FPx4
destFetchFP, // Format_RGBA32FPx4 destFetchFP, // Format_RGBA32FPx4
destFetchRGBFP, // Format_RGBA32FPx4_Premultiplied destFetchRGBFP, // Format_RGBA32FPx4_Premultiplied
destFetchFP, // Format_CMYK32
}; };
static_assert(std::size(destFetchProcFP) == QImage::NImageFormats);
#endif #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 nullptr, // Format_Invalid
destStoreMono, // Format_Mono, destStoreMono, // Format_Mono,
@ -695,8 +710,11 @@ static DestStoreProc destStoreProc[QImage::NImageFormats] =
destStore, // Format_RGBX32FPx4 destStore, // Format_RGBX32FPx4
destStore, // Format_RGBA32FPx4 destStore, // Format_RGBA32FPx4
destStore, // Format_RGBA32FPx4_Premultiplied destStore, // Format_RGBA32FPx4_Premultiplied
destStore, // Format_CMYK32
}; };
static_assert(std::size(destStoreProc) == QImage::NImageFormats);
#if QT_CONFIG(raster_64bit) #if QT_CONFIG(raster_64bit)
static void QT_FASTCALL destStore64(QRasterBuffer *rasterBuffer, int x, int y, const QRgba64 *buffer, int length) 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_Invalid
nullptr, // Format_Mono, nullptr, // Format_Mono,
@ -795,7 +813,10 @@ static DestStoreProc64 destStoreProc64[QImage::NImageFormats] =
destStore64, // Format_RGBX32FPx4 destStore64, // Format_RGBX32FPx4
destStore64, // Format_RGBA32FPx4 destStore64, // Format_RGBA32FPx4
destStore64, // Format_RGBA32FPx4_Premultiplied destStore64, // Format_RGBA32FPx4_Premultiplied
destStore64, // Format_CMYK32
}; };
static_assert(std::size(destStoreProc64) == QImage::NImageFormats);
#endif #endif
#if QT_CONFIG(raster_fp) #if QT_CONFIG(raster_fp)
@ -3070,7 +3091,7 @@ static const QRgbaFloat32 *QT_FASTCALL fetchTransformedBilinearFP(QRgbaFloat32 *
#endif // QT_CONFIG(raster_fp) #endif // QT_CONFIG(raster_fp)
// FetchUntransformed can have more specialized methods added depending on SIMD features. // FetchUntransformed can have more specialized methods added depending on SIMD features.
static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = { static SourceFetchProc sourceFetchUntransformed[] = {
nullptr, // Invalid nullptr, // Invalid
fetchUntransformed, // Mono fetchUntransformed, // Mono
fetchUntransformed, // MonoLsb fetchUntransformed, // MonoLsb
@ -3107,9 +3128,12 @@ static SourceFetchProc sourceFetchUntransformed[QImage::NImageFormats] = {
fetchUntransformed, // RGBX32Px4 fetchUntransformed, // RGBX32Px4
fetchUntransformed, // RGBA32FPx4 fetchUntransformed, // RGBA32FPx4
fetchUntransformed, // RGBA32FPx4_Premultiplied fetchUntransformed, // RGBA32FPx4_Premultiplied
fetchUntransformed, // CMYK32
}; };
static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = { static_assert(std::size(sourceFetchUntransformed) == QImage::NImageFormats);
static const SourceFetchProc sourceFetchGeneric[] = {
fetchUntransformed, // Untransformed fetchUntransformed, // Untransformed
fetchUntransformed, // Tiled fetchUntransformed, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPPNone>, // Transformed fetchTransformed<BlendTransformed, QPixelLayout::BPPNone>, // Transformed
@ -3118,7 +3142,9 @@ static const SourceFetchProc sourceFetchGeneric[NBlendTypes] = {
fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPPNone> // TransformedBilinearTiled fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPPNone> // TransformedBilinearTiled
}; };
static SourceFetchProc sourceFetchARGB32PM[NBlendTypes] = { static_assert(std::size(sourceFetchGeneric) == NBlendTypes);
static SourceFetchProc sourceFetchARGB32PM[] = {
fetchUntransformedARGB32PM, // Untransformed fetchUntransformedARGB32PM, // Untransformed
fetchUntransformedARGB32PM, // Tiled fetchUntransformedARGB32PM, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
@ -3127,7 +3153,9 @@ static SourceFetchProc sourceFetchARGB32PM[NBlendTypes] = {
fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled> // BilinearTiled fetchTransformedBilinearARGB32PM<BlendTransformedBilinearTiled> // BilinearTiled
}; };
static SourceFetchProc sourceFetchAny16[NBlendTypes] = { static_assert(std::size(sourceFetchARGB32PM) == NBlendTypes);
static SourceFetchProc sourceFetchAny16[] = {
fetchUntransformed, // Untransformed fetchUntransformed, // Untransformed
fetchUntransformed, // Tiled fetchUntransformed, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPP16>, // Transformed fetchTransformed<BlendTransformed, QPixelLayout::BPP16>, // Transformed
@ -3136,7 +3164,9 @@ static SourceFetchProc sourceFetchAny16[NBlendTypes] = {
fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP16> // TransformedBilinearTiled fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP16> // TransformedBilinearTiled
}; };
static SourceFetchProc sourceFetchAny32[NBlendTypes] = { static_assert(std::size(sourceFetchAny16) == NBlendTypes);
static SourceFetchProc sourceFetchAny32[] = {
fetchUntransformed, // Untransformed fetchUntransformed, // Untransformed
fetchUntransformed, // Tiled fetchUntransformed, // Tiled
fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed fetchTransformed<BlendTransformed, QPixelLayout::BPP32>, // Transformed
@ -3145,6 +3175,8 @@ static SourceFetchProc sourceFetchAny32[NBlendTypes] = {
fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP32> // TransformedBilinearTiled fetchTransformedBilinear<BlendTransformedBilinearTiled, QPixelLayout::BPP32> // TransformedBilinearTiled
}; };
static_assert(std::size(sourceFetchAny32) == NBlendTypes);
static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format) static inline SourceFetchProc getSourceFetch(TextureBlendType blendType, QImage::Format format)
{ {
if (format == QImage::Format_RGB32 || format == QImage::Format_ARGB32_Premultiplied) 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) #if QT_CONFIG(raster_64bit)
static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = { static const SourceFetchProc64 sourceFetchGeneric64[] = {
fetchUntransformed64, // Untransformed fetchUntransformed64, // Untransformed
fetchUntransformed64, // Tiled fetchUntransformed64, // Tiled
fetchTransformed64<BlendTransformed>, // Transformed fetchTransformed64<BlendTransformed>, // Transformed
@ -3168,7 +3200,9 @@ static const SourceFetchProc64 sourceFetchGeneric64[NBlendTypes] = {
fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
}; };
static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = { static_assert(std::size(sourceFetchGeneric64) == NBlendTypes);
static const SourceFetchProc64 sourceFetchRGBA64PM[] = {
fetchUntransformedRGBA64PM, // Untransformed fetchUntransformedRGBA64PM, // Untransformed
fetchUntransformedRGBA64PM, // Tiled fetchUntransformedRGBA64PM, // Tiled
fetchTransformed64<BlendTransformed>, // Transformed fetchTransformed64<BlendTransformed>, // Transformed
@ -3177,6 +3211,8 @@ static const SourceFetchProc64 sourceFetchRGBA64PM[NBlendTypes] = {
fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled fetchTransformedBilinear64<BlendTransformedBilinearTiled> // BilinearTiled
}; };
static_assert(std::size(sourceFetchRGBA64PM) == NBlendTypes);
static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format) static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QImage::Format format)
{ {
if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64_Premultiplied) if (format == QImage::Format_RGBX64 || format == QImage::Format_RGBA64_Premultiplied)
@ -3186,7 +3222,7 @@ static inline SourceFetchProc64 getSourceFetch64(TextureBlendType blendType, QIm
#endif #endif
#if QT_CONFIG(raster_fp) #if QT_CONFIG(raster_fp)
static const SourceFetchProcFP sourceFetchGenericFP[NBlendTypes] = { static const SourceFetchProcFP sourceFetchGenericFP[] = {
fetchUntransformedFP, // Untransformed fetchUntransformedFP, // Untransformed
fetchUntransformedFP, // Tiled fetchUntransformedFP, // Tiled
fetchTransformedFP<BlendTransformed>, // Transformed fetchTransformedFP<BlendTransformed>, // Transformed
@ -3195,6 +3231,8 @@ static const SourceFetchProcFP sourceFetchGenericFP[NBlendTypes] = {
fetchTransformedBilinearFP<BlendTransformedBilinearTiled> // BilinearTiled fetchTransformedBilinearFP<BlendTransformedBilinearTiled> // BilinearTiled
}; };
static_assert(std::size(sourceFetchGenericFP) == NBlendTypes);
static inline SourceFetchProcFP getSourceFetchFP(TextureBlendType blendType, QImage::Format /*format*/) static inline SourceFetchProcFP getSourceFetchFP(TextureBlendType blendType, QImage::Format /*format*/)
{ {
return sourceFetchGenericFP[blendType]; return sourceFetchGenericFP[blendType];
@ -3612,7 +3650,6 @@ static inline Operator getOperator(const QSpanData *data, const QT_FT_Span *span
{ {
Operator op; Operator op;
bool solidSource = false; bool solidSource = false;
switch(data->type) { switch(data->type) {
case QSpanData::Solid: case QSpanData::Solid:
solidSource = data->solidColor.alphaF() >= 1.0f; 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 // Map table for destination image format. Contains function pointers
// for blends of various types unto the destination // for blends of various types unto the destination
DrawHelper qDrawHelper[QImage::NImageFormats] = DrawHelper qDrawHelper[] =
{ {
// Format_Invalid, // Format_Invalid,
{ nullptr, nullptr, nullptr, nullptr, nullptr }, { 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) #if !defined(Q_PROCESSOR_X86)
void qt_memfill64(quint64 *dest, quint64 color, qsizetype count) void qt_memfill64(quint64 *dest, quint64 color, qsizetype count)
{ {

View File

@ -1765,9 +1765,12 @@ bool QPainter::begin(QPaintDevice *pd)
qWarning("QPainter::begin: Cannot paint on a null image"); qWarning("QPainter::begin: Cannot paint on a null image");
qt_cleanup_painter_state(d); qt_cleanup_painter_state(d);
return false; return false;
} else if (img->format() == QImage::Format_Indexed8) { } else if (img->format() == QImage::Format_Indexed8 ||
// Painting on indexed8 images is not supported. img->format() == QImage::Format_CMYK32) {
qWarning("QPainter::begin: Cannot paint on an image with the QImage::Format_Indexed8 format"); // 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); qt_cleanup_painter_state(d);
return false; return false;
} }

View File

@ -7,6 +7,7 @@
#include "qpixellayout_p.h" #include "qpixellayout_p.h"
#include "qrgba64_p.h" #include "qrgba64_p.h"
#include <QtCore/private/qsimd_p.h> #include <QtCore/private/qsimd_p.h>
#include <QtGui/private/qcmyk_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -1657,6 +1658,66 @@ static const QRgba64 *QT_FASTCALL fetchRGBA32FPMToRGBA64PM(QRgba64 *buffer, cons
return buffer; 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: // Note:
// convertToArgb32() assumes that no color channel is less than 4 bits. // convertToArgb32() assumes that no color channel is less than 4 bits.
// storeRGBFromARGB32PM() assumes that no color channel is more than 8 bits. // storeRGBFromARGB32PM() assumes that no color channel is more than 8 bits.
@ -1779,6 +1840,10 @@ QPixelLayout qPixelLayouts[] = {
convertPassThrough, nullptr, convertPassThrough, nullptr,
fetchRGB32FToRGB32, fetchRGBA32FPMToRGBA64PM, fetchRGB32FToRGB32, fetchRGBA32FPMToRGBA64PM,
storeRGB32FFromRGB32, storeRGB32FFromRGB32 }, // Format_RGBA32FPx4_Premultiplied 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); 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]); 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[] = { ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[] = {
nullptr, nullptr,
nullptr, nullptr,
@ -1953,6 +2026,7 @@ ConvertAndStorePixelsFunc64 qStoreFromRGBA64PM[] = {
storeRGBX32FFromRGBA64PM, storeRGBX32FFromRGBA64PM,
storeRGBA32FFromRGBA64PM, storeRGBA32FFromRGBA64PM,
storeRGBA32FPMFromRGBA64PM, storeRGBA32FPMFromRGBA64PM,
storeCMYKFromRGBA64PM,
}; };
static_assert(std::size(qStoreFromRGBA64PM) == QImage::NImageFormats); static_assert(std::size(qStoreFromRGBA64PM) == QImage::NImageFormats);
@ -2002,6 +2076,15 @@ static const QRgbaFloat32 * QT_FASTCALL convertRGB30ToRGBA32F(QRgbaFloat32 *buff
return buffer; 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[] = { ConvertToFPFunc qConvertToRGBA32F[] = {
nullptr, nullptr,
convertIndexedTo<QRgbaFloat32>, convertIndexedTo<QRgbaFloat32>,
@ -2039,6 +2122,7 @@ ConvertToFPFunc qConvertToRGBA32F[] = {
nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr,
convertCMYKToRGBA32F,
}; };
static_assert(std::size(qConvertToRGBA32F) == QImage::NImageFormats); static_assert(std::size(qConvertToRGBA32F) == QImage::NImageFormats);
@ -2107,6 +2191,16 @@ static const QRgbaFloat32 *QT_FASTCALL fetchRGBA32F(QRgbaFloat32 *, const uchar
return s; 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[] = { FetchAndConvertPixelsFuncFP qFetchToRGBA32F[] = {
nullptr, nullptr,
fetchIndexedToRGBA32F<QPixelLayout::BPP1MSB>, fetchIndexedToRGBA32F<QPixelLayout::BPP1MSB>,
@ -2144,6 +2238,7 @@ FetchAndConvertPixelsFuncFP qFetchToRGBA32F[] = {
fetchRGBA32F, fetchRGBA32F,
fetchRGBA32FToRGBA32F, fetchRGBA32FToRGBA32F,
fetchRGBA32F, fetchRGBA32F,
fetchCMYKToRGBA32F,
}; };
static_assert(std::size(qFetchToRGBA32F) == QImage::NImageFormats); 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[] = { ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[] = {
nullptr, nullptr,
nullptr, nullptr,
@ -2321,6 +2426,7 @@ ConvertAndStorePixelsFuncFP qStoreFromRGBA32F[] = {
storeRGBX32FFromRGBA32F, storeRGBX32FFromRGBA32F,
storeRGBA32FFromRGBA32F, storeRGBA32FFromRGBA32F,
storeRGBA32FPMFromRGBA32F, storeRGBA32FPMFromRGBA32F,
storeCMYKFromRGBA32F,
}; };
static_assert(std::size(qStoreFromRGBA32F) == QImage::NImageFormats); static_assert(std::size(qStoreFromRGBA32F) == QImage::NImageFormats);

View File

@ -321,7 +321,9 @@ static QLatin1String formatToString(QImage::Format format)
return QLatin1String("RGBA32FPx4"); return QLatin1String("RGBA32FPx4");
case QImage::Format_RGBA32FPx4_Premultiplied: case QImage::Format_RGBA32FPx4_Premultiplied:
return QLatin1String("RGBA32FPx4pm"); return QLatin1String("RGBA32FPx4pm");
default: case QImage::Format_CMYK32:
return QLatin1String("CMYK32");
case QImage::NImageFormats:
break; break;
}; };
Q_UNREACHABLE(); Q_UNREACHABLE();
@ -1507,6 +1509,8 @@ void tst_QImage::setPixelWithAlpha_data()
continue; continue;
if (c == QImage::Format_Alpha8) if (c == QImage::Format_Alpha8)
continue; continue;
if (c == QImage::Format_CMYK32)
continue;
QTest::newRow(qPrintable(formatToString(QImage::Format(c)))) << QImage::Format(c); 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) { for (int i = QImage::Format_Indexed8; i < QImage::NImageFormats; ++i) {
if (i == QImage::Format_Alpha8 if (i == QImage::Format_Alpha8
|| i == QImage::Format_Grayscale8 || i == QImage::Format_Grayscale8
|| i == QImage::Format_Grayscale16) { || i == QImage::Format_Grayscale16
|| i == QImage::Format_CMYK32) {
continue; continue;
} }
QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i); 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) { for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) {
if (i == QImage::Format_Alpha8 if (i == QImage::Format_Alpha8
|| i == QImage::Format_Grayscale8 || i == QImage::Format_Grayscale8
|| i == QImage::Format_Grayscale16) { || i == QImage::Format_Grayscale16
|| i == QImage::Format_CMYK32) {
continue; continue;
} }
for (int j = QImage::Format_RGB32; j < QImage::NImageFormats; ++j) { for (int j = QImage::Format_RGB32; j < QImage::NImageFormats; ++j) {
if (j == QImage::Format_Alpha8 if (j == QImage::Format_Alpha8
|| j == QImage::Format_Grayscale8 || j == QImage::Format_Grayscale8
|| j == QImage::Format_Grayscale16) { || j == QImage::Format_Grayscale16
|| j == QImage::Format_CMYK32) {
continue; continue;
} }
if (i == j) if (i == j)
@ -3345,7 +3352,8 @@ void tst_QImage::invertPixelsRGB_data()
for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) { for (int i = QImage::Format_RGB32; i < QImage::NImageFormats; ++i) {
if (i == QImage::Format_Alpha8 if (i == QImage::Format_Alpha8
|| i == QImage::Format_Grayscale8 || i == QImage::Format_Grayscale8
|| i == QImage::Format_Grayscale16) { || i == QImage::Format_Grayscale16
|| i == QImage::Format_CMYK32) {
continue; continue;
} }
QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i); QTest::addRow("%s", formatToString(QImage::Format(i)).data()) << QImage::Format(i);

View File

@ -2784,7 +2784,7 @@ void tst_QPainter::monoImages()
for (int i = 1; i < QImage::NImageFormats; ++i) { for (int i = 1; i < QImage::NImageFormats; ++i) {
for (int j = 0; j < numColorPairs; ++j) { for (int j = 0; j < numColorPairs; ++j) {
const QImage::Format format = QImage::Format(i); const QImage::Format format = QImage::Format(i);
if (format == QImage::Format_Indexed8) if (format == QImage::Format_Indexed8 || format == QImage::Format_CMYK32)
continue; continue;
QImage img(2, 2, format); 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 srcFormat = QImage::Format_Mono; srcFormat < QImage::NImageFormats; ++srcFormat) {
for (int dstFormat = QImage::Format_Mono; dstFormat < QImage::NImageFormats; ++dstFormat) { for (int dstFormat = QImage::Format_Mono; dstFormat < QImage::NImageFormats; ++dstFormat) {
// Indexed8 can't be painted to, and Alpha8 can't hold a color. // Indexed8 and CMYK32 can't be painted to, and Alpha8 can't hold a color.
if (dstFormat == QImage::Format_Indexed8 || dstFormat == QImage::Format_Alpha8) if (dstFormat == QImage::Format_Indexed8 ||
dstFormat == QImage::Format_CMYK32 ||
dstFormat == QImage::Format_Alpha8) {
continue; continue;
}
for (int odd_x = 0; odd_x <= 1; ++odd_x) { for (int odd_x = 0; odd_x <= 1; ++odd_x) {
for (int odd_width = 0; odd_width <= 1; ++odd_width) { for (int odd_width = 0; odd_width <= 1; ++odd_width) {
QTest::addRow("srcFormat %d, dstFormat %d, odd x: %d, odd width: %d", QTest::addRow("srcFormat %d, dstFormat %d, odd x: %d, odd width: %d",

View File

@ -173,6 +173,7 @@ const char *PaintCommands::imageFormatTable[] = {
"RGBx32FPx4", "RGBx32FPx4",
"RGBA32FPx4", "RGBA32FPx4",
"RGBA32FPx4_Premultiplied", "RGBA32FPx4_Premultiplied",
"CMYK32",
}; };
const char *PaintCommands::renderHintTable[] = { const char *PaintCommands::renderHintTable[] = {