Blender  V3.3
COM_TonemapOperation.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2011 Blender Foundation. */
3 
4 #include "COM_TonemapOperation.h"
5 
6 #include "COM_ExecutionSystem.h"
7 
8 #include "IMB_colormanagement.h"
9 
10 namespace blender::compositor {
11 
13 {
16  image_reader_ = nullptr;
17  data_ = nullptr;
18  cached_instance_ = nullptr;
19  flags_.complex = true;
20 }
22 {
25 }
26 
27 void TonemapOperation::execute_pixel(float output[4], int x, int y, void *data)
28 {
29  AvgLogLum *avg = (AvgLogLum *)data;
30 
31  image_reader_->read(output, x, y, nullptr);
32  mul_v3_fl(output, avg->al);
33  float dr = output[0] + data_->offset;
34  float dg = output[1] + data_->offset;
35  float db = output[2] + data_->offset;
36  output[0] /= ((dr == 0.0f) ? 1.0f : dr);
37  output[1] /= ((dg == 0.0f) ? 1.0f : dg);
38  output[2] /= ((db == 0.0f) ? 1.0f : db);
39  const float igm = avg->igm;
40  if (igm != 0.0f) {
41  output[0] = powf(MAX2(output[0], 0.0f), igm);
42  output[1] = powf(MAX2(output[1], 0.0f), igm);
43  output[2] = powf(MAX2(output[2], 0.0f), igm);
44  }
45 }
47 {
48  AvgLogLum *avg = (AvgLogLum *)data;
49  NodeTonemap *ntm = data_;
50 
51  const float f = expf(-data_->f);
52  const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f));
53  const float ic = 1.0f - ntm->c, ia = 1.0f - ntm->a;
54 
55  image_reader_->read(output, x, y, nullptr);
56 
58  float I_l = output[0] + ic * (L - output[0]);
59  float I_g = avg->cav[0] + ic * (avg->lav - avg->cav[0]);
60  float I_a = I_l + ia * (I_g - I_l);
61  output[0] /= (output[0] + powf(f * I_a, m));
62  I_l = output[1] + ic * (L - output[1]);
63  I_g = avg->cav[1] + ic * (avg->lav - avg->cav[1]);
64  I_a = I_l + ia * (I_g - I_l);
65  output[1] /= (output[1] + powf(f * I_a, m));
66  I_l = output[2] + ic * (L - output[2]);
67  I_g = avg->cav[2] + ic * (avg->lav - avg->cav[2]);
68  I_a = I_l + ia * (I_g - I_l);
69  output[2] /= (output[2] + powf(f * I_a, m));
70 }
71 
73 {
74  image_reader_ = nullptr;
75  delete cached_instance_;
77 }
78 
80  ReadBufferOperation *read_operation,
81  rcti *output)
82 {
83  rcti image_input;
84 
85  NodeOperation *operation = get_input_operation(0);
86  image_input.xmax = operation->get_width();
87  image_input.xmin = 0;
88  image_input.ymax = operation->get_height();
89  image_input.ymin = 0;
90  if (operation->determine_depending_area_of_interest(&image_input, read_operation, output)) {
91  return true;
92  }
93  return false;
94 }
95 
97 {
98  lock_mutex();
99  if (cached_instance_ == nullptr) {
101  AvgLogLum *data = new AvgLogLum();
102 
103  float *buffer = tile->get_buffer();
104 
105  float lsum = 0.0f;
106  int p = tile->get_width() * tile->get_height();
107  float *bc = buffer;
108  float avl, maxl = -1e10f, minl = 1e10f;
109  const float sc = 1.0f / p;
110  float Lav = 0.0f;
111  float cav[4] = {0.0f, 0.0f, 0.0f, 0.0f};
112  while (p--) {
114  Lav += L;
115  add_v3_v3(cav, bc);
116  lsum += logf(MAX2(L, 0.0f) + 1e-5f);
117  maxl = (L > maxl) ? L : maxl;
118  minl = (L < minl) ? L : minl;
119  bc += 4;
120  }
121  data->lav = Lav * sc;
122  mul_v3_v3fl(data->cav, cav, sc);
123  maxl = log((double)maxl + 1e-5);
124  minl = log((double)minl + 1e-5);
125  avl = lsum * sc;
126  data->auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.0f;
127  float al = exp((double)avl);
128  data->al = (al == 0.0f) ? 0.0f : (data_->key / al);
129  data->igm = (data_->gamma == 0.0f) ? 1 : (1.0f / data_->gamma);
131  }
132  unlock_mutex();
133  return cached_instance_;
134 }
135 
136 void TonemapOperation::deinitialize_tile_data(rcti * /*rect*/, void * /*data*/)
137 {
138  /* pass */
139 }
140 
141 void TonemapOperation::get_area_of_interest(const int input_idx,
142  const rcti &UNUSED(output_area),
143  rcti &r_input_area)
144 {
145  BLI_assert(input_idx == 0);
146  r_input_area = get_input_operation(input_idx)->get_canvas();
147 }
148 
149 struct Luminance {
150  float sum;
151  float color_sum[3];
152  float log_sum;
153  float min;
154  float max;
156 };
157 
159 {
160  Luminance lum = {0};
161  for (const float *elem : input->get_buffer_area(area)) {
162  const float lu = IMB_colormanagement_get_luminance(elem);
163  lum.sum += lu;
164  add_v3_v3(lum.color_sum, elem);
165  lum.log_sum += logf(MAX2(lu, 0.0f) + 1e-5f);
166  lum.max = MAX2(lu, lum.max);
167  lum.min = MIN2(lu, lum.min);
168  lum.num_pixels++;
169  }
170  return lum;
171 }
172 
174  const rcti &UNUSED(area),
176 {
177  if (cached_instance_ == nullptr) {
178  Luminance lum = {0};
179  const MemoryBuffer *input = inputs[0];
181  input->get_rect(),
182  [=](const rcti &split) { return calc_area_luminance(input, split); },
183  lum,
184  [](Luminance &join, const Luminance &chunk) {
185  join.sum += chunk.sum;
186  add_v3_v3(join.color_sum, chunk.color_sum);
187  join.log_sum += chunk.log_sum;
188  join.max = MAX2(join.max, chunk.max);
189  join.min = MIN2(join.min, chunk.min);
190  join.num_pixels += chunk.num_pixels;
191  });
192 
193  AvgLogLum *avg = new AvgLogLum();
194  avg->lav = lum.sum / lum.num_pixels;
195  mul_v3_v3fl(avg->cav, lum.color_sum, 1.0f / lum.num_pixels);
196  const float max_log = log((double)lum.max + 1e-5);
197  const float min_log = log((double)lum.min + 1e-5);
198  const float avg_log = lum.log_sum / lum.num_pixels;
199  avg->auto_key = (max_log > min_log) ? ((max_log - avg_log) / (max_log - min_log)) : 1.0f;
200  const float al = exp((double)avg_log);
201  avg->al = (al == 0.0f) ? 0.0f : (data_->key / al);
202  avg->igm = (data_->gamma == 0.0f) ? 1 : (1.0f / data_->gamma);
203  cached_instance_ = avg;
204  }
205 }
206 
208  const rcti &area,
210 {
212  const float igm = avg->igm;
213  const float offset = data_->offset;
214  for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
215  copy_v4_v4(it.out, it.in(0));
216  mul_v3_fl(it.out, avg->al);
217  float dr = it.out[0] + offset;
218  float dg = it.out[1] + offset;
219  float db = it.out[2] + offset;
220  it.out[0] /= ((dr == 0.0f) ? 1.0f : dr);
221  it.out[1] /= ((dg == 0.0f) ? 1.0f : dg);
222  it.out[2] /= ((db == 0.0f) ? 1.0f : db);
223  if (igm != 0.0f) {
224  it.out[0] = powf(MAX2(it.out[0], 0.0f), igm);
225  it.out[1] = powf(MAX2(it.out[1], 0.0f), igm);
226  it.out[2] = powf(MAX2(it.out[2], 0.0f), igm);
227  }
228  }
229 }
230 
232  const rcti &area,
234 {
236  NodeTonemap *ntm = data_;
237  const float f = expf(-data_->f);
238  const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f));
239  const float ic = 1.0f - ntm->c;
240  const float ia = 1.0f - ntm->a;
241  for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
242  copy_v4_v4(it.out, it.in(0));
243  const float L = IMB_colormanagement_get_luminance(it.out);
244  float I_l = it.out[0] + ic * (L - it.out[0]);
245  float I_g = avg->cav[0] + ic * (avg->lav - avg->cav[0]);
246  float I_a = I_l + ia * (I_g - I_l);
247  it.out[0] /= (it.out[0] + powf(f * I_a, m));
248  I_l = it.out[1] + ic * (L - it.out[1]);
249  I_g = avg->cav[1] + ic * (avg->lav - avg->cav[1]);
250  I_a = I_l + ia * (I_g - I_l);
251  it.out[1] /= (it.out[1] + powf(f * I_a, m));
252  I_l = it.out[2] + ic * (L - it.out[2]);
253  I_g = avg->cav[2] + ic * (avg->lav - avg->cav[2]);
254  I_a = I_l + ia * (I_g - I_l);
255  it.out[2] /= (it.out[2] + powf(f * I_a, m));
256  }
257 }
258 
259 } // namespace blender::compositor
#define BLI_assert(a)
Definition: BLI_assert.h:46
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f)
MINLINE void add_v3_v3(float r[3], const float a[3])
#define UNUSED(x)
#define MAX2(a, b)
#define MIN2(a, b)
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3])
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
void execute_work(const rcti &work_rect, std::function< void(const rcti &split_rect)> work_func)
a MemoryBuffer contains access to the data of a chunk
NodeOperation contains calculation logic.
void add_output_socket(DataType datatype)
SocketReader * get_input_socket_reader(unsigned int index)
NodeOperation * get_input_operation(int index)
void read(float result[4], int x, int y, void *chunk_data)
virtual bool determine_depending_area_of_interest(rcti *input, ReadBufferOperation *read_operation, rcti *output)
void add_input_socket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
virtual void * initialize_tile_data(rcti *)
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void execute_pixel(float output[4], int x, int y, void *data) override
NodeTonemap * data_
settings of the Tonemap
void execute_pixel(float output[4], int x, int y, void *data) override
void update_memory_buffer_started(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void deinitialize_tile_data(rcti *rect, void *data) override
AvgLogLum * cached_instance_
temporarily cache of the execution storage
bool determine_depending_area_of_interest(rcti *input, ReadBufferOperation *read_operation, rcti *output) override
SocketReader * image_reader_
Cached reference to the reader.
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override
Get input operation area being read by this operation on rendering given output area.
virtual void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void * initialize_tile_data(rcti *rect) override
#define logf(x)
Definition: cuda/compat.h:105
#define expf(x)
Definition: cuda/compat.h:106
#define powf(x, y)
Definition: cuda/compat.h:103
ccl_global float * buffer
ccl_global KernelShaderEvalInput ccl_global float * output
ccl_global const KernelWorkTile * tile
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
ccl_global KernelShaderEvalInput * input
ccl_device_inline float3 exp(float3 v)
Definition: math_float3.h:392
ccl_device_inline float3 log(float3 v)
Definition: math_float3.h:397
#define L
static void area(int d1, int d2, int e1, int e2, float weights[2])
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
struct blender::compositor::AvgLogLum AvgLogLum
temporarily storage during execution of Tone-map
static Luminance calc_area_luminance(const MemoryBuffer *input, const rcti &area)
void split(const std::string &s, const char delim, std::vector< std::string > &tokens)
Definition: abc_util.cc:92
static bNodeSocketTemplate inputs[]
temporarily storage during execution of Tone-map
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63