svn merge ^/trunk/blender -r49757:49763
[blender.git] / source / blender / compositor / operations / COM_BokehBlurOperation.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_BokehBlurOperation.h"
24 #include "BLI_math.h"
25 #include "COM_OpenCLDevice.h"
26
27 extern "C" {
28         #include "RE_pipeline.h"
29 }
30
31 BokehBlurOperation::BokehBlurOperation() : NodeOperation()
32 {
33         this->addInputSocket(COM_DT_COLOR);
34         this->addInputSocket(COM_DT_COLOR, COM_SC_NO_RESIZE);
35         this->addInputSocket(COM_DT_VALUE);
36         this->addInputSocket(COM_DT_VALUE);
37         this->addOutputSocket(COM_DT_COLOR);
38         this->setComplex(true);
39         this->setOpenCL(true);
40
41         this->m_size = 1.0f;
42         this->m_sizeavailable = false;
43         this->m_inputProgram = NULL;
44         this->m_inputBokehProgram = NULL;
45         this->m_inputBoundingBoxReader = NULL;
46 }
47
48 void *BokehBlurOperation::initializeTileData(rcti *rect)
49 {
50         lockMutex();
51         if (!this->m_sizeavailable) {
52                 updateSize();
53         }
54         void *buffer = getInputOperation(0)->initializeTileData(NULL);
55         unlockMutex();
56         return buffer;
57 }
58
59 void BokehBlurOperation::initExecution()
60 {
61         initMutex();
62         this->m_inputProgram = getInputSocketReader(0);
63         this->m_inputBokehProgram = getInputSocketReader(1);
64         this->m_inputBoundingBoxReader = getInputSocketReader(2);
65
66         int width = this->m_inputBokehProgram->getWidth();
67         int height = this->m_inputBokehProgram->getHeight();
68
69         float dimension = min(width, height);
70
71         this->m_bokehMidX = width / 2.0f;
72         this->m_bokehMidY = height / 2.0f;
73         this->m_bokehDimension = dimension / 2.0f;
74         QualityStepHelper::initExecution(COM_QH_INCREASE);
75 }
76
77 void BokehBlurOperation::executePixel(float *color, int x, int y, void *data)
78 {
79         float color_accum[4];
80         float tempBoundingBox[4];
81         float bokeh[4];
82
83         this->m_inputBoundingBoxReader->read(tempBoundingBox, x, y, COM_PS_NEAREST);
84         if (tempBoundingBox[0] > 0.0f) {
85                 float multiplier_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f};
86                 MemoryBuffer *inputBuffer = (MemoryBuffer *)data;
87                 float *buffer = inputBuffer->getBuffer();
88                 int bufferwidth = inputBuffer->getWidth();
89                 int bufferstartx = inputBuffer->getRect()->xmin;
90                 int bufferstarty = inputBuffer->getRect()->ymin;
91                 const float max_dim = max(this->getWidth(), this->getHeight());
92                 int pixelSize = this->m_size * max_dim / 100.0f;
93                 zero_v4(color_accum);
94
95                 if (pixelSize<2) {
96                         this->m_inputProgram->read(color_accum, x, y, COM_PS_NEAREST);
97                         multiplier_accum[0] = 1.0f;
98                         multiplier_accum[1] = 1.0f;
99                         multiplier_accum[2] = 1.0f;
100                         multiplier_accum[3] = 1.0f;
101                 }
102                 int miny = y - pixelSize;
103                 int maxy = y + pixelSize;
104                 int minx = x - pixelSize;
105                 int maxx = x + pixelSize;
106                 miny = max(miny, inputBuffer->getRect()->ymin);
107                 minx = max(minx, inputBuffer->getRect()->xmin);
108                 maxy = min(maxy, inputBuffer->getRect()->ymax);
109                 maxx = min(maxx, inputBuffer->getRect()->xmax);
110
111
112                 int step = getStep();
113                 int offsetadd = getOffsetAdd();
114
115                 float m = this->m_bokehDimension / pixelSize;
116                 for (int ny = miny; ny < maxy; ny += step) {
117                         int bufferindex = ((minx - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth);
118                         for (int nx = minx; nx < maxx; nx += step) {
119                                 float u = this->m_bokehMidX - (nx - x) * m;
120                                 float v = this->m_bokehMidY - (ny - y) * m;
121                                 this->m_inputBokehProgram->read(bokeh, u, v, COM_PS_NEAREST);
122                                 madd_v4_v4v4(color_accum, bokeh, &buffer[bufferindex]);
123                                 add_v4_v4(multiplier_accum, bokeh);
124                                 bufferindex += offsetadd;
125                         }
126                 }
127                 color[0] = color_accum[0] * (1.0f / multiplier_accum[0]);
128                 color[1] = color_accum[1] * (1.0f / multiplier_accum[1]);
129                 color[2] = color_accum[2] * (1.0f / multiplier_accum[2]);
130                 color[3] = color_accum[3] * (1.0f / multiplier_accum[3]);
131         }
132         else {
133                 this->m_inputProgram->read(color, x, y, COM_PS_NEAREST);
134         }
135 }
136
137 void BokehBlurOperation::deinitExecution()
138 {
139         deinitMutex();
140         this->m_inputProgram = NULL;
141         this->m_inputBokehProgram = NULL;
142         this->m_inputBoundingBoxReader = NULL;
143 }
144
145 bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
146 {
147         rcti newInput;
148         rcti bokehInput;
149         const float max_dim = max(this->getWidth(), this->getHeight());
150
151         if (this->m_sizeavailable) {
152                 newInput.xmax = input->xmax + (this->m_size * max_dim / 100.0f);
153                 newInput.xmin = input->xmin - (this->m_size * max_dim / 100.0f);
154                 newInput.ymax = input->ymax + (this->m_size * max_dim / 100.0f);
155                 newInput.ymin = input->ymin - (this->m_size * max_dim / 100.0f);
156         }
157         else {
158                 newInput.xmax = input->xmax + (10.0f * max_dim / 100.0f);
159                 newInput.xmin = input->xmin - (10.0f * max_dim / 100.0f);
160                 newInput.ymax = input->ymax + (10.0f * max_dim / 100.0f);
161                 newInput.ymin = input->ymin - (10.0f * max_dim / 100.0f);
162         }
163
164         NodeOperation *operation = getInputOperation(1);
165         bokehInput.xmax = operation->getWidth();
166         bokehInput.xmin = 0;
167         bokehInput.ymax = operation->getHeight();
168         bokehInput.ymin = 0;
169         if (operation->determineDependingAreaOfInterest(&bokehInput, readOperation, output) ) {
170                 return true;
171         }
172         operation = getInputOperation(0);
173         if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output) ) {
174                 return true;
175         }
176         operation = getInputOperation(2);
177         if (operation->determineDependingAreaOfInterest(input, readOperation, output) ) {
178                 return true;
179         }
180         if (!this->m_sizeavailable) {
181                 rcti sizeInput;
182                 sizeInput.xmin = 0;
183                 sizeInput.ymin = 0;
184                 sizeInput.xmax = 5;
185                 sizeInput.ymax = 5;
186                 operation = getInputOperation(3);
187                 if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output) ) {
188                         return true;
189                 }
190         }
191         return false;
192 }
193
194 void BokehBlurOperation::executeOpenCL(OpenCLDevice *device,
195                                        MemoryBuffer *outputMemoryBuffer, cl_mem clOutputBuffer, 
196                                        MemoryBuffer **inputMemoryBuffers, list<cl_mem> *clMemToCleanUp, 
197                                        list<cl_kernel> *clKernelsToCleanUp) 
198 {
199         cl_kernel kernel = device->COM_clCreateKernel("bokehBlurKernel", NULL);
200         if (!this->m_sizeavailable) {
201                 updateSize();
202         }
203         const float max_dim = max(this->getWidth(), this->getHeight());
204         cl_int radius = this->m_size * max_dim / 100.0f;
205         cl_int step = this->getStep();
206         
207         device->COM_clAttachMemoryBufferToKernelParameter(kernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputBoundingBoxReader);
208         device->COM_clAttachMemoryBufferToKernelParameter(kernel, 1,  4, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram);
209         device->COM_clAttachMemoryBufferToKernelParameter(kernel, 2,  -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputBokehProgram);
210         device->COM_clAttachOutputMemoryBufferToKernelParameter(kernel, 3, clOutputBuffer);
211         device->COM_clAttachMemoryBufferOffsetToKernelParameter(kernel, 5, outputMemoryBuffer);
212         clSetKernelArg(kernel, 6, sizeof(cl_int), &radius);
213         clSetKernelArg(kernel, 7, sizeof(cl_int), &step);
214         device->COM_clAttachSizeToKernelParameter(kernel, 8, this);
215         
216         device->COM_clEnqueueRange(kernel, outputMemoryBuffer, 9, this);
217 }
218
219 void BokehBlurOperation::updateSize()
220 {
221         if (!this->m_sizeavailable) {
222                 float result[4];
223                 this->getInputSocketReader(3)->read(result, 0, 0, COM_PS_NEAREST);
224                 this->m_size = result[0];
225                 CLAMP(this->m_size, 0.0f, 10.0f);
226                 this->m_sizeavailable = true;
227         }
228 }