Added switch in dilate/erode between old (Step) and new (Distance)
[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 DilateErodeDistanceOperation::DilateErodeDistanceOperation(): NodeOperation()
27 {
28         this->addInputSocket(COM_DT_VALUE);
29         this->addOutputSocket(COM_DT_VALUE);
30         this->setComplex(true);
31         this->inputProgram = NULL;
32         this->inset = 0.0f;
33         this->_switch = 0.5f;
34         this->distance = 0.0f;
35 }
36 void DilateErodeDistanceOperation::initExecution()
37 {
38         this->inputProgram = this->getInputSocketReader(0);
39         if (this->distance < 0.0f) {
40                 this->scope = - this->distance + this->inset;
41         }
42         else {
43                 if (this->inset*2 > this->distance) {
44                         this->scope = max(this->inset*2 - this->distance, this->distance);
45                 }
46                 else {
47                         this->scope = distance;
48                 }
49         }
50         if (scope < 3) {
51                 scope = 3;
52         }
53 }
54
55 void *DilateErodeDistanceOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
56 {
57         void *buffer = inputProgram->initializeTileData(NULL, memoryBuffers);
58         return buffer;
59 }
60
61 void DilateErodeDistanceOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
62 {
63         float inputValue[4];
64         const float sw = this->_switch;
65         const float distance = this->distance;
66         float pixelvalue;
67         const float rd = scope * scope;
68         const float inset = this->inset;
69         float mindist = rd*2;
70
71         MemoryBuffer *inputBuffer = (MemoryBuffer*)data;
72         float *buffer = inputBuffer->getBuffer();
73         rcti *rect = inputBuffer->getRect();
74         const int minx = max(x - scope, rect->xmin);
75         const int miny = max(y - scope, rect->ymin);
76         const int maxx = min(x + scope, rect->xmax);
77         const int maxy = min(y + scope, rect->ymax);
78         const int bufferWidth = rect->xmax-rect->xmin;
79         int offset;
80
81         this->inputProgram->read(inputValue, x, y, inputBuffers, NULL);
82         if (inputValue[0]>sw) {
83                 for (int yi = miny ; yi<maxy;yi++) {
84                         offset = ((yi-rect->ymin)*bufferWidth+(minx-rect->xmin))*4;
85                         for (int xi = minx ; xi<maxx;xi++) {
86                                 if (buffer[offset]<sw) {
87                                         const float dx = xi-x;
88                                         const float dy = yi-y;
89                                         const float dis = dx*dx+dy*dy;
90                                         mindist = min(mindist, dis);
91                                 }
92                                 offset +=4;
93                         }
94                 }
95                 pixelvalue = -sqrtf(mindist);
96         }
97         else {
98                 for (int yi = miny ; yi<maxy;yi++) {
99                         offset = ((yi-rect->ymin)*bufferWidth+(minx-rect->xmin))*4;
100                         for (int xi = minx ; xi<maxx;xi++) {
101                                 if (buffer[offset]>sw) {
102                                         const float dx = xi-x;
103                                         const float dy = yi-y;
104                                         const float dis = dx*dx+dy*dy;
105                                         mindist = min(mindist, dis);
106                                 }
107                                 offset +=4;
108
109                         }
110                 }
111                 pixelvalue = sqrtf(mindist);
112         }
113
114         if (distance > 0.0f) {
115                 const float delta = distance - pixelvalue;
116                 if (delta >= 0.0f) {
117                         if (delta >= inset) {
118                                 color[0] = 1.0f;
119                         }
120                         else {
121                                 color[0] = delta/inset;
122                         }
123                 }
124                 else {
125                         color[0] = 0.0f;
126                 }
127         }
128         else {
129                 const float delta = -distance+pixelvalue;
130                 if (delta < 0.0f) {
131                         if (delta < -inset) {
132                                 color[0] = 1.0f;
133                         }
134                         else {
135                                 color[0] = (-delta)/inset;
136                         }
137                 }
138                 else {
139                         color[0] = 0.0f;
140                 }
141         }
142 }
143
144 void DilateErodeDistanceOperation::deinitExecution()
145 {
146         this->inputProgram = NULL;
147 }
148
149 bool DilateErodeDistanceOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
150 {
151         rcti newInput;
152
153         newInput.xmax = input->xmax + scope;
154         newInput.xmin = input->xmin - scope;
155         newInput.ymax = input->ymax + scope;
156         newInput.ymin = input->ymin - scope;
157
158         return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
159 }
160
161 // Dilate step
162 DilateStepOperation::DilateStepOperation(): NodeOperation()
163 {
164         this->addInputSocket(COM_DT_VALUE);
165         this->addOutputSocket(COM_DT_VALUE);
166         this->setComplex(true);
167         this->inputProgram = NULL;
168 }
169 void DilateStepOperation::initExecution()
170 {
171         this->inputProgram = this->getInputSocketReader(0);
172         this->cached_buffer = NULL;
173         this->initMutex();
174 }
175
176 void *DilateStepOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
177 {
178         if (this->cached_buffer != NULL) {
179                 return this->cached_buffer;
180         }
181         BLI_mutex_lock(getMutex());
182         if (this->cached_buffer == NULL) {
183                 MemoryBuffer *buffer = (MemoryBuffer*)inputProgram->initializeTileData(NULL, memoryBuffers);
184                 float *rectf = buffer->convertToValueBuffer();
185                 int x, y, i;
186                 float *p;
187                 int bwidth = buffer->getWidth();
188                 int bheight = buffer->getHeight();
189                 for (i = 0 ; i < this->iterations ; i ++) {
190                         for (y=0; y < bheight; y++) {
191                                 for (x=0; x < bwidth-1; x++) {
192                                         p = rectf + (bwidth*y + x);
193                                         *p = MAX2(*p, *(p + 1));
194                                 }
195                         }
196                 
197                         for (y=0; y < bheight; y++) {
198                                 for (x=bwidth-1; x >= 1; x--) {
199                                         p = rectf + (bwidth*y + x);
200                                         *p = MAX2(*p, *(p - 1));
201                                 }
202                         }
203                 
204                         for (x=0; x < bwidth; x++) {
205                                 for (y=0; y < bheight-1; y++) {
206                                         p = rectf + (bwidth*y + x);
207                                         *p = MAX2(*p, *(p + bwidth));
208                                 }
209                         }
210                 
211                         for (x=0; x < bwidth; x++) {
212                                 for (y=bheight-1; y >= 1; y--) {
213                                         p = rectf + (bwidth*y + x);
214                                         *p = MAX2(*p, *(p - bwidth));
215                                 }
216                         }
217                 }
218                 this->cached_buffer = rectf;
219         }
220         BLI_mutex_unlock(getMutex());
221         return this->cached_buffer;
222 }
223
224
225 void DilateStepOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
226 {
227         color[0] = this->cached_buffer[y*this->getWidth()+x];
228 }
229
230 void DilateStepOperation::deinitExecution()
231 {
232         this->inputProgram = NULL;
233         this->deinitMutex();
234         if (this->cached_buffer) {
235                 delete cached_buffer;
236                 this->cached_buffer = NULL;
237         }
238 }
239
240 bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
241 {
242         if (this->cached_buffer) {
243                 return false;
244         } else {
245                 rcti newInput;
246         
247                 newInput.xmax = getWidth();
248                 newInput.xmin = 0;
249                 newInput.ymax = getHeight();
250                 newInput.ymin = 0;
251         
252                 return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
253         }
254 }
255
256 // Erode step
257 ErodeStepOperation::ErodeStepOperation(): DilateStepOperation()
258 {
259 }
260
261 void *ErodeStepOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
262 {
263         if (this->cached_buffer != NULL) {
264                 return this->cached_buffer;
265         }
266         BLI_mutex_lock(getMutex());
267         if (this->cached_buffer == NULL) {
268                 MemoryBuffer *buffer = (MemoryBuffer*)inputProgram->initializeTileData(NULL, memoryBuffers);
269                 float *rectf = buffer->convertToValueBuffer();
270                 int x, y, i;
271                 float *p;
272                 int bwidth = buffer->getWidth();
273                 int bheight = buffer->getHeight();
274                 for (i = 0 ; i < this->iterations ; i ++) {
275                         for (y=0; y < bheight; y++) {
276                                 for (x=0; x < bwidth-1; x++) {
277                                         p = rectf + (bwidth*y + x);
278                                         *p = MIN2(*p, *(p + 1));
279                                 }
280                         }
281                 
282                         for (y=0; y < bheight; y++) {
283                                 for (x=bwidth-1; x >= 1; x--) {
284                                         p = rectf + (bwidth*y + x);
285                                         *p = MIN2(*p, *(p - 1));
286                                 }
287                         }
288                 
289                         for (x=0; x < bwidth; x++) {
290                                 for (y=0; y < bheight-1; y++) {
291                                         p = rectf + (bwidth*y + x);
292                                         *p = MIN2(*p, *(p + bwidth));
293                                 }
294                         }
295                 
296                         for (x=0; x < bwidth; x++) {
297                                 for (y=bheight-1; y >= 1; y--) {
298                                         p = rectf + (bwidth*y + x);
299                                         *p = MIN2(*p, *(p - bwidth));
300                                 }
301                         }
302                 }
303                 this->cached_buffer = rectf;
304         }
305         BLI_mutex_unlock(getMutex());
306         return this->cached_buffer;
307 }