7bc49fa695c3876c7c3c6b12bb459326e2620304
[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 DilateErodeDistanceOperation::DilateErodeDistanceOperation(): 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 DilateErodeDistanceOperation::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 *DilateErodeDistanceOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
57 {
58         void *buffer = inputProgram->initializeTileData(NULL, memoryBuffers);
59         return buffer;
60 }
61
62 void DilateErodeDistanceOperation::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 DilateErodeDistanceOperation::deinitExecution()
146 {
147         this->inputProgram = NULL;
148 }
149
150 bool DilateErodeDistanceOperation::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 }
171 void DilateDistanceOperation::initExecution()
172 {
173         this->inputProgram = this->getInputSocketReader(0);
174         this->scope = distance;
175         if (scope < 3) {
176                 scope = 3;
177         }
178 }
179
180 void *DilateDistanceOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
181 {
182         void *buffer = inputProgram->initializeTileData(NULL, memoryBuffers);
183         return buffer;
184 }
185
186 void DilateDistanceOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
187 {
188         const float distance = this->distance;
189         float mindist = distance * distance;
190
191         MemoryBuffer *inputBuffer = (MemoryBuffer*)data;
192         float *buffer = inputBuffer->getBuffer();
193         rcti *rect = inputBuffer->getRect();
194         const int minx = max(x - scope, rect->xmin);
195         const int miny = max(y - scope, rect->ymin);
196         const int maxx = min(x + scope, rect->xmax);
197         const int maxy = min(y + scope, rect->ymax);
198         const int bufferWidth = rect->xmax-rect->xmin;
199         int offset;
200         
201         float value = 0.0f;
202
203         for (int yi = miny ; yi<maxy;yi++) {
204                 offset = ((yi-rect->ymin)*bufferWidth+(minx-rect->xmin))*4;
205                 for (int xi = minx ; xi<maxx;xi++) {
206                         const float dx = xi-x;
207                         const float dy = yi-y;
208                         const float dis = dx*dx+dy*dy;
209                         if (dis <= mindist) {
210                                 value = max(buffer[offset], value);
211                         }
212                         offset +=4;
213                 }
214         }
215         color[0] = value;
216 }
217
218 void DilateDistanceOperation::deinitExecution()
219 {
220         this->inputProgram = NULL;
221 }
222
223 bool DilateDistanceOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
224 {
225         rcti newInput;
226
227         newInput.xmax = input->xmax + scope;
228         newInput.xmin = input->xmin - scope;
229         newInput.ymax = input->ymax + scope;
230         newInput.ymin = input->ymin - scope;
231
232         return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
233 }
234 // Erode Distance
235 ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() 
236 {
237 }
238
239 void ErodeDistanceOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
240 {
241         const float distance = this->distance;
242         float mindist = distance * distance;
243
244         MemoryBuffer *inputBuffer = (MemoryBuffer*)data;
245         float *buffer = inputBuffer->getBuffer();
246         rcti *rect = inputBuffer->getRect();
247         const int minx = max(x - scope, rect->xmin);
248         const int miny = max(y - scope, rect->ymin);
249         const int maxx = min(x + scope, rect->xmax);
250         const int maxy = min(y + scope, rect->ymax);
251         const int bufferWidth = rect->xmax-rect->xmin;
252         int offset;
253         
254         float value = 1.0f;
255
256         for (int yi = miny ; yi<maxy;yi++) {
257                 offset = ((yi-rect->ymin)*bufferWidth+(minx-rect->xmin))*4;
258                 for (int xi = minx ; xi<maxx;xi++) {
259                         const float dx = xi-x;
260                         const float dy = yi-y;
261                         const float dis = dx*dx+dy*dy;
262                         if (dis <= mindist) {
263                                 value = min(buffer[offset], value);
264                         }
265                         offset +=4;
266                 }
267         }
268         color[0] = value;
269 }
270
271 // Dilate step
272 DilateStepOperation::DilateStepOperation(): NodeOperation()
273 {
274         this->addInputSocket(COM_DT_VALUE);
275         this->addOutputSocket(COM_DT_VALUE);
276         this->setComplex(true);
277         this->inputProgram = NULL;
278 }
279 void DilateStepOperation::initExecution()
280 {
281         this->inputProgram = this->getInputSocketReader(0);
282         this->cached_buffer = NULL;
283         this->initMutex();
284 }
285
286 void *DilateStepOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
287 {
288         if (this->cached_buffer != NULL) {
289                 return this->cached_buffer;
290         }
291         BLI_mutex_lock(getMutex());
292         if (this->cached_buffer == NULL) {
293                 MemoryBuffer *buffer = (MemoryBuffer*)inputProgram->initializeTileData(NULL, memoryBuffers);
294                 float *rectf = buffer->convertToValueBuffer();
295                 int x, y, i;
296                 float *p;
297                 int bwidth = buffer->getWidth();
298                 int bheight = buffer->getHeight();
299                 for (i = 0 ; i < this->iterations ; i ++) {
300                         for (y=0; y < bheight; y++) {
301                                 for (x=0; x < bwidth-1; x++) {
302                                         p = rectf + (bwidth*y + x);
303                                         *p = MAX2(*p, *(p + 1));
304                                 }
305                         }
306                 
307                         for (y=0; y < bheight; y++) {
308                                 for (x=bwidth-1; x >= 1; x--) {
309                                         p = rectf + (bwidth*y + x);
310                                         *p = MAX2(*p, *(p - 1));
311                                 }
312                         }
313                 
314                         for (x=0; x < bwidth; x++) {
315                                 for (y=0; y < bheight-1; y++) {
316                                         p = rectf + (bwidth*y + x);
317                                         *p = MAX2(*p, *(p + bwidth));
318                                 }
319                         }
320                 
321                         for (x=0; x < bwidth; x++) {
322                                 for (y=bheight-1; y >= 1; y--) {
323                                         p = rectf + (bwidth*y + x);
324                                         *p = MAX2(*p, *(p - bwidth));
325                                 }
326                         }
327                 }
328                 this->cached_buffer = rectf;
329         }
330         BLI_mutex_unlock(getMutex());
331         return this->cached_buffer;
332 }
333
334
335 void DilateStepOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
336 {
337         color[0] = this->cached_buffer[y*this->getWidth()+x];
338 }
339
340 void DilateStepOperation::deinitExecution()
341 {
342         this->inputProgram = NULL;
343         this->deinitMutex();
344         if (this->cached_buffer) {
345                 delete cached_buffer;
346                 this->cached_buffer = NULL;
347         }
348 }
349
350 bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
351 {
352         if (this->cached_buffer) {
353                 return false;
354         }
355         else {
356                 rcti newInput;
357         
358                 newInput.xmax = getWidth();
359                 newInput.xmin = 0;
360                 newInput.ymax = getHeight();
361                 newInput.ymin = 0;
362         
363                 return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
364         }
365 }
366
367 // Erode step
368 ErodeStepOperation::ErodeStepOperation(): DilateStepOperation()
369 {
370 }
371
372 void *ErodeStepOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
373 {
374         if (this->cached_buffer != NULL) {
375                 return this->cached_buffer;
376         }
377         BLI_mutex_lock(getMutex());
378         if (this->cached_buffer == NULL) {
379                 MemoryBuffer *buffer = (MemoryBuffer*)inputProgram->initializeTileData(NULL, memoryBuffers);
380                 float *rectf = buffer->convertToValueBuffer();
381                 int x, y, i;
382                 float *p;
383                 int bwidth = buffer->getWidth();
384                 int bheight = buffer->getHeight();
385                 for (i = 0 ; i < this->iterations ; i ++) {
386                         for (y=0; y < bheight; y++) {
387                                 for (x=0; x < bwidth-1; x++) {
388                                         p = rectf + (bwidth*y + x);
389                                         *p = MIN2(*p, *(p + 1));
390                                 }
391                         }
392                 
393                         for (y=0; y < bheight; y++) {
394                                 for (x=bwidth-1; x >= 1; x--) {
395                                         p = rectf + (bwidth*y + x);
396                                         *p = MIN2(*p, *(p - 1));
397                                 }
398                         }
399                 
400                         for (x=0; x < bwidth; x++) {
401                                 for (y=0; y < bheight-1; y++) {
402                                         p = rectf + (bwidth*y + x);
403                                         *p = MIN2(*p, *(p + bwidth));
404                                 }
405                         }
406                 
407                         for (x=0; x < bwidth; x++) {
408                                 for (y=bheight-1; y >= 1; y--) {
409                                         p = rectf + (bwidth*y + x);
410                                         *p = MIN2(*p, *(p - bwidth));
411                                 }
412                         }
413                 }
414                 this->cached_buffer = rectf;
415         }
416         BLI_mutex_unlock(getMutex());
417         return this->cached_buffer;
418 }