Merged changes in the trunk up to revision 49090.
[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 #include "MEM_guardedalloc.h"
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)
36 {
37         lockMutex();
38         if (!this->m_sizeavailable) {
39                 updateGauss();
40         }
41         void *buffer = getInputOperation(0)->initializeTileData(NULL);
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();
54         }
55 }
56
57 void GaussianBokehBlurOperation::updateGauss()
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();
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, 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         const int addConst = (minx - x + this->m_radx);
142         const int mulConst = (this->m_radx * 2 + 1);
143         for (int ny = miny; ny < maxy; ny += step) {
144                 index = ((ny - y) + this->m_rady) * mulConst + addConst;
145                 int bufferindex = ((minx - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth);
146                 for (int nx = minx; nx < maxx; nx += step) {
147                         const float multiplier = this->m_gausstab[index];
148                         madd_v4_v4fl(tempColor, &buffer[bufferindex], multiplier);
149                         multiplier_accum += multiplier;
150                         index += step;
151                         bufferindex += offsetadd;
152                 }
153         }
154
155         mul_v4_v4fl(color, tempColor, 1.0f / multiplier_accum);
156 }
157
158 void GaussianBokehBlurOperation::deinitExecution()
159 {
160         BlurBaseOperation::deinitExecution();
161         delete [] this->m_gausstab;
162         this->m_gausstab = NULL;
163
164         deinitMutex();
165 }
166
167 bool GaussianBokehBlurOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
168 {
169         rcti newInput;
170         rcti sizeInput;
171         sizeInput.xmin = 0;
172         sizeInput.ymin = 0;
173         sizeInput.xmax = 5;
174         sizeInput.ymax = 5;
175         NodeOperation *operation = this->getInputOperation(1);
176         
177         if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) {
178                 return true;
179         }
180         else {
181                 if (this->m_sizeavailable && this->m_gausstab != NULL) {
182                         newInput.xmin = 0;
183                         newInput.ymin = 0;
184                         newInput.xmax = this->getWidth();
185                         newInput.ymax = this->getHeight();
186                 }
187                 else {
188                         int addx = this->m_radx;
189                         int addy = this->m_rady;
190                         newInput.xmax = input->xmax + addx;
191                         newInput.xmin = input->xmin - addx;
192                         newInput.ymax = input->ymax + addy;
193                         newInput.ymin = input->ymin - addy;
194
195                 }
196                 return BlurBaseOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
197         }
198 }
199
200 // reference image
201 GaussianBlurReferenceOperation::GaussianBlurReferenceOperation() : BlurBaseOperation(COM_DT_COLOR)
202 {
203         this->m_maintabs = NULL;
204 }
205
206 void *GaussianBlurReferenceOperation::initializeTileData(rcti *rect)
207 {
208         void *buffer = getInputOperation(0)->initializeTileData(NULL);
209         return buffer;
210 }
211
212 void GaussianBlurReferenceOperation::initExecution()
213 {
214         BlurBaseOperation::initExecution();
215         // setup gaustab
216         this->m_data->image_in_width = this->getWidth();
217         this->m_data->image_in_height = this->getHeight();
218         if (this->m_data->relative) {
219                 switch (this->m_data->aspect) {
220                         case CMP_NODE_BLUR_ASPECT_NONE:
221                                 this->m_data->sizex = (int)(this->m_data->percentx * 0.01f * this->m_data->image_in_width);
222                                 this->m_data->sizey = (int)(this->m_data->percenty * 0.01f * this->m_data->image_in_height);
223                                 break;
224                         case CMP_NODE_BLUR_ASPECT_Y:
225                                 this->m_data->sizex = (int)(this->m_data->percentx * 0.01f * this->m_data->image_in_width);
226                                 this->m_data->sizey = (int)(this->m_data->percenty * 0.01f * this->m_data->image_in_width);
227                                 break;
228                         case CMP_NODE_BLUR_ASPECT_X:
229                                 this->m_data->sizex = (int)(this->m_data->percentx * 0.01f * this->m_data->image_in_height);
230                                 this->m_data->sizey = (int)(this->m_data->percenty * 0.01f * this->m_data->image_in_height);
231                                 break;
232                 }
233         }
234         
235         
236         /* horizontal */
237         m_radx = (float)this->m_data->sizex;
238         int imgx = getWidth()/2;
239         if (m_radx > imgx)
240                 m_radx = imgx;
241         else if (m_radx < 1)
242                 m_radx = 1;
243         m_radxf = (float)m_radx;
244
245         /* vertical */
246         m_rady = (float)this->m_data->sizey;
247         int imgy = getHeight()/2;
248         if (m_rady > imgy)
249                 m_rady = imgy;
250         else if (m_rady < 1)
251                 m_rady = 1;
252         m_radyf = (float)m_rady;
253         updateGauss();
254 }
255
256 void GaussianBlurReferenceOperation::updateGauss()
257 {
258         int i;
259         int x = MAX2(m_radx, m_rady);
260         this->m_maintabs = (float**)MEM_mallocN(x * sizeof(float *), "gauss array");
261         for (i = 0; i < x; i++)
262                 m_maintabs[i] = make_gausstab(i + 1);
263 }
264
265 void GaussianBlurReferenceOperation::executePixel(float *color, int x, int y, void *data)
266 {
267         MemoryBuffer *memorybuffer = (MemoryBuffer*)data;
268         float *buffer = memorybuffer->getBuffer();
269         float *gausstabx, *gausstabcenty;
270         float *gausstaby, *gausstabcentx;
271         int i, j;
272         float *src;
273         register float sum, val;
274         float rval, gval, bval, aval;
275         int imgx = getWidth();
276         int imgy = getHeight();
277         float tempSize[4];
278         this->m_inputSize->read(tempSize, x, y, data);
279         float refSize = tempSize[0];
280         int refradx = (int)(refSize * m_radxf);
281         int refrady = (int)(refSize * m_radyf);
282         if (refradx > m_radx) refradx = m_radx;
283         else if (refradx < 1) refradx = 1;
284         if (refrady > m_rady) refrady = m_rady;
285         else if (refrady < 1) refrady = 1;
286
287         if (refradx == 1 && refrady == 1) {
288                 memorybuffer->readNoCheck(color, x, y);
289         } else {
290                 int minxr = x - refradx < 0 ? -x : -refradx;
291                 int maxxr = x + refradx > imgx ? imgx - x : refradx;
292                 int minyr = y - refrady < 0 ? -y : -refrady;
293                 int maxyr = y + refrady > imgy ? imgy - y : refrady;
294
295                 float *srcd = buffer + COM_NUMBER_OF_CHANNELS * ( (y + minyr) * imgx + x + minxr);
296
297                 gausstabx = m_maintabs[refradx - 1];
298                 gausstabcentx = gausstabx + refradx;
299                 gausstaby = m_maintabs[refrady - 1];
300                 gausstabcenty = gausstaby + refrady;
301
302                 sum = gval = rval = bval = aval = 0.0f;
303                 for (i = minyr; i < maxyr; i++, srcd += COM_NUMBER_OF_CHANNELS * imgx) {
304                         src = srcd;
305                         for (j = minxr; j < maxxr; j++, src += COM_NUMBER_OF_CHANNELS) {
306                         
307                                 val = gausstabcenty[i] * gausstabcentx[j];
308                                 sum += val;
309                                 rval += val * src[0];
310                                 gval += val * src[1];
311                                 bval += val * src[2];
312                                 aval += val * src[3];
313                         }
314                 }
315                 sum = 1.0f / sum;
316                 color[0] = rval * sum;
317                 color[1] = gval * sum;
318                 color[2] = bval * sum;
319                 color[3] = aval * sum;
320         }
321
322 }
323
324 void GaussianBlurReferenceOperation::deinitExecution()
325 {
326         int x, i;
327         x = MAX2(m_radx, m_rady);
328         for (i = 0; i < x; i++)
329                 delete []m_maintabs[i];
330         MEM_freeN(m_maintabs);
331         BlurBaseOperation::deinitExecution();
332 }
333
334 bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
335 {
336         rcti newInput;
337         NodeOperation *operation = this->getInputOperation(1);
338         
339         if (operation->determineDependingAreaOfInterest(input, readOperation, output)) {
340                 return true;
341         }
342         else {
343                 int addx = this->m_data->sizex+2;
344                 int addy = this->m_data->sizey+2;
345                 newInput.xmax = input->xmax + addx;
346                 newInput.xmin = input->xmin - addx;
347                 newInput.ymax = input->ymax + addy;
348                 newInput.ymin = input->ymin - addy;
349                 return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
350         }
351 }
352