Blender  V2.93
COM_FastGaussianBlurOperation.cc
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 2011, Blender Foundation.
17  */
18 
19 #include <climits>
20 
21 #include "BLI_utildefines.h"
23 #include "MEM_guardedalloc.h"
24 
25 namespace blender::compositor {
26 
28 {
29  this->m_iirgaus = nullptr;
30 }
31 
32 void FastGaussianBlurOperation::executePixel(float output[4], int x, int y, void *data)
33 {
34  MemoryBuffer *newData = (MemoryBuffer *)data;
35  newData->read(output, x, y);
36 }
37 
39  rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output)
40 {
41  rcti newInput;
42  rcti sizeInput;
43  sizeInput.xmin = 0;
44  sizeInput.ymin = 0;
45  sizeInput.xmax = 5;
46  sizeInput.ymax = 5;
47 
48  NodeOperation *operation = this->getInputOperation(1);
49  if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) {
50  return true;
51  }
52 
53  if (this->m_iirgaus) {
54  return false;
55  }
56 
57  newInput.xmin = 0;
58  newInput.ymin = 0;
59  newInput.xmax = this->getWidth();
60  newInput.ymax = this->getHeight();
61 
62  return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
63 }
64 
66 {
69 }
70 
72 {
73  if (this->m_iirgaus) {
74  delete this->m_iirgaus;
75  this->m_iirgaus = nullptr;
76  }
78 }
79 
81 {
82  lockMutex();
83  if (!this->m_iirgaus) {
85  MemoryBuffer *copy = new MemoryBuffer(*newBuf);
86  updateSize();
87 
88  int c;
89  this->m_sx = this->m_data.sizex * this->m_size / 2.0f;
90  this->m_sy = this->m_data.sizey * this->m_size / 2.0f;
91 
92  if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) {
93  for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
94  IIR_gauss(copy, this->m_sx, c, 3);
95  }
96  }
97  else {
98  if (this->m_sx > 0.0f) {
99  for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
100  IIR_gauss(copy, this->m_sx, c, 1);
101  }
102  }
103  if (this->m_sy > 0.0f) {
104  for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) {
105  IIR_gauss(copy, this->m_sy, c, 2);
106  }
107  }
108  }
109  this->m_iirgaus = copy;
110  }
111  unlockMutex();
112  return this->m_iirgaus;
113 }
114 
116  float sigma,
117  unsigned int chan,
118  unsigned int xy)
119 {
120  double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3];
121  double *X, *Y, *W;
122  const unsigned int src_width = src->getWidth();
123  const unsigned int src_height = src->getHeight();
124  unsigned int x, y, sz;
125  unsigned int i;
126  float *buffer = src->getBuffer();
127  const uint8_t num_channels = src->get_num_channels();
128 
129  // <0.5 not valid, though can have a possibly useful sort of sharpening effect
130  if (sigma < 0.5f) {
131  return;
132  }
133 
134  if ((xy < 1) || (xy > 3)) {
135  xy = 3;
136  }
137 
138  // XXX The YVV macro defined below explicitly expects sources of at least 3x3 pixels,
139  // so just skipping blur along faulty direction if src's def is below that limit!
140  if (src_width < 3) {
141  xy &= ~1;
142  }
143  if (src_height < 3) {
144  xy &= ~2;
145  }
146  if (xy < 1) {
147  return;
148  }
149 
150  // see "Recursive Gabor Filtering" by Young/VanVliet
151  // all factors here in double.prec.
152  // Required, because for single.prec it seems to blow up if sigma > ~200
153  if (sigma >= 3.556f) {
154  q = 0.9804f * (sigma - 3.556f) + 2.5091f;
155  }
156  else { // sigma >= 0.5
157  q = (0.0561f * sigma + 0.5784f) * sigma - 0.2568f;
158  }
159  q2 = q * q;
160  sc = (1.1668 + q) * (3.203729649 + (2.21566 + q) * q);
161  // no gabor filtering here, so no complex multiplies, just the regular coefs.
162  // all negated here, so as not to have to recalc Triggs/Sdika matrix
163  cf[1] = q * (5.788961737 + (6.76492 + 3.0 * q) * q) / sc;
164  cf[2] = -q2 * (3.38246 + 3.0 * q) / sc;
165  // 0 & 3 unchanged
166  cf[3] = q2 * q / sc;
167  cf[0] = 1.0 - cf[1] - cf[2] - cf[3];
168 
169  // Triggs/Sdika border corrections,
170  // it seems to work, not entirely sure if it is actually totally correct,
171  // Besides J.M.Geusebroek's anigauss.c (see http://www.science.uva.nl/~mark),
172  // found one other implementation by Cristoph Lampert,
173  // but neither seem to be quite the same, result seems to be ok so far anyway.
174  // Extra scale factor here to not have to do it in filter,
175  // though maybe this had something to with the precision errors
176  sc = cf[0] / ((1.0 + cf[1] - cf[2] + cf[3]) * (1.0 - cf[1] - cf[2] - cf[3]) *
177  (1.0 + cf[2] + (cf[1] - cf[3]) * cf[3]));
178  tsM[0] = sc * (-cf[3] * cf[1] + 1.0 - cf[3] * cf[3] - cf[2]);
179  tsM[1] = sc * ((cf[3] + cf[1]) * (cf[2] + cf[3] * cf[1]));
180  tsM[2] = sc * (cf[3] * (cf[1] + cf[3] * cf[2]));
181  tsM[3] = sc * (cf[1] + cf[3] * cf[2]);
182  tsM[4] = sc * (-(cf[2] - 1.0) * (cf[2] + cf[3] * cf[1]));
183  tsM[5] = sc * (-(cf[3] * cf[1] + cf[3] * cf[3] + cf[2] - 1.0) * cf[3]);
184  tsM[6] = sc * (cf[3] * cf[1] + cf[2] + cf[1] * cf[1] - cf[2] * cf[2]);
185  tsM[7] = sc * (cf[1] * cf[2] + cf[3] * cf[2] * cf[2] - cf[1] * cf[3] * cf[3] -
186  cf[3] * cf[3] * cf[3] - cf[3] * cf[2] + cf[3]);
187  tsM[8] = sc * (cf[3] * (cf[1] + cf[3] * cf[2]));
188 
189 #define YVV(L) \
190  { \
191  W[0] = cf[0] * X[0] + cf[1] * X[0] + cf[2] * X[0] + cf[3] * X[0]; \
192  W[1] = cf[0] * X[1] + cf[1] * W[0] + cf[2] * X[0] + cf[3] * X[0]; \
193  W[2] = cf[0] * X[2] + cf[1] * W[1] + cf[2] * W[0] + cf[3] * X[0]; \
194  for (i = 3; i < L; i++) { \
195  W[i] = cf[0] * X[i] + cf[1] * W[i - 1] + cf[2] * W[i - 2] + cf[3] * W[i - 3]; \
196  } \
197  tsu[0] = W[L - 1] - X[L - 1]; \
198  tsu[1] = W[L - 2] - X[L - 1]; \
199  tsu[2] = W[L - 3] - X[L - 1]; \
200  tsv[0] = tsM[0] * tsu[0] + tsM[1] * tsu[1] + tsM[2] * tsu[2] + X[L - 1]; \
201  tsv[1] = tsM[3] * tsu[0] + tsM[4] * tsu[1] + tsM[5] * tsu[2] + X[L - 1]; \
202  tsv[2] = tsM[6] * tsu[0] + tsM[7] * tsu[1] + tsM[8] * tsu[2] + X[L - 1]; \
203  Y[L - 1] = cf[0] * W[L - 1] + cf[1] * tsv[0] + cf[2] * tsv[1] + cf[3] * tsv[2]; \
204  Y[L - 2] = cf[0] * W[L - 2] + cf[1] * Y[L - 1] + cf[2] * tsv[0] + cf[3] * tsv[1]; \
205  Y[L - 3] = cf[0] * W[L - 3] + cf[1] * Y[L - 2] + cf[2] * Y[L - 1] + cf[3] * tsv[0]; \
206  /* 'i != UINT_MAX' is really 'i >= 0', but necessary for unsigned int wrapping */ \
207  for (i = L - 4; i != UINT_MAX; i--) { \
208  Y[i] = cf[0] * W[i] + cf[1] * Y[i + 1] + cf[2] * Y[i + 2] + cf[3] * Y[i + 3]; \
209  } \
210  } \
211  (void)0
212 
213  // intermediate buffers
214  sz = MAX2(src_width, src_height);
215  X = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss X buf");
216  Y = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss Y buf");
217  W = (double *)MEM_callocN(sz * sizeof(double), "IIR_gauss W buf");
218  if (xy & 1) { // H
219  int offset;
220  for (y = 0; y < src_height; y++) {
221  const int yx = y * src_width;
222  offset = yx * num_channels + chan;
223  for (x = 0; x < src_width; x++) {
224  X[x] = buffer[offset];
225  offset += num_channels;
226  }
227  YVV(src_width);
228  offset = yx * num_channels + chan;
229  for (x = 0; x < src_width; x++) {
230  buffer[offset] = Y[x];
231  offset += num_channels;
232  }
233  }
234  }
235  if (xy & 2) { // V
236  int offset;
237  const int add = src_width * num_channels;
238 
239  for (x = 0; x < src_width; x++) {
240  offset = x * num_channels + chan;
241  for (y = 0; y < src_height; y++) {
242  X[y] = buffer[offset];
243  offset += add;
244  }
245  YVV(src_height);
246  offset = x * num_channels + chan;
247  for (y = 0; y < src_height; y++) {
248  buffer[offset] = Y[y];
249  offset += add;
250  }
251  }
252  }
253 
254  MEM_freeN(X);
255  MEM_freeN(W);
256  MEM_freeN(Y);
257 #undef YVV
258 }
259 
262 {
265  this->m_iirgaus = nullptr;
266  this->m_inputprogram = nullptr;
267  this->m_sigma = 1.0f;
268  this->m_overlay = 0;
269  flags.complex = true;
270 }
271 
273 {
274  MemoryBuffer *newData = (MemoryBuffer *)data;
275  newData->read(output, x, y);
276 }
277 
279  rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output)
280 {
281  rcti newInput;
282 
283  if (this->m_iirgaus) {
284  return false;
285  }
286 
287  newInput.xmin = 0;
288  newInput.ymin = 0;
289  newInput.xmax = this->getWidth();
290  newInput.ymax = this->getHeight();
291 
292  return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
293 }
294 
296 {
297  this->m_inputprogram = getInputSocketReader(0);
298  initMutex();
299 }
300 
302 {
303  if (this->m_iirgaus) {
304  delete this->m_iirgaus;
305  this->m_iirgaus = nullptr;
306  }
307  deinitMutex();
308 }
309 
311 {
312  lockMutex();
313  if (!this->m_iirgaus) {
314  MemoryBuffer *newBuf = (MemoryBuffer *)this->m_inputprogram->initializeTileData(rect);
315  MemoryBuffer *copy = new MemoryBuffer(*newBuf);
316  FastGaussianBlurOperation::IIR_gauss(copy, this->m_sigma, 0, 3);
317 
318  if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) {
319  float *src = newBuf->getBuffer();
320  float *dst = copy->getBuffer();
321  for (int i = copy->getWidth() * copy->getHeight(); i != 0;
323  if (*src < *dst) {
324  *dst = *src;
325  }
326  }
327  }
328  else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) {
329  float *src = newBuf->getBuffer();
330  float *dst = copy->getBuffer();
331  for (int i = copy->getWidth() * copy->getHeight(); i != 0;
333  if (*src > *dst) {
334  *dst = *src;
335  }
336  }
337  }
338 
339  // newBuf->
340 
341  this->m_iirgaus = copy;
342  }
343  unlockMutex();
344  return this->m_iirgaus;
345 }
346 
347 } // namespace blender::compositor
#define MAX2(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:213
#define Y
Definition: GeomUtils.cpp:214
Read Guarded memory(de)allocation.
Group RGB to Bright Vector Camera Vector Combine Material Light Line Style Layer Add Ambient Diffuse Glossy Refraction Transparent Toon Principled Hair Volume Principled Light Particle Volume Image Sky Noise Wave Voronoi Brick Texture Vector Combine Vertex Color
#define output
void executePixel(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)
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override
bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output) override
void executePixel(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)
const int getHeight() const
get the height of this MemoryBuffer
const int getWidth() const
get the width of this MemoryBuffer
float * getBuffer()
get the data of this MemoryBuffer
NodeOperation contains calculation logic.
virtual void * initializeTileData(rcti *)
void addInputSocket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
NodeOperation * getInputOperation(unsigned int inputSocketindex)
void addOutputSocket(DataType datatype)
SocketReader * getInputSocketReader(unsigned int inputSocketindex)
virtual bool determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
DataType
possible data types for sockets
Definition: COM_defines.h:27
__kernel void ccl_constant KernelData ccl_global void ccl_global char ccl_global int ccl_global char ccl_global unsigned int ccl_global float * buffer
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
static void add(GHash *messages, MemArena *memarena, const Message *msg)
Definition: msgfmt.c:268
static unsigned c
Definition: RandGen.cpp:97
constexpr int COM_DATA_TYPE_VALUE_CHANNELS
Definition: COM_defines.h:52
constexpr int COM_DATA_TYPE_COLOR_CHANNELS
Definition: COM_defines.h:53
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
unsigned char uint8_t
Definition: stdint.h:81
int ymin
Definition: DNA_vec_types.h:80
int ymax
Definition: DNA_vec_types.h:80
int xmin
Definition: DNA_vec_types.h:79
int xmax
Definition: DNA_vec_types.h:79