d9c0350eb09daa45c60351aa447ed9447fb1b825
[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 DilateErodeOperation::DilateErodeOperation(): 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 DilateErodeOperation::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 *DilateErodeOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
56 {
57         void *buffer = inputProgram->initializeTileData(NULL, memoryBuffers);
58         return buffer;
59 }
60
61 void DilateErodeOperation::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 DilateErodeOperation::deinitExecution()
145 {
146         this->inputProgram = NULL;
147 }
148
149 bool DilateErodeOperation::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 }