Krita Source Code Documentation
Loading...
Searching...
No Matches
KoLabColorSpace.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2004-2009 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net>
4 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
5 *
6 * SPDX-License-Identifier: LGPL-2.1-or-later
7 */
8#include "KoLabColorSpace.h"
9
10#include <limits.h>
11#include <stdlib.h>
12#include <math.h>
13
14#include <QBitArray>
15
16#include <klocalizedstring.h>
17
18#include "KoChannelInfo.h"
19#include "KoID.h"
20#include "KoIntegerMaths.h"
21#include "KoColorConversions.h"
22
25
27 KoSimpleColorSpace<KoLabU16Traits>(colorSpaceId(),
28 i18n("L*a*b* (16-bit integer/channel, unmanaged)"),
31{
32 addChannel(new KoChannelInfo(i18nc("Lightness value in Lab color model", "Lightness"), CHANNEL_L * sizeof(quint16), CHANNEL_L, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), QColor(100, 100, 100)));
33 addChannel(new KoChannelInfo(i18n("a*"), CHANNEL_A * sizeof(quint16), CHANNEL_A, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), QColor(150, 150, 150)));
34 addChannel(new KoChannelInfo(i18n("b*"), CHANNEL_B * sizeof(quint16), CHANNEL_B, KoChannelInfo::COLOR, KoChannelInfo::UINT16, sizeof(quint16), QColor(200, 200, 200)));
35 addChannel(new KoChannelInfo(i18n("Alpha"), CHANNEL_ALPHA * sizeof(quint16), CHANNEL_ALPHA, KoChannelInfo::ALPHA, KoChannelInfo::UINT16, sizeof(quint16)));
36
37 // ADD, ALPHA_DARKEN, BURN, DIVIDE, DODGE, ERASE, MULTIPLY, OVER, OVERLAY, SCREEN, SUBTRACT
38 addStandardCompositeOps<KoLabU16Traits>(this);
39 addStandardDitherOps<KoLabU16Traits>(this);
40}
41
45
46
48{
49 return QStringLiteral("LABA");
50}
51
52
54{
55 return new KoLabColorSpace();
56}
57
58void KoLabColorSpace::fromQColor(const QColor& c, quint8 *dst) const
59{
60 // Convert between RGB and CIE-Lab color spaces
61 // Uses ITU-R recommendation BT.709 with D65 as reference white.
62 // algorithm contributed by "Mark A. Ruzon" <ruzon@CS.Stanford.EDU>
63
64 int R, G, B, A;
65 c.getRgb(&R, &G, &B, &A);
66
67 double X, Y, Z, fX, fY, fZ;
68
69 X = 0.412453 * R + 0.357580 * G + 0.180423 * B;
70 Y = 0.212671 * R + 0.715160 * G + 0.072169 * B;
71 Z = 0.019334 * R + 0.119193 * G + 0.950227 * B;
72
73 X /= (255 * 0.950456);
74 Y /= 255;
75 Z /= (255 * 1.088754);
76
77 quint8 L, a, b;
78
79 if (Y > 0.008856) {
80 fY = pow(Y, 1.0 / 3.0);
81 L = static_cast<int>(116.0 * fY - 16.0 + 0.5);
82 } else {
83 fY = 7.787 * Y + 16.0 / 116.0;
84 L = static_cast<int>(903.3 * Y + 0.5);
85 }
86
87 if (X > 0.008856)
88 fX = pow(X, 1.0 / 3.0);
89 else
90 fX = 7.787 * X + 16.0 / 116.0;
91
92 if (Z > 0.008856)
93 fZ = pow(Z, 1.0 / 3.0);
94 else
95 fZ = 7.787 * Z + 16.0 / 116.0;
96
97 a = static_cast<int>(500.0 * (fX - fY) + 0.5);
98 b = static_cast<int>(200.0 * (fY - fZ) + 0.5);
99
100 dst[CHANNEL_L] = UINT8_TO_UINT16(L);
101 dst[CHANNEL_A] = UINT8_TO_UINT16(a);
102 dst[CHANNEL_B] = UINT8_TO_UINT16(b);
104}
105
106void KoLabColorSpace::toQColor(const quint8 * src, QColor *c) const
107{
108 // Convert between RGB and CIE-Lab color spaces
109 // Uses ITU-R recommendation BT.709 with D65 as reference white.
110 // algorithm contributed by "Mark A. Ruzon" <ruzon@CS.Stanford.EDU>
111 quint8 L, a, b, A;
112 L = UINT16_TO_UINT8(src[CHANNEL_L]);
113 a = UINT16_TO_UINT8(src[CHANNEL_A]);
114 b = UINT16_TO_UINT8(src[CHANNEL_B]);
116
117 double X, Y, Z, fX, fY, fZ;
118 int RR, GG, BB;
119
120 fY = pow((L + 16.0) / 116.0, 3.0);
121 if (fY < 0.008856)
122 fY = L / 903.3;
123 Y = fY;
124
125 if (fY > 0.008856)
126 fY = pow(fY, 1.0 / 3.0);
127 else
128 fY = 7.787 * fY + 16.0 / 116.0;
129
130 fX = a / 500.0 + fY;
131 if (fX > 0.206893)
132 X = pow(fX, 3.0);
133 else
134 X = (fX - 16.0 / 116.0) / 7.787;
135
136 fZ = fY - b / 200.0;
137 if (fZ > 0.206893)
138 Z = pow(fZ, 3.0);
139 else
140 Z = (fZ - 16.0 / 116.0) / 7.787;
141
142 X *= 0.950456 * 255;
143 Y *= 255;
144 Z *= 1.088754 * 255;
145
146 RR = static_cast<int>(3.240479 * X - 1.537150 * Y - 0.498535 * Z + 0.5);
147 GG = static_cast<int>(-0.969256 * X + 1.875992 * Y + 0.041556 * Z + 0.5);
148 BB = static_cast<int>(0.055648 * X - 0.204043 * Y + 1.057311 * Z + 0.5);
149
150 quint8 R = RR < 0 ? 0 : RR > 255 ? 255 : RR;
151 quint8 G = GG < 0 ? 0 : GG > 255 ? 255 : GG;
152 quint8 B = BB < 0 ? 0 : BB > 255 ? 255 : BB;
153
154 c->setRgba(qRgba(R, G, B, A));
155}
156
157void KoLabColorSpace::toHSY(const QVector<double> &channelValues, qreal *hue, qreal *sat, qreal *luma) const
158{
159 LabToLCH(channelValues[0],channelValues[1],channelValues[2], luma, sat, hue);
160}
161
162QVector <double> KoLabColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const
163{
164 QVector <double> channelValues(4);
165 LCHToLab(*luma, *sat, *hue, &channelValues[0],&channelValues[1],&channelValues[2]);
166 channelValues[3]=1.0;
167 return channelValues;
168}
169
170void KoLabColorSpace::toYUV(const QVector<double> &channelValues, qreal *y, qreal *u, qreal *v) const
171{
172 *y =channelValues[0];
173 *v=channelValues[1];
174 *u=channelValues[2];
175}
176
177QVector <double> KoLabColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const
178{
179 QVector <double> channelValues(4);
180 channelValues[0]=*y;
181 channelValues[1]=*v;
182 channelValues[2]=*u;
183 channelValues[3]=1.0;
184 return channelValues;
185}
186
187quint8 KoLabColorSpace::scaleToU8(const quint8 *srcPixel, qint32 channelIndex) const
188{
189 typename ColorSpaceTraits::channels_type c = ColorSpaceTraits::nativeArray(srcPixel)[channelIndex];
190 qreal b = 0;
191 switch (channelIndex) {
192 case ColorSpaceTraits::L_pos:
193 b = ((qreal)c) / ColorSpaceTraits::math_trait::unitValueL;
194 break;
195 case ColorSpaceTraits::a_pos:
196 case ColorSpaceTraits::b_pos:
197 if (c <= ColorSpaceTraits::math_trait::halfValueAB) {
198 b = ((qreal)c - ColorSpaceTraits::math_trait::zeroValueAB) / (2.0 * (ColorSpaceTraits::math_trait::halfValueAB - ColorSpaceTraits::math_trait::zeroValueAB));
199 } else {
200 b = 0.5 + ((qreal)c - ColorSpaceTraits::math_trait::halfValueAB) / (2.0 * (ColorSpaceTraits::math_trait::unitValueAB - ColorSpaceTraits::math_trait::halfValueAB));
201 }
202 break;
203 default:
204 b = ((qreal)c) / ColorSpaceTraits::math_trait::unitValue;
205 break;
206 }
207
209}
210
211void KoLabColorSpace::convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const qint32 selectedChannelIndex) const
212{
213 for (uint pixelIndex = 0; pixelIndex < nPixels; ++pixelIndex) {
214 for (uint channelIndex = 0; channelIndex < ColorSpaceTraits::channels_nb; ++channelIndex) {
215 if (channelIndex != ColorSpaceTraits::alpha_pos) {
216 if (channelIndex == ColorSpaceTraits::L_pos) {
217 ColorSpaceTraits::channels_type c = ColorSpaceTraits::nativeArray((src + (pixelIndex * ColorSpaceTraits::pixelSize)))[selectedChannelIndex];
218 switch (selectedChannelIndex) {
219 case ColorSpaceTraits::L_pos:
220 break;
221 case ColorSpaceTraits::a_pos:
222 case ColorSpaceTraits::b_pos:
223 if (c <= ColorSpaceTraits::math_trait::halfValueAB) {
224 c = ColorSpaceTraits::math_trait::unitValueL * (((qreal)c - ColorSpaceTraits::math_trait::zeroValueAB) / (2.0 * (ColorSpaceTraits::math_trait::halfValueAB - ColorSpaceTraits::math_trait::zeroValueAB)));
225 } else {
226 c = ColorSpaceTraits::math_trait::unitValueL * (0.5 + ((qreal)c - ColorSpaceTraits::math_trait::halfValueAB) / (2.0 * (ColorSpaceTraits::math_trait::unitValueAB - ColorSpaceTraits::math_trait::halfValueAB)));
227 }
228 break;
229 // As per KoChannelInfo alpha channels are [0..1]
230 default:
231 c = ColorSpaceTraits::math_trait::unitValueL * (qreal)c / ColorSpaceTraits::math_trait::unitValue;
232 break;
233 }
234 ColorSpaceTraits::nativeArray(dst + (pixelIndex * ColorSpaceTraits::pixelSize))[channelIndex] = c;
235 } else {
236 ColorSpaceTraits::nativeArray(dst + (pixelIndex * ColorSpaceTraits::pixelSize))[channelIndex] = ColorSpaceTraits::math_trait::halfValueAB;
237 }
238 } else {
239 ColorSpaceTraits::nativeArray((dst + (pixelIndex * ColorSpaceTraits::pixelSize)))[channelIndex] =
240 ColorSpaceTraits::nativeArray((src + (pixelIndex * ColorSpaceTraits::pixelSize)))[channelIndex];
241 }
242 }
243 }
244}
245
246void KoLabColorSpace::convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const QBitArray selectedChannels) const
247{
248 for (uint pixelIndex = 0; pixelIndex < nPixels; ++pixelIndex) {
249 for (uint channelIndex = 0; channelIndex < ColorSpaceTraits::channels_nb; ++channelIndex) {
250
251 if (selectedChannels.testBit(channelIndex)) {
252 ColorSpaceTraits::nativeArray((dst + (pixelIndex * ColorSpaceTraits::pixelSize)))[channelIndex] =
253 ColorSpaceTraits::nativeArray((src + (pixelIndex * ColorSpaceTraits::pixelSize)))[channelIndex];
254 } else {
255 ColorSpaceTraits::channels_type v;
256 switch (channelIndex) {
257 case ColorSpaceTraits::L_pos:
258 v = ColorSpaceTraits::math_trait::halfValueL;
259 break;
260 case ColorSpaceTraits::a_pos:
261 case ColorSpaceTraits::b_pos:
262 v = ColorSpaceTraits::math_trait::halfValueAB;
263 break;
264 default:
265 v = ColorSpaceTraits::math_trait::zeroValue;
266 break;
267 }
268 ColorSpaceTraits::nativeArray((dst + (pixelIndex * ColorSpaceTraits::pixelSize)))[channelIndex] = v;
269 }
270 }
271 }
272}
Eigen::Matrix< double, 4, 2 > R
qreal v
qreal u
void LabToLCH(const qreal l, const qreal a, const qreal b, qreal *L, qreal *C, qreal *H)
void LCHToLab(const qreal L, const qreal C, const qreal H, qreal *l, qreal *a, qreal *b)
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
const KoID LABAColorModelID("LABA", ki18n("L*a*b*/Alpha"))
unsigned int uint
uint UINT8_TO_UINT16(uint c)
uint UINT16_TO_UINT8(uint c)
@ ALPHA
The channel represents the opacity of a pixel.
@ COLOR
The channel represents a color.
@ UINT16
use this for an integer 16bits channel
static _Tdst scaleToA(_T a)
virtual void addChannel(KoChannelInfo *ci)
void toQColor(const quint8 *src, QColor *c) const override
void convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const qint32 selectedChannelIndex) const override
static const quint32 CHANNEL_ALPHA
QVector< double > fromHSY(qreal *hue, qreal *sat, qreal *luma) const override
QVector< double > fromYUV(qreal *y, qreal *u, qreal *v) const override
static const quint32 CHANNEL_A
~KoLabColorSpace() override
static const quint32 CHANNEL_L
static const quint32 CHANNEL_B
virtual KoColorSpace * clone() const
void toYUV(const QVector< double > &channelValues, qreal *y, qreal *u, qreal *v) const override
static QString colorSpaceId()
void fromQColor(const QColor &color, quint8 *dst) const override
void toHSY(const QVector< double > &channelValues, qreal *hue, qreal *sat, qreal *luma) const override
quint8 scaleToU8(const quint8 *srcPixel, qint32 channelIndex) const override