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