Blender  V3.3
COM_FastGaussianBlurOperation.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 <climits>
5 
7 
8 namespace blender::compositor {
9 
11 {
12  iirgaus_ = nullptr;
13 }
14 
15 void FastGaussianBlurOperation::execute_pixel(float output[4], int x, int y, void *data)
16 {
17  MemoryBuffer *new_data = (MemoryBuffer *)data;
18  new_data->read(output, x, y);
19 }
20 
22  rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output)
23 {
24  rcti new_input;
25  rcti size_input;
26  size_input.xmin = 0;
27  size_input.ymin = 0;
28  size_input.xmax = 5;
29  size_input.ymax = 5;
30 
31  NodeOperation *operation = this->get_input_operation(1);
32  if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) {
33  return true;
34  }
35 
36  if (iirgaus_) {
37  return false;
38  }
39 
40  new_input.xmin = 0;
41  new_input.ymin = 0;
42  new_input.xmax = this->get_width();
43  new_input.ymax = this->get_height();
44 
45  return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output);
46 }
47 
49 {
51  sx_ = data_.sizex * size_ / 2.0f;
52  sy_ = data_.sizey * size_ / 2.0f;
53 }
54 
56 {
59 }
60 
62 {
63  if (iirgaus_) {
64  delete iirgaus_;
65  iirgaus_ = nullptr;
66  }
68 }
69 
71 {
72  lock_mutex();
73  if (!iirgaus_) {
75  MemoryBuffer *copy = new MemoryBuffer(*new_buf);
76  update_size();
77 
78  int c;
79  sx_ = data_.sizex * size_ / 2.0f;
80  sy_ = data_.sizey * size_ / 2.0f;
81 
82  if ((sx_ == sy_) && (sx_ > 0.0f)) {
83  for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
84  IIR_gauss(copy, sx_, c, 3);
85  }
86  }
87  else {
88  if (sx_ > 0.0f) {
89  for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
90  IIR_gauss(copy, sx_, c, 1);
91  }
92  }
93  if (sy_ > 0.0f) {
94  for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
95  IIR_gauss(copy, sy_, c, 2);
96  }
97  }
98  }
99  iirgaus_ = copy;
100  }
101  unlock_mutex();
102  return iirgaus_;
103 }
104 
106  float sigma,
107  unsigned int chan,
108  unsigned int xy)
109 {
110  BLI_assert(!src->is_a_single_elem());
111  double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3];
112  double *X, *Y, *W;
113  const unsigned int src_width = src->get_width();
114  const unsigned int src_height = src->get_height();
115  unsigned int x, y, src_dim_max;
116  unsigned int i;
117  float *buffer = src->get_buffer();
118  const uint8_t num_channels = src->get_num_channels();
119 
120  /* <0.5 not valid, though can have a possibly useful sort of sharpening effect. */
121  if (sigma < 0.5f) {
122  return;
123  }
124 
125  if ((xy < 1) || (xy > 3)) {
126  xy = 3;
127  }
128 
129  /* XXX The YVV macro defined below explicitly expects sources of at least 3x3 pixels,
130  * so just skipping blur along faulty direction if src's def is below that limit! */
131  if (src_width < 3) {
132  xy &= ~1;
133  }
134  if (src_height < 3) {
135  xy &= ~2;
136  }
137  if (xy < 1) {
138  return;
139  }
140 
141  /* See "Recursive Gabor Filtering" by Young/VanVliet
142  * all factors here in double-precision.
143  * Required, because for single-precision floating point seems to blow up if `sigma > ~200`. */
144  if (sigma >= 3.556f) {
145  q = 0.9804f * (sigma - 3.556f) + 2.5091f;
146  }
147  else { /* `sigma >= 0.5`. */
148  q = (0.0561f * sigma + 0.5784f) * sigma - 0.2568f;
149  }
150  q2 = q * q;
151  sc = (1.1668 + q) * (3.203729649 + (2.21566 + q) * q);
152  /* No gabor filtering here, so no complex multiplies, just the regular coefficients.
153  * all negated here, so as not to have to recalc Triggs/Sdika matrix. */
154  cf[1] = q * (5.788961737 + (6.76492 + 3.0 * q) * q) / sc;
155  cf[2] = -q2 * (3.38246 + 3.0 * q) / sc;
156  /* 0 & 3 unchanged. */
157  cf[3] = q2 * q / sc;
158  cf[0] = 1.0 - cf[1] - cf[2] - cf[3];
159 
160  /* Triggs/Sdika border corrections,
161  * it seems to work, not entirely sure if it is actually totally correct,
162  * Besides J.M.Geusebroek's `anigauss.c` (see http://www.science.uva.nl/~mark),
163  * found one other implementation by Cristoph Lampert,
164  * but neither seem to be quite the same, result seems to be ok so far anyway.
165  * Extra scale factor here to not have to do it in filter,
166  * though maybe this had something to with the precision errors */
167  sc = cf[0] / ((1.0 + cf[1] - cf[2] + cf[3]) * (1.0 - cf[1] - cf[2] - cf[3]) *
168  (1.0 + cf[2] + (cf[1] - cf[3]) * cf[3]));
169  tsM[0] = sc * (-cf[3] * cf[1] + 1.0 - cf[3] * cf[3] - cf[2]);
170  tsM[1] = sc * ((cf[3] + cf[1]) * (cf[2] + cf[3] * cf[1]));
171  tsM[2] = sc * (cf[3] * (cf[1] + cf[3] * cf[2]));
172  tsM[3] = sc * (cf[1] + cf[3] * cf[2]);
173  tsM[4] = sc * (-(cf[2] - 1.0) * (cf[2] + cf[3] * cf[1]));
174  tsM[5] = sc * (-(cf[3] * cf[1] + cf[3] * cf[3] + cf[2] - 1.0) * cf[3]);
175  tsM[6] = sc * (cf[3] * cf[1] + cf[2] + cf[1] * cf[1] - cf[2] * cf[2]);
176  tsM[7] = sc * (cf[1] * cf[2] + cf[3] * cf[2] * cf[2] - cf[1] * cf[3] * cf[3] -
177  cf[3] * cf[3] * cf[3] - cf[3] * cf[2] + cf[3]);
178  tsM[8] = sc * (cf[3] * (cf[1] + cf[3] * cf[2]));
179 
180 #define YVV(L) \
181  { \
182  W[0] = cf[0] * X[0] + cf[1] * X[0] + cf[2] * X[0] + cf[3] * X[0]; \
183  W[1] = cf[0] * X[1] + cf[1] * W[0] + cf[2] * X[0] + cf[3] * X[0]; \
184  W[2] = cf[0] * X[2] + cf[1] * W[1] + cf[2] * W[0] + cf[3] * X[0]; \
185  for (i = 3; i < L; i++) { \
186  W[i] = cf[0] * X[i] + cf[1] * W[i - 1] + cf[2] * W[i - 2] + cf[3] * W[i - 3]; \
187  } \
188  tsu[0] = W[L - 1] - X[L - 1]; \
189  tsu[1] = W[L - 2] - X[L - 1]; \
190  tsu[2] = W[L - 3] - X[L - 1]; \
191  tsv[0] = tsM[0] * tsu[0] + tsM[1] * tsu[1] + tsM[2] * tsu[2] + X[L - 1]; \
192  tsv[1] = tsM[3] * tsu[0] + tsM[4] * tsu[1] + tsM[5] * tsu[2] + X[L - 1]; \
193  tsv[2] = tsM[6] * tsu[0] + tsM[7] * tsu[1] + tsM[8] * tsu[2] + X[L - 1]; \
194  Y[L - 1] = cf[0] * W[L - 1] + cf[1] * tsv[0] + cf[2] * tsv[1] + cf[3] * tsv[2]; \
195  Y[L - 2] = cf[0] * W[L - 2] + cf[1] * Y[L - 1] + cf[2] * tsv[0] + cf[3] * tsv[1]; \
196  Y[L - 3] = cf[0] * W[L - 3] + cf[1] * Y[L - 2] + cf[2] * Y[L - 1] + cf[3] * tsv[0]; \
197  /* 'i != UINT_MAX' is really 'i >= 0', but necessary for unsigned int wrapping */ \
198  for (i = L - 4; i != UINT_MAX; i--) { \
199  Y[i] = cf[0] * W[i] + cf[1] * Y[i + 1] + cf[2] * Y[i + 2] + cf[3] * Y[i + 3]; \
200  } \
201  } \
202  (void)0
203 
204  /* Intermediate buffers. */
205  src_dim_max = MAX2(src_width, src_height);
206  X = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss X buf");
207  Y = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss Y buf");
208  W = (double *)MEM_callocN(src_dim_max * sizeof(double), "IIR_gauss W buf");
209  if (xy & 1) { /* H. */
210  int offset;
211  for (y = 0; y < src_height; y++) {
212  const int yx = y * src_width;
213  offset = yx * num_channels + chan;
214  for (x = 0; x < src_width; x++) {
215  X[x] = buffer[offset];
216  offset += num_channels;
217  }
218  YVV(src_width);
219  offset = yx * num_channels + chan;
220  for (x = 0; x < src_width; x++) {
221  buffer[offset] = Y[x];
222  offset += num_channels;
223  }
224  }
225  }
226  if (xy & 2) { /* V. */
227  int offset;
228  const int add = src_width * num_channels;
229 
230  for (x = 0; x < src_width; x++) {
231  offset = x * num_channels + chan;
232  for (y = 0; y < src_height; y++) {
233  X[y] = buffer[offset];
234  offset += add;
235  }
236  YVV(src_height);
237  offset = x * num_channels + chan;
238  for (y = 0; y < src_height; y++) {
239  buffer[offset] = Y[y];
240  offset += add;
241  }
242  }
243  }
244 
245  MEM_freeN(X);
246  MEM_freeN(W);
247  MEM_freeN(Y);
248 #undef YVV
249 }
250 
252  const rcti &output_area,
253  rcti &r_input_area)
254 {
255  switch (input_idx) {
256  case IMAGE_INPUT_INDEX:
257  r_input_area = this->get_canvas();
258  break;
259  default:
260  BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area);
261  return;
262  }
263 }
264 
266  const rcti &area,
268 {
269  /* TODO(manzanilla): Add a render test and make #IIR_gauss multi-threaded with support for
270  * an output buffer. */
272  MemoryBuffer *image = nullptr;
273  const bool is_full_output = BLI_rcti_compare(&output->get_rect(), &area);
274  if (is_full_output) {
275  image = output;
276  }
277  else {
278  image = new MemoryBuffer(get_output_socket()->get_data_type(), area);
279  }
280  image->copy_from(input, area);
281 
282  if ((sx_ == sy_) && (sx_ > 0.0f)) {
283  for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
284  IIR_gauss(image, sx_, c, 3);
285  }
286  }
287  else {
288  if (sx_ > 0.0f) {
289  for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
290  IIR_gauss(image, sx_, c, 1);
291  }
292  }
293  if (sy_ > 0.0f) {
294  for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) {
295  IIR_gauss(image, sy_, c, 2);
296  }
297  }
298  }
299 
300  if (!is_full_output) {
301  output->copy_from(image, area);
302  delete image;
303  }
304 }
305 
307 {
310  iirgaus_ = nullptr;
311  inputprogram_ = nullptr;
312  sigma_ = 1.0f;
313  overlay_ = 0;
314  flags_.complex = true;
315 }
316 
318 {
319  MemoryBuffer *new_data = (MemoryBuffer *)data;
320  new_data->read(output, x, y);
321 }
322 
324  rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output)
325 {
326  rcti new_input;
327 
328  if (iirgaus_) {
329  return false;
330  }
331 
332  new_input.xmin = 0;
333  new_input.ymin = 0;
334  new_input.xmax = this->get_width();
335  new_input.ymax = this->get_height();
336 
337  return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output);
338 }
339 
341 {
342  inputprogram_ = get_input_socket_reader(0);
343  init_mutex();
344 }
345 
347 {
348  if (iirgaus_) {
349  delete iirgaus_;
350  iirgaus_ = nullptr;
351  }
352  deinit_mutex();
353 }
354 
356 {
357  lock_mutex();
358  if (!iirgaus_) {
359  MemoryBuffer *new_buf = (MemoryBuffer *)inputprogram_->initialize_tile_data(rect);
360  MemoryBuffer *copy = new MemoryBuffer(*new_buf);
362 
363  if (overlay_ == FAST_GAUSS_OVERLAY_MIN) {
364  float *src = new_buf->get_buffer();
365  float *dst = copy->get_buffer();
366  for (int i = copy->get_width() * copy->get_height(); i != 0;
368  if (*src < *dst) {
369  *dst = *src;
370  }
371  }
372  }
373  else if (overlay_ == FAST_GAUSS_OVERLAY_MAX) {
374  float *src = new_buf->get_buffer();
375  float *dst = copy->get_buffer();
376  for (int i = copy->get_width() * copy->get_height(); i != 0;
378  if (*src > *dst) {
379  *dst = *src;
380  }
381  }
382  }
383 
384  iirgaus_ = copy;
385  }
386  unlock_mutex();
387  return iirgaus_;
388 }
389 
391  const rcti &UNUSED(output_area),
392  rcti &r_input_area)
393 {
394  r_input_area = this->get_canvas();
395 }
396 
398  const rcti &UNUSED(area),
400 {
401  if (iirgaus_ == nullptr) {
402  const MemoryBuffer *image = inputs[0];
403  MemoryBuffer *gauss = new MemoryBuffer(*image);
404  FastGaussianBlurOperation::IIR_gauss(gauss, sigma_, 0, 3);
405  iirgaus_ = gauss;
406  }
407 }
408 
410  const rcti &area,
412 {
413  MemoryBuffer *image = inputs[0];
414  BuffersIterator<float> it = output->iterate_with({image, iirgaus_}, area);
415  if (overlay_ == FAST_GAUSS_OVERLAY_MIN) {
416  for (; !it.is_end(); ++it) {
417  *it.out = MIN2(*it.in(0), *it.in(1));
418  }
419  }
420  else if (overlay_ == FAST_GAUSS_OVERLAY_MAX) {
421  for (; !it.is_end(); ++it) {
422  *it.out = MAX2(*it.in(0), *it.in(1));
423  }
424  }
425 }
426 
427 } // namespace blender::compositor
#define BLI_assert(a)
Definition: BLI_assert.h:46
bool BLI_rcti_compare(const struct rcti *rect_a, const struct rcti *rect_b)
#define UNUSED(x)
#define MAX2(a, b)
#define MIN2(a, b)
#define YVV(L)
_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
#define X
Definition: GeomUtils.cpp:199
#define Y
Definition: GeomUtils.cpp:200
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Sky Generate a procedural sky texture Noise Generate fractal Perlin noise Wave Generate procedural bands or rings with noise Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a or normal between and object coordinate space Combine Create a color from its and value channels Color Retrieve a color or the default fallback if none is specified Separate Split a vector into its and Z components Generates normals with round corners and may slow down renders Vector Displace the surface along an arbitrary direction White Return a random value or color based on an input seed Float Map an input float to a curve and outputs a float value Separate Color
virtual 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.
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.
bool determine_depending_area_of_interest(rcti *input, ReadBufferOperation *read_operation, rcti *output) override
void execute_pixel(float output[4], int x, int y, void *data) override
calculate a single pixel
static void IIR_gauss(MemoryBuffer *src, float sigma, unsigned int channel, unsigned int xy)
void update_memory_buffer_started(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void update_memory_buffer_started(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
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.
bool determine_depending_area_of_interest(rcti *input, ReadBufferOperation *read_operation, rcti *output) override
void execute_pixel(float output[4], int x, int y, void *data) override
calculate a single pixel
a MemoryBuffer contains access to the data of a chunk
void read(float *result, int x, int y, MemoryBufferExtend extend_x=MemoryBufferExtend::Clip, MemoryBufferExtend extend_y=MemoryBufferExtend::Clip)
float * get_buffer()
get the data of this MemoryBuffer
NodeOperation contains calculation logic.
void add_output_socket(DataType datatype)
SocketReader * get_input_socket_reader(unsigned int index)
NodeOperationOutput * get_output_socket(unsigned int index=0)
NodeOperation * get_input_operation(int index)
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 *)
SyclQueue void void * src
depth_tx normal_tx diffuse_light_tx specular_light_tx volume_light_tx environment_tx ambient_occlusion_tx aov_value_tx in_weight_img image(1, GPU_R32F, Qualifier::WRITE, ImageType::FLOAT_2D_ARRAY, "out_weight_img") .image(3
DataType
possible data types for sockets
Definition: COM_defines.h:30
ccl_global float * buffer
ccl_global KernelShaderEvalInput ccl_global float * output
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
ccl_global KernelShaderEvalInput * input
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
static unsigned c
Definition: RandGen.cpp:83
bool add(void *owner, const AttributeIDRef &attribute_id, eAttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer)
static void area(int d1, int d2, int e1, int e2, float weights[2])
constexpr int COM_DATA_TYPE_VALUE_CHANNELS
Definition: COM_defines.h:60
constexpr int COM_DATA_TYPE_COLOR_CHANNELS
Definition: COM_defines.h:62
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
static bNodeSocketTemplate inputs[]
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
unsigned char uint8_t
Definition: stdint.h:78
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
int xy[2]
Definition: wm_draw.c:135