Blender V5.0
fallback_display_cpu_processor.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
6
7#include "BLI_math_base.hh"
8#include "BLI_math_color.h"
9#include "BLI_math_matrix.h"
10#include "BLI_math_matrix.hh"
11#include "BLI_math_vector.h"
12
13#include "OCIO_config.hh"
14#include "OCIO_cpu_processor.hh"
15#include "OCIO_packed_image.hh"
16
17#include "../white_point.hh"
18
19namespace blender::ocio {
20
21namespace {
22
23using PixelSpaceProcessor3 = void (*)(float dst[3], const float src[3]);
24
25class NOOPDisplayCPUProcessor : public CPUProcessor {
26 public:
27 static std::shared_ptr<const CPUProcessor> get()
28 {
29 static auto processor = std::make_shared<NOOPDisplayCPUProcessor>();
30 return processor;
31 }
32
33 bool is_noop() const override
34 {
35 return true;
36 }
37
38 void apply_rgb(float /*rgb*/[3]) const override {}
39 void apply_rgba(float /*rgba*/[4]) const override {}
40
41 void apply_rgba_predivide(float /*rgba*/[4]) const override {}
42
43 void apply(const PackedImage & /*image*/) const override {}
44 void apply_predivide(const PackedImage & /*image*/) const override {}
45};
46
47class BaseDisplayCPUProcessor : public CPUProcessor {
48 public:
49 /* Matrix transform which is applied in the linear space.
50 *
51 * NOTE: The matrix is inversed when the processor is configured to go from display space to
52 * linear. */
54
55 float exponent = 1.0f;
56};
57
58template<PixelSpaceProcessor3 pixel_space_processor, bool is_inverse>
59class DisplayCPUProcessor : public BaseDisplayCPUProcessor {
60 public:
61 bool is_noop() const override
62 {
63 return false;
64 }
65
66 void apply_rgb(float rgb[3]) const override
67 {
68 process_rgb(rgb);
69 }
70 void apply_rgba(float rgba[4]) const override
71 {
72 process_rgb(rgba);
73 }
74
75 void apply_rgba_predivide(float rgba[4]) const override
76 {
77 if (ELEM(rgba[3], 1.0f, 0.0f)) {
78 process_rgb(rgba);
79 return;
80 }
81
82 const float alpha = rgba[3];
83 const float inv_alpha = 1.0f / alpha;
84
85 rgba[0] *= inv_alpha;
86 rgba[1] *= inv_alpha;
87 rgba[2] *= inv_alpha;
88
89 process_rgb(rgba);
90
91 rgba[0] *= alpha;
92 rgba[1] *= alpha;
93 rgba[2] *= alpha;
94 }
95
96 void apply(const PackedImage &image) const override
97 {
98 /* TODO(sergey): Stride not respected, channels must be 3 or 4, bit depth is float32. */
99
100 BLI_assert(image.get_num_channels() >= 3);
101 BLI_assert(image.get_bit_depth() == BitDepth::BIT_DEPTH_F32);
102
103 const int num_channels = image.get_num_channels();
104 const size_t width = image.get_width();
105 const size_t height = image.get_height();
106
107 float *pixels = static_cast<float *>(image.get_data());
108
109 for (size_t y = 0; y < height; y++) {
110 for (size_t x = 0; x < width; x++) {
111 float *pixel = pixels + num_channels * (y * width + x);
112 process_rgb(pixel);
113 }
114 }
115 }
116
117 void apply_predivide(const PackedImage &image) const override
118 {
119 /* TODO(sergey): Stride not respected, channels must be 3 or 4, bit depth is float32. */
120
121 BLI_assert(image.get_num_channels() >= 3);
122 BLI_assert(image.get_bit_depth() == BitDepth::BIT_DEPTH_F32);
123
124 const int num_channels = image.get_num_channels();
125 if (num_channels < 4) {
126 apply(image);
127 return;
128 }
129
130 const size_t width = image.get_width();
131 const size_t height = image.get_height();
132
133 float *pixels = static_cast<float *>(image.get_data());
134
135 for (size_t y = 0; y < height; y++) {
136 for (size_t x = 0; x < width; x++) {
137 float *pixel = pixels + num_channels * (y * width + x);
138 apply_rgba_predivide(pixel);
139 }
140 }
141 }
142
143 private:
144 void process_rgb(float rgb[3]) const
145 {
146 if constexpr (is_inverse) {
147 if (exponent != 0) {
148 const float inv_exponent = 1.0f / exponent;
149 rgb[0] = rgb[0] != 0.0f ? math::pow(rgb[0], inv_exponent) : 0.0f;
150 rgb[1] = rgb[1] != 0.0f ? math::pow(rgb[1], inv_exponent) : 0.0f;
151 rgb[2] = rgb[2] != 0.0f ? math::pow(rgb[2], inv_exponent) : 0.0f;
152 }
153 else {
154 rgb[0] = 0;
155 rgb[1] = 0;
156 rgb[2] = 0;
157 }
158
159 pixel_space_processor(rgb, rgb);
160
161 mul_v3_m3v3(rgb, this->matrix.ptr(), rgb);
162 }
163 else {
164 mul_v3_m3v3(rgb, this->matrix.ptr(), rgb);
165
166 pixel_space_processor(rgb, rgb);
167
168 rgb[0] = math::pow(math::max(0.0f, rgb[0]), exponent);
169 rgb[1] = math::pow(math::max(0.0f, rgb[1]), exponent);
170 rgb[2] = math::pow(math::max(0.0f, rgb[2]), exponent);
171 }
172 }
173};
174
175} // namespace
176
177std::shared_ptr<const CPUProcessor> create_fallback_display_cpu_processor(
178 const Config &config, const DisplayParameters &display_parameters)
179{
180 if (display_parameters.display != "sRGB") {
181 return NOOPDisplayCPUProcessor::get();
182 }
183
184 if (display_parameters.view != "Standard") {
185 return NOOPDisplayCPUProcessor::get();
186 }
187 if (!ELEM(display_parameters.look, "", "None")) {
188 return NOOPDisplayCPUProcessor::get();
189 }
190
191 if (display_parameters.from_colorspace == "Non-Color") {
192 return NOOPDisplayCPUProcessor::get();
193 }
194
195 std::shared_ptr<BaseDisplayCPUProcessor> processor;
196
197 if (display_parameters.from_colorspace == "Linear") {
198 if (display_parameters.inverse) {
199 processor = std::make_shared<DisplayCPUProcessor<srgb_to_linearrgb_v3_v3, true>>();
200 }
201 else {
202 processor = std::make_shared<DisplayCPUProcessor<linearrgb_to_srgb_v3_v3, false>>();
203 }
204 }
205 else if (display_parameters.from_colorspace == "sRGB") {
206 if (display_parameters.inverse) {
207 processor = std::make_shared<DisplayCPUProcessor<linearrgb_to_srgb_v3_v3, true>>();
208 }
209 else {
210 processor = std::make_shared<DisplayCPUProcessor<srgb_to_linearrgb_v3_v3, false>>();
211 }
212 }
213 else {
214 return NOOPDisplayCPUProcessor::get();
215 }
216
217 processor->matrix = float3x3::identity() * display_parameters.scale;
218 processor->exponent = display_parameters.exponent;
219
220 /* Apply white balance. */
221 if (display_parameters.use_white_balance) {
223 config, display_parameters.temperature, display_parameters.tint);
224 processor->matrix *= matrix;
225 }
226
227 if (display_parameters.inverse) {
228 processor->matrix = math::invert(processor->matrix);
229 }
230
231 return processor;
232}
233
234} // namespace blender::ocio
#define BLI_assert(a)
Definition BLI_assert.h:46
void mul_v3_m3v3(float r[3], const float M[3][3], const float a[3])
#define ELEM(...)
void apply(bContext &C, GestureData &gesture_data, wmOperator &op)
T pow(const T &x, const T &power)
CartesianBasis invert(const CartesianBasis &basis)
T max(const T &a, const T &b)
std::shared_ptr< const T > get(const GenericKey &key, FunctionRef< std::unique_ptr< T >()> compute_fn)
std::shared_ptr< const CPUProcessor > create_fallback_display_cpu_processor(const Config &config, const DisplayParameters &display_parameters)
float3x3 calculate_white_point_matrix(const Config &config, const float temperature, const float tint)
MatBase< float, 3, 3 > float3x3