svn merge ^/trunk/blender -r49520:49531
[blender.git] / source / blender / compositor / operations / COM_DilateErodeOperation.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor: 
19  *              Jeroen Bakker 
20  *              Monique Dewanchand
21  */
22
23 #include "COM_DilateErodeOperation.h"
24 #include "BLI_math.h"
25 #include "COM_OpenCLDevice.h"
26
27 // DilateErode Distance Threshold
28 DilateErodeThresholdOperation::DilateErodeThresholdOperation() : NodeOperation()
29 {
30         this->addInputSocket(COM_DT_VALUE);
31         this->addOutputSocket(COM_DT_VALUE);
32         this->setComplex(true);
33         this->m_inputProgram = NULL;
34         this->m_inset = 0.0f;
35         this->m__switch = 0.5f;
36         this->m_distance = 0.0f;
37 }
38 void DilateErodeThresholdOperation::initExecution()
39 {
40         this->m_inputProgram = this->getInputSocketReader(0);
41         if (this->m_distance < 0.0f) {
42                 this->m_scope = -this->m_distance + this->m_inset;
43         }
44         else {
45                 if (this->m_inset * 2 > this->m_distance) {
46                         this->m_scope = max(this->m_inset * 2 - this->m_distance, this->m_distance);
47                 }
48                 else {
49                         this->m_scope = this->m_distance;
50                 }
51         }
52         if (this->m_scope < 3) {
53                 this->m_scope = 3;
54         }
55 }
56
57 void *DilateErodeThresholdOperation::initializeTileData(rcti *rect)
58 {
59         void *buffer = this->m_inputProgram->initializeTileData(NULL);
60         return buffer;
61 }
62
63 void DilateErodeThresholdOperation::executePixel(float *color, int x, int y, void *data)
64 {
65         float inputValue[4];
66         const float sw = this->m__switch;
67         const float distance = this->m_distance;
68         float pixelvalue;
69         const float rd = this->m_scope * this->m_scope;
70         const float inset = this->m_inset;
71         float mindist = rd * 2;
72
73         MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
74         float *buffer = inputBuffer->getBuffer();
75         rcti *rect = inputBuffer->getRect();
76         const int minx = max(x - this->m_scope, rect->xmin);
77         const int miny = max(y - this->m_scope, rect->ymin);
78         const int maxx = min(x + this->m_scope, rect->xmax);
79         const int maxy = min(y + this->m_scope, rect->ymax);
80         const int bufferWidth = rect->xmax - rect->xmin;
81         int offset;
82
83         this->m_inputProgram->read(inputValue, x, y, NULL);
84         if (inputValue[0] > sw) {
85                 for (int yi = miny; yi < maxy; yi++) {
86                         const float dy = yi - y;
87                         offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4;
88                         for (int xi = minx; xi < maxx; xi++) {
89                                 if (buffer[offset] < sw) {
90                                         const float dx = xi - x;
91                                         const float dis = dx * dx + dy * dy;
92                                         mindist = min(mindist, dis);
93                                 }
94                                 offset += 4;
95                         }
96                 }
97                 pixelvalue = -sqrtf(mindist);
98         }
99         else {
100                 for (int yi = miny; yi < maxy; yi++) {
101                         const float dy = yi - y;
102                         offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4;
103                         for (int xi = minx; xi < maxx; xi++) {
104                                 if (buffer[offset] > sw) {
105                                         const float dx = xi - x;
106                                         const float dis = dx * dx + dy * dy;
107                                         mindist = min(mindist, dis);
108                                 }
109                                 offset += 4;
110
111                         }
112                 }
113                 pixelvalue = sqrtf(mindist);
114         }
115
116         if (distance > 0.0f) {
117                 const float delta = distance - pixelvalue;
118                 if (delta >= 0.0f) {
119                         if (delta >= inset) {
120                                 color[0] = 1.0f;
121                         }
122                         else {
123                                 color[0] = delta / inset;
124                         }
125                 }
126                 else {
127                         color[0] = 0.0f;
128                 }
129         }
130         else {
131                 const float delta = -distance + pixelvalue;
132                 if (delta < 0.0f) {
133                         if (delta < -inset) {
134                                 color[0] = 1.0f;
135                         }
136                         else {
137                                 color[0] = (-delta) / inset;
138                         }
139                 }
140                 else {
141                         color[0] = 0.0f;
142                 }
143         }
144 }
145
146 void DilateErodeThresholdOperation::deinitExecution()
147 {
148         this->m_inputProgram = NULL;
149 }
150
151 bool DilateErodeThresholdOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
152 {
153         rcti newInput;
154
155         newInput.xmax = input->xmax + this->m_scope;
156         newInput.xmin = input->xmin - this->m_scope;
157         newInput.ymax = input->ymax + this->m_scope;
158         newInput.ymin = input->ymin - this->m_scope;
159
160         return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
161 }
162
163 // Dilate Distance
164 DilateDistanceOperation::DilateDistanceOperation() : NodeOperation()
165 {
166         this->addInputSocket(COM_DT_VALUE);
167         this->addOutputSocket(COM_DT_VALUE);
168         this->setComplex(true);
169         this->m_inputProgram = NULL;
170         this->m_distance = 0.0f;
171         this->setOpenCL(true);
172 }
173 void DilateDistanceOperation::initExecution()
174 {
175         this->m_inputProgram = this->getInputSocketReader(0);
176         this->m_scope = this->m_distance;
177         if (this->m_scope < 3) {
178                 this->m_scope = 3;
179         }
180 }
181
182 void *DilateDistanceOperation::initializeTileData(rcti *rect)
183 {
184         void *buffer = this->m_inputProgram->initializeTileData(NULL);
185         return buffer;
186 }
187
188 void DilateDistanceOperation::executePixel(float *color, int x, int y, void *data)
189 {
190         const float distance = this->m_distance;
191         const float mindist = distance * distance;
192
193         MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
194         float *buffer = inputBuffer->getBuffer();
195         rcti *rect = inputBuffer->getRect();
196         const int minx = max(x - this->m_scope, rect->xmin);
197         const int miny = max(y - this->m_scope, rect->ymin);
198         const int maxx = min(x + this->m_scope, rect->xmax);
199         const int maxy = min(y + this->m_scope, rect->ymax);
200         const int bufferWidth = rect->xmax - rect->xmin;
201         int offset;
202         
203         float value = 0.0f;
204
205         for (int yi = miny; yi < maxy; yi++) {
206                 const float dy = yi - y;
207                 offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4;
208                 for (int xi = minx; xi < maxx; xi++) {
209                         const float dx = xi - x;
210                         const float dis = dx * dx + dy * dy;
211                         if (dis <= mindist) {
212                                 value = max(buffer[offset], value);
213                         }
214                         offset += 4;
215                 }
216         }
217         color[0] = value;
218 }
219
220 void DilateDistanceOperation::deinitExecution()
221 {
222         this->m_inputProgram = NULL;
223 }
224
225 bool DilateDistanceOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
226 {
227         rcti newInput;
228
229         newInput.xmax = input->xmax + this->m_scope;
230         newInput.xmin = input->xmin - this->m_scope;
231         newInput.ymax = input->ymax + this->m_scope;
232         newInput.ymin = input->ymin - this->m_scope;
233
234         return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
235 }
236
237 void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device,
238                                             MemoryBuffer *outputMemoryBuffer, cl_mem clOutputBuffer,
239                                             MemoryBuffer **inputMemoryBuffers, list<cl_mem> *clMemToCleanUp,
240                                             list<cl_kernel> *clKernelsToCleanUp)
241 {
242         cl_kernel dilateKernel = device->COM_clCreateKernel("dilateKernel", NULL);
243
244         cl_int distanceSquared = this->m_distance * this->m_distance;
245         cl_int scope = this->m_scope;
246         
247         device->COM_clAttachMemoryBufferToKernelParameter(dilateKernel, 0,  2, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram);
248         device->COM_clAttachOutputMemoryBufferToKernelParameter(dilateKernel, 1, clOutputBuffer);
249         device->COM_clAttachMemoryBufferOffsetToKernelParameter(dilateKernel, 3, outputMemoryBuffer);
250         clSetKernelArg(dilateKernel, 4, sizeof(cl_int), &scope);
251         clSetKernelArg(dilateKernel, 5, sizeof(cl_int), &distanceSquared);
252         device->COM_clAttachSizeToKernelParameter(dilateKernel, 6, this);
253         device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this);
254 }
255
256 // Erode Distance
257 ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() 
258 {
259         /* pass */
260 }
261
262 void ErodeDistanceOperation::executePixel(float *color, int x, int y, void *data)
263 {
264         const float distance = this->m_distance;
265         const float mindist = distance * distance;
266
267         MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
268         float *buffer = inputBuffer->getBuffer();
269         rcti *rect = inputBuffer->getRect();
270         const int minx = max(x - this->m_scope, rect->xmin);
271         const int miny = max(y - this->m_scope, rect->ymin);
272         const int maxx = min(x + this->m_scope, rect->xmax);
273         const int maxy = min(y + this->m_scope, rect->ymax);
274         const int bufferWidth = rect->xmax - rect->xmin;
275         int offset;
276         
277         float value = 1.0f;
278
279         for (int yi = miny; yi < maxy; yi++) {
280                 const float dy = yi - y;
281                 offset = ((yi - rect->ymin) * bufferWidth + (minx - rect->xmin)) * 4;
282                 for (int xi = minx; xi < maxx; xi++) {
283                         const float dx = xi - x;
284                         const float dis = dx * dx + dy * dy;
285                         if (dis <= mindist) {
286                                 value = min(buffer[offset], value);
287                         }
288                         offset += 4;
289                 }
290         }
291         color[0] = value;
292 }
293
294 void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device,
295                                            MemoryBuffer *outputMemoryBuffer, cl_mem clOutputBuffer,
296                                            MemoryBuffer **inputMemoryBuffers, list<cl_mem> *clMemToCleanUp,
297                                            list<cl_kernel> *clKernelsToCleanUp)
298 {
299         cl_kernel erodeKernel = device->COM_clCreateKernel("erodeKernel", NULL);
300
301         cl_int distanceSquared = this->m_distance * this->m_distance;
302         cl_int scope = this->m_scope;
303         
304         device->COM_clAttachMemoryBufferToKernelParameter(erodeKernel, 0,  2, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram);
305         device->COM_clAttachOutputMemoryBufferToKernelParameter(erodeKernel, 1, clOutputBuffer);
306         device->COM_clAttachMemoryBufferOffsetToKernelParameter(erodeKernel, 3, outputMemoryBuffer);
307         clSetKernelArg(erodeKernel, 4, sizeof(cl_int), &scope);
308         clSetKernelArg(erodeKernel, 5, sizeof(cl_int), &distanceSquared);
309         device->COM_clAttachSizeToKernelParameter(erodeKernel, 6, this);
310         device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this);
311 }
312
313 // Dilate step
314 DilateStepOperation::DilateStepOperation() : NodeOperation()
315 {
316         this->addInputSocket(COM_DT_VALUE);
317         this->addOutputSocket(COM_DT_VALUE);
318         this->setComplex(true);
319         this->m_inputProgram = NULL;
320 }
321 void DilateStepOperation::initExecution()
322 {
323         this->m_inputProgram = this->getInputSocketReader(0);
324         this->m_cached_buffer = NULL;
325         this->initMutex();
326 }
327
328 void *DilateStepOperation::initializeTileData(rcti *rect)
329 {
330         if (this->m_cached_buffer != NULL) {
331                 return this->m_cached_buffer;
332         }
333         lockMutex();
334         if (this->m_cached_buffer == NULL) {
335                 MemoryBuffer *buffer = (MemoryBuffer *)this->m_inputProgram->initializeTileData(NULL);
336                 float *rectf = buffer->convertToValueBuffer();
337                 int x, y, i;
338                 float *p;
339                 int bwidth = buffer->getWidth();
340                 int bheight = buffer->getHeight();
341                 for (i = 0; i < this->m_iterations; i++) {
342                         for (y = 0; y < bheight; y++) {
343                                 for (x = 0; x < bwidth - 1; x++) {
344                                         p = rectf + (bwidth * y + x);
345                                         *p = MAX2(*p, *(p + 1));
346                                 }
347                         }
348                 
349                         for (y = 0; y < bheight; y++) {
350                                 for (x = bwidth - 1; x >= 1; x--) {
351                                         p = rectf + (bwidth * y + x);
352                                         *p = MAX2(*p, *(p - 1));
353                                 }
354                         }
355                 
356                         for (x = 0; x < bwidth; x++) {
357                                 for (y = 0; y < bheight - 1; y++) {
358                                         p = rectf + (bwidth * y + x);
359                                         *p = MAX2(*p, *(p + bwidth));
360                                 }
361                         }
362                 
363                         for (x = 0; x < bwidth; x++) {
364                                 for (y = bheight - 1; y >= 1; y--) {
365                                         p = rectf + (bwidth * y + x);
366                                         *p = MAX2(*p, *(p - bwidth));
367                                 }
368                         }
369                 }
370                 this->m_cached_buffer = rectf;
371         }
372         unlockMutex();
373         return this->m_cached_buffer;
374 }
375
376
377 void DilateStepOperation::executePixel(float *color, int x, int y, void *data)
378 {
379         color[0] = this->m_cached_buffer[y * this->getWidth() + x];
380 }
381
382 void DilateStepOperation::deinitExecution()
383 {
384         this->m_inputProgram = NULL;
385         this->deinitMutex();
386         if (this->m_cached_buffer) {
387                 delete [] this->m_cached_buffer;
388                 this->m_cached_buffer = NULL;
389         }
390 }
391
392 bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
393 {
394         if (this->m_cached_buffer) {
395                 return false;
396         }
397         else {
398                 rcti newInput;
399         
400                 newInput.xmax = getWidth();
401                 newInput.xmin = 0;
402                 newInput.ymax = getHeight();
403                 newInput.ymin = 0;
404         
405                 return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
406         }
407 }
408
409 // Erode step
410 ErodeStepOperation::ErodeStepOperation() : DilateStepOperation()
411 {
412         /* pass */
413 }
414
415 void *ErodeStepOperation::initializeTileData(rcti *rect)
416 {
417         if (this->m_cached_buffer != NULL) {
418                 return this->m_cached_buffer;
419         }
420         lockMutex();
421         if (this->m_cached_buffer == NULL) {
422                 MemoryBuffer *buffer = (MemoryBuffer *)this->m_inputProgram->initializeTileData(NULL);
423                 float *rectf = buffer->convertToValueBuffer();
424                 int x, y, i;
425                 float *p;
426                 int bwidth = buffer->getWidth();
427                 int bheight = buffer->getHeight();
428                 for (i = 0; i < this->m_iterations; i++) {
429                         for (y = 0; y < bheight; y++) {
430                                 for (x = 0; x < bwidth - 1; x++) {
431                                         p = rectf + (bwidth * y + x);
432                                         *p = MIN2(*p, *(p + 1));
433                                 }
434                         }
435                 
436                         for (y = 0; y < bheight; y++) {
437                                 for (x = bwidth - 1; x >= 1; x--) {
438                                         p = rectf + (bwidth * y + x);
439                                         *p = MIN2(*p, *(p - 1));
440                                 }
441                         }
442                 
443                         for (x = 0; x < bwidth; x++) {
444                                 for (y = 0; y < bheight - 1; y++) {
445                                         p = rectf + (bwidth * y + x);
446                                         *p = MIN2(*p, *(p + bwidth));
447                                 }
448                         }
449                 
450                         for (x = 0; x < bwidth; x++) {
451                                 for (y = bheight - 1; y >= 1; y--) {
452                                         p = rectf + (bwidth * y + x);
453                                         *p = MIN2(*p, *(p - bwidth));
454                                 }
455                         }
456                 }
457                 this->m_cached_buffer = rectf;
458         }
459         unlockMutex();
460         return this->m_cached_buffer;
461 }