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