Krita Source Code Documentation
Loading...
Searching...
No Matches
KoColorTransferFunctions.h
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2021 Wolthera van Hövell tot Westerflier <griffinvalley@gmail.com>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#ifndef KOCOLORTRANSFERFUNCTIONS_H
8#define KOCOLORTRANSFERFUNCTIONS_H
9
10#include <cmath>
11
12#include <QtGlobal>
13
14#include <KoAlwaysInline.h>
15
26// Not all embedded nclx color space definitions can be converted to icc, so we
27// keep an enum to load those.
29
30static constexpr uint16_t max12bit = 4095.f;
31static constexpr float max16bit = 65535.0f;
32static constexpr float multiplier10bit = 1.0f / 1023.0f;
33static constexpr float multiplier12bit = 1.0f / 4095.0f;
34static constexpr float multiplier16bit = 1.0f / max16bit;
35
37
38ALWAYS_INLINE float applySmpte2048Curve(float x) noexcept
39{
40 const float m1 = 2610.0f / 4096.0f / 4.0f;
41 const float m2 = 2523.0f / 4096.0f * 128.0f;
42 const float a1 = 3424.0f / 4096.0f;
43 const float c2 = 2413.0f / 4096.0f * 32.0f;
44 const float c3 = 2392.0f / 4096.0f * 32.0f;
45 const float a4 = 1.0f;
46 const float x_p = powf(0.008f * std::max(0.0f, x), m1);
47 const float res = powf((a1 + c2 * x_p) / (a4 + c3 * x_p), m2);
48 return res;
49}
50
51ALWAYS_INLINE float removeSmpte2048Curve(float x) noexcept
52{
53 const float m1_r = 4096.0f * 4.0f / 2610.0f;
54 const float m2_r = 4096.0f / 2523.0f / 128.0f;
55 const float a1 = 3424.0f / 4096.0f;
56 const float c2 = 2413.0f / 4096.0f * 32.0f;
57 const float c3 = 2392.0f / 4096.0f * 32.0f;
58
59 const float x_p = powf(x, m2_r);
60 const float res = powf(qMax(0.0f, x_p - a1) / (c2 - c3 * x_p), m1_r);
61 return res * 125.0f;
62}
63
64// From ITU Bt. 2390-8 pg. 31, this calculates the gamma for the nominal peak.
65// This may differ per display regardless, but this is a good baseline.
66ALWAYS_INLINE float HLGOOTFGamma(float nominalPeak) noexcept
67{
68 const float k = 1.111f;
69 return 1.2f * powf(k, log2f(nominalPeak * (1.f / 1000.0f)));
70}
71
72// The HLG OOTF needs to be applied to convert from 'display linear' to 'scene linear'.
73// Krita doesn't support sending tagged HLG to the display, so we have to pretend
74// we're always converting from PQ to HLG.
76 const double *lumaCoefficients,
77 float gamma = 1.2f,
78 float nominalPeak = 1000.0f) noexcept
79{
80 const float luma = (rgb[0] * static_cast<float>(lumaCoefficients[0]))
81 + (rgb[1] * static_cast<float>(lumaCoefficients[1]))
82 + (rgb[2] * static_cast<float>(lumaCoefficients[2]));
83 const float a = powf(luma, gamma);
84 rgb[0] *= a;
85 rgb[1] *= a;
86 rgb[2] *= a;
87}
88
89// The HLG OOTF needs to be removed to convert from 'scene linear' to 'display linear'.
90// Krita doesn't support sending tagged HLG to the display, so we have to pretend
91// we're always converting from HLG to PQ.
93 const double *lumaCoefficients,
94 float gamma = 1.2f,
95 float nominalPeak = 1000.0f) noexcept
96{
97 const float luma = (rgb[0] * static_cast<float>(lumaCoefficients[0]))
98 + (rgb[1] * static_cast<float>(lumaCoefficients[1]))
99 + (rgb[2] * static_cast<float>(lumaCoefficients[2]));
100 const float multiplier = powf(luma, 1.0 / gamma) ;
101 rgb[0] *= multiplier;
102 rgb[1] *= multiplier;
103 rgb[2] *= multiplier;
104}
105
106ALWAYS_INLINE float applyHLGCurve(float x) noexcept
107{
108 const float a = 0.17883277f;
109 const float b = 0.28466892f;
110 const float c = 0.55991073f;
111
114 const float scale = (1.0f / 3.7743f);
115
116 float val = x * scale;
117
118 if (val > 1.0f / 12.0f) {
119 val = (a * logf(12.0f * (val) - b) + c) ;
120 } else {
121 val = (sqrtf(3.0f * (val)));
122 }
123 return qBound(0.0f, val, 1.0f);
124}
125
126ALWAYS_INLINE float removeHLGCurve(float x) noexcept
127{
128 const float a = 0.17883277f;
129 const float b = 0.28466892f;
130 const float c = 0.55991073f;
131
132 const float scale = 3.7743f;
133
134 if (x <= 0.5f) {
135 // return (powf(x, 2.0) / 3.0);
136 return ((x * x) / 3.0f) * scale;
137 } else {
138 return ((expf((x - c) * (1.f / a)) + b) * (1.f / 12.0f)) * scale;
139 }
140}
141
143{
144 return powf(48.0f * x * (1.f / 52.37f), (1.f / 2.6f));
145}
146
148{
149 return (52.37f / 48.0f) * powf(x, 2.6f);
150}
151
153
154#if !defined(XSIMD_NO_SUPPORTED_ARCHITECTURE)
155
156#include <KoStreamedMath.h>
157
158template<typename Arch>
161
163 {
164 constexpr float m1_r = 4096.0f * 4.0f / 2610.0f;
165 constexpr float m2_r = 4096.0f / 2523.0f / 128.0f;
166 constexpr float a1 = 3424.0f / 4096.0f;
167 constexpr float c2 = 2413.0f / 4096.0f * 32.0f;
168 constexpr float c3 = 2392.0f / 4096.0f * 32.0f;
169
170 const float_v x_p = xsimd::pow(x, float_v(m2_r));
171 const float_v res =
172 xsimd::pow(xsimd::max(float_v(0.0f), x_p - a1) / (c2 - c3 * x_p),
173 float_v(m1_r));
174 x = res * 125.0f;
175 }
176
177 static ALWAYS_INLINE void removeHLGCurve(float_v &x) noexcept
178 {
179 constexpr float a = 0.17883277f;
180 constexpr float b = 0.28466892f;
181 constexpr float c = 0.55991073f;
182
183 constexpr float scale = 3.7743f;
184
185 const float_v x1 = x * x * (1.f / 3.0f);
186 const float_v x2 =
187 (xsimd::exp((x - c) * (1.f / a)) + b) * (1.f / 12.0f);
188 x = xsimd::select(x <= float_v(0.5f), x1, x2) * scale;
189 }
190
192 {
193 x = (52.37f / 48.0f) * xsimd::pow(x, float_v(2.6f));
194 }
195};
196
197#endif // !defined(XSIMD_NO_SUPPORTED_ARCHITECTURE)
198
199#endif // KOCOLORTRANSFERFUNCTIONS_H
#define ALWAYS_INLINE
ALWAYS_INLINE void applyHLGOOTF(float *rgb, const double *lumaCoefficients, float gamma=1.2f, float nominalPeak=1000.0f) noexcept
ALWAYS_INLINE float removeHLGCurve(float x) noexcept
ALWAYS_INLINE void removeHLGOOTF(float *rgb, const double *lumaCoefficients, float gamma=1.2f, float nominalPeak=1000.0f) noexcept
static constexpr float max16bit
LinearizePolicy
The KoColorTransferFunctions class.
static constexpr uint16_t max12bit
static constexpr float multiplier12bit
static constexpr float multiplier10bit
ALWAYS_INLINE float removeSMPTE_ST_428Curve(float x) noexcept
static constexpr float multiplier16bit
ALWAYS_INLINE float applySmpte2048Curve(float x) noexcept
ALWAYS_INLINE float applySMPTE_ST_428Curve(float x) noexcept
ALWAYS_INLINE float removeSmpte2048Curve(float x) noexcept
ALWAYS_INLINE float HLGOOTFGamma(float nominalPeak) noexcept
ALWAYS_INLINE float applyHLGCurve(float x) noexcept
constexpr std::enable_if< sizeof...(values)==0, size_t >::type max()
static ALWAYS_INLINE void removeSmpte2048Curve(float_v &x) noexcept
typename KoStreamedMath< Arch >::float_v float_v
static ALWAYS_INLINE void removeSMPTE_ST_428Curve(float_v &x) noexcept
static ALWAYS_INLINE void removeHLGCurve(float_v &x) noexcept
xsimd::batch< float, _impl > float_v