Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_hsv_adjustment.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2007 Cyrille Berger <cberger@cberger.net>
3 *
4 * SPDX-License-Identifier: LGPL-2.0-only
5*/
6
8#include <KoConfig.h>
9#ifdef HAVE_OPENEXR
10#include <half.h>
11#endif
12
13
14#include <kis_debug.h>
15#include <klocalizedstring.h>
16
17#include <KoColorConversions.h>
19#include <KoColorSpace.h>
20#include <KoColorSpaceTraits.h>
22#include <KoID.h>
23
24#define SCALE_TO_FLOAT( v ) KoColorSpaceMaths< _channel_type_, float>::scaleToA( v )
25#define SCALE_FROM_FLOAT( v ) KoColorSpaceMaths< float, _channel_type_>::scaleToA( v )
26
27template<typename _channel_type_>
28void clamp(float* r, float* g, float* b);
29
30#define FLOAT_CLAMP( v ) *v = (*v < 0.0) ? 0.0 : ( (*v>1.0) ? 1.0 : *v )
31
32template<>
33void clamp<quint8>(float* r, float* g, float* b)
34{
35 FLOAT_CLAMP(r);
36 FLOAT_CLAMP(g);
37 FLOAT_CLAMP(b);
38}
39
40template<>
41void clamp<quint16>(float* r, float* g, float* b)
42{
43 FLOAT_CLAMP(r);
44 FLOAT_CLAMP(g);
45 FLOAT_CLAMP(b);
46}
47
48#ifdef HAVE_OPENEXR
49template<>
50void clamp<half>(float* r, float* g, float* b)
51{
52 *r = qMax(0.0f, *r);
53 *g = qMax(0.0f, *g);
54 *b = qMax(0.0f, *b);
55}
56#endif
57
58template<>
59void clamp<float>(float* r, float* g, float* b)
60{
61 *r = qMax(0.0f, *r);
62 *g = qMax(0.0f, *g);
63 *b = qMax(0.0f, *b);
64}
65
66#include "kis_global.h"
67
68static inline void writeRGBSimple(float *r, float *g, float *b,
69 int sextant,
70 float x, float m, float M)
71{
72 switch (sextant) {
73 case 0: *r = M; *g = x + m; *b = m; break;
74 case 1: *r = x + m; *g = M; *b = m; break;
75 case 2: *r = m; *g = M; *b = x + m; break;
76 case 3: *r = m; *g = x + m; *b = M; break;
77 case 4: *r = x + m; *g = m; *b = M; break;
78 case 5: *r = M; *g = m; *b = x + m; break;
79 }
80}
81
83{
84 inline bool hasChroma(float v) {
85 static const float EPSILON = 1e-9f;
86 return v > EPSILON;
87 }
88
89 inline float valueFromRGB(float r, float g, float b, float m, float M) {
90 Q_UNUSED(r);
91 Q_UNUSED(g);
92 Q_UNUSED(b);
93 Q_UNUSED(m);
94 return M;
95 }
96
97 inline float fixupChroma(float c, float v) {
98 return qMin(v, c);
99 }
100
101 inline void writeRGB(float *r, float *g, float *b,
102 int sextant,
103 float x, float c, float v) {
104
105 const float m = v - c;
106 writeRGBSimple(r, g, b, sextant, x, m, v);
107 }
108};
109
111{
112 inline bool hasChroma(float v) {
113 static const float EPSILON = 1e-9f;
114 return v > EPSILON && v < 1.0f - EPSILON;
115 }
116
117 inline float valueFromRGB(float r, float g, float b, float m, float M) {
118 Q_UNUSED(r);
119 Q_UNUSED(g);
120 Q_UNUSED(b);
121 return 0.5f * (M + m);
122 }
123
124 inline float fixupChroma(float c, float v) {
125 if (v >= 0.5f) {
126 c = qMin(c, 2.0f - 2.0f * v);
127 } else {
128 c = qMin(c, 2.0f * v);
129 }
130
131 return c;
132 }
133
134 inline void writeRGB(float *r, float *g, float *b,
135 int sextant,
136 float x, float c, float v) {
137
138 const float M = v + 0.5f * c;
139 const float m = v - 0.5f * c;
140
141 writeRGBSimple(r, g, b, sextant, x, m, M);
142 }
143};
144
146{
147 inline bool hasChroma(float v) {
148 static const float EPSILON = 1e-9f;
149 return v > EPSILON && v < 1.0f - EPSILON;
150 }
151
152 inline float valueFromRGB(float r, float g, float b, float m, float M) {
153 Q_UNUSED(m);
154 Q_UNUSED(M);
155 return (r + g + b) / 3.0f;
156 }
157
158 inline float fixupChroma(float c, float v) {
159 static const float oneThird = 1.0f / 3.0f;
160
161 if (v >= oneThird) {
162 c = qMin(c, 1.5f * (1.0f - v));
163 } else {
164 c = qMin(c, 3.0f * v);
165 }
166
167 return c;
168 }
169
170 inline void writeRGB(float *r, float *g, float *b,
171 int sextant,
172 float x, float c, float v) {
173
174 static const float oneThird = 1.0f / 3.0f;
175
176 const float m = v - oneThird * (c + x);
177 const float M = c + m;
178
179 writeRGBSimple(r, g, b, sextant, x, m, M);
180 }
181};
182
184{
185 HCYPolicy(float _rCoeff = 0.299f, float _gCoeff = 0.587f, float _bCoeff = 0.114f)
186 : rCoeff(_rCoeff),
187 gCoeff(_gCoeff),
188 bCoeff(_bCoeff)
189 {
190 }
191
192 const float rCoeff = 0.299f;
193 const float gCoeff = 0.587f;
194 const float bCoeff = 0.114f;
195
196 inline bool hasChroma(float v) {
197 static const float EPSILON = 1e-9f;
198 return v > EPSILON && v < 1.0f - EPSILON;
199 }
200
201 inline float valueFromRGB(float r, float g, float b, float m, float M) {
202 Q_UNUSED(m);
203 Q_UNUSED(M);
204 return rCoeff * r + gCoeff * g + bCoeff * b;
205 }
206
207 inline float fixupChroma(float c, float v) {
208 Q_UNUSED(v);
209 // NOTE: no sliding in HCY, because the shape of the triangle
210 // depends on Hue, which complicated code a lot. And it
211 // seems to work fine without it :)
212 return c;
213 }
214
215 inline void writeRGB(float *r, float *g, float *b,
216 int sextant,
217 float x, float c, float v) {
218
219
220 switch (sextant) {
221 case 0: *r = c; *g = x; *b = 0; break;
222 case 1: *r = x; *g = c; *b = 0; break;
223 case 2: *r = 0; *g = c; *b = x; break;
224 case 3: *r = 0; *g = x; *b = c; break;
225 case 4: *r = x; *g = 0; *b = c; break;
226 case 5: *r = c; *g = 0; *b = x; break;
227 }
228
229 const float m = v - *r * rCoeff - *g * gCoeff - *b * bCoeff;
230 *r += m;
231 *g += m;
232 *b += m;
233 }
234};
235
236template <class ValuePolicy>
237void HSVTransform(float *r, float *g, float *b, float dh, float ds, float dv, ValuePolicy valuePolicy)
238{
239 static const float EPSILON = 1e-9f;
240
241 float h;
242
243 float M = qMax(*r, qMax(*g, *b));
244 float m = qMin(*r, qMin(*g, *b));
245
246 float chroma = M - m;
247
248 float v = valuePolicy.valueFromRGB(*r, *g, *b, m, M);
249
250 if (!valuePolicy.hasChroma(v)) {
251 chroma = 0.0f;
252 h = 0.0f;
253 if (dv < 0) {
254 v *= dv + 1.0f;
255 } else {
256 v += dv * (1.0f - v);
257 }
258 } else {
259 if (chroma > EPSILON) {
260 if (*r == M)
261 h = (*g - *b) / chroma;
262 else if (*g == M)
263 h = 2 + (*b - *r) / chroma;
264 else
265 h = 4 + (*r - *g) / chroma;
266
267 h *= 60;
268 h += dh * 180;
269
271
272 if (ds > 0) {
277
278 chroma = qMin(1.0f, chroma * (1.0f + ds + 2.0f * pow2(ds)));
279 } else {
280 chroma *= ds + 1.0f;
281 }
282
283 } else {
284 h = 0.0f;
285 }
286
287 {
288 const float dstV = dv > 0.0f ? 1.0f : 0.0f;
289 const float vCoeff = dstV - v;
290 const float chromaCoeff = 0 - chroma;
291 const float movement = std::abs(dv);
292
293 v += movement * vCoeff;
294 chroma += movement * chromaCoeff;
295 }
296
297 v = qBound(0.0f, v, 1.0f);
298 chroma = valuePolicy.fixupChroma(chroma, v);
299 }
300
301 if (v <= EPSILON) {
302 *r = *g = *b = 0.0;
303 } else {
304 h /= 60.0f;
305 const int sextant = static_cast<int>(h);
306 const float fract = h - sextant;
307
308 const float x =
309 sextant & 0x1 ?
310 chroma - chroma * fract :
311 chroma * fract;
312
313 valuePolicy.writeRGB(r, g, b, sextant, x, chroma, v);
314 }
315}
316
317
318template<typename _channel_type_,typename traits>
320{
321 typedef traits RGBTrait;
322 typedef typename RGBTrait::Pixel RGBPixel;
323
324public:
326 m_adj_h(0.0),
327 m_adj_s(0.0),
328 m_adj_v(0.0),
329 m_lumaRed(0.0),
330 m_lumaGreen(0.0),
331 m_lumaBlue(0.0),
332 m_type(0),
333 m_colorize(false),
335 {
336 }
337
338public:
339
340 void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const override
341 {
342
343 //if (m_model="RGBA" || m_colorize) {
344 /*It'd be nice to have LCH automatically selector for LAB in the future, but I don't know how to select LAB
345 * */
346 const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
347 RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
348 float h, s, v;
349 float r = 0.0;
350 float g = 0.0;
351 float b = 0.0;
352 qreal lumaR, lumaG, lumaB;
353 //Default to rec 709 when there's no coefficients given//
354 if (m_lumaRed<=0 || m_lumaGreen<=0 || m_lumaBlue<=0) {
355 lumaR = 0.2126;
356 lumaG = 0.7152;
357 lumaB = 0.0722;
358 } else {
359 lumaR = m_lumaRed;
360 lumaG = m_lumaGreen;
361 lumaB = m_lumaBlue;
362 }
363 while (nPixels > 0) {
364
365 if (m_colorize) {
366 h = m_adj_h * 360;
367 if (h >= 360.0) h = 0;
368
369 s = m_adj_s;
370
371 r = SCALE_TO_FLOAT(src->red);
372 g = SCALE_TO_FLOAT(src->green);
373 b = SCALE_TO_FLOAT(src->blue);
374
375 float luminance = r * lumaR + g * lumaG + b * lumaB;
376
377 if (m_adj_v > 0) {
378 luminance *= (1.0 - m_adj_v);
379 luminance += 1.0 - (1.0 - m_adj_v);
380 }
381 else if (m_adj_v < 0 ){
382 luminance *= (m_adj_v + 1.0);
383 }
384 v = luminance;
385 HSLToRGB(h, s, v, &r, &g, &b);
386
387 } else {
388
389 if (m_type == 0) {
390 if (!m_compatibilityMode) {
391 r = SCALE_TO_FLOAT(src->red);
392 g = SCALE_TO_FLOAT(src->green);
393 b = SCALE_TO_FLOAT(src->blue);
394 HSVTransform(&r, &g, &b, m_adj_h, m_adj_s, m_adj_v, HSVPolicy());
395 } else {
396 RGBToHSV(SCALE_TO_FLOAT(src->red), SCALE_TO_FLOAT(src->green), SCALE_TO_FLOAT(src->blue), &h, &s, &v);
397 h += m_adj_h * 180;
399 s += m_adj_s;
400 v += m_adj_v;
401 HSVToRGB(h, s, v, &r, &g, &b);
402 }
403 } else if (m_type == 1) {
404
405 if (!m_compatibilityMode) {
406 r = SCALE_TO_FLOAT(src->red);
407 g = SCALE_TO_FLOAT(src->green);
408 b = SCALE_TO_FLOAT(src->blue);
409 HSVTransform(&r, &g, &b, m_adj_h, m_adj_s, m_adj_v, HSLPolicy());
410 } else {
411 RGBToHSL(SCALE_TO_FLOAT(src->red), SCALE_TO_FLOAT(src->green), SCALE_TO_FLOAT(src->blue), &h, &s, &v);
412
413 h += m_adj_h * 180;
415 s *= (m_adj_s + 1.0);
416 if (m_adj_v < 0) {
417 v *= (m_adj_v + 1.0);
418 } else {
419 v += (m_adj_v * (1.0 - v));
420 }
421 HSLToRGB(h, s, v, &r, &g, &b);
422 }
423 } else if (m_type == 2) {
424
425 if (!m_compatibilityMode) {
426 r = SCALE_TO_FLOAT(src->red);
427 g = SCALE_TO_FLOAT(src->green);
428 b = SCALE_TO_FLOAT(src->blue);
429 HSVTransform(&r, &g, &b, m_adj_h, m_adj_s, m_adj_v, HCIPolicy());
430 } else {
431 qreal red = SCALE_TO_FLOAT(src->red);
432 qreal green = SCALE_TO_FLOAT(src->green);
433 qreal blue = SCALE_TO_FLOAT(src->blue);
434 qreal hue, sat, intensity;
435 RGBToHCI(red, green, blue, &hue, &sat, &intensity);
436
437 hue *= 360.0;
438 hue += m_adj_h * 180;
439 hue = normalizeAngleDegrees(hue);
440 sat *= (m_adj_s + 1.0);
441 intensity += m_adj_v;
442
443 HCIToRGB(hue/360.0, sat, intensity, &red, &green, &blue);
444
445 r = red;
446 g = green;
447 b = blue;
448 }
449 } else if (m_type == 3) {
450
451 if (!m_compatibilityMode) {
452 r = SCALE_TO_FLOAT(src->red);
453 g = SCALE_TO_FLOAT(src->green);
454 b = SCALE_TO_FLOAT(src->blue);
455 HSVTransform(&r, &g, &b, m_adj_h, m_adj_s, m_adj_v, HCYPolicy(lumaR, lumaG, lumaB));
456 } else {
457 qreal red = SCALE_TO_FLOAT(src->red);
458 qreal green = SCALE_TO_FLOAT(src->green);
459 qreal blue = SCALE_TO_FLOAT(src->blue);
460 qreal hue, sat, luma;
461 RGBToHCY(red, green, blue, &hue, &sat, &luma, lumaR, lumaG, lumaB);
462
463 hue *= 360.0;
464 hue += m_adj_h * 180;
465 hue = normalizeAngleDegrees(hue);
466 sat *= (m_adj_s + 1.0);
467 luma += m_adj_v;
468
469 HCYToRGB(hue/360.0, sat, luma, &red, &green, &blue, lumaR, lumaG, lumaB);
470 r = red;
471 g = green;
472 b = blue;
473 }
474
475 } else if (m_type == 4) {
476
477 qreal red = SCALE_TO_FLOAT(src->red);
478 qreal green = SCALE_TO_FLOAT(src->green);
479 qreal blue = SCALE_TO_FLOAT(src->blue);
480 qreal y, cb, cr;
481 RGBToYUV(red, green, blue, &y, &cb, &cr, lumaR, lumaG, lumaB);
482
483 cb *= (m_adj_h + 1.0);
484 cr *= (m_adj_s + 1.0);
485 y += (m_adj_v);
486
487 YUVToRGB(y, cb, cr, &red, &green, &blue, lumaR, lumaG, lumaB);
488 r = red;
489 g = green;
490 b = blue;
491 } else {
492 Q_ASSERT_X(false, "", "invalid type");
493 }
494 }
495
496 clamp< _channel_type_ >(&r, &g, &b);
497 dst->red = SCALE_FROM_FLOAT(r);
498 dst->green = SCALE_FROM_FLOAT(g);
499 dst->blue = SCALE_FROM_FLOAT(b);
500 dst->alpha = src->alpha;
501
502 --nPixels;
503 ++src;
504 ++dst;
505 }
506 /*} else if (m_model="LABA"){
507 const LABPixel* src = reinterpret_cast<const LABPixel*>(srcU8);
508 LABPixel* dst = reinterpret_cast<LABPixel*>(dstU8);
509 qreal lightness = SCALE_TO_FLOAT(src->L);
510 qreal a = SCALE_TO_FLOAT(src->a);
511 qreal b = SCALE_TO_FLOAT(src->b);
512 qreal L, C, H;
513
514 while (nPixels > 0) {
515 if (m_type = 4) {
516 a *= (m_adj_h + 1.0);
517 a = qBound(0.0, a, 1.0);
518
519 b *= (m_adj_s + 1.0);
520 b = qBound(0.0, b, 1.0);
521
522 if (m_adj_v < 0)
523 lightness *= (m_adj_v + 1.0);
524 else
525 lightness += (m_adj_v * (1.0 - lightness));
526 } else {//lch
527 LABToLCH(lightness, a, b, &L, &C, &H);
528 H *=360;
529 H += m_adj_h * 180;
530 if (H > 360) h -= 360;
531 if (H < 0) h += 360;
532 C += m_adj_s;
533 C = qBound(0.0,C,1.0);
534 L += m_adj_v;
535 L = qBound(0.0,L,1.0);
536 LCHToLAB(L, C, H/360.0, &lightness, &a, &b);
537 }
538 clamp< _channel_type_ >(&lightness, &a, &b);
539 dst->L = SCALE_FROM_FLOAT(lightness);
540 dst->a = SCALE_FROM_FLOAT(a);
541 dst->b = SCALE_FROM_FLOAT(b);
542 dst->alpha = src->alpha;
543
544 --nPixels;
545 ++src;
546 ++dst;
547 }
548 }*/
549 }
550
551 QList<QString> parameters() const override
552 {
553 QList<QString> list;
554 list << "h" << "s" << "v" << "type" << "colorize" << "lumaRed" << "lumaGreen"<< "lumaBlue" << "compatibilityMode";
555 return list;
556 }
557
558 int parameterId(const QString& name) const override
559 {
560 if (name == "h") {
561 return 0;
562 } else if (name == "s") {
563 return 1;
564 } else if (name == "v") {
565 return 2;
566 } else if (name == "type") {
567 return 3;
568 } else if (name == "colorize") {
569 return 4;
570 } else if (name == "lumaRed") {
571 return 5;
572 } else if (name == "lumaGreen") {
573 return 6;
574 } else if (name == "lumaBlue") {
575 return 7;
576 } else if (name == "compatibilityMode") {
577 return 8;
578 }
579 return -1;
580 }
581
591 void setParameter(int id, const QVariant& parameter) override
592 {
593 switch(id)
594 {
595 case 0:
596 m_adj_h = parameter.toDouble();
597 break;
598 case 1:
599 m_adj_s = parameter.toDouble();
600 break;
601 case 2:
602 m_adj_v = parameter.toDouble();
603 break;
604 case 3:
605 m_type = parameter.toInt();
606 break;
607 case 4:
608 m_colorize = parameter.toBool();
609 break;
610 case 5:
611 m_lumaRed = parameter.toDouble();
612 break;
613 case 6:
614 m_lumaGreen = parameter.toDouble();
615 break;
616 case 7:
617 m_lumaBlue = parameter.toDouble();
618 break;
619 case 8:
620 m_compatibilityMode = parameter.toBool();
621 break;
622 default:
623 KIS_ASSERT_RECOVER_NOOP(false && "Unknown parameter ID. Ignored!");
624 ;
625 }
626 }
627
628private:
629
635};
636
637template<typename _channel_type_,typename traits>
639{
640 typedef traits RGBTrait;
641 typedef typename RGBTrait::Pixel RGBPixel;
642
643public:
645 m_lumaRed(0.0),
646 m_lumaGreen(0.0),
647 m_lumaBlue(0.0)
648 {}
649
650 QList<QString> parameters() const override
651 {
652 QList<QString> list;
653 list << "curve" << "channel" << "driverChannel" << "relative" << "lumaRed" << "lumaGreen"<< "lumaBlue";
654 return list;
655 }
656
657 int parameterId(const QString& name) const override
658 {
659 if (name == "curve") {
660 return PAR_CURVE;
661 } else if (name == "channel") {
662 return PAR_CHANNEL;
663 } else if (name == "driverChannel") {
664 return PAR_DRIVER_CHANNEL;
665 } else if (name == "relative") {
666 return PAR_RELATIVE;
667 } else if (name == "lumaRed") {
668 return PAR_LUMA_R;
669 } else if (name == "lumaGreen") {
670 return PAR_LUMA_G;
671 } else if (name == "lumaBlue") {
672 return PAR_LUMA_B;
673 }
674 return -1;
675 }
676
686 void setParameter(int id, const QVariant& parameter) override
687 {
688 switch(id)
689 {
690 case PAR_CURVE:
691 m_curve = parameter.value<QVector<quint16>>();
692 break;
693 case PAR_CHANNEL:
694 case PAR_DRIVER_CHANNEL: {
695 int channel = parameter.toInt();
696 KIS_ASSERT_RECOVER_RETURN(0 <= channel && channel < KisHSVCurve::ChannelCount && "Invalid channel. Ignored!");
697
698 if (id == PAR_CHANNEL) {
699 m_channel = channel;
700 } else {
701 m_driverChannel = channel;
702 }
703 } break;
704 case PAR_RELATIVE:
705 m_relative = parameter.toBool();
706 break;
707 case PAR_LUMA_R:
708 m_lumaRed = parameter.toDouble();
709 break;
710 case PAR_LUMA_G:
711 m_lumaGreen = parameter.toDouble();
712 break;
713 case PAR_LUMA_B:
714 m_lumaBlue = parameter.toDouble();
715 break;
716 default:
717 KIS_ASSERT_RECOVER_NOOP(false && "Unknown parameter ID. Ignored!");
718 }
719 }
720
721 const float SCALE_FROM_16BIT = 1.0f / 0xFFFF;
722 void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const override
723 {
724 const RGBPixel* src = reinterpret_cast<const RGBPixel*>(srcU8);
725 RGBPixel* dst = reinterpret_cast<RGBPixel*>(dstU8);
726 float max = m_curve.size() - 1;
727
728 int driverChannel = m_relative ? m_driverChannel : m_channel;
729
730 float component[KisHSVCurve::ChannelCount];
731
732 // Aliases for convenience
733 float &h = component[KisHSVCurve::Hue];
734 float &s = component[KisHSVCurve::Saturation];
735 float &v = component[KisHSVCurve::Value];
736 float &r = component[traits::red_pos];
737 float &g = component[traits::green_pos];
738 float &b = component[traits::blue_pos];
739 float &a = component[KisHSVCurve::Alpha];
740
741 while (nPixels > 0) {
742 r = SCALE_TO_FLOAT(src->red);
743 g = SCALE_TO_FLOAT(src->green);
744 b = SCALE_TO_FLOAT(src->blue);
745 a = SCALE_TO_FLOAT(src->alpha);
746
747 RGBToHSV(r, g, b, &h, &s, &v);
748
749 // Normalize hue to 0.0 to 1.0 range
750 h /= 360.0f;
751
752 float adjustment = lookupComponent(component[driverChannel], max) * SCALE_FROM_16BIT;
753
754 if (m_relative) {
755 // Curve uses range 0.0 to 1.0, but for adjustment we need -1.0 to 1.0
756 adjustment = 2.0f * adjustment - 1.0f;
757
759 r += adjustment;
760 g += adjustment;
761 b += adjustment;
762 } else {
763 component[m_channel] += adjustment;
764 }
765 } else {
767 r = b = g = adjustment;
768 } else {
769 component[m_channel] = adjustment;
770 }
771 }
772
773 h *= 360.0f;
774 if (h > 360) h -= 360;
775 if (h < 0) h += 360;
776
778 HSVToRGB(h, s, v, &r, &g, &b);
779 }
780
781 clamp< _channel_type_ >(&r, &g, &b);
782 FLOAT_CLAMP(&a);
783
784 dst->red = SCALE_FROM_FLOAT(r);
785 dst->green = SCALE_FROM_FLOAT(g);
786 dst->blue = SCALE_FROM_FLOAT(b);
787 dst->alpha = SCALE_FROM_FLOAT(a);
788
789 --nPixels;
790 ++src;
791 ++dst;
792 }
793 }
794
795
796 float lookupComponent(float x, float max) const
797 {
798 // No curve for this component? Pass through unmodified
799 if (max < 2) return x;
800 if (x < 0) return m_curve[0];
801
802 float lookup = x * max;
803 float base = floor(lookup);
804 float offset = lookup - base;
805
806 if (base >= max) {
807 base = max - 1.0f;
808 offset = 1.0f;
809 }
810 int index = (int)base;
811
812 return (1.0f - offset) * m_curve[index]
813 + offset * m_curve[index + 1];
814 }
815
816
817private:
828
830 int m_channel = 0;
832 bool m_relative = false;
833
834 /* Note: the filter currently only supports HSV, so these are
835 * unused, but will be needed once HSL, etc.
836 */
838};
839
840
845
847{
849 l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
850 l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
851 l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
852 l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
853 return l;
854}
855
856KoColorTransformation* KisHSVAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
857{
859 if (colorSpace->colorModelId() != RGBAColorModelID) {
860 dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisHSVAdjustmentFactory::createTransformation";
861 return 0;
862 }
863 if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
865 } else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
867 }
868#ifdef HAVE_OPENEXR
869 else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
871 }
872#endif
873 else if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
875 } else {
876 dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisHSVAdjustmentFactory::createTransformation";
877 return 0;
878 }
879 adj->setParameters(parameters);
880 return adj;
881
882}
883
884
889
891{
893 l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer8BitsColorDepthID));
894 l.append(QPair< KoID, KoID >(RGBAColorModelID , Integer16BitsColorDepthID));
895 l.append(QPair< KoID, KoID >(RGBAColorModelID , Float16BitsColorDepthID));
896 l.append(QPair< KoID, KoID >(RGBAColorModelID , Float32BitsColorDepthID));
897 return l;
898}
899
900KoColorTransformation* KisHSVCurveAdjustmentFactory::createTransformation(const KoColorSpace* colorSpace, QHash<QString, QVariant> parameters) const
901{
903 if (colorSpace->colorModelId() != RGBAColorModelID) {
904 dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisHSVCurveAdjustmentFactory::createTransformation";
905 return 0;
906 }
907 if (colorSpace->colorDepthId() == Integer8BitsColorDepthID) {
909 } else if (colorSpace->colorDepthId() == Integer16BitsColorDepthID) {
911 }
912#ifdef HAVE_OPENEXR
913 else if (colorSpace->colorDepthId() == Float16BitsColorDepthID) {
915 }
916#endif
917 else if (colorSpace->colorDepthId() == Float32BitsColorDepthID) {
919 } else {
920 dbgKrita << "Unsupported color space " << colorSpace->id() << " in KisHSVCurveAdjustmentFactory::createTransformation";
921 return 0;
922 }
923 adj->setParameters(parameters);
924 return adj;
925
926}
qreal v
#define EPSILON
void RGBToHSV(float r, float g, float b, float *h, float *s, float *v)
void HCIToRGB(const qreal h, const qreal c, const qreal i, qreal *red, qreal *green, qreal *blue)
void RGBToYUV(const qreal r, const qreal g, const qreal b, qreal *y, qreal *u, qreal *v, qreal R, qreal G, qreal B)
void RGBToHCI(const qreal r, const qreal g, const qreal b, qreal *h, qreal *c, qreal *i)
void RGBToHCY(const qreal r, const qreal g, const qreal b, qreal *h, qreal *c, qreal *y, qreal R, qreal G, qreal B)
void RGBToHSL(float r, float g, float b, float *h, float *s, float *l)
void HSVToRGB(float h, float s, float v, float *r, float *g, float *b)
void YUVToRGB(const qreal y, const qreal u, const qreal v, qreal *r, qreal *g, qreal *b, qreal R, qreal G, qreal B)
void HSLToRGB(float h, float sl, float l, float *r, float *g, float *b)
void HCYToRGB(const qreal h, const qreal c, const qreal y, qreal *red, qreal *green, qreal *blue, qreal R, qreal G, qreal B)
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID Float16BitsColorDepthID("F16", ki18n("16-bit float/channel"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
QList< QPair< KoID, KoID > > supportedModels() const override
KoColorTransformation * createTransformation(const KoColorSpace *colorSpace, QHash< QString, QVariant > parameters) const override
int parameterId(const QString &name) const override
QList< QString > parameters() const override
RGBTrait::Pixel RGBPixel
void setParameter(int id, const QVariant &parameter) override
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const override
KoColorTransformation * createTransformation(const KoColorSpace *colorSpace, QHash< QString, QVariant > parameters) const override
QList< QPair< KoID, KoID > > supportedModels() const override
int parameterId(const QString &name) const override
void setParameter(int id, const QVariant &parameter) override
float lookupComponent(float x, float max) const
void transform(const quint8 *srcU8, quint8 *dstU8, qint32 nPixels) const override
QList< QString > parameters() const override
virtual KoID colorModelId() const =0
virtual KoID colorDepthId() const =0
void setParameters(const QHash< QString, QVariant > &parameters)
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define dbgKrita
Definition kis_debug.h:45
T pow2(const T &x)
Definition kis_global.h:166
std::enable_if< std::is_floating_point< T >::value, T >::type normalizeAngleDegrees(T a)
Definition kis_global.h:132
void clamp< quint8 >(float *r, float *g, float *b)
void clamp< float >(float *r, float *g, float *b)
void clamp< quint16 >(float *r, float *g, float *b)
#define SCALE_FROM_FLOAT(v)
#define FLOAT_CLAMP(v)
void HSVTransform(float *r, float *g, float *b, float dh, float ds, float dv, ValuePolicy valuePolicy)
#define SCALE_TO_FLOAT(v)
static void writeRGBSimple(float *r, float *g, float *b, int sextant, float x, float m, float M)
void clamp(float *r, float *g, float *b)
void writeRGB(float *r, float *g, float *b, int sextant, float x, float c, float v)
bool hasChroma(float v)
float valueFromRGB(float r, float g, float b, float m, float M)
float fixupChroma(float c, float v)
const float gCoeff
void writeRGB(float *r, float *g, float *b, int sextant, float x, float c, float v)
float valueFromRGB(float r, float g, float b, float m, float M)
const float bCoeff
bool hasChroma(float v)
const float rCoeff
float fixupChroma(float c, float v)
HCYPolicy(float _rCoeff=0.299f, float _gCoeff=0.587f, float _bCoeff=0.114f)
bool hasChroma(float v)
float valueFromRGB(float r, float g, float b, float m, float M)
void writeRGB(float *r, float *g, float *b, int sextant, float x, float c, float v)
float fixupChroma(float c, float v)
bool hasChroma(float v)
float valueFromRGB(float r, float g, float b, float m, float M)
void writeRGB(float *r, float *g, float *b, int sextant, float x, float c, float v)
float fixupChroma(float c, float v)