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