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 = nominalPeak * powf(luma, gamma - 1.f);
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.f / nominalPeak), (1.f - gamma) * (1.f / gamma)) * (1.f / nominalPeak);
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
112 if (x > 1.0f / 12.0f) {
113 return (a * logf(12.0f * x - b) + c);
114 } else {
115 // return (sqrt(3.0) * powf(x, 0.5));
116 return (sqrtf(3.0f) * sqrtf(x));
117 }
118}
119
120ALWAYS_INLINE float removeHLGCurve(float x) noexcept
121{
122 const float a = 0.17883277f;
123 const float b = 0.28466892f;
124 const float c = 0.55991073f;
125 if (x <= 0.5f) {
126 // return (powf(x, 2.0) / 3.0);
127 return x * x * (1.f / 3.0f);
128 } else {
129 return (expf((x - c) * (1.f / a)) + b) * (1.f / 12.0f);
130 }
131}
132
134{
135 return powf(48.0f * x * (1.f / 52.37f), (1.f / 2.6f));
136}
137
139{
140 return (52.37f / 48.0f) * powf(x, 2.6f);
141}
142
144
145#if !defined(XSIMD_NO_SUPPORTED_ARCHITECTURE)
146
147#include <KoStreamedMath.h>
148
149template<typename Arch>
152
154 {
155 constexpr float m1_r = 4096.0f * 4.0f / 2610.0f;
156 constexpr float m2_r = 4096.0f / 2523.0f / 128.0f;
157 constexpr float a1 = 3424.0f / 4096.0f;
158 constexpr float c2 = 2413.0f / 4096.0f * 32.0f;
159 constexpr float c3 = 2392.0f / 4096.0f * 32.0f;
160
161 const float_v x_p = xsimd::pow(x, float_v(m2_r));
162 const float_v res =
163 xsimd::pow(xsimd::max(float_v(0.0f), x_p - a1) / (c2 - c3 * x_p),
164 float_v(m1_r));
165 x = res * 125.0f;
166 }
167
168 static ALWAYS_INLINE void removeHLGCurve(float_v &x) noexcept
169 {
170 constexpr float a = 0.17883277f;
171 constexpr float b = 0.28466892f;
172 constexpr float c = 0.55991073f;
173
174 const float_v x1 = x * x * (1.f / 3.0f);
175 const float_v x2 =
176 (xsimd::exp((x - c) * (1.f / a)) + b) * (1.f / 12.0f);
177 x = xsimd::select(x <= float_v(0.5f), x1, x2);
178 }
179
181 {
182 x = (52.37f / 48.0f) * xsimd::pow(x, float_v(2.6f));
183 }
184};
185
186#endif // !defined(XSIMD_NO_SUPPORTED_ARCHITECTURE)
187
188#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