Small optimizations in compositor.
[blender-staging.git] / source / blender / compositor / operations / COM_GaussianBokehBlurOperation.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_GaussianBokehBlurOperation.h"
24 #include "BLI_math.h"
25
26 extern "C" {
27         #include "RE_pipeline.h"
28 }
29
30 GaussianBokehBlurOperation::GaussianBokehBlurOperation() : BlurBaseOperation(COM_DT_COLOR)
31 {
32         this->m_gausstab = NULL;
33 }
34
35 void *GaussianBokehBlurOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
36 {
37         lockMutex();
38         if (!this->m_sizeavailable) {
39                 updateGauss(memoryBuffers);
40         }
41         void *buffer = getInputOperation(0)->initializeTileData(NULL, memoryBuffers);
42         unlockMutex();
43         return buffer;
44 }
45
46 void GaussianBokehBlurOperation::initExecution()
47 {
48         BlurBaseOperation::initExecution();
49
50         initMutex();
51
52         if (this->m_sizeavailable) {
53                 updateGauss(NULL);
54         }
55 }
56
57 void GaussianBokehBlurOperation::updateGauss(MemoryBuffer **memoryBuffers)
58 {
59         if (this->m_gausstab == NULL) {
60                 float radxf;
61                 float radyf;
62                 int n;
63                 float *dgauss;
64                 float *ddgauss;
65                 float val;
66                 int j, i;
67                 const float width = this->getWidth();
68                 const float height = this->getHeight();
69                 if (!this->m_sizeavailable) {
70                         updateSize(memoryBuffers);
71                 }
72                 radxf = this->m_size * (float)this->m_data->sizex;
73                 if (radxf > width / 2.0f)
74                         radxf = width / 2.0f;
75                 else if (radxf < 1.0f)
76                         radxf = 1.0f;
77         
78                 /* vertical */
79                 radyf = this->m_size * (float)this->m_data->sizey;
80                 if (radyf > height / 2.0f)
81                         radyf = height / 2.0f;
82                 else if (radyf < 1.0f)
83                         radyf = 1.0f;
84         
85                 this->m_radx = ceil(radxf);
86                 this->m_rady = ceil(radyf);
87         
88                 n = (2 * this->m_radx + 1) * (2 * this->m_rady + 1);
89         
90                 /* create a full filter image */
91                 ddgauss = new float[n];
92                 dgauss = ddgauss;
93                 val = 0.0f;
94                 for (j = -this->m_rady; j <= this->m_rady; j++) {
95                         for (i = -this->m_radx; i <= this->m_radx; i++, dgauss++) {
96                                 float fj = (float)j / radyf;
97                                 float fi = (float)i / radxf;
98                                 float dist = sqrt(fj * fj + fi * fi);
99                                 *dgauss = RE_filter_value(this->m_data->filtertype, dist);
100                                 
101                                 val += *dgauss;
102                         }
103                 }
104                 if (val != 0.0f) {
105                         val = 1.0f / val;
106                         for (j = n - 1; j >= 0; j--)
107                                 ddgauss[j] *= val;
108                 }
109                 else ddgauss[4] = 1.0f;
110                 
111                 this->m_gausstab = ddgauss;
112         }
113 }
114
115 void GaussianBokehBlurOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
116 {
117         float tempColor[4];
118         tempColor[0] = 0;
119         tempColor[1] = 0;
120         tempColor[2] = 0;
121         tempColor[3] = 0;
122         float multiplier_accum = 0;
123         MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
124         float *buffer = inputBuffer->getBuffer();
125         int bufferwidth = inputBuffer->getWidth();
126         int bufferstartx = inputBuffer->getRect()->xmin;
127         int bufferstarty = inputBuffer->getRect()->ymin;
128
129         int miny = y - this->m_rady;
130         int maxy = y + this->m_rady;
131         int minx = x - this->m_radx;
132         int maxx = x + this->m_radx;
133         miny = max(miny, inputBuffer->getRect()->ymin);
134         minx = max(minx, inputBuffer->getRect()->xmin);
135         maxy = min(maxy, inputBuffer->getRect()->ymax);
136         maxx = min(maxx, inputBuffer->getRect()->xmax);
137
138         int index;
139         int step = QualityStepHelper::getStep();
140         int offsetadd = QualityStepHelper::getOffsetAdd();
141         for (int ny = miny; ny < maxy; ny += step) {
142                 index = ((ny - y) + this->m_rady) * (this->m_radx * 2 + 1) + (minx - x + this->m_radx);
143                 int bufferindex = ((minx - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth);
144                 for (int nx = minx; nx < maxx; nx += step) {
145                         const float multiplier = this->m_gausstab[index];
146                         madd_v4_v4fl(tempColor, &buffer[bufferindex], multiplier);
147                         multiplier_accum += multiplier;
148                         index += step;
149                         bufferindex += offsetadd;
150                 }
151         }
152
153         mul_v4_v4fl(color, tempColor, 1.0f / multiplier_accum);
154 }
155
156 void GaussianBokehBlurOperation::deinitExecution()
157 {
158         BlurBaseOperation::deinitExecution();
159         delete [] this->m_gausstab;
160         this->m_gausstab = NULL;
161
162         deinitMutex();
163 }
164
165 bool GaussianBokehBlurOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
166 {
167         rcti newInput;
168         rcti sizeInput;
169         sizeInput.xmin = 0;
170         sizeInput.ymin = 0;
171         sizeInput.xmax = 5;
172         sizeInput.ymax = 5;
173         NodeOperation *operation = this->getInputOperation(1);
174         
175         if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) {
176                 return true;
177         }
178         else {
179                 if (this->m_sizeavailable && this->m_gausstab != NULL) {
180                         newInput.xmin = 0;
181                         newInput.ymin = 0;
182                         newInput.xmax = this->getWidth();
183                         newInput.ymax = this->getHeight();
184                 }
185                 else {
186                         int addx = this->m_radx;
187                         int addy = this->m_rady;
188                         newInput.xmax = input->xmax + addx;
189                         newInput.xmin = input->xmin - addx;
190                         newInput.ymax = input->ymax + addy;
191                         newInput.ymin = input->ymin - addy;
192
193                 }
194                 return BlurBaseOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
195         }
196 }
197
198 // reference image
199 GaussianBokehBlurReferenceOperation::GaussianBokehBlurReferenceOperation() : NodeOperation()
200 {
201         this->addInputSocket(COM_DT_COLOR);
202         this->addInputSocket(COM_DT_VALUE);
203         this->addOutputSocket(COM_DT_COLOR);
204         this->setComplex(true);
205         this->m_gausstab = NULL;
206         this->m_inputImage = NULL;
207         this->m_inputSize = NULL;
208 }
209
210 void *GaussianBokehBlurReferenceOperation::initializeTileData(rcti *rect, MemoryBuffer **memoryBuffers)
211 {
212         void *buffer = getInputOperation(0)->initializeTileData(NULL, memoryBuffers);
213         return buffer;
214 }
215
216 void GaussianBokehBlurReferenceOperation::initExecution()
217 {
218         // setup gaustab
219         this->m_data->image_in_width = this->getWidth();
220         this->m_data->image_in_height = this->getHeight();
221         if (this->m_data->relative) {
222                 switch (this->m_data->aspect) {
223                         case CMP_NODE_BLUR_ASPECT_NONE:
224                                 this->m_data->sizex = (int)(this->m_data->percentx * 0.01f * this->m_data->image_in_width);
225                                 this->m_data->sizey = (int)(this->m_data->percenty * 0.01f * this->m_data->image_in_height);
226                                 break;
227                         case CMP_NODE_BLUR_ASPECT_Y:
228                                 this->m_data->sizex = (int)(this->m_data->percentx * 0.01f * this->m_data->image_in_width);
229                                 this->m_data->sizey = (int)(this->m_data->percenty * 0.01f * this->m_data->image_in_width);
230                                 break;
231                         case CMP_NODE_BLUR_ASPECT_X:
232                                 this->m_data->sizex = (int)(this->m_data->percentx * 0.01f * this->m_data->image_in_height);
233                                 this->m_data->sizey = (int)(this->m_data->percenty * 0.01f * this->m_data->image_in_height);
234                                 break;
235                 }
236         }
237         
238         updateGauss();
239         this->m_inputImage = this->getInputSocketReader(0);
240         this->m_inputSize = this->getInputSocketReader(1);
241 }
242
243 void GaussianBokehBlurReferenceOperation::updateGauss()
244 {
245         int n;
246         float *dgauss;
247         float *ddgauss;
248         int j, i;
249
250         n = (2 * radx + 1) * (2 * rady + 1);
251
252         /* create a full filter image */
253         ddgauss = new float[n];
254         dgauss = ddgauss;
255         for (j = -rady; j <= rady; j++) {
256                 for (i = -radx; i <= radx; i++, dgauss++) {
257                         float fj = (float)j / radyf;
258                         float fi = (float)i / radxf;
259                         float dist = sqrt(fj * fj + fi * fi);
260                         *dgauss = RE_filter_value(this->m_data->filtertype, dist);
261                 }
262         }
263         this->m_gausstab = ddgauss;
264 }
265
266 void GaussianBokehBlurReferenceOperation::executePixel(float *color, int x, int y, MemoryBuffer *inputBuffers[], void *data)
267 {
268         float tempColor[4];
269         float tempSize[4];
270         tempColor[0] = 0;
271         tempColor[1] = 0;
272         tempColor[2] = 0;
273         tempColor[3] = 0;
274         float multiplier_accum = 0;
275         MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
276         float *buffer = inputBuffer->getBuffer();
277         int bufferwidth = inputBuffer->getWidth();
278         int bufferstartx = inputBuffer->getRect()->xmin;
279         int bufferstarty = inputBuffer->getRect()->ymin;
280         this->m_inputSize->read(tempSize, x, y, inputBuffers, data);
281         float size = tempSize[0];
282         CLAMP(size, 0.0f, 1.0f);
283         float sizeX = ceil(this->m_data->sizex * size);
284         float sizeY = ceil(this->m_data->sizey * size);
285
286         if (sizeX <= 0.5f && sizeY <= 0.5f) {
287                 this->m_inputImage->read(color, x, y, inputBuffers, data);
288                 return;
289         }
290         
291         int miny = y - sizeY;
292         int maxy = y + sizeY;
293         int minx = x - sizeX;
294         int maxx = x + sizeX;
295         miny = max(miny, inputBuffer->getRect()->ymin);
296         minx = max(minx, inputBuffer->getRect()->xmin);
297         maxy = min(maxy, inputBuffer->getRect()->ymax);
298         maxx = min(maxx, inputBuffer->getRect()->xmax);
299
300         int step = QualityStepHelper::getStep();
301         int offsetadd = QualityStepHelper::getOffsetAdd();
302         for (int ny = miny; ny < maxy; ny += step) {
303                 int u = ny - y;
304                 float uf = ((u/sizeY)*radyf)+radyf;
305                 int indexu = uf * (radx*2+1);
306                 int bufferindex = ((minx - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth);
307                 for (int nx = minx; nx < maxx; nx += step) {
308                         int v = nx - x;
309                         float vf = ((v/sizeX)*radxf)+radxf;
310                         int index = indexu + vf;
311                         const float multiplier = this->m_gausstab[index];
312                         madd_v4_v4fl(tempColor, &buffer[bufferindex], multiplier);
313                         multiplier_accum += multiplier;
314                         index += step;
315                         bufferindex += offsetadd;
316                 }
317         }
318
319         mul_v4_v4fl(color, tempColor, 1.0f / multiplier_accum);
320 }
321
322 void GaussianBokehBlurReferenceOperation::deinitExecution()
323 {
324         delete [] this->m_gausstab;
325         this->m_gausstab = NULL;
326         this->m_inputImage = NULL;
327         this->m_inputSize = NULL;
328         
329 }
330
331 bool GaussianBokehBlurReferenceOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
332 {
333         rcti newInput;
334         NodeOperation *operation = this->getInputOperation(1);
335         
336         if (operation->determineDependingAreaOfInterest(input, readOperation, output)) {
337                 return true;
338         }
339         else {
340                 int addx = this->m_data->sizex+2;
341                 int addy = this->m_data->sizey+2;
342                 newInput.xmax = input->xmax + addx;
343                 newInput.xmin = input->xmin - addx;
344                 newInput.ymax = input->ymax + addy;
345                 newInput.ymin = input->ymin - addy;
346                 return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
347         }
348 }
349