c8052667fa0f41ac9bc6b765c3cf290167c36093
[blender.git] / source / blender / compositor / operations / COM_ScreenLensDistortionOperation.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_ScreenLensDistortionOperation.h"
24 #include "BLI_math.h"
25 #include "BLI_utildefines.h"
26 extern "C" {
27         #include "BLI_rand.h"
28 }
29
30 ScreenLensDistortionOperation::ScreenLensDistortionOperation() : NodeOperation()
31 {
32         this->addInputSocket(COM_DT_COLOR);
33         this->addInputSocket(COM_DT_VALUE);
34         this->addInputSocket(COM_DT_VALUE);
35         this->addOutputSocket(COM_DT_COLOR);
36         this->setComplex(true);
37         this->m_inputProgram = NULL;
38         this->m_valuesAvailable = false;
39         this->m_dispersion = 0.0f;
40         this->m_distortion = 0.0f;
41 }
42 void ScreenLensDistortionOperation::initExecution()
43 {
44         this->m_inputProgram = this->getInputSocketReader(0);
45         this->initMutex();
46         this->m_cx = 0.5f * (float)getWidth();
47         this->m_cy = 0.5f * (float)getHeight();
48         
49 }
50
51 void *ScreenLensDistortionOperation::initializeTileData(rcti *rect)
52 {
53         void *buffer = this->m_inputProgram->initializeTileData(NULL);
54         updateDispersionAndDistortion();
55         return buffer;
56 }
57
58 void ScreenLensDistortionOperation::executePixel(float *outputColor, int x, int y, void *data)
59 {
60         const float height = this->getHeight();
61         const float width = this->getWidth();
62         MemoryBuffer *buffer = (MemoryBuffer *)data;
63
64         int dr = 0, dg = 0, db = 0;
65         float d, t, ln[6] = {0, 0, 0, 0, 0, 0};
66         float tc[4] = {0, 0, 0, 0};
67         const float v = this->m_sc * ((y + 0.5f) - this->m_cy) / this->m_cy;
68         const float u = this->m_sc * ((x + 0.5f) - this->m_cx) / this->m_cx;
69         const float uv_dot = u * u + v * v;
70         int sta = 0, mid = 0, end = 0;
71
72         if ((t = 1.f - this->m_kr4 * uv_dot) >= 0.f) {
73                 d = 1.f / (1.f + sqrtf(t));
74                 ln[0] = (u * d + 0.5f) * width - 0.5f, ln[1] = (v * d + 0.5f) * height - 0.5f;
75                 sta = 1;
76         }
77         if ((t = 1.f - this->m_kg4 * uv_dot) >= 0.f) {
78                 d = 1.f / (1.f + sqrtf(t));
79                 ln[2] = (u * d + 0.5f) * width - 0.5f, ln[3] = (v * d + 0.5f) * height - 0.5f;
80                 mid = 1;
81         }
82         if ((t = 1.f - this->m_kb4 * uv_dot) >= 0.f) {
83                 d = 1.f / (1.f + sqrtf(t));
84                 ln[4] = (u * d + 0.5f) * width - 0.5f, ln[5] = (v * d + 0.5f) * height - 0.5f;
85                 end = 1;
86         }
87
88         if (sta && mid && end) {
89                 float jit = this->m_data->jit;
90                 float z;
91                 float color[4];
92                 {
93                         // RG
94                         const int dx = ln[2] - ln[0], dy = ln[3] - ln[1];
95                         const float dsf = sqrtf((float)dx * dx + dy * dy) + 1.f;
96                         const int ds = (int)(jit ? ((dsf < 4.f) ? 2.f : sqrtf(dsf)) : dsf);
97                         const float sd = 1.f / (float)ds;
98
99                         for (z = 0; z < ds; ++z) {
100                                 const float tz = ((float)z + (jit ? BLI_frand() : 0.5f)) * sd;
101                                 t = 1.0f - (this->m_kr4 + tz * this->m_drg) * uv_dot;
102                                 d = 1.0f / (1.f + sqrtf(t));
103                                 const float nx = (u * d + 0.5f) * width - 0.5f;
104                                 const float ny = (v * d + 0.5f) * height - 0.5f;
105                                 buffer->readCubic(color, nx, ny);
106                                 tc[0] += (1.f - tz) * color[0], tc[1] += tz * color[1];
107                                 dr++, dg++;
108                         }
109                 }
110                 {
111                         // GB
112                         const int dx = ln[4] - ln[2], dy = ln[5] - ln[3];
113                         const float dsf = sqrtf((float)dx * dx + dy * dy) + 1.f;
114                         const int ds = (int)(jit ? ((dsf < 4.f) ? 2.f : sqrtf(dsf)) : dsf);
115                         const float sd = 1.f / (float)ds;
116
117                         for (z = 0; z < ds; ++z) {
118                                 const float tz = ((float)z + (jit ? BLI_frand() : 0.5f)) * sd;
119                                 t = 1.f - (this->m_kg4 + tz * this->m_dgb) * uv_dot;
120                                 d = 1.f / (1.f + sqrtf(t));
121                                 const float nx = (u * d + 0.5f) * width - 0.5f;
122                                 const float ny = (v * d + 0.5f) * height - 0.5f;
123                                 buffer->readCubic(color, nx, ny);
124                                 tc[1] += (1.f - tz) * color[1], tc[2] += tz * color[2];
125                                 dg++, db++;
126                         }
127
128                 }
129                 if (dr) outputColor[0] = 2.0f * tc[0] / (float)dr;
130                 if (dg) outputColor[1] = 2.0f * tc[1] / (float)dg;
131                 if (db) outputColor[2] = 2.0f * tc[2] / (float)db;
132
133                 /* set alpha */
134                 outputColor[3] = 1.0f;
135         }
136         else {
137                 outputColor[0] = 0.0f;
138                 outputColor[1] = 0.0f;
139                 outputColor[2] = 0.0f;
140                 outputColor[3] = 0.0f;
141         }
142 }
143
144 void ScreenLensDistortionOperation::deinitExecution()
145 {
146         this->deinitMutex();
147         this->m_inputProgram = NULL;
148 }
149
150 void ScreenLensDistortionOperation::determineUV(float result[4], float x, float y, float distortion, float dispersion) 
151 {
152         if (!this->m_valuesAvailable) {
153                 updateVariables(distortion, dispersion);
154         }
155         determineUV(result, x, y);
156 }
157
158 void ScreenLensDistortionOperation::determineUV(float result[4], float x, float y) const
159 {
160         const float height = this->getHeight();
161         const float width = this->getWidth();
162         
163         float d, t, ln[6] = {0, 0, 0, 0, 0, 0};
164         const float v = this->m_sc * ((y + 0.5f) - this->m_cy) / this->m_cy;
165         const float u = this->m_sc * ((x + 0.5f) - this->m_cx) / this->m_cx;
166         const float uv_dot = u * u + v * v;
167
168         if ((t = 1.f - this->m_kr4 * uv_dot) >= 0.f) {
169                 d = 1.f / (1.f + sqrtf(t));
170                 ln[0] = (u * d + 0.5f) * width - 0.5f, ln[1] = (v * d + 0.5f) * height - 0.5f;
171         }
172         if ((t = 1.f - this->m_kg4 * uv_dot) >= 0.f) {
173                 d = 1.f / (1.f + sqrtf(t));
174                 ln[2] = (u * d + 0.5f) * width - 0.5f, ln[3] = (v * d + 0.5f) * height - 0.5f;
175         }
176         if ((t = 1.f - this->m_kb4 * uv_dot) >= 0.f) {
177                 d = 1.f / (1.f + sqrtf(t));
178                 ln[4] = (u * d + 0.5f) * width - 0.5f, ln[5] = (v * d + 0.5f) * height - 0.5f;
179         }
180
181         float jit = this->m_data->jit;
182         float z;
183         {
184                 // RG
185                 const int dx = ln[2] - ln[0], dy = ln[3] - ln[1];
186                 const float dsf = sqrtf((float)dx * dx + dy * dy) + 1.f;
187                 const int ds = (int)(jit ? ((dsf < 4.f) ? 2.f : sqrtf(dsf)) : dsf);
188                 const float sd = 1.f / (float)ds;
189
190                 z = ds;
191                 const float tz = ((float)z + (1.0f)) * sd;
192                 t = 1.0f - (this->m_kr4 + tz * this->m_drg) * uv_dot;
193                 d = 1.0f / (1.f + sqrtf(t));
194                 const float nx = (u * d + 0.5f) * width - 0.5f;
195                 const float ny = (v * d + 0.5f) * height - 0.5f;
196                 result[0] = nx;
197                 result[1] = ny;
198         }
199         {
200                 // GB
201                 const int dx = ln[4] - ln[2], dy = ln[5] - ln[3];
202                 const float dsf = sqrtf((float)dx * dx + dy * dy) + 1.f;
203                 const int ds = (int)(jit ? ((dsf < 4.f) ? 2.f : sqrtf(dsf)) : dsf);
204                 const float sd = 1.f / (float)ds;
205
206                 z = ds;
207                 const float tz = ((float)z + (1.0f)) * sd;
208                 t = 1.f - (this->m_kg4 + tz * this->m_dgb) * uv_dot;
209                 d = 1.f / (1.f + sqrtf(t));
210                 const float nx = (u * d + 0.5f) * width - 0.5f;
211                 const float ny = (v * d + 0.5f) * height - 0.5f;
212                 result[2] = nx;
213                 result[3] = ny;
214         }
215 }
216
217 bool ScreenLensDistortionOperation::determineDependingAreaOfInterest(rcti *input, ReadBufferOperation *readOperation, rcti *output)
218 {
219         rcti newInputValue;
220         newInputValue.xmin = 0;
221         newInputValue.ymin = 0;
222         newInputValue.xmax = 2;
223         newInputValue.ymax = 2;
224         
225         NodeOperation *operation = getInputOperation(1);
226         if (operation->determineDependingAreaOfInterest(&newInputValue, readOperation, output) ) {
227                 return true;
228         }
229
230         operation = getInputOperation(2);
231         if (operation->determineDependingAreaOfInterest(&newInputValue, readOperation, output) ) {
232                 return true;
233         }
234
235 #define MARGIN 96
236
237 #define UPDATE_INPUT \
238                 newInput.xmin = MIN3(newInput.xmin, coords[0], coords[2]); \
239                 newInput.ymin = MIN3(newInput.ymin, coords[1], coords[3]); \
240                 newInput.xmax = MAX3(newInput.xmax, coords[0], coords[2]); \
241                 newInput.ymax = MAX3(newInput.ymax, coords[1], coords[3]);
242         
243         rcti newInput;
244         float margin;
245         float coords[4];
246         if (m_valuesAvailable) {
247                 determineUV(coords, input->xmin, input->ymin);
248                 newInput.xmin = coords[0];
249                 newInput.ymin = coords[1];
250                 newInput.xmax = coords[0];
251                 newInput.ymax = coords[1];
252                 UPDATE_INPUT;
253                 determineUV(coords, input->xmin, input->ymax);
254                 UPDATE_INPUT;
255                 determineUV(coords, input->xmax, input->ymax);
256                 UPDATE_INPUT;
257                 determineUV(coords, input->xmax, input->ymin);
258                 UPDATE_INPUT;
259                 margin = (ABS(this->m_distortion) + this->m_dispersion) * MARGIN + 2.0f;
260         } 
261         else 
262         {
263                 determineUV(coords, input->xmin, input->ymin, 1.0f, 1.0f);
264                 newInput.xmin = coords[0];
265                 newInput.ymin = coords[1];
266                 newInput.xmax = coords[0];
267                 newInput.ymax = coords[1];
268                 UPDATE_INPUT;
269                 determineUV(coords, input->xmin, input->ymin, -1.0f, 1.0f);
270                 UPDATE_INPUT;
271                 
272                 determineUV(coords, input->xmin, input->ymax, -1.0f, 1.0f);
273                 UPDATE_INPUT;
274                 determineUV(coords, input->xmin, input->ymax, 1.0f, 1.0f);
275                 UPDATE_INPUT;
276                 
277                 determineUV(coords, input->xmax, input->ymax, -1.0f, 1.0f);
278                 UPDATE_INPUT;
279                 determineUV(coords, input->xmax, input->ymax, 1.0f, 1.0f);
280                 UPDATE_INPUT;
281                 
282                 determineUV(coords, input->xmax, input->ymin, -1.0f, 1.0f);
283                 UPDATE_INPUT;
284                 determineUV(coords, input->xmax, input->ymin, 1.0f, 1.0f);
285                 UPDATE_INPUT;
286                 margin = MARGIN;
287         }
288
289 #undef UPDATE_INPUT
290         newInput.xmin -= margin;
291         newInput.ymin -= margin;
292         newInput.xmax += margin;
293         newInput.ymax += margin;
294
295         operation = getInputOperation(0);
296         if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output) ) {
297                 return true;
298         }
299         return false;
300 }
301
302 void ScreenLensDistortionOperation::updateVariables(float distortion, float dispersion)
303 {
304         this->m_kg = maxf(minf(distortion, 1.0f), -0.999f);
305         // smaller dispersion range for somewhat more control
306         const float d = 0.25f * MAX2(MIN2(dispersion, 1.f), 0.f);
307         this->m_kr = maxf(minf((this->m_kg + d), 1.0f), -0.999f);
308         this->m_kb = maxf(minf((this->m_kg - d), 1.0f), -0.999f);
309         this->m_maxk = MAX3(this->m_kr, this->m_kg, this->m_kb);
310         this->m_sc = (this->m_data->fit && (this->m_maxk > 0.f)) ? (1.f / (1.f + 2.f * this->m_maxk)) : (1.f / (1.f + this->m_maxk));
311         this->m_drg = 4.f * (this->m_kg - this->m_kr);
312         this->m_dgb = 4.f * (this->m_kb - this->m_kg);
313
314         this->m_kr4 = this->m_kr * 4.0f;
315         this->m_kg4 = this->m_kg * 4.0f;
316         this->m_kb4 = this->m_kb * 4.0f;        
317 }
318
319 void ScreenLensDistortionOperation::updateDispersionAndDistortion()
320 {
321         if (this->m_valuesAvailable) return;
322         
323         this->lockMutex();
324         if (!this->m_valuesAvailable) {
325                 float result[4];
326                 this->getInputSocketReader(1)->read(result, 0, 0, COM_PS_NEAREST);
327                 this->m_distortion = result[0];
328                 this->getInputSocketReader(2)->read(result, 0, 0, COM_PS_NEAREST);
329                 this->m_dispersion = result[0];
330                 updateVariables(this->m_distortion, this->m_dispersion);
331                 this->m_valuesAvailable = true;
332         }
333         this->unlockMutex();
334 }