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