Blender  V3.3
COM_DilateErodeOperation.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2011 Blender Foundation. */
3 
5 #include "COM_OpenCLDevice.h"
6 
7 namespace blender::compositor {
8 
10 {
13  flags_.complex = true;
14  input_program_ = nullptr;
15  inset_ = 0.0f;
16  switch_ = 0.5f;
17  distance_ = 0.0f;
18 }
19 
21 {
22  if (distance_ < 0.0f) {
23  scope_ = -distance_ + inset_;
24  }
25  else {
26  if (inset_ * 2 > distance_) {
27  scope_ = MAX2(inset_ * 2 - distance_, distance_);
28  }
29  else {
30  scope_ = distance_;
31  }
32  }
33  if (scope_ < 3) {
34  scope_ = 3;
35  }
36 }
37 
39 {
40  input_program_ = this->get_input_socket_reader(0);
41 }
42 
44 {
45  void *buffer = input_program_->initialize_tile_data(nullptr);
46  return buffer;
47 }
48 
50 {
51  float input_value[4];
52  const float sw = switch_;
53  const float distance = distance_;
54  float pixelvalue;
55  const float rd = scope_ * scope_;
56  const float inset = inset_;
57  float mindist = rd * 2;
58 
59  MemoryBuffer *input_buffer = (MemoryBuffer *)data;
60  float *buffer = input_buffer->get_buffer();
61  const rcti &input_rect = input_buffer->get_rect();
62  const int minx = MAX2(x - scope_, input_rect.xmin);
63  const int miny = MAX2(y - scope_, input_rect.ymin);
64  const int maxx = MIN2(x + scope_, input_rect.xmax);
65  const int maxy = MIN2(y + scope_, input_rect.ymax);
66  const int buffer_width = input_buffer->get_width();
67  int offset;
68 
69  input_buffer->read(input_value, x, y);
70  if (input_value[0] > sw) {
71  for (int yi = miny; yi < maxy; yi++) {
72  const float dy = yi - y;
73  offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin));
74  for (int xi = minx; xi < maxx; xi++) {
75  if (buffer[offset] < sw) {
76  const float dx = xi - x;
77  const float dis = dx * dx + dy * dy;
78  mindist = MIN2(mindist, dis);
79  }
80  offset++;
81  }
82  }
83  pixelvalue = -sqrtf(mindist);
84  }
85  else {
86  for (int yi = miny; yi < maxy; yi++) {
87  const float dy = yi - y;
88  offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin));
89  for (int xi = minx; xi < maxx; xi++) {
90  if (buffer[offset] > sw) {
91  const float dx = xi - x;
92  const float dis = dx * dx + dy * dy;
93  mindist = MIN2(mindist, dis);
94  }
95  offset++;
96  }
97  }
98  pixelvalue = sqrtf(mindist);
99  }
100 
101  if (distance > 0.0f) {
102  const float delta = distance - pixelvalue;
103  if (delta >= 0.0f) {
104  if (delta >= inset) {
105  output[0] = 1.0f;
106  }
107  else {
108  output[0] = delta / inset;
109  }
110  }
111  else {
112  output[0] = 0.0f;
113  }
114  }
115  else {
116  const float delta = -distance + pixelvalue;
117  if (delta < 0.0f) {
118  if (delta < -inset) {
119  output[0] = 1.0f;
120  }
121  else {
122  output[0] = (-delta) / inset;
123  }
124  }
125  else {
126  output[0] = 0.0f;
127  }
128  }
129 }
130 
132 {
133  input_program_ = nullptr;
134 }
135 
137  rcti *input, ReadBufferOperation *read_operation, rcti *output)
138 {
139  rcti new_input;
140 
141  new_input.xmax = input->xmax + scope_;
142  new_input.xmin = input->xmin - scope_;
143  new_input.ymax = input->ymax + scope_;
144  new_input.ymin = input->ymin - scope_;
145 
146  return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output);
147 }
148 
150  const rcti &output_area,
151  rcti &r_input_area)
152 {
153  BLI_assert(input_idx == 0);
154  UNUSED_VARS_NDEBUG(input_idx);
155  r_input_area.xmin = output_area.xmin - scope_;
156  r_input_area.xmax = output_area.xmax + scope_;
157  r_input_area.ymin = output_area.ymin - scope_;
158  r_input_area.ymax = output_area.ymax + scope_;
159 }
160 
162  int x;
163  int y;
164  int xmin;
165  int xmax;
166  int ymin;
167  int ymax;
168  const float *elem;
169  float distance;
173  float sw;
174 };
175 
176 template<template<typename> typename TCompare>
178 {
179  /* TODO(manzanilla): bad performance, generate a table with relative offsets on operation
180  * initialization to loop from less to greater distance and break as soon as #compare is
181  * true. */
182  const TCompare compare;
183  float min_dist = p.distance;
184  const float *row = p.elem + ((intptr_t)p.ymin - p.y) * p.row_stride +
185  ((intptr_t)p.xmin - p.x) * p.elem_stride;
186  for (int yi = p.ymin; yi < p.ymax; yi++) {
187  const float dy = yi - p.y;
188  const float dist_y = dy * dy;
189  const float *elem = row;
190  for (int xi = p.xmin; xi < p.xmax; xi++) {
191  if (compare(*elem, p.sw)) {
192  const float dx = xi - p.x;
193  const float dist = dx * dx + dist_y;
194  min_dist = MIN2(min_dist, dist);
195  }
196  elem += p.elem_stride;
197  }
198  row += p.row_stride;
199  }
200  return min_dist;
201 }
202 
204  const rcti &area,
206 {
207  const MemoryBuffer *input = inputs[0];
208  const rcti &input_rect = input->get_rect();
209  const float rd = scope_ * scope_;
210  const float inset = inset_;
211 
212  PixelData p;
213  p.sw = switch_;
214  p.distance = rd * 2;
215  p.elem_stride = input->elem_stride;
216  p.row_stride = input->row_stride;
217  for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
218  p.x = it.x;
219  p.y = it.y;
220  p.xmin = MAX2(p.x - scope_, input_rect.xmin);
221  p.ymin = MAX2(p.y - scope_, input_rect.ymin);
222  p.xmax = MIN2(p.x + scope_, input_rect.xmax);
223  p.ymax = MIN2(p.y + scope_, input_rect.ymax);
224  p.elem = it.in(0);
225 
226  float pixel_value;
227  if (*p.elem > p.sw) {
228  pixel_value = -sqrtf(get_min_distance<std::less>(p));
229  }
230  else {
231  pixel_value = sqrtf(get_min_distance<std::greater>(p));
232  }
233 
234  if (distance_ > 0.0f) {
235  const float delta = distance_ - pixel_value;
236  if (delta >= 0.0f) {
237  *it.out = delta >= inset ? 1.0f : delta / inset;
238  }
239  else {
240  *it.out = 0.0f;
241  }
242  }
243  else {
244  const float delta = -distance_ + pixel_value;
245  if (delta < 0.0f) {
246  *it.out = delta < -inset ? 1.0f : (-delta) / inset;
247  }
248  else {
249  *it.out = 0.0f;
250  }
251  }
252  }
253 }
254 
256 {
259  input_program_ = nullptr;
260  distance_ = 0.0f;
261  flags_.complex = true;
262  flags_.open_cl = true;
263 }
264 
266 {
267  scope_ = distance_;
268  if (scope_ < 3) {
269  scope_ = 3;
270  }
271 }
272 
274 {
276 }
277 
279 {
280  void *buffer = input_program_->initialize_tile_data(nullptr);
281  return buffer;
282 }
283 
284 void DilateDistanceOperation::execute_pixel(float output[4], int x, int y, void *data)
285 {
286  const float distance = distance_;
287  const float mindist = distance * distance;
288 
289  MemoryBuffer *input_buffer = (MemoryBuffer *)data;
290  float *buffer = input_buffer->get_buffer();
291  const rcti &input_rect = input_buffer->get_rect();
292  const int minx = MAX2(x - scope_, input_rect.xmin);
293  const int miny = MAX2(y - scope_, input_rect.ymin);
294  const int maxx = MIN2(x + scope_, input_rect.xmax);
295  const int maxy = MIN2(y + scope_, input_rect.ymax);
296  const int buffer_width = input_buffer->get_width();
297  int offset;
298 
299  float value = 0.0f;
300 
301  for (int yi = miny; yi < maxy; yi++) {
302  const float dy = yi - y;
303  offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin));
304  for (int xi = minx; xi < maxx; xi++) {
305  const float dx = xi - x;
306  const float dis = dx * dx + dy * dy;
307  if (dis <= mindist) {
308  value = MAX2(buffer[offset], value);
309  }
310  offset++;
311  }
312  }
313  output[0] = value;
314 }
315 
317 {
318  input_program_ = nullptr;
319 }
320 
322  rcti *input, ReadBufferOperation *read_operation, rcti *output)
323 {
324  rcti new_input;
325 
326  new_input.xmax = input->xmax + scope_;
327  new_input.xmin = input->xmin - scope_;
328  new_input.ymax = input->ymax + scope_;
329  new_input.ymin = input->ymin - scope_;
330 
331  return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output);
332 }
333 
335  MemoryBuffer *output_memory_buffer,
336  cl_mem cl_output_buffer,
337  MemoryBuffer **input_memory_buffers,
338  std::list<cl_mem> *cl_mem_to_clean_up,
339  std::list<cl_kernel> * /*cl_kernels_to_clean_up*/)
340 {
341  cl_kernel dilate_kernel = device->COM_cl_create_kernel("dilate_kernel", nullptr);
342 
344  cl_int scope = scope_;
345 
347  dilate_kernel, 0, 2, cl_mem_to_clean_up, input_memory_buffers, input_program_);
349  dilate_kernel, 1, cl_output_buffer);
351  dilate_kernel, 3, output_memory_buffer);
352  clSetKernelArg(dilate_kernel, 4, sizeof(cl_int), &scope);
353  clSetKernelArg(dilate_kernel, 5, sizeof(cl_int), &distance_squared);
354  device->COM_cl_attach_size_to_kernel_parameter(dilate_kernel, 6, this);
355  device->COM_cl_enqueue_range(dilate_kernel, output_memory_buffer, 7, this);
356 }
357 
359  const rcti &output_area,
360  rcti &r_input_area)
361 {
362  BLI_assert(input_idx == 0);
363  UNUSED_VARS_NDEBUG(input_idx);
364  r_input_area.xmin = output_area.xmin - scope_;
365  r_input_area.xmax = output_area.xmax + scope_;
366  r_input_area.ymin = output_area.ymin - scope_;
367  r_input_area.ymax = output_area.ymax + scope_;
368 }
369 
371  int x;
372  int y;
373  int xmin;
374  int xmax;
375  int ymin;
376  int ymax;
377  const float *elem;
379  int scope;
382  const rcti &input_rect;
383 
384  PixelData(MemoryBuffer *input, const int distance, const int scope)
386  scope(scope),
389  input_rect(input->get_rect())
390  {
391  }
392 
394  {
395  x = it.x;
396  y = it.y;
397  xmin = MAX2(x - scope, input_rect.xmin);
398  ymin = MAX2(y - scope, input_rect.ymin);
399  xmax = MIN2(x + scope, input_rect.xmax);
400  ymax = MIN2(y + scope, input_rect.ymax);
401  elem = it.in(0);
402  }
403 };
404 
405 template<template<typename> typename TCompare>
406 static float get_distance_value(DilateDistanceOperation::PixelData &p, const float start_value)
407 {
408  /* TODO(manzanilla): bad performance, only loop elements within minimum distance removing
409  * coordinates and conditional if `dist <= min_dist`. May need to generate a table of offsets. */
410  const TCompare compare;
411  const float min_dist = p.min_distance;
412  float value = start_value;
413  const float *row = p.elem + ((intptr_t)p.ymin - p.y) * p.row_stride +
414  ((intptr_t)p.xmin - p.x) * p.elem_stride;
415  for (int yi = p.ymin; yi < p.ymax; yi++) {
416  const float dy = yi - p.y;
417  const float dist_y = dy * dy;
418  const float *elem = row;
419  for (int xi = p.xmin; xi < p.xmax; xi++) {
420  const float dx = xi - p.x;
421  const float dist = dx * dx + dist_y;
422  if (dist <= min_dist) {
423  value = compare(*elem, value) ? *elem : value;
424  }
425  elem += p.elem_stride;
426  }
427  row += p.row_stride;
428  }
429 
430  return value;
431 }
432 
434  const rcti &area,
436 {
438  for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
439  p.update(it);
440  *it.out = get_distance_value<std::greater>(p, 0.0f);
441  }
442 }
443 
445 {
446  /* pass */
447 }
448 
449 void ErodeDistanceOperation::execute_pixel(float output[4], int x, int y, void *data)
450 {
451  const float distance = distance_;
452  const float mindist = distance * distance;
453 
454  MemoryBuffer *input_buffer = (MemoryBuffer *)data;
455  float *buffer = input_buffer->get_buffer();
456  const rcti &input_rect = input_buffer->get_rect();
457  const int minx = MAX2(x - scope_, input_rect.xmin);
458  const int miny = MAX2(y - scope_, input_rect.ymin);
459  const int maxx = MIN2(x + scope_, input_rect.xmax);
460  const int maxy = MIN2(y + scope_, input_rect.ymax);
461  const int buffer_width = input_buffer->get_width();
462  int offset;
463 
464  float value = 1.0f;
465 
466  for (int yi = miny; yi < maxy; yi++) {
467  const float dy = yi - y;
468  offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin));
469  for (int xi = minx; xi < maxx; xi++) {
470  const float dx = xi - x;
471  const float dis = dx * dx + dy * dy;
472  if (dis <= mindist) {
473  value = MIN2(buffer[offset], value);
474  }
475  offset++;
476  }
477  }
478  output[0] = value;
479 }
480 
482  MemoryBuffer *output_memory_buffer,
483  cl_mem cl_output_buffer,
484  MemoryBuffer **input_memory_buffers,
485  std::list<cl_mem> *cl_mem_to_clean_up,
486  std::list<cl_kernel> * /*cl_kernels_to_clean_up*/)
487 {
488  cl_kernel erode_kernel = device->COM_cl_create_kernel("erode_kernel", nullptr);
489 
491  cl_int scope = scope_;
492 
494  erode_kernel, 0, 2, cl_mem_to_clean_up, input_memory_buffers, input_program_);
496  erode_kernel, 1, cl_output_buffer);
498  erode_kernel, 3, output_memory_buffer);
499  clSetKernelArg(erode_kernel, 4, sizeof(cl_int), &scope);
500  clSetKernelArg(erode_kernel, 5, sizeof(cl_int), &distance_squared);
501  device->COM_cl_attach_size_to_kernel_parameter(erode_kernel, 6, this);
502  device->COM_cl_enqueue_range(erode_kernel, output_memory_buffer, 7, this);
503 }
504 
506  const rcti &area,
508 {
510  for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
511  p.update(it);
512  *it.out = get_distance_value<std::less>(p, 1.0f);
513  }
514 }
515 
517 {
520  flags_.complex = true;
521  input_program_ = nullptr;
522 }
524 {
526 }
527 
528 /* Small helper to pass data from initialize_tile_data to execute_pixel. */
529 struct tile_info {
531  int width;
532  float *buffer;
533 };
534 
535 static tile_info *create_cache(int xmin, int xmax, int ymin, int ymax)
536 {
537  tile_info *result = (tile_info *)MEM_mallocN(sizeof(tile_info), "dilate erode tile");
538  result->rect.xmin = xmin;
539  result->rect.xmax = xmax;
540  result->rect.ymin = ymin;
541  result->rect.ymax = ymax;
542  result->width = xmax - xmin;
543  result->buffer = (float *)MEM_callocN(sizeof(float) * (ymax - ymin) * result->width,
544  "dilate erode cache");
545  return result;
546 }
547 
549 {
551  int x, y, i;
552  int width = tile->get_width();
553  int height = tile->get_height();
554  float *buffer = tile->get_buffer();
555 
556  int half_window = iterations_;
557  int window = half_window * 2 + 1;
558 
559  int xmin = MAX2(0, rect->xmin - half_window);
560  int ymin = MAX2(0, rect->ymin - half_window);
561  int xmax = MIN2(width, rect->xmax + half_window);
562  int ymax = MIN2(height, rect->ymax + half_window);
563 
564  int bwidth = rect->xmax - rect->xmin;
565  int bheight = rect->ymax - rect->ymin;
566 
567  /* NOTE: Cache buffer has original tile-size width, but new height.
568  * We have to calculate the additional rows in the first pass,
569  * to have valid data available for the second pass. */
570  tile_info *result = create_cache(rect->xmin, rect->xmax, ymin, ymax);
571  float *rectf = result->buffer;
572 
573  /* temp holds maxima for every step in the algorithm, buf holds a
574  * single row or column of input values, padded with FLT_MAX's to
575  * simplify the logic. */
576  float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp");
577  float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window),
578  "dilate erode buf");
579 
580  /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations.
581  * first pass, horizontal dilate/erode. */
582  for (y = ymin; y < ymax; y++) {
583  for (x = 0; x < bwidth + 5 * half_window; x++) {
584  buf[x] = -FLT_MAX;
585  }
586  for (x = xmin; x < xmax; x++) {
587  buf[x - rect->xmin + window - 1] = buffer[(y * width + x)];
588  }
589 
590  for (i = 0; i < (bwidth + 3 * half_window) / window; i++) {
591  int start = (i + 1) * window - 1;
592 
593  temp[window - 1] = buf[start];
594  for (x = 1; x < window; x++) {
595  temp[window - 1 - x] = MAX2(temp[window - x], buf[start - x]);
596  temp[window - 1 + x] = MAX2(temp[window + x - 2], buf[start + x]);
597  }
598 
599  start = half_window + (i - 1) * window + 1;
600  for (x = -MIN2(0, start); x < window - MAX2(0, start + window - bwidth); x++) {
601  rectf[bwidth * (y - ymin) + (start + x)] = MAX2(temp[x], temp[x + window - 1]);
602  }
603  }
604  }
605 
606  /* Second pass, vertical dilate/erode. */
607  for (x = 0; x < bwidth; x++) {
608  for (y = 0; y < bheight + 5 * half_window; y++) {
609  buf[y] = -FLT_MAX;
610  }
611  for (y = ymin; y < ymax; y++) {
612  buf[y - rect->ymin + window - 1] = rectf[(y - ymin) * bwidth + x];
613  }
614 
615  for (i = 0; i < (bheight + 3 * half_window) / window; i++) {
616  int start = (i + 1) * window - 1;
617 
618  temp[window - 1] = buf[start];
619  for (y = 1; y < window; y++) {
620  temp[window - 1 - y] = MAX2(temp[window - y], buf[start - y]);
621  temp[window - 1 + y] = MAX2(temp[window + y - 2], buf[start + y]);
622  }
623 
624  start = half_window + (i - 1) * window + 1;
625  for (y = -MIN2(0, start); y < window - MAX2(0, start + window - bheight); y++) {
626  rectf[bwidth * (y + start + (rect->ymin - ymin)) + x] = MAX2(temp[y],
627  temp[y + window - 1]);
628  }
629  }
630  }
631 
632  MEM_freeN(temp);
633  MEM_freeN(buf);
634 
635  return result;
636 }
637 
638 void DilateStepOperation::execute_pixel(float output[4], int x, int y, void *data)
639 {
641  int nx = x - tile->rect.xmin;
642  int ny = y - tile->rect.ymin;
643  output[0] = tile->buffer[tile->width * ny + nx];
644 }
645 
647 {
648  input_program_ = nullptr;
649 }
650 
652 {
654  MEM_freeN(tile->buffer);
655  MEM_freeN(tile);
656 }
657 
659  ReadBufferOperation *read_operation,
660  rcti *output)
661 {
662  rcti new_input;
663  int it = iterations_;
664  new_input.xmax = input->xmax + it;
665  new_input.xmin = input->xmin - it;
666  new_input.ymax = input->ymax + it;
667  new_input.ymin = input->ymin - it;
668 
669  return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output);
670 }
671 
673  const rcti &output_area,
674  rcti &r_input_area)
675 {
676  BLI_assert(input_idx == 0);
677  UNUSED_VARS_NDEBUG(input_idx);
678  r_input_area.xmin = output_area.xmin - iterations_;
679  r_input_area.xmax = output_area.xmax + iterations_;
680  r_input_area.ymin = output_area.ymin - iterations_;
681  r_input_area.ymax = output_area.ymax + iterations_;
682 }
683 
684 template<typename TCompareSelector>
686  const MemoryBuffer *input,
687  const rcti &area,
688  const int num_iterations,
689  const float compare_min_value)
690 {
691  TCompareSelector selector;
692 
693  const int width = output->get_width();
694  const int height = output->get_height();
695 
696  const int half_window = num_iterations;
697  const int window = half_window * 2 + 1;
698 
699  const int xmin = MAX2(0, area.xmin - half_window);
700  const int ymin = MAX2(0, area.ymin - half_window);
701  const int xmax = MIN2(width, area.xmax + half_window);
702  const int ymax = MIN2(height, area.ymax + half_window);
703 
704  const int bwidth = area.xmax - area.xmin;
705  const int bheight = area.ymax - area.ymin;
706 
707  /* NOTE: #result has area width, but new height.
708  * We have to calculate the additional rows in the first pass,
709  * to have valid data available for the second pass. */
710  rcti result_area;
711  BLI_rcti_init(&result_area, area.xmin, area.xmax, ymin, ymax);
712  MemoryBuffer result(DataType::Value, result_area);
713 
714  /* #temp holds maxima for every step in the algorithm, #buf holds a
715  * single row or column of input values, padded with #limit values to
716  * simplify the logic. */
717  float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp");
718  float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window),
719  "dilate erode buf");
720 
721  /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations. */
722  /* First pass, horizontal dilate/erode. */
723  for (int y = ymin; y < ymax; y++) {
724  for (int x = 0; x < bwidth + 5 * half_window; x++) {
725  buf[x] = compare_min_value;
726  }
727  for (int x = xmin; x < xmax; x++) {
728  buf[x - area.xmin + window - 1] = input->get_value(x, y, 0);
729  }
730 
731  for (int i = 0; i < (bwidth + 3 * half_window) / window; i++) {
732  int start = (i + 1) * window - 1;
733 
734  temp[window - 1] = buf[start];
735  for (int x = 1; x < window; x++) {
736  temp[window - 1 - x] = selector(temp[window - x], buf[start - x]);
737  temp[window - 1 + x] = selector(temp[window + x - 2], buf[start + x]);
738  }
739 
740  start = half_window + (i - 1) * window + 1;
741  for (int x = -MIN2(0, start); x < window - MAX2(0, start + window - bwidth); x++) {
742  result.get_value(start + x + area.xmin, y, 0) = selector(temp[x], temp[x + window - 1]);
743  }
744  }
745  }
746 
747  /* Second pass, vertical dilate/erode. */
748  for (int x = 0; x < bwidth; x++) {
749  for (int y = 0; y < bheight + 5 * half_window; y++) {
750  buf[y] = compare_min_value;
751  }
752  for (int y = ymin; y < ymax; y++) {
753  buf[y - area.ymin + window - 1] = result.get_value(x + area.xmin, y, 0);
754  }
755 
756  for (int i = 0; i < (bheight + 3 * half_window) / window; i++) {
757  int start = (i + 1) * window - 1;
758 
759  temp[window - 1] = buf[start];
760  for (int y = 1; y < window; y++) {
761  temp[window - 1 - y] = selector(temp[window - y], buf[start - y]);
762  temp[window - 1 + y] = selector(temp[window + y - 2], buf[start + y]);
763  }
764 
765  start = half_window + (i - 1) * window + 1;
766  for (int y = -MIN2(0, start); y < window - MAX2(0, start + window - bheight); y++) {
767  result.get_value(x + area.xmin, y + start + area.ymin, 0) = selector(temp[y],
768  temp[y + window - 1]);
769  }
770  }
771  }
772 
773  MEM_freeN(temp);
774  MEM_freeN(buf);
775 
776  output->copy_from(&result, area);
777 }
778 
779 struct Max2Selector {
780  float operator()(float f1, float f2) const
781  {
782  return MAX2(f1, f2);
783  }
784 };
785 
787  const rcti &area,
789 {
790  step_update_memory_buffer<Max2Selector>(output, inputs[0], area, iterations_, -FLT_MAX);
791 }
792 
794 {
795  /* pass */
796 }
797 
799 {
801  int x, y, i;
802  int width = tile->get_width();
803  int height = tile->get_height();
804  float *buffer = tile->get_buffer();
805 
806  int half_window = iterations_;
807  int window = half_window * 2 + 1;
808 
809  int xmin = MAX2(0, rect->xmin - half_window);
810  int ymin = MAX2(0, rect->ymin - half_window);
811  int xmax = MIN2(width, rect->xmax + half_window);
812  int ymax = MIN2(height, rect->ymax + half_window);
813 
814  int bwidth = rect->xmax - rect->xmin;
815  int bheight = rect->ymax - rect->ymin;
816 
817  /* NOTE: Cache buffer has original tile-size width, but new height.
818  * We have to calculate the additional rows in the first pass,
819  * to have valid data available for the second pass. */
820  tile_info *result = create_cache(rect->xmin, rect->xmax, ymin, ymax);
821  float *rectf = result->buffer;
822 
823  /* temp holds maxima for every step in the algorithm, buf holds a
824  * single row or column of input values, padded with FLT_MAX's to
825  * simplify the logic. */
826  float *temp = (float *)MEM_mallocN(sizeof(float) * (2 * window - 1), "dilate erode temp");
827  float *buf = (float *)MEM_mallocN(sizeof(float) * (MAX2(bwidth, bheight) + 5 * half_window),
828  "dilate erode buf");
829 
830  /* The following is based on the van Herk/Gil-Werman algorithm for morphology operations.
831  * first pass, horizontal dilate/erode */
832  for (y = ymin; y < ymax; y++) {
833  for (x = 0; x < bwidth + 5 * half_window; x++) {
834  buf[x] = FLT_MAX;
835  }
836  for (x = xmin; x < xmax; x++) {
837  buf[x - rect->xmin + window - 1] = buffer[(y * width + x)];
838  }
839 
840  for (i = 0; i < (bwidth + 3 * half_window) / window; i++) {
841  int start = (i + 1) * window - 1;
842 
843  temp[window - 1] = buf[start];
844  for (x = 1; x < window; x++) {
845  temp[window - 1 - x] = MIN2(temp[window - x], buf[start - x]);
846  temp[window - 1 + x] = MIN2(temp[window + x - 2], buf[start + x]);
847  }
848 
849  start = half_window + (i - 1) * window + 1;
850  for (x = -MIN2(0, start); x < window - MAX2(0, start + window - bwidth); x++) {
851  rectf[bwidth * (y - ymin) + (start + x)] = MIN2(temp[x], temp[x + window - 1]);
852  }
853  }
854  }
855 
856  /* Second pass, vertical dilate/erode. */
857  for (x = 0; x < bwidth; x++) {
858  for (y = 0; y < bheight + 5 * half_window; y++) {
859  buf[y] = FLT_MAX;
860  }
861  for (y = ymin; y < ymax; y++) {
862  buf[y - rect->ymin + window - 1] = rectf[(y - ymin) * bwidth + x];
863  }
864 
865  for (i = 0; i < (bheight + 3 * half_window) / window; i++) {
866  int start = (i + 1) * window - 1;
867 
868  temp[window - 1] = buf[start];
869  for (y = 1; y < window; y++) {
870  temp[window - 1 - y] = MIN2(temp[window - y], buf[start - y]);
871  temp[window - 1 + y] = MIN2(temp[window + y - 2], buf[start + y]);
872  }
873 
874  start = half_window + (i - 1) * window + 1;
875  for (y = -MIN2(0, start); y < window - MAX2(0, start + window - bheight); y++) {
876  rectf[bwidth * (y + start + (rect->ymin - ymin)) + x] = MIN2(temp[y],
877  temp[y + window - 1]);
878  }
879  }
880  }
881 
882  MEM_freeN(temp);
883  MEM_freeN(buf);
884 
885  return result;
886 }
887 
888 struct Min2Selector {
889  float operator()(float f1, float f2) const
890  {
891  return MIN2(f1, f2);
892  }
893 };
894 
896  const rcti &area,
898 {
899  step_update_memory_buffer<Min2Selector>(output, inputs[0], area, iterations_, FLT_MAX);
900 }
901 
902 } // namespace blender::compositor
#define BLI_assert(a)
Definition: BLI_assert.h:46
void BLI_rcti_init(struct rcti *rect, int xmin, int xmax, int ymin, int ymax)
Definition: rct.c:417
#define UNUSED_VARS_NDEBUG(...)
#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 GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble ny
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei height
_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
_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 GLsizei width
void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final
Get input operation area being read by this operation on rendering given output area.
void execute_pixel(float output[4], int x, int y, void *data) override
virtual void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
bool determine_depending_area_of_interest(rcti *input, ReadBufferOperation *read_operation, rcti *output) override
void execute_opencl(OpenCLDevice *device, MemoryBuffer *output_memory_buffer, cl_mem cl_output_buffer, MemoryBuffer **input_memory_buffers, std::list< cl_mem > *cl_mem_to_clean_up, std::list< cl_kernel > *cl_kernels_to_clean_up) override
custom handle to add new tasks to the OpenCL command queue in order to execute a chunk on an GPUDevic...
void execute_pixel(float output[4], int x, int y, void *data) override
bool determine_depending_area_of_interest(rcti *input, ReadBufferOperation *read_operation, rcti *output) 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.
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
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
virtual void update_memory_buffer_partial(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) final
Get input operation area being read by this operation on rendering given output area.
void deinitialize_tile_data(rcti *rect, void *data) override
void execute_pixel(float output[4], int x, int y, void *data) override
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
void execute_opencl(OpenCLDevice *device, MemoryBuffer *output_memory_buffer, cl_mem cl_output_buffer, MemoryBuffer **input_memory_buffers, std::list< cl_mem > *cl_mem_to_clean_up, std::list< cl_kernel > *cl_kernels_to_clean_up) override
custom handle to add new tasks to the OpenCL command queue in order to execute a chunk on an GPUDevic...
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) override
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)
const rcti & get_rect() const
get the rect of this MemoryBuffer
const int get_width() const
get the width of this MemoryBuffer
float * get_buffer()
get the data of this MemoryBuffer
void add_output_socket(DataType datatype)
SocketReader * get_input_socket_reader(unsigned 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 *)
device representing an GPU OpenCL device. an instance of this class represents a single cl_device
void COM_cl_attach_size_to_kernel_parameter(cl_kernel kernel, int offset_index, NodeOperation *operation)
cl_mem COM_cl_attach_memory_buffer_to_kernel_parameter(cl_kernel kernel, int parameter_index, int offset_index, std::list< cl_mem > *cleanup, MemoryBuffer **input_memory_buffers, SocketReader *reader)
void COM_cl_attach_output_memory_buffer_to_kernel_parameter(cl_kernel kernel, int parameter_index, cl_mem cl_output_memory_buffer)
void COM_cl_enqueue_range(cl_kernel kernel, MemoryBuffer *output_memory_buffer)
cl_kernel COM_cl_create_kernel(const char *kernelname, std::list< cl_kernel > *cl_kernels_to_clean_up)
void COM_cl_attach_memory_buffer_offset_to_kernel_parameter(cl_kernel kernel, int offset_index, MemoryBuffer *memory_buffers)
ccl_global float * buffer
ccl_global KernelShaderEvalInput ccl_global float * output
ccl_gpu_kernel_postfix ccl_global float int int int sw
ccl_global const KernelWorkTile * tile
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
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
#define sqrtf(x)
Definition: metal/compat.h:243
static void area(int d1, int d2, int e1, int e2, float weights[2])
static tile_info * create_cache(int xmin, int xmax, int ymin, int ymax)
static void step_update_memory_buffer(MemoryBuffer *output, const MemoryBuffer *input, const rcti &area, const int num_iterations, const float compare_min_value)
static float get_min_distance(DilateErodeThresholdOperation::PixelData &p)
typename BuffersIteratorBuilder< T >::Iterator BuffersIterator
static float get_distance_value(DilateDistanceOperation::PixelData &p, const float start_value)
T distance_squared(const vec_base< T, Size > &a, const vec_base< T, Size > &b)
T distance(const T &a, const T &b)
static bNodeSocketTemplate inputs[]
_W64 int intptr_t
Definition: stdint.h:118
PixelData(MemoryBuffer *input, const int distance, const int scope)
float operator()(float f1, float f2) const
float operator()(float f1, float f2) const
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