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