Changeset View
Changeset View
Standalone View
Standalone View
plugins/color/lcms2engine/LcmsRGBP2020PQColorSpaceTransformation.h
- This file was added.
1 | /* | ||||
---|---|---|---|---|---|
2 | * Copyright (c) 2019 Dmitry Kazakov <dimula73@gmail.com> | ||||
3 | * | ||||
4 | * This program is free software; you can redistribute it and/or modify | ||||
5 | * it under the terms of the GNU General Public License as published by | ||||
6 | * the Free Software Foundation; either version 2 of the License, or | ||||
7 | * (at your option) any later version. | ||||
8 | * | ||||
9 | * This program is distributed in the hope that it will be useful, | ||||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
12 | * GNU General Public License for more details. | ||||
13 | * | ||||
14 | * You should have received a copy of the GNU General Public License | ||||
15 | * along with this program; if not, write to the Free Software | ||||
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
17 | */ | ||||
18 | | ||||
19 | #ifndef LCMSRGBP2020PQCOLORSPACETRANSFORMATION_H | ||||
20 | #define LCMSRGBP2020PQCOLORSPACETRANSFORMATION_H | ||||
21 | | ||||
22 | #include "KoAlwaysInline.h" | ||||
23 | #include "KoColorModelStandardIds.h" | ||||
24 | #include "KoColorSpaceMaths.h" | ||||
25 | #include "KoColorModelStandardIdsUtils.h" | ||||
26 | #include "KoColorConversionTransformationFactory.h" | ||||
27 | | ||||
28 | #include <colorspaces/rgb_u8/RgbU8ColorSpace.h> | ||||
29 | #include <colorspaces/rgb_u16/RgbU16ColorSpace.h> | ||||
30 | #include <colorspaces/rgb_f16/RgbF16ColorSpace.h> | ||||
31 | #include <colorspaces/rgb_f32/RgbF32ColorSpace.h> | ||||
32 | | ||||
33 | | ||||
34 | namespace | ||||
35 | { | ||||
36 | | ||||
37 | ALWAYS_INLINE float applySmpte2048Curve(float x) { | ||||
38 | const float m1 = 2610.0 / 4096.0 / 4.0; | ||||
39 | const float m2 = 2523.0 / 4096.0 * 128.0; | ||||
40 | const float a1 = 3424.0 / 4096.0; | ||||
41 | const float c2 = 2413.0 / 4096.0 * 32.0; | ||||
42 | const float c3 = 2392.0 / 4096.0 * 32.0; | ||||
43 | const float a4 = 1.0; | ||||
44 | const float x_p = powf(0.008 * std::max(0.0f, x), m1); | ||||
45 | const float res = powf((a1 + c2 * x_p) / (a4 + c3 * x_p), m2); | ||||
46 | return res; | ||||
47 | } | ||||
48 | | ||||
49 | ALWAYS_INLINE float removeSmpte2048Curve(float x) { | ||||
50 | const float m1_r = 4096.0 * 4.0 / 2610.0; | ||||
51 | const float m2_r = 4096.0 / 2523.0 / 128.0; | ||||
52 | const float a1 = 3424.0 / 4096.0; | ||||
53 | const float c2 = 2413.0 / 4096.0 * 32.0; | ||||
54 | const float c3 = 2392.0 / 4096.0 * 32.0; | ||||
55 | | ||||
56 | const float x_p = powf(x, m2_r); | ||||
57 | const float res = powf(qMax(0.0f, x_p - a1) / (c2 - c3 * x_p), m1_r); | ||||
58 | return res * 125.0f; | ||||
59 | } | ||||
60 | | ||||
61 | template <class T> | ||||
62 | struct DstTraitsForSource { | ||||
63 | typedef KoRgbF32Traits result; | ||||
64 | }; | ||||
65 | | ||||
66 | template <> | ||||
67 | struct DstTraitsForSource<KoBgrU16Traits> { | ||||
68 | typedef KoRgbF16Traits result; | ||||
69 | }; | ||||
70 | | ||||
71 | template <> | ||||
72 | struct DstTraitsForSource<KoBgrU8Traits> { | ||||
73 | typedef KoRgbF16Traits result; | ||||
74 | }; | ||||
75 | | ||||
76 | template <typename src_channel_type, | ||||
77 | typename dst_channel_type> | ||||
78 | struct RemoveSmpte2048Policy { | ||||
79 | static ALWAYS_INLINE dst_channel_type process(src_channel_type value) { | ||||
80 | return | ||||
81 | KoColorSpaceMaths<float, dst_channel_type>::scaleToA( | ||||
82 | removeSmpte2048Curve( | ||||
83 | KoColorSpaceMaths<src_channel_type, float>::scaleToA( | ||||
84 | value))); | ||||
85 | } | ||||
86 | }; | ||||
87 | | ||||
88 | template <typename src_channel_type, | ||||
89 | typename dst_channel_type> | ||||
90 | struct ApplySmpte2048Policy { | ||||
91 | static ALWAYS_INLINE dst_channel_type process(src_channel_type value) { | ||||
92 | return | ||||
93 | KoColorSpaceMaths<float, dst_channel_type>::scaleToA( | ||||
94 | applySmpte2048Curve( | ||||
95 | KoColorSpaceMaths<src_channel_type, float>::scaleToA( | ||||
96 | value))); | ||||
97 | } | ||||
98 | }; | ||||
99 | | ||||
100 | template <typename src_channel_type, | ||||
101 | typename dst_channel_type> | ||||
102 | struct NoopPolicy { | ||||
103 | static ALWAYS_INLINE dst_channel_type process(src_channel_type value) { | ||||
104 | return KoColorSpaceMaths<src_channel_type, dst_channel_type>::scaleToA(value); | ||||
105 | } | ||||
106 | }; | ||||
107 | | ||||
108 | } | ||||
109 | | ||||
110 | template<typename SrcCSTraits, | ||||
111 | typename DstCSTraits, | ||||
112 | template<typename, typename> class Policy> | ||||
113 | struct ApplyRgbShaper : public KoColorConversionTransformation | ||||
114 | { | ||||
115 | ApplyRgbShaper(const KoColorSpace* srcCs, | ||||
116 | const KoColorSpace* dstCs, | ||||
117 | Intent renderingIntent, | ||||
118 | ConversionFlags conversionFlags) | ||||
119 | : KoColorConversionTransformation(srcCs, | ||||
120 | dstCs, | ||||
121 | renderingIntent, | ||||
122 | conversionFlags) | ||||
123 | { | ||||
124 | } | ||||
125 | | ||||
126 | void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override { | ||||
127 | KIS_ASSERT(src != dst); | ||||
128 | | ||||
129 | const typename SrcCSTraits::Pixel *srcPixel = reinterpret_cast<const typename SrcCSTraits::Pixel*>(src); | ||||
130 | typename DstCSTraits::Pixel *dstPixel = reinterpret_cast<typename DstCSTraits::Pixel*>(dst); | ||||
131 | | ||||
132 | typedef typename SrcCSTraits::channels_type src_channel_type; | ||||
133 | typedef typename DstCSTraits::channels_type dst_channel_type; | ||||
134 | typedef Policy<src_channel_type, dst_channel_type> ConcretePolicy; | ||||
135 | | ||||
136 | for (int i = 0; i < nPixels; i++) { | ||||
137 | dstPixel->red = ConcretePolicy::process(srcPixel->red); | ||||
138 | dstPixel->green = ConcretePolicy::process(srcPixel->green); | ||||
139 | dstPixel->blue = ConcretePolicy::process(srcPixel->blue); | ||||
140 | dstPixel->alpha = | ||||
141 | KoColorSpaceMaths<src_channel_type, dst_channel_type>::scaleToA( | ||||
142 | srcPixel->alpha); | ||||
143 | | ||||
144 | srcPixel++; | ||||
145 | dstPixel++; | ||||
146 | } | ||||
147 | } | ||||
148 | | ||||
149 | }; | ||||
150 | | ||||
151 | template<class ParentColorSpace, class DstColorSpaceTraits = typename DstTraitsForSource<typename ParentColorSpace::ColorSpaceTraits>::result> | ||||
152 | class LcmsFromRGBP2020PQTransformationFactory : public KoColorConversionTransformationFactory | ||||
153 | { | ||||
154 | public: | ||||
155 | LcmsFromRGBP2020PQTransformationFactory() | ||||
156 | : KoColorConversionTransformationFactory(RGBAColorModelID.id(), | ||||
157 | colorDepthIdForChannelType<typename ParentColorSpace::ColorSpaceTraits::channels_type>().id(), | ||||
158 | "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF", | ||||
159 | RGBAColorModelID.id(), | ||||
160 | colorDepthIdForChannelType<typename DstColorSpaceTraits::channels_type>().id(), | ||||
161 | "Rec2020-elle-V4-g10.icc") | ||||
162 | { | ||||
163 | } | ||||
164 | | ||||
165 | bool conserveColorInformation() const override { | ||||
166 | return true; | ||||
167 | } | ||||
168 | | ||||
169 | bool conserveDynamicRange() const override { | ||||
170 | return | ||||
171 | dstColorDepthId() == Float16BitsColorDepthID.id() || | ||||
172 | dstColorDepthId() == Float32BitsColorDepthID.id() || | ||||
173 | dstColorDepthId() == Float64BitsColorDepthID.id(); | ||||
174 | } | ||||
175 | | ||||
176 | KoColorConversionTransformation* createColorTransformation(const KoColorSpace* srcColorSpace, | ||||
177 | const KoColorSpace* dstColorSpace, | ||||
178 | KoColorConversionTransformation::Intent renderingIntent, | ||||
179 | KoColorConversionTransformation::ConversionFlags conversionFlags) const override | ||||
180 | { | ||||
181 | return new ApplyRgbShaper< | ||||
182 | typename ParentColorSpace::ColorSpaceTraits, | ||||
183 | DstColorSpaceTraits, | ||||
184 | RemoveSmpte2048Policy>(srcColorSpace, | ||||
185 | dstColorSpace, | ||||
186 | renderingIntent, | ||||
187 | conversionFlags); | ||||
188 | } | ||||
189 | }; | ||||
190 | | ||||
191 | template<class ParentColorSpace, class DstColorSpaceTraits = typename DstTraitsForSource<typename ParentColorSpace::ColorSpaceTraits>::result> | ||||
192 | class LcmsToRGBP2020PQTransformationFactory : public KoColorConversionTransformationFactory | ||||
193 | { | ||||
194 | public: | ||||
195 | LcmsToRGBP2020PQTransformationFactory() | ||||
196 | : KoColorConversionTransformationFactory(RGBAColorModelID.id(), | ||||
197 | colorDepthIdForChannelType<typename DstColorSpaceTraits::channels_type>().id(), | ||||
198 | "Rec2020-elle-V4-g10.icc", | ||||
199 | RGBAColorModelID.id(), | ||||
200 | colorDepthIdForChannelType<typename ParentColorSpace::ColorSpaceTraits::channels_type>().id(), | ||||
201 | "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF") | ||||
202 | { | ||||
203 | } | ||||
204 | | ||||
205 | bool conserveColorInformation() const override { | ||||
206 | return true; | ||||
207 | } | ||||
208 | | ||||
209 | bool conserveDynamicRange() const override { | ||||
210 | return true; | ||||
211 | } | ||||
212 | | ||||
213 | KoColorConversionTransformation* createColorTransformation(const KoColorSpace* srcColorSpace, | ||||
214 | const KoColorSpace* dstColorSpace, | ||||
215 | KoColorConversionTransformation::Intent renderingIntent, | ||||
216 | KoColorConversionTransformation::ConversionFlags conversionFlags) const override | ||||
217 | { | ||||
218 | return new ApplyRgbShaper< | ||||
219 | DstColorSpaceTraits, | ||||
220 | typename ParentColorSpace::ColorSpaceTraits, | ||||
221 | ApplySmpte2048Policy>(srcColorSpace, | ||||
222 | dstColorSpace, | ||||
223 | renderingIntent, | ||||
224 | conversionFlags); | ||||
225 | } | ||||
226 | }; | ||||
227 | | ||||
228 | template<class ParentColorSpace, class DstColorSpaceTraits> | ||||
229 | class LcmsScaleRGBP2020PQTransformationFactory : public KoColorConversionTransformationFactory | ||||
230 | { | ||||
231 | public: | ||||
232 | LcmsScaleRGBP2020PQTransformationFactory() | ||||
233 | : KoColorConversionTransformationFactory(RGBAColorModelID.id(), | ||||
234 | colorDepthIdForChannelType<typename ParentColorSpace::ColorSpaceTraits::channels_type>().id(), | ||||
235 | "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF", | ||||
236 | RGBAColorModelID.id(), | ||||
237 | colorDepthIdForChannelType<typename DstColorSpaceTraits::channels_type>().id(), | ||||
238 | "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF") | ||||
239 | { | ||||
240 | KIS_SAFE_ASSERT_RECOVER_NOOP(srcColorDepthId() != dstColorDepthId()); | ||||
241 | } | ||||
242 | | ||||
243 | bool conserveColorInformation() const override { | ||||
244 | return true; | ||||
245 | } | ||||
246 | | ||||
247 | bool conserveDynamicRange() const override { | ||||
248 | return | ||||
249 | srcColorDepthId() == Float16BitsColorDepthID.id() || | ||||
250 | srcColorDepthId() == Float32BitsColorDepthID.id() || | ||||
251 | srcColorDepthId() == Float64BitsColorDepthID.id(); | ||||
252 | } | ||||
253 | | ||||
254 | KoColorConversionTransformation* createColorTransformation(const KoColorSpace* srcColorSpace, | ||||
255 | const KoColorSpace* dstColorSpace, | ||||
256 | KoColorConversionTransformation::Intent renderingIntent, | ||||
257 | KoColorConversionTransformation::ConversionFlags conversionFlags) const override | ||||
258 | { | ||||
259 | return new ApplyRgbShaper< | ||||
260 | typename ParentColorSpace::ColorSpaceTraits, | ||||
261 | DstColorSpaceTraits, | ||||
262 | NoopPolicy>(srcColorSpace, | ||||
263 | dstColorSpace, | ||||
264 | renderingIntent, | ||||
265 | conversionFlags); | ||||
266 | } | ||||
267 | }; | ||||
268 | | ||||
269 | #endif // LCMSRGBP2020PQCOLORSPACETRANSFORMATION_H |