edl plugin names eng, fix segv for opengl brender, renderfarm rework strategy, perf...
[goodguy/history.git] / cinelerra-5.1 / plugins / C41 / c41.C
1 /*
2  * C41 plugin for Cinelerra
3  * Copyright (C) 2011 Florent Delannoy <florent at plui dot es>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
20 #include "c41.h"
21 #include "bccolors.h"
22 #include "clip.h"
23 #include "edlsession.h"
24 #include "filexml.h"
25 #include "language.h"
26 #include "vframe.h"
27
28 REGISTER_PLUGIN(C41Effect);
29
30 C41Config::C41Config()
31 {
32         active = 0;
33         compute_magic = 0;
34         postproc = 0;
35         show_box = 0;
36
37         fix_min_r = fix_min_g = fix_min_b = fix_light = 0.;
38         fix_gamma_g = fix_gamma_b = fix_coef1 = fix_coef2 = 0.;
39         min_col = max_col = min_row = max_row = 0;
40         window_w = 500; window_h = 510;
41 }
42
43 void C41Config::copy_from(C41Config &src)
44 {
45         active = src.active;
46         compute_magic = src.compute_magic;
47         postproc = src.postproc;
48         show_box = src.show_box;
49
50         fix_min_r = src.fix_min_r;
51         fix_min_g = src.fix_min_g;
52         fix_min_b = src.fix_min_b;
53         fix_light = src.fix_light;
54         fix_gamma_g = src.fix_gamma_g;
55         fix_gamma_b = src.fix_gamma_b;
56         fix_coef1 = src.fix_coef1;
57         fix_coef2 = src.fix_coef2;
58         min_row = src.min_row;
59         max_row = src.max_row;
60         min_col = src.min_col;
61         max_col = src.max_col;
62 }
63
64 int C41Config::equivalent(C41Config &src)
65 {
66         return (active == src.active &&
67                 compute_magic == src.compute_magic &&
68                 postproc == src.postproc &&
69                 show_box == src.show_box &&
70                 EQUIV(fix_min_r, src.fix_min_r) &&
71                 EQUIV(fix_min_g, src.fix_min_g) &&
72                 EQUIV(fix_min_b, src.fix_min_b) &&
73                 EQUIV(fix_light, src.fix_light) &&
74                 EQUIV(fix_gamma_g, src.fix_gamma_g) &&
75                 EQUIV(fix_gamma_b, src.fix_gamma_b) &&
76                 EQUIV(fix_coef1, src.fix_coef1) &&
77                 EQUIV(fix_coef2, src.fix_coef2) &&
78                 min_row == src.min_row &&
79                 max_row == src.max_row &&
80                 min_col == src.min_col &&
81                 max_col == src.max_col);
82 }
83
84 void C41Config::interpolate(C41Config &prev, C41Config &next,
85                 long prev_frame, long next_frame, long current_frame)
86 {
87         active = prev.active;
88         compute_magic = prev.compute_magic;
89         postproc = prev.postproc;
90         show_box = prev.show_box;
91
92         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
93         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
94         fix_min_r = prev.fix_min_r * prev_scale + next.fix_min_r * next_scale;
95         fix_min_g = prev.fix_min_g * prev_scale + next.fix_min_g * next_scale;
96         fix_min_b = prev.fix_min_b * prev_scale + next.fix_min_b * next_scale;
97         fix_light = prev.fix_light * prev_scale + next.fix_light * next_scale;
98         fix_gamma_g = prev.fix_gamma_g * prev_scale + next.fix_gamma_g * next_scale;
99         fix_gamma_b = prev.fix_gamma_b * prev_scale + next.fix_gamma_b * next_scale;
100         fix_coef1 = prev.fix_coef1 * prev_scale + next.fix_coef1 * next_scale;
101         fix_coef2 = prev.fix_coef2 * prev_scale + next.fix_coef2 * next_scale;
102         min_row = round(prev.min_row * prev_scale + next.min_row * next_scale);
103         min_col = round(prev.min_col * prev_scale + next.min_col * next_scale);
104         max_row = round(prev.max_row * prev_scale + next.max_row * next_scale);
105         max_col = round(prev.max_col * prev_scale + next.max_col * next_scale);
106 }
107
108 // C41Enable
109 C41Enable::C41Enable(C41Effect *plugin, int *output, int x, int y, const char *text)
110  : BC_CheckBox(x, y, *output, text)
111 {
112         this->plugin = plugin;
113         this->output = output;
114 }
115
116 int C41Enable::handle_event()
117 {
118         *output = get_value();
119         plugin->send_configure_change();
120         return 1;
121 }
122
123 // C41TextBox
124 C41TextBox::C41TextBox(C41Effect *plugin, float *value, int x, int y)
125  : BC_TextBox(x, y, 160, 1, *value)
126 {
127         this->plugin = plugin;
128         this->boxValue = value;
129 }
130
131 int C41TextBox::handle_event()
132 {
133         *boxValue = atof(get_text());
134         plugin->send_configure_change();
135         return 1;
136 }
137
138
139 // C41Button
140 C41Button::C41Button(C41Effect *plugin, C41Window *window, int x, int y)
141  : BC_GenericButton(x, y, _("Apply values"))
142 {
143         this->plugin = plugin;
144         this->window = window;
145 }
146
147 int C41Button::handle_event()
148 {
149         plugin->config.fix_min_r = plugin->values.min_r;
150         plugin->config.fix_min_g = plugin->values.min_g;
151         plugin->config.fix_min_b = plugin->values.min_b;
152         plugin->config.fix_light = plugin->values.light;
153         plugin->config.fix_gamma_g = plugin->values.gamma_g;
154         plugin->config.fix_gamma_b = plugin->values.gamma_b;
155         if( plugin->values.coef1 > 0 )
156                 plugin->config.fix_coef1 = plugin->values.coef1;
157         if( plugin->values.coef2 > 0 )
158                 plugin->config.fix_coef2 = plugin->values.coef2;
159
160         window->update();
161
162         plugin->send_configure_change();
163         return 1;
164 }
165
166
167 C41BoxButton::C41BoxButton(C41Effect *plugin, C41Window *window, int x, int y)
168  : BC_GenericButton(x, y, _("Apply default box"))
169 {
170         this->plugin = plugin;
171         this->window = window;
172 }
173
174 int C41BoxButton::handle_event()
175 {
176         plugin->config.min_row = plugin->values.shave_min_row;
177         plugin->config.max_row = plugin->values.shave_max_row;
178         plugin->config.min_col = plugin->values.shave_min_col;
179         plugin->config.max_col = plugin->values.shave_max_col;
180
181         window->update();
182
183         plugin->send_configure_change();
184         return 1;
185 }
186
187
188 C41Slider::C41Slider(C41Effect *plugin, int *output, int x, int y, int is_row)
189  : BC_ISlider(x, y, 0, 200, 200, 0, is_row ?
190         plugin->get_edlsession()->output_h :
191         plugin->get_edlsession()->output_w , *output)
192 {
193         this->plugin = plugin;
194         this->output = output;
195         this->is_row = is_row;
196         EDLSession *session = plugin->get_edlsession();
197         this->max = is_row ? session->output_h : session->output_w;
198 }
199
200 int C41Slider::handle_event()
201 {
202         *output = get_value();
203         plugin->send_configure_change();
204         return 1;
205 }
206
207 int C41Slider::update(int v)
208 {
209         EDLSession *session = plugin->get_edlsession();
210         int max = is_row ? session->output_h : session->output_w;
211         bclamp(v, 0, max);
212         if( this->max != max ) return BC_ISlider::update(200, v, 0, this->max = max);
213         if( v != get_value() ) return BC_ISlider::update(v);
214         return 1;
215 }
216
217 const char* C41Effect::plugin_title() { return N_("C41"); }
218 int C41Effect::is_realtime() { return 1; }
219 int C41Effect::is_synthesis() { return 1; }
220
221 NEW_WINDOW_MACRO(C41Effect, C41Window);
222 LOAD_CONFIGURATION_MACRO(C41Effect, C41Config)
223
224 // C41Window
225 C41Window::C41Window(C41Effect *plugin)
226  : PluginClientWindow(plugin,
227         plugin->config.window_w, plugin->config.window_h,
228         500, 510, 1)
229 {
230         int x = 10, y = 10;
231
232         add_subwindow(active = new C41Enable(plugin,
233                 &plugin->config.active, x, y, _("Activate processing")));
234         y += 40;
235
236         add_subwindow(compute_magic = new C41Enable(plugin,
237                 &plugin->config.compute_magic, x, y, _("Compute negfix values")));
238         y += 20;
239
240         add_subwindow(new BC_Title(x + 20, y, _("(uncheck for faster rendering)")));
241         y += 40;
242
243         add_subwindow(new BC_Title(x, y, _("Computed negfix values:")));
244         y += 30;
245
246         add_subwindow(new BC_Title(x, y, _("Min/Max R:")));
247         add_subwindow(min_r = new BC_Title(x + 80, y, "0.0000 / 0.0000"));
248         y += 30;
249
250         add_subwindow(new BC_Title(x, y, _("Min/Max G:")));
251         add_subwindow(min_g = new BC_Title(x + 80, y, "0.0000 / 0.0000"));
252         y += 30;
253
254         add_subwindow(new BC_Title(x, y, _("Min/Max B:")));
255         add_subwindow(min_b = new BC_Title(x + 80, y, "0.0000 / 0.0000"));
256         y += 30;
257
258         add_subwindow(new BC_Title(x, y, _("Light:")));
259         add_subwindow(light = new BC_Title(x + 80, y, "0.0000"));
260         y += 30;
261
262         add_subwindow(new BC_Title(x, y, _("Gamma G:")));
263         add_subwindow(gamma_g = new BC_Title(x + 80, y, "0.0000"));
264         y += 30;
265
266         add_subwindow(new BC_Title(x, y, _("Gamma B:")));
267         add_subwindow(gamma_b = new BC_Title(x + 80, y, "0.0000"));
268         y += 30;
269
270         add_subwindow(new BC_Title(x, y, _("Contrast:")));
271         add_subwindow(coef1 = new BC_Title(x + 80, y, "0.0000"));
272         y += 30;
273
274         add_subwindow(new BC_Title(x, y, _("Brightness:")));
275         add_subwindow(coef2 = new BC_Title(x + 80, y, "0.0000"));
276         y += 30;
277
278         add_subwindow(lock = new C41Button(plugin, this, x, y));
279         y += 30;
280
281 #define BOX_COL 120
282         add_subwindow(new BC_Title(x, y, _("Box col:")));
283         add_subwindow(box_col_min = new BC_Title(x + 80, y, "0"));
284         add_subwindow(box_col_max = new BC_Title(x + BOX_COL, y, "0"));
285         y += 30;
286
287         add_subwindow(new BC_Title(x, y, _("Box row:")));
288         add_subwindow(box_row_min = new BC_Title(x + 80, y, "0"));
289         add_subwindow(box_row_max = new BC_Title(x + BOX_COL, y, "0"));
290         y += 30;
291
292         add_subwindow(boxlock = new C41BoxButton(plugin, this, x, y));
293
294         y = 10;
295         x = 250;
296         add_subwindow(show_box = new C41Enable(plugin,
297                 &plugin->config.show_box, x, y, _("Show active area")));
298         y += 40;
299
300         add_subwindow(postproc = new C41Enable(plugin,
301                 &plugin->config.postproc, x, y, _("Postprocess")));
302         y += 60;
303
304         add_subwindow(new BC_Title(x, y, _("negfix values to apply:")));
305         y += 30;
306
307         add_subwindow(new BC_Title(x, y, _("Min R:")));
308         add_subwindow(fix_min_r = new C41TextBox(plugin,
309                 &plugin->config.fix_min_r, x + 80, y));
310         y += 30;
311
312         add_subwindow(new BC_Title(x, y, _("Min G:")));
313         add_subwindow(fix_min_g = new C41TextBox(plugin,
314                 &plugin->config.fix_min_g, x + 80, y));
315         y += 30;
316
317         add_subwindow(new BC_Title(x, y, _("Min B:")));
318         add_subwindow(fix_min_b = new C41TextBox(plugin,
319                 &plugin->config.fix_min_b, x + 80, y));
320         y += 30;
321
322         add_subwindow(new BC_Title(x, y, _("Light:")));
323         add_subwindow(fix_light = new C41TextBox(plugin,
324                  &plugin->config.fix_light, x + 80, y));
325         y += 30;
326
327         add_subwindow(new BC_Title(x, y, _("Gamma G:")));
328         add_subwindow(fix_gamma_g = new C41TextBox(plugin,
329                  &plugin->config.fix_gamma_g, x + 80, y));
330         y += 30;
331
332         add_subwindow(new BC_Title(x, y, _("Gamma B:")));
333         add_subwindow(fix_gamma_b = new C41TextBox(plugin,
334                  &plugin->config.fix_gamma_b, x + 80, y));
335         y += 30;
336
337         add_subwindow(new BC_Title(x, y, _("Contrast:")));
338         add_subwindow(fix_coef1 = new C41TextBox(plugin,
339                  &plugin->config.fix_coef1, x + 80, y));
340         y += 30;
341
342         add_subwindow(new BC_Title(x, y, _("Brightness:")));
343         add_subwindow(fix_coef2 = new C41TextBox(plugin,
344                  &plugin->config.fix_coef2, x + 80, y));
345         y += 30;
346
347         x += 40;
348         add_subwindow(new BC_Title(x - 40, y, _("Col:")));
349         add_subwindow(min_col = new C41Slider(plugin,
350                  &plugin->config.min_col, x, y, 0));
351         y += 25;
352
353         add_subwindow(max_col = new C41Slider(plugin,
354                  &plugin->config.max_col, x, y, 0));
355         y += 25;
356
357         add_subwindow(new BC_Title(x - 40, y, _("Row:")));
358         add_subwindow(min_row = new C41Slider(plugin,
359                  &plugin->config.min_row, x, y, 1));
360         y += 25;
361
362         add_subwindow(max_row = new C41Slider(plugin,
363                  &plugin->config.max_row, x, y, 1));
364         y += 25;
365
366         update_magic();
367         show_window(1);
368 }
369
370 void C41Window::update()
371 {
372         C41Effect *plugin = (C41Effect *)client;
373         // Updating the GUI itself
374         active->update(plugin->config.active);
375         compute_magic->update(plugin->config.compute_magic);
376         postproc->update(plugin->config.postproc);
377         show_box->update(plugin->config.show_box);
378
379         fix_min_r->update(plugin->config.fix_min_r);
380         fix_min_g->update(plugin->config.fix_min_g);
381         fix_min_b->update(plugin->config.fix_min_b);
382         fix_light->update(plugin->config.fix_light);
383         fix_gamma_g->update(plugin->config.fix_gamma_g);
384         fix_gamma_b->update(plugin->config.fix_gamma_b);
385         fix_coef1->update(plugin->config.fix_coef1);
386         fix_coef2->update(plugin->config.fix_coef2);
387
388         min_row->update(plugin->config.min_row);
389         max_row->update(plugin->config.max_row);
390         min_col->update(plugin->config.min_col);
391         max_col->update(plugin->config.max_col);
392
393         update_magic();
394 }
395
396 void C41Window::update_magic()
397 {
398         C41Effect *plugin = (C41Effect *)client;
399         char text[BCSTRLEN];
400         sprintf(text, "%0.4f / %0.4f", plugin->values.min_r, plugin->values.max_r);
401         min_r->update(text);
402         sprintf(text, "%0.4f / %0.4f", plugin->values.min_g, plugin->values.max_g);
403         min_g->update(text);
404         sprintf(text, "%0.4f / %0.4f", plugin->values.min_b, plugin->values.max_b);
405         min_b->update(text);
406         light->update(plugin->values.light);
407         gamma_g->update(plugin->values.gamma_g);
408         gamma_b->update(plugin->values.gamma_b);
409         // Avoid blinking
410         if( plugin->values.coef1 > 0 || plugin->values.coef2 > 0 ) {
411                 coef1->update(plugin->values.coef1);
412                 coef2->update(plugin->values.coef2);
413         }
414         sprintf(text, "%d", plugin->values.shave_min_col);
415         box_col_min->update(text);
416         sprintf(text, "%d", plugin->values.shave_max_col);
417         box_col_max->update(text);
418         sprintf(text, "%d", plugin->values.shave_min_row);
419         box_row_min->update(text);
420         sprintf(text, "%d", plugin->values.shave_max_row);
421         box_row_max->update(text);
422 }
423
424
425 // C41Effect
426 C41Effect::C41Effect(PluginServer *server)
427 : PluginVClient(server)
428 {
429         frame = 0;
430         tmp_frame = 0;
431         blurry_frame = 0;
432         memset(&values, 0, sizeof(values));
433         shave_min_row = shave_max_row = 0;
434         shave_min_col = shave_max_col = 0;
435         min_col = max_col = 0;
436         min_row = max_row = 0;
437         pix_max = 0;  pix_len = 0;
438 }
439
440 C41Effect::~C41Effect()
441 {
442         delete frame;
443         delete tmp_frame;
444         delete blurry_frame;
445 }
446
447 void C41Effect::render_gui(void* data)
448 {
449         // Updating values computed by process_frame
450         struct magic *vp = (struct magic *)data;
451         values = *vp;
452
453         if( thread ) {
454                 C41Window *window = (C41Window *) thread->window;
455                 window->lock_window("C41Effect::render_gui");
456                 window->update_magic();
457                 window->unlock_window();
458         }
459 }
460
461 void C41Effect::save_data(KeyFrame *keyframe)
462 {
463         FileXML output;
464         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
465
466         output.tag.set_title("C41");
467         output.tag.set_property("ACTIVE", config.active);
468         output.tag.set_property("COMPUTE_MAGIC", config.compute_magic);
469         output.tag.set_property("POSTPROC", config.postproc);
470         output.tag.set_property("SHOW_BOX", config.show_box);
471
472         output.tag.set_property("FIX_MIN_R", config.fix_min_r);
473         output.tag.set_property("FIX_MIN_G", config.fix_min_g);
474         output.tag.set_property("FIX_MIN_B", config.fix_min_b);
475         output.tag.set_property("FIX_LIGHT", config.fix_light);
476         output.tag.set_property("FIX_GAMMA_G", config.fix_gamma_g);
477         output.tag.set_property("FIX_GAMMA_B", config.fix_gamma_b);
478         output.tag.set_property("FIX_COEF1", config.fix_coef1);
479         output.tag.set_property("FIX_COEF2", config.fix_coef2);
480         output.tag.set_property("MIN_ROW", config.min_row);
481         output.tag.set_property("MAX_ROW", config.max_row);
482         output.tag.set_property("MIN_COL", config.min_col);
483         output.tag.set_property("MAX_COL", config.max_col);
484         output.append_tag();
485         output.tag.set_title("/C41");
486         output.append_tag();
487         output.terminate_string();
488 }
489
490 void C41Effect::read_data(KeyFrame *keyframe)
491 {
492         FileXML input;
493         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
494
495         while(!input.read_tag())
496         {
497                 if( input.tag.title_is("C41") ) {
498                         config.active = input.tag.get_property("ACTIVE", config.active);
499                         config.compute_magic = input.tag.get_property("COMPUTE_MAGIC", config.compute_magic);
500                         config.postproc = input.tag.get_property("POSTPROC", config.postproc);
501                         config.show_box = input.tag.get_property("SHOW_BOX", config.show_box);
502                         config.fix_min_r = input.tag.get_property("FIX_MIN_R", config.fix_min_r);
503                         config.fix_min_g = input.tag.get_property("FIX_MIN_G", config.fix_min_g);
504                         config.fix_min_b = input.tag.get_property("FIX_MIN_B", config.fix_min_b);
505                         config.fix_light = input.tag.get_property("FIX_LIGHT", config.fix_light);
506                         config.fix_gamma_g = input.tag.get_property("FIX_GAMMA_G", config.fix_gamma_g);
507                         config.fix_gamma_b = input.tag.get_property("FIX_GAMMA_B", config.fix_gamma_b);
508                         config.fix_coef1 = input.tag.get_property("FIX_COEF1", config.fix_coef2);
509                         config.fix_coef2 = input.tag.get_property("FIX_COEF2", config.fix_coef2);
510                         config.min_row = input.tag.get_property("MIN_ROW", config.min_row);
511                         config.max_row = input.tag.get_property("MAX_ROW", config.max_row);
512                         config.min_col = input.tag.get_property("MIN_COL", config.min_col);
513                         config.max_col = input.tag.get_property("MAX_COL", config.max_col);
514                 }
515         }
516 }
517
518 #if defined(C41_FAST_POW)
519
520 float C41Effect::myLog2(float i)
521 {
522         float x, y;
523         float LogBodge = 0.346607f;
524         x = *(int *)&i;
525         x *= 1.0 / (1 << 23); // 1/pow(2,23);
526         x = x - 127;
527
528         y = x - floorf(x);
529         y = (y - y * y) * LogBodge;
530         return x + y;
531 }
532
533 float C41Effect::myPow2(float i)
534 {
535         float PowBodge = 0.33971f;
536         float x;
537         float y = i - floorf(i);
538         y = (y - y * y) * PowBodge;
539
540         x = i + 127 - y;
541         x *= (1 << 23);
542         *(int*)&x = (int)x;
543         return x;
544 }
545
546 float C41Effect::myPow(float a, float b)
547 {
548         return myPow2(b * myLog2(a));
549 }
550
551 #endif
552
553 void C41Effect::pix_fix(float &pix, float min, float gamma)
554 {
555         float val = pix;
556         if( val >= 1e-3 ) {
557                 val = min / val;
558                 if( gamma != 1 ) val = POWF(val, gamma);
559                 bclamp(val -= config.fix_light, 0, pix_max);
560                 if( config.postproc )
561                         val = config.fix_coef1 * val + config.fix_coef2;
562         }
563         else
564                 val = 1;
565         pix = val;
566 }
567
568 int C41Effect::process_realtime(VFrame *input, VFrame *output)
569 {
570         load_configuration();
571
572         int frame_w = input->get_w(), frame_h = input->get_h();
573         int input_model = input->get_color_model();
574         int has_alpha = BC_CModels::has_alpha(input_model);
575         int color_model = has_alpha ? BC_RGBA_FLOAT : BC_RGB_FLOAT;
576
577         if( input_model != color_model ) {
578                 if( frame &&
579                     ( frame->get_w() != frame_w || frame->get_h() != frame_h ||
580                       frame->get_color_model() != color_model ) ) {
581                         delete frame;  frame = 0;
582                 }
583                 if( !frame )
584                         frame = new VFrame(frame_w, frame_h, color_model);
585                 frame->transfer_from(input);
586         }
587         else {
588                 delete frame;
589                 frame = input;
590         }
591
592         pix_max = BC_CModels::calculate_max(color_model);
593         pix_len = BC_CModels::components(color_model);
594
595         min_row = config.min_row;  bclamp(min_row, 0,frame_h);
596         max_row = config.max_row;  bclamp(max_row, 0,frame_h);
597         min_col = config.min_col;  bclamp(min_col, 0,frame_w);
598         max_col = config.max_col;  bclamp(max_col, 0,frame_w);
599
600         if( config.compute_magic ) {
601                 // Convert frame to RGB for magic computing
602                 if( tmp_frame &&
603                     (tmp_frame->get_w() != frame_w || tmp_frame->get_h() != frame_h) ) {
604                         delete tmp_frame;  tmp_frame = 0;
605                 }
606                 if( !tmp_frame )
607                         tmp_frame = new VFrame(frame_w, frame_h, BC_RGB_FLOAT);
608                 if( blurry_frame &&
609                     (blurry_frame->get_w() != frame_w || blurry_frame->get_h() != frame_h) ) {
610                         delete blurry_frame;  blurry_frame = 0;
611                 }
612                 if( !blurry_frame )
613                         blurry_frame = new VFrame(frame_w, frame_h, BC_RGB_FLOAT);
614
615                 float **rows = (float **)frame->get_rows();
616                 float **tmp_rows = (float **)tmp_frame->get_rows();
617                 float **blurry_rows = (float **)blurry_frame->get_rows();
618
619                 for( int i=0; i<frame_h; ++i ) {
620                         float *row = rows[i], *tmp = tmp_rows[i];
621                         for( int j=frame_w; --j>=0; row+=pix_len ) {
622                                 *tmp++ = bclip(row[0], 0, pix_max);
623                                 *tmp++ = bclip(row[1], 0, pix_max);
624                                 *tmp++ = bclip(row[2], 0, pix_max);
625                         }
626                 }
627
628                 // 10 passes of Box blur should be good
629                 int dx = 5, dy = 5, y_up, y_dn, x_rt, x_lt;
630                 float **src = tmp_rows, **tgt = blurry_rows;
631                 for( int pass = 0; pass < 10; pass++ ) {
632                         for( int y = 0; y < frame_h; y++ ) {
633                                 if( (y_up=y-dy) < 0 ) y_up = 0;
634                                 if( (y_dn=y+dy) >= frame_h ) y_dn = frame_h-1;
635                                 float *up = src[y_up], *dn = src[y_dn], *row = tgt[y];
636                                 for( int x = 0; x < frame_w; ++x ) {
637                                         if( (x_lt=x-dx) < 0 ) x_lt = 0;
638                                         if( (x_rt=x+dx) >= frame_w ) x_lt = frame_w-1;
639                                         float *dn_l = dn + 3*x_lt, *dn_r = dn + 3*x_rt;
640                                         float *up_l = up + 3*x_lt, *up_r = up + 3*x_rt;
641                                         row[0] = (dn_l[0] + dn_r[0] + up_l[0] + up_r[0]) / 4;
642                                         row[1] = (dn_l[1] + dn_r[1] + up_l[1] + up_r[1]) / 4;
643                                         row[2] = (dn_l[2] + dn_r[2] + up_l[2] + up_r[2]) / 4;
644                                         row += 3;
645                                 }
646                         }
647                         float **rows = src;  src = tgt;  tgt = rows;
648                 }
649
650                 // Shave image: cut off border areas where min max difference
651                 // is less than C41_SHAVE_TOLERANCE, starting/ending at C41_SHAVE_MARGIN
652
653                 shave_min_row = shave_min_col = 0;
654                 shave_max_col = frame_w;
655                 shave_max_row = frame_h;
656                 int min_w = frame_w * C41_SHAVE_MARGIN;
657                 int max_w = frame_w * (1-C41_SHAVE_MARGIN);
658                 int min_h = frame_h * C41_SHAVE_MARGIN;
659                 int max_h = frame_h * (1-C41_SHAVE_MARGIN);
660
661                 float yv_min[frame_h], yv_max[frame_h];
662                 for( int y=0; y<frame_h; ++y ) {
663                         yv_min[y] = pix_max;
664                         yv_max[y] = 0.;
665                 }
666
667                 for( int y = 0; y < frame_h; y++ ) {
668                         float *row = blurry_rows[y];
669                         for( int x = 0; x < frame_w; x++, row += 3 ) {
670                                 float yv = (row[0] + row[1] + row[2]) / 3;
671                                 if( yv_min[y] > yv ) yv_min[y] = yv;
672                                 if( yv_max[y] < yv ) yv_max[y] = yv;
673                         }
674                 }
675
676                 for( shave_min_row = min_h; shave_min_row < max_h; shave_min_row++ )
677                         if( yv_max[shave_min_row] - yv_min[shave_min_row] > C41_SHAVE_TOLERANCE )
678                                 break;
679                 for( shave_max_row = max_h - 1; shave_max_row > shave_min_row; shave_max_row-- )
680                         if( yv_max[shave_max_row] - yv_min[shave_max_row] > C41_SHAVE_TOLERANCE )
681                                 break;
682                 shave_max_row++;
683
684                 float xv_min[frame_w], xv_max[frame_w];
685                 for( int x=0; x<frame_w; ++x ) {
686                         xv_min[x] = pix_max;
687                         xv_max[x] = 0.;
688                 }
689
690                 for( int y = shave_min_row; y < shave_max_row; y++ ) {
691                         float *row = blurry_rows[y];
692                         for( int x = 0; x < frame_w; x++, row += 3 ) {
693                                 float xv = (row[0] + row[1] + row[2]) / 3;
694                                 if( xv_min[x] > xv ) xv_min[x] = xv;
695                                 if( xv_max[x] < xv ) xv_max[x] = xv;
696                         }
697                 }
698
699                 for( shave_min_col = min_w; shave_min_col < max_w; shave_min_col++ )
700                         if( xv_max[shave_min_col] - xv_min[shave_min_col] > C41_SHAVE_TOLERANCE )
701                                 break;
702                 for( shave_max_col = max_w - 1; shave_max_col > shave_min_col; shave_max_col-- )
703                         if( xv_max[shave_max_col] - xv_min[shave_max_col] > C41_SHAVE_TOLERANCE )
704                                 break;
705                 shave_max_col++;
706
707                 // Compute magic negfix values
708                 float minima_r = 50., minima_g = 50., minima_b = 50.;
709                 float maxima_r = 0., maxima_g = 0., maxima_b = 0.;
710
711                 // Check if config_parameters are usable
712                 if( config.min_col >= config.max_col || config.min_row >= config.max_row ) {
713                         min_col = shave_min_col;
714                         max_col = shave_max_col;
715                         min_row = shave_min_row;
716                         max_row = shave_max_row;
717                 }
718
719                 for( int i = min_row; i < max_row; i++ ) {
720                         float *row = blurry_rows[i] + 3 * min_col;
721                         for( int j = min_col; j < max_col; j++, row+=3 ) {
722                                 if( row[0] < minima_r ) minima_r = row[0];
723                                 if( row[0] > maxima_r ) maxima_r = row[0];
724                                 if( row[1] < minima_g ) minima_g = row[1];
725                                 if( row[1] > maxima_g ) maxima_g = row[1];
726                                 if( row[2] < minima_b ) minima_b = row[2];
727                                 if( row[2] > maxima_b ) maxima_b = row[2];
728                         }
729                 }
730                 values.min_r = minima_r;  values.max_r = maxima_r;
731                 values.min_g = minima_g;  values.max_g = maxima_g;
732                 values.min_b = minima_b;  values.max_b = maxima_b;
733                 values.light = maxima_r < 1e-6 ? 1.0 : minima_r / maxima_r;
734                 bclamp(minima_r, 1e-6, 1-1e-6);  bclamp(maxima_r, 1e-6, 1-1e-6);
735                 bclamp(minima_g, 1e-6, 1-1e-6);  bclamp(maxima_g, 1e-6, 1-1e-6);
736                 bclamp(minima_b, 1e-6, 1-1e-6);  bclamp(maxima_b, 1e-6, 1-1e-6);
737                 float log_r = logf(maxima_r / minima_r);
738                 float log_g = logf(maxima_g / minima_g);
739                 float log_b = logf(maxima_b / minima_b);
740                 if( log_g < 1e-6 ) log_g = 1e-6;
741                 if( log_b < 1e-6 ) log_b = 1e-6;
742                 values.gamma_g = log_r / log_g;
743                 values.gamma_b = log_r / log_b;
744                 values.shave_min_col = shave_min_col;
745                 values.shave_max_col = shave_max_col;
746                 values.shave_min_row = shave_min_row;
747                 values.shave_max_row = shave_max_row;
748                 values.coef1 = values.coef2 = -1;
749
750                 // Update GUI
751                 send_render_gui(&values);
752         }
753
754         if( config.compute_magic ) {
755                 float minima_r = 50., minima_g = 50., minima_b = 50.;
756                 float maxima_r = 0., maxima_g = 0., maxima_b = 0.;
757
758                 for( int i = min_row; i < max_row; i++ ) {
759                         float *row = (float*)frame->get_rows()[i];
760                         row += 3 * min_col;
761                         for( int j = min_col; j < max_col; j++, row += 3 ) {
762                                 if( row[0] < minima_r ) minima_r = row[0];
763                                 if( row[0] > maxima_r ) maxima_r = row[0];
764
765                                 if( row[1] < minima_g ) minima_g = row[1];
766                                 if( row[1] > maxima_g ) maxima_g = row[1];
767
768                                 if( row[2] < minima_b ) minima_b = row[2];
769                                 if( row[2] > maxima_b ) maxima_b = row[2];
770                         }
771                 }
772
773                 // Calculate postprocessing coeficents
774                 values.coef2 = minima_r;
775                 if( minima_g < values.coef2 ) values.coef2 = minima_g;
776                 if( minima_b < values.coef2 ) values.coef2 = minima_b;
777                 values.coef1 = maxima_r;
778                 if( maxima_g > values.coef1 ) values.coef1 = maxima_g;
779                 if( maxima_b > values.coef1 ) values.coef1 = maxima_b;
780                 // Try not to overflow RGB601 (235 - 16) / 256 * 0.9
781                 float den = values.coef1 - values.coef2;
782                 if( fabs(den) < 1e-6 ) den = 1e-6;
783                 values.coef1 = 0.770 / den;
784                 values.coef2 = 0.065 - values.coef2 * values.coef1;
785                 send_render_gui(&values);
786         }
787
788         // Apply the transformation
789         if( config.active ) {
790                 for( int i = 0; i < frame_h; i++ ) {
791                         float *row = (float*)frame->get_rows()[i];
792                         for( int j = 0; j < frame_w; j++, row += pix_len ) {
793                                 pix_fix(row[0], config.fix_min_r, 1);
794                                 pix_fix(row[1], config.fix_min_g, config.fix_gamma_g);
795                                 pix_fix(row[2], config.fix_min_b, config.fix_gamma_b);
796                         }
797                 }
798         }
799
800         if( config.show_box ) {
801                 EDLSession *session = get_edlsession();
802                 int line_w = bmax(session->output_w,session->output_h) / 600 + 1;
803                 for( int k=0; k<line_w; ++k ) {
804                         float **rows = (float **)frame->get_rows();
805                         if( min_row < max_row - 1 ) {
806                                 float *row1 = (float *)rows[min_row+k];
807                                 float *row2 = (float *)rows[max_row-k - 1];
808
809                                 for( int i = 0; i < frame_w; i++ ) {
810                                         for( int j = 0; j < 3; j++ ) {
811                                                 row1[j] = pix_max - row1[j];
812                                                 row2[j] = pix_max - row2[j];
813                                         }
814                                         if( has_alpha ) {
815                                                 row1[3] = pix_max;
816                                                 row2[3] = pix_max;
817                                         }
818                                         row1 += pix_len;
819                                         row2 += pix_len;
820                                 }
821                         }
822
823                         if( min_col < max_col - 1 ) {
824                                 int pix1 = pix_len * (min_col+k);
825                                 int pix2 = pix_len * (max_col-k - 1);
826
827                                 for( int i = 0; i < frame_h; i++ ) {
828                                         float *row1 = rows[i] + pix1;
829                                         float *row2 = rows[i] + pix2;
830
831                                         for( int j = 0; j < 3; j++ ) {
832                                                 row1[j] = pix_max - row1[j];
833                                                 row2[j] = pix_max - row2[j];
834                                         }
835                                         if( has_alpha ) {
836                                                 row1[3] = pix_max;
837                                                 row2[3] = pix_max;
838                                         }
839                                 }
840                         }
841                 }
842         }
843
844         if( frame != output )
845                 output->transfer_from(frame);
846         if( frame == input )
847                 frame = 0;
848
849         return 0;
850 }
851