1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * Copyright 2011, Blender Foundation.
17  */
18 
19 #include "COM_GaussianBokehBlurOperation.h"
20 #include "BLI_math.h"
21 #include "MEM_guardedalloc.h"
22 
23 #include "RE_pipeline.h"
24 
GaussianBokehBlurOperation()25 GaussianBokehBlurOperation::GaussianBokehBlurOperation() : BlurBaseOperation(COM_DT_COLOR)
26 {
27   this->m_gausstab = NULL;
28 }
29 
initializeTileData(rcti *)30 void *GaussianBokehBlurOperation::initializeTileData(rcti * /*rect*/)
31 {
32   lockMutex();
33   if (!this->m_sizeavailable) {
34     updateGauss();
35   }
36   void *buffer = getInputOperation(0)->initializeTileData(NULL);
37   unlockMutex();
38   return buffer;
39 }
40 
initExecution()41 void GaussianBokehBlurOperation::initExecution()
42 {
43   BlurBaseOperation::initExecution();
44 
45   initMutex();
46 
47   if (this->m_sizeavailable) {
48     updateGauss();
49   }
50 }
51 
updateGauss()52 void GaussianBokehBlurOperation::updateGauss()
53 {
54   if (this->m_gausstab == NULL) {
55     float radxf;
56     float radyf;
57     int n;
58     float *dgauss;
59     float *ddgauss;
60     int j, i;
61     const float width = this->getWidth();
62     const float height = this->getHeight();
63     if (!this->m_sizeavailable) {
64       updateSize();
65     }
66     radxf = this->m_size * (float)this->m_data.sizex;
67     CLAMP(radxf, 0.0f, width / 2.0f);
68 
69     /* vertical */
70     radyf = this->m_size * (float)this->m_data.sizey;
71     CLAMP(radyf, 0.0f, height / 2.0f);
72 
73     this->m_radx = ceil(radxf);
74     this->m_rady = ceil(radyf);
75 
76     int ddwidth = 2 * this->m_radx + 1;
77     int ddheight = 2 * this->m_rady + 1;
78     n = ddwidth * ddheight;
79 
80     /* create a full filter image */
81     ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__);
82     dgauss = ddgauss;
83     float sum = 0.0f;
84     float facx = (radxf > 0.0f ? 1.0f / radxf : 0.0f);
85     float facy = (radyf > 0.0f ? 1.0f / radyf : 0.0f);
86     for (j = -this->m_rady; j <= this->m_rady; j++) {
87       for (i = -this->m_radx; i <= this->m_radx; i++, dgauss++) {
88         float fj = (float)j * facy;
89         float fi = (float)i * facx;
90         float dist = sqrt(fj * fj + fi * fi);
91         *dgauss = RE_filter_value(this->m_data.filtertype, dist);
92 
93         sum += *dgauss;
94       }
95     }
96 
97     if (sum > 0.0f) {
98       /* normalize */
99       float norm = 1.0f / sum;
100       for (j = n - 1; j >= 0; j--) {
101         ddgauss[j] *= norm;
102       }
103     }
104     else {
105       int center = m_rady * ddwidth + m_radx;
106       ddgauss[center] = 1.0f;
107     }
108 
109     this->m_gausstab = ddgauss;
110   }
111 }
112 
executePixel(float output[4],int x,int y,void * data)113 void GaussianBokehBlurOperation::executePixel(float output[4], int x, int y, void *data)
114 {
115   float tempColor[4];
116   tempColor[0] = 0;
117   tempColor[1] = 0;
118   tempColor[2] = 0;
119   tempColor[3] = 0;
120   float multiplier_accum = 0;
121   MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
122   float *buffer = inputBuffer->getBuffer();
123   int bufferwidth = inputBuffer->getWidth();
124   int bufferstartx = inputBuffer->getRect()->xmin;
125   int bufferstarty = inputBuffer->getRect()->ymin;
126 
127   rcti &rect = *inputBuffer->getRect();
128   int ymin = max_ii(y - this->m_rady, rect.ymin);
129   int ymax = min_ii(y + this->m_rady + 1, rect.ymax);
130   int xmin = max_ii(x - this->m_radx, rect.xmin);
131   int xmax = min_ii(x + this->m_radx + 1, rect.xmax);
132 
133   int index;
134   int step = QualityStepHelper::getStep();
135   int offsetadd = QualityStepHelper::getOffsetAdd();
136   const int addConst = (xmin - x + this->m_radx);
137   const int mulConst = (this->m_radx * 2 + 1);
138   for (int ny = ymin; ny < ymax; ny += step) {
139     index = ((ny - y) + this->m_rady) * mulConst + addConst;
140     int bufferindex = ((xmin - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth);
141     for (int nx = xmin; nx < xmax; nx += step) {
142       const float multiplier = this->m_gausstab[index];
143       madd_v4_v4fl(tempColor, &buffer[bufferindex], multiplier);
144       multiplier_accum += multiplier;
145       index += step;
146       bufferindex += offsetadd;
147     }
148   }
149 
150   mul_v4_v4fl(output, tempColor, 1.0f / multiplier_accum);
151 }
152 
deinitExecution()153 void GaussianBokehBlurOperation::deinitExecution()
154 {
155   BlurBaseOperation::deinitExecution();
156 
157   if (this->m_gausstab) {
158     MEM_freeN(this->m_gausstab);
159     this->m_gausstab = NULL;
160   }
161 
162   deinitMutex();
163 }
164 
determineDependingAreaOfInterest(rcti * input,ReadBufferOperation * readOperation,rcti * output)165 bool GaussianBokehBlurOperation::determineDependingAreaOfInterest(
166     rcti *input, ReadBufferOperation *readOperation, rcti *output)
167 {
168   rcti newInput;
169   rcti sizeInput;
170   sizeInput.xmin = 0;
171   sizeInput.ymin = 0;
172   sizeInput.xmax = 5;
173   sizeInput.ymax = 5;
174   NodeOperation *operation = this->getInputOperation(1);
175 
176   if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) {
177     return true;
178   }
179 
180   if (this->m_sizeavailable && this->m_gausstab != NULL) {
181     newInput.xmin = 0;
182     newInput.ymin = 0;
183     newInput.xmax = this->getWidth();
184     newInput.ymax = this->getHeight();
185   }
186   else {
187     int addx = this->m_radx;
188     int addy = this->m_rady;
189     newInput.xmax = input->xmax + addx;
190     newInput.xmin = input->xmin - addx;
191     newInput.ymax = input->ymax + addy;
192     newInput.ymin = input->ymin - addy;
193   }
194   return BlurBaseOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
195 }
196 
197 // reference image
GaussianBlurReferenceOperation()198 GaussianBlurReferenceOperation::GaussianBlurReferenceOperation() : BlurBaseOperation(COM_DT_COLOR)
199 {
200   this->m_maintabs = NULL;
201 }
202 
initializeTileData(rcti *)203 void *GaussianBlurReferenceOperation::initializeTileData(rcti * /*rect*/)
204 {
205   void *buffer = getInputOperation(0)->initializeTileData(NULL);
206   return buffer;
207 }
208 
initExecution()209 void GaussianBlurReferenceOperation::initExecution()
210 {
211   BlurBaseOperation::initExecution();
212   // setup gaustab
213   this->m_data.image_in_width = this->getWidth();
214   this->m_data.image_in_height = this->getHeight();
215   if (this->m_data.relative) {
216     switch (this->m_data.aspect) {
217       case CMP_NODE_BLUR_ASPECT_NONE:
218         this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_width);
219         this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_height);
220         break;
221       case CMP_NODE_BLUR_ASPECT_Y:
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_width);
224         break;
225       case CMP_NODE_BLUR_ASPECT_X:
226         this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_height);
227         this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_height);
228         break;
229     }
230   }
231 
232   /* horizontal */
233   m_filtersizex = (float)this->m_data.sizex;
234   int imgx = getWidth() / 2;
235   if (m_filtersizex > imgx) {
236     m_filtersizex = imgx;
237   }
238   else if (m_filtersizex < 1) {
239     m_filtersizex = 1;
240   }
241   m_radx = (float)m_filtersizex;
242 
243   /* vertical */
244   m_filtersizey = (float)this->m_data.sizey;
245   int imgy = getHeight() / 2;
246   if (m_filtersizey > imgy) {
247     m_filtersizey = imgy;
248   }
249   else if (m_filtersizey < 1) {
250     m_filtersizey = 1;
251   }
252   m_rady = (float)m_filtersizey;
253   updateGauss();
254 }
255 
updateGauss()256 void GaussianBlurReferenceOperation::updateGauss()
257 {
258   int i;
259   int x = max(m_filtersizex, m_filtersizey);
260   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, i + 1);
263   }
264 }
265 
executePixel(float output[4],int x,int y,void * data)266 void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, void *data)
267 {
268   MemoryBuffer *memorybuffer = (MemoryBuffer *)data;
269   float *buffer = memorybuffer->getBuffer();
270   float *gausstabx, *gausstabcenty;
271   float *gausstaby, *gausstabcentx;
272   int i, j;
273   float *src;
274   float sum, val;
275   float rval, gval, bval, aval;
276   int imgx = getWidth();
277   int imgy = getHeight();
278   float tempSize[4];
279   this->m_inputSize->read(tempSize, x, y, data);
280   float refSize = tempSize[0];
281   int refradx = (int)(refSize * m_radx);
282   int refrady = (int)(refSize * m_rady);
283   if (refradx > m_filtersizex) {
284     refradx = m_filtersizex;
285   }
286   else if (refradx < 1) {
287     refradx = 1;
288   }
289   if (refrady > m_filtersizey) {
290     refrady = m_filtersizey;
291   }
292   else if (refrady < 1) {
293     refrady = 1;
294   }
295 
296   if (refradx == 1 && refrady == 1) {
297     memorybuffer->readNoCheck(output, x, y);
298   }
299   else {
300     int minxr = x - refradx < 0 ? -x : -refradx;
301     int maxxr = x + refradx > imgx ? imgx - x : refradx;
302     int minyr = y - refrady < 0 ? -y : -refrady;
303     int maxyr = y + refrady > imgy ? imgy - y : refrady;
304 
305     float *srcd = buffer + COM_NUM_CHANNELS_COLOR * ((y + minyr) * imgx + x + minxr);
306 
307     gausstabx = m_maintabs[refradx - 1];
308     gausstabcentx = gausstabx + refradx;
309     gausstaby = m_maintabs[refrady - 1];
310     gausstabcenty = gausstaby + refrady;
311 
312     sum = gval = rval = bval = aval = 0.0f;
313     for (i = minyr; i < maxyr; i++, srcd += COM_NUM_CHANNELS_COLOR * imgx) {
314       src = srcd;
315       for (j = minxr; j < maxxr; j++, src += COM_NUM_CHANNELS_COLOR) {
316 
317         val = gausstabcenty[i] * gausstabcentx[j];
318         sum += val;
319         rval += val * src[0];
320         gval += val * src[1];
321         bval += val * src[2];
322         aval += val * src[3];
323       }
324     }
325     sum = 1.0f / sum;
326     output[0] = rval * sum;
327     output[1] = gval * sum;
328     output[2] = bval * sum;
329     output[3] = aval * sum;
330   }
331 }
332 
deinitExecution()333 void GaussianBlurReferenceOperation::deinitExecution()
334 {
335   int x, i;
336   x = max(this->m_filtersizex, this->m_filtersizey);
337   for (i = 0; i < x; i++) {
338     MEM_freeN(this->m_maintabs[i]);
339   }
340   MEM_freeN(this->m_maintabs);
341   BlurBaseOperation::deinitExecution();
342 }
343 
determineDependingAreaOfInterest(rcti * input,ReadBufferOperation * readOperation,rcti * output)344 bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest(
345     rcti *input, ReadBufferOperation *readOperation, rcti *output)
346 {
347   rcti newInput;
348   NodeOperation *operation = this->getInputOperation(1);
349 
350   if (operation->determineDependingAreaOfInterest(input, readOperation, output)) {
351     return true;
352   }
353 
354   int addx = this->m_data.sizex + 2;
355   int addy = this->m_data.sizey + 2;
356   newInput.xmax = input->xmax + addx;
357   newInput.xmin = input->xmin - addx;
358   newInput.ymax = input->ymax + addy;
359   newInput.ymin = input->ymin - addy;
360   return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
361 }
362