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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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