* optimized threading
[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
26 // DilateErode Distance Threshold
27 DilateErodeThresholdOperation::DilateErodeThresholdOperation(): NodeOperation()
28 {
29         this->addInputSocket(COM_DT_VALUE);
30         this->addOutputSocket(COM_DT_VALUE);
31         this->setComplex(true);
32         this->inputProgram = NULL;
33         this->inset = 0.0f;
34         this->_switch = 0.5f;
35         this->distance = 0.0f;
36 }
37 void DilateErodeThresholdOperation::initExecution()
38 {
39         this->inputProgram = this->getInputSocketReader(0);
40         if (this->distance < 0.0f) {
41                 this->scope = - this->distance + this->inset;
42         }
43         else {
44                 if (this->inset*2 > this->distance) {
45                         this->scope = max(this->inset*2 - this->distance, this->distance);
46                 }
47                 else {
48                         this->scope = distance;
49                 }
50         }
51         if (scope < 3) {
52                 scope = 3;
53         }
54 }
55
56 void *DilateErodeThresholdOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
57 {
58         void *buffer = inputProgram->initializeTileData(NULL, memoryBuffers);
59         return buffer;
60 }
61
62 void DilateErodeThresholdOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
63 {
64         float inputValue[4];
65         const float sw = this->_switch;
66         const float distance = this->distance;
67         float pixelvalue;
68         const float rd = scope * scope;
69         const float inset = this->inset;
70         float mindist = rd*2;
71
72         MemoryBuffer *inputBuffer = (MemoryBuffer*)data;
73         float *buffer = inputBuffer->getBuffer();
74         rcti *rect = inputBuffer->getRect();
75         const int minx = max(x - scope, rect->xmin);
76         const int miny = max(y - scope, rect->ymin);
77         const int maxx = min(x + scope, rect->xmax);
78         const int maxy = min(y + scope, rect->ymax);
79         const int bufferWidth = rect->xmax-rect->xmin;
80         int offset;
81
82         this->inputProgram->read(inputValue, x, y, inputBuffers, NULL);
83         if (inputValue[0]>sw) {
84                 for (int yi = miny ; yi<maxy;yi++) {
85                         offset = ((yi-rect->ymin)*bufferWidth+(minx-rect->xmin))*4;
86                         for (int xi = minx ; xi<maxx;xi++) {
87                                 if (buffer[offset]<sw) {
88                                         const float dx = xi-x;
89                                         const float dy = yi-y;
90                                         const float dis = dx*dx+dy*dy;
91                                         mindist = min(mindist, dis);
92                                 }
93                                 offset +=4;
94                         }
95                 }
96                 pixelvalue = -sqrtf(mindist);
97         }
98         else {
99                 for (int yi = miny ; yi<maxy;yi++) {
100                         offset = ((yi-rect->ymin)*bufferWidth+(minx-rect->xmin))*4;
101                         for (int xi = minx ; xi<maxx;xi++) {
102                                 if (buffer[offset]>sw) {
103                                         const float dx = xi-x;
104                                         const float dy = yi-y;
105                                         const float dis = dx*dx+dy*dy;
106                                         mindist = min(mindist, dis);
107                                 }
108                                 offset +=4;
109
110                         }
111                 }
112                 pixelvalue = sqrtf(mindist);
113         }
114
115         if (distance > 0.0f) {
116                 const float delta = distance - pixelvalue;
117                 if (delta >= 0.0f) {
118                         if (delta >= inset) {
119                                 color[0] = 1.0f;
120                         }
121                         else {
122                                 color[0] = delta/inset;
123                         }
124                 }
125                 else {
126                         color[0] = 0.0f;
127                 }
128         }
129         else {
130                 const float delta = -distance+pixelvalue;
131                 if (delta < 0.0f) {
132                         if (delta < -inset) {
133                                 color[0] = 1.0f;
134                         }
135                         else {
136                                 color[0] = (-delta)/inset;
137                         }
138                 }
139                 else {
140                         color[0] = 0.0f;
141                 }
142         }
143 }
144
145 void DilateErodeThresholdOperation::deinitExecution()
146 {
147         this->inputProgram = NULL;
148 }
149
150 bool DilateErodeThresholdOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
151 {
152         rcti newInput;
153
154         newInput.xmax = input->xmax + scope;
155         newInput.xmin = input->xmin - scope;
156         newInput.ymax = input->ymax + scope;
157         newInput.ymin = input->ymin - scope;
158
159         return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
160 }
161
162 // Dilate Distance
163 DilateDistanceOperation::DilateDistanceOperation(): NodeOperation()
164 {
165         this->addInputSocket(COM_DT_VALUE);
166         this->addOutputSocket(COM_DT_VALUE);
167         this->setComplex(true);
168         this->inputProgram = NULL;
169         this->distance = 0.0f;
170         this->setOpenCL(true);
171 }
172 void DilateDistanceOperation::initExecution()
173 {
174         this->inputProgram = this->getInputSocketReader(0);
175         this->scope = distance;
176         if (scope < 3) {
177                 scope = 3;
178         }
179 }
180
181 void *DilateDistanceOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
182 {
183         void *buffer = inputProgram->initializeTileData(NULL, memoryBuffers);
184         return buffer;
185 }
186
187 void DilateDistanceOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
188 {
189         const float distance = this->distance;
190         float mindist = distance * distance;
191
192         MemoryBuffer *inputBuffer = (MemoryBuffer*)data;
193         float *buffer = inputBuffer->getBuffer();
194         rcti *rect = inputBuffer->getRect();
195         const int minx = max(x - scope, rect->xmin);
196         const int miny = max(y - scope, rect->ymin);
197         const int maxx = min(x + scope, rect->xmax);
198         const int maxy = min(y + scope, rect->ymax);
199         const int bufferWidth = rect->xmax-rect->xmin;
200         int offset;
201         
202         float value = 0.0f;
203
204         for (int yi = miny ; yi<maxy;yi++) {
205                 offset = ((yi-rect->ymin)*bufferWidth+(minx-rect->xmin))*4;
206                 for (int xi = minx ; xi<maxx;xi++) {
207                         const float dx = xi-x;
208                         const float dy = yi-y;
209                         const float dis = dx*dx+dy*dy;
210                         if (dis <= mindist) {
211                                 value = max(buffer[offset], value);
212                         }
213                         offset +=4;
214                 }
215         }
216         color[0] = value;
217 }
218
219 void DilateDistanceOperation::deinitExecution()
220 {
221         this->inputProgram = NULL;
222 }
223
224 bool DilateDistanceOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
225 {
226         rcti newInput;
227
228         newInput.xmax = input->xmax + scope;
229         newInput.xmin = input->xmin - scope;
230         newInput.ymax = input->ymax + scope;
231         newInput.ymin = input->ymin - scope;
232
233         return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
234 }
235
236 static cl_kernel dilateKernel = 0;
237 void DilateDistanceOperation::executeOpenCL(cl_context context, cl_program program, cl_command_queue queue, 
238                                        MemoryBuffer *outputMemoryBuffer, cl_mem clOutputBuffer, 
239                                        MemoryBuffer **inputMemoryBuffers, list<cl_mem> *clMemToCleanUp, 
240                                        list<cl_kernel> *clKernelsToCleanUp) 
241 {
242         if (!dilateKernel) {
243                 dilateKernel = COM_clCreateKernel(program, "dilateKernel", NULL);
244         }
245         cl_int distanceSquared = this->distance*this->distance;
246         cl_int scope = this->scope;
247         
248         COM_clAttachMemoryBufferToKernelParameter(context, dilateKernel, 0,  2, clMemToCleanUp, inputMemoryBuffers, this->inputProgram);
249         COM_clAttachOutputMemoryBufferToKernelParameter(dilateKernel, 1, clOutputBuffer);
250         COM_clAttachMemoryBufferOffsetToKernelParameter(dilateKernel, 3, outputMemoryBuffer);
251         clSetKernelArg(dilateKernel, 4, sizeof(cl_int), &scope);
252         clSetKernelArg(dilateKernel, 5, sizeof(cl_int), &distanceSquared);
253         COM_clAttachSizeToKernelParameter(dilateKernel, 6);
254         COM_clEnqueueRange(queue, dilateKernel, outputMemoryBuffer, 7);
255 }
256
257 // Erode Distance
258 ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() 
259 {
260 }
261
262 void ErodeDistanceOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
263 {
264         const float distance = this->distance;
265         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 - scope, rect->xmin);
271         const int miny = max(y - scope, rect->ymin);
272         const int maxx = min(x + scope, rect->xmax);
273         const int maxy = min(y + 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                 offset = ((yi-rect->ymin)*bufferWidth+(minx-rect->xmin))*4;
281                 for (int xi = minx ; xi<maxx;xi++) {
282                         const float dx = xi-x;
283                         const float dy = yi-y;
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 static cl_kernel erodeKernel = 0;
295 void ErodeDistanceOperation::executeOpenCL(cl_context context, cl_program program, cl_command_queue queue, 
296                                        MemoryBuffer *outputMemoryBuffer, cl_mem clOutputBuffer, 
297                                        MemoryBuffer **inputMemoryBuffers, list<cl_mem> *clMemToCleanUp, 
298                                        list<cl_kernel> *clKernelsToCleanUp) 
299 {
300         if (!erodeKernel) {
301                 erodeKernel = COM_clCreateKernel(program, "erodeKernel", NULL);
302         }
303         cl_int distanceSquared = this->distance*this->distance;
304         cl_int scope = this->scope;
305         
306         COM_clAttachMemoryBufferToKernelParameter(context, erodeKernel, 0,  2, clMemToCleanUp, inputMemoryBuffers, this->inputProgram);
307         COM_clAttachOutputMemoryBufferToKernelParameter(erodeKernel, 1, clOutputBuffer);
308         COM_clAttachMemoryBufferOffsetToKernelParameter(erodeKernel, 3, outputMemoryBuffer);
309         clSetKernelArg(erodeKernel, 4, sizeof(cl_int), &scope);
310         clSetKernelArg(erodeKernel, 5, sizeof(cl_int), &distanceSquared);
311         COM_clAttachSizeToKernelParameter(erodeKernel, 6);
312         COM_clEnqueueRange(queue, erodeKernel, outputMemoryBuffer, 7);
313 }
314
315 // Dilate step
316 DilateStepOperation::DilateStepOperation(): NodeOperation()
317 {
318         this->addInputSocket(COM_DT_VALUE);
319         this->addOutputSocket(COM_DT_VALUE);
320         this->setComplex(true);
321         this->inputProgram = NULL;
322 }
323 void DilateStepOperation::initExecution()
324 {
325         this->inputProgram = this->getInputSocketReader(0);
326         this->cached_buffer = NULL;
327         this->initMutex();
328 }
329
330 void *DilateStepOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
331 {
332         if (this->cached_buffer != NULL) {
333                 return this->cached_buffer;
334         }
335         lockMutex();
336         if (this->cached_buffer == NULL) {
337                 MemoryBuffer *buffer = (MemoryBuffer*)inputProgram->initializeTileData(NULL, memoryBuffers);
338                 float *rectf = buffer->convertToValueBuffer();
339                 int x, y, i;
340                 float *p;
341                 int bwidth = buffer->getWidth();
342                 int bheight = buffer->getHeight();
343                 for (i = 0 ; i < this->iterations ; i ++) {
344                         for (y=0; y < bheight; y++) {
345                                 for (x=0; x < bwidth-1; x++) {
346                                         p = rectf + (bwidth*y + x);
347                                         *p = MAX2(*p, *(p + 1));
348                                 }
349                         }
350                 
351                         for (y=0; y < bheight; y++) {
352                                 for (x=bwidth-1; x >= 1; x--) {
353                                         p = rectf + (bwidth*y + x);
354                                         *p = MAX2(*p, *(p - 1));
355                                 }
356                         }
357                 
358                         for (x=0; x < bwidth; x++) {
359                                 for (y=0; y < bheight-1; y++) {
360                                         p = rectf + (bwidth*y + x);
361                                         *p = MAX2(*p, *(p + bwidth));
362                                 }
363                         }
364                 
365                         for (x=0; x < bwidth; x++) {
366                                 for (y=bheight-1; y >= 1; y--) {
367                                         p = rectf + (bwidth*y + x);
368                                         *p = MAX2(*p, *(p - bwidth));
369                                 }
370                         }
371                 }
372                 this->cached_buffer = rectf;
373         }
374         unlockMutex();
375         return this->cached_buffer;
376 }
377
378
379 void DilateStepOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
380 {
381         color[0] = this->cached_buffer[y*this->getWidth()+x];
382 }
383
384 void DilateStepOperation::deinitExecution()
385 {
386         this->inputProgram = NULL;
387         this->deinitMutex();
388         if (this->cached_buffer) {
389                 delete cached_buffer;
390                 this->cached_buffer = NULL;
391         }
392 }
393
394 bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
395 {
396         if (this->cached_buffer) {
397                 return false;
398         }
399         else {
400                 rcti newInput;
401         
402                 newInput.xmax = getWidth();
403                 newInput.xmin = 0;
404                 newInput.ymax = getHeight();
405                 newInput.ymin = 0;
406         
407                 return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
408         }
409 }
410
411 // Erode step
412 ErodeStepOperation::ErodeStepOperation(): DilateStepOperation()
413 {
414 }
415
416 void *ErodeStepOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
417 {
418         if (this->cached_buffer != NULL) {
419                 return this->cached_buffer;
420         }
421         lockMutex();
422         if (this->cached_buffer == NULL) {
423                 MemoryBuffer *buffer = (MemoryBuffer*)inputProgram->initializeTileData(NULL, memoryBuffers);
424                 float *rectf = buffer->convertToValueBuffer();
425                 int x, y, i;
426                 float *p;
427                 int bwidth = buffer->getWidth();
428                 int bheight = buffer->getHeight();
429                 for (i = 0 ; i < this->iterations ; i ++) {
430                         for (y=0; y < bheight; y++) {
431                                 for (x=0; x < bwidth-1; x++) {
432                                         p = rectf + (bwidth*y + x);
433                                         *p = MIN2(*p, *(p + 1));
434                                 }
435                         }
436                 
437                         for (y=0; y < bheight; y++) {
438                                 for (x=bwidth-1; x >= 1; x--) {
439                                         p = rectf + (bwidth*y + x);
440                                         *p = MIN2(*p, *(p - 1));
441                                 }
442                         }
443                 
444                         for (x=0; x < bwidth; x++) {
445                                 for (y=0; y < bheight-1; y++) {
446                                         p = rectf + (bwidth*y + x);
447                                         *p = MIN2(*p, *(p + bwidth));
448                                 }
449                         }
450                 
451                         for (x=0; x < bwidth; x++) {
452                                 for (y=bheight-1; y >= 1; y--) {
453                                         p = rectf + (bwidth*y + x);
454                                         *p = MIN2(*p, *(p - bwidth));
455                                 }
456                         }
457                 }
458                 this->cached_buffer = rectf;
459         }
460         unlockMutex();
461         return this->cached_buffer;
462 }