modify clr btn 16 plugins, add regdmp for sigtraps, rework mask_engine, mask rotate...
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / colorbalance / colorbalance.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bccolors.h"
23 #include "filexml.h"
24 #include "clip.h"
25 #include "colorbalance.h"
26 #include "bchash.h"
27 #include "language.h"
28 #include "playback3d.h"
29
30 #include "aggregated.h"
31 #include "../interpolate/aggregated.h"
32 #include "../gamma/aggregated.h"
33
34 #include <stdio.h>
35 #include <string.h>
36
37 // 1000 corresponds to (1.0 + MAX_COLOR) * input
38 #define MAX_COLOR 1.0
39
40 REGISTER_PLUGIN(ColorBalanceMain)
41
42
43
44 ColorBalanceConfig::ColorBalanceConfig()
45 {
46         reset(RESET_ALL);
47 }
48
49 void ColorBalanceConfig::reset(int clear)
50 {
51         switch(clear) {
52                 case RESET_CYAN : cyan = 0;
53                         break;
54                 case RESET_MAGENTA : magenta = 0;
55                         break;
56                 case RESET_YELLOW : yellow = 0;
57                         break;
58                 case RESET_ALL :
59                 case RESET_DEFAULT_SETTINGS :
60                 default:
61                         cyan = 0;
62                         magenta = 0;
63                         yellow = 0;
64                         lock_params = 0;
65                         preserve = 0;
66                         break;
67         }
68 }
69
70 int ColorBalanceConfig::equivalent(ColorBalanceConfig &that)
71 {
72         return (cyan == that.cyan &&
73                 magenta == that.magenta &&
74                 yellow == that.yellow &&
75                 lock_params == that.lock_params &&
76         preserve == that.preserve);
77 }
78
79 void ColorBalanceConfig::copy_from(ColorBalanceConfig &that)
80 {
81         cyan = that.cyan;
82         magenta = that.magenta;
83         yellow = that.yellow;
84         lock_params = that.lock_params;
85     preserve = that.preserve;
86 }
87
88 void ColorBalanceConfig::interpolate(ColorBalanceConfig &prev,
89         ColorBalanceConfig &next,
90         int64_t prev_frame,
91         int64_t next_frame,
92         int64_t current_frame)
93 {
94         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
95         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
96
97         this->cyan = prev.cyan * prev_scale + next.cyan * next_scale;
98         this->magenta = prev.magenta * prev_scale + next.magenta * next_scale;
99         this->yellow = prev.yellow * prev_scale + next.yellow * next_scale;
100         this->preserve = prev.preserve;
101         this->lock_params = prev.lock_params;
102 }
103
104
105
106
107
108
109
110
111
112
113 ColorBalanceEngine::ColorBalanceEngine(ColorBalanceMain *plugin)
114  : Thread()
115 {
116         this->plugin = plugin;
117         last_frame = 0;
118         set_synchronous(1);
119 }
120
121 ColorBalanceEngine::~ColorBalanceEngine()
122 {
123         last_frame = 1;
124         input_lock.unlock();
125         Thread::join();
126 }
127
128
129 int ColorBalanceEngine::start_process_frame(VFrame *output, VFrame *input, int row_start, int row_end)
130 {
131         this->output = output;
132         this->input = input;
133         this->row_start = row_start;
134         this->row_end = row_end;
135         input_lock.unlock();
136         return 0;
137 }
138
139
140 int ColorBalanceEngine::wait_process_frame()
141 {
142         output_lock.lock("ColorBalanceEngine::wait_process_frame");
143         return 0;
144 }
145
146 void ColorBalanceEngine::run()
147 {
148         while(1)
149         {
150                 input_lock.lock("ColorBalanceEngine::run");
151                 if(last_frame)
152                 {
153                         output_lock.unlock();
154                         return;
155                 }
156
157 #define PROCESS(yuvtorgb,  \
158         rgbtoyuv,  \
159         r_lookup,  \
160         g_lookup,  \
161         b_lookup,  \
162         type,  \
163         max,  \
164         components,  \
165         do_yuv) \
166 { \
167         int j, k; \
168         int y, cr, cb, r, g, b, r_n, g_n, b_n; \
169         float h, s, v, h_old, s_old, r_f, g_f, b_f; \
170         type **input_rows, **output_rows; \
171         input_rows = (type**)input->get_rows(); \
172         output_rows = (type**)output->get_rows(); \
173  \
174         for(j = row_start; j < row_end; j++) \
175         { \
176                 for(k = 0; k < input->get_w() * components; k += components) \
177                 { \
178                         if(do_yuv) \
179                         { \
180                                 y = input_rows[j][k]; \
181                                 cb = input_rows[j][k + 1]; \
182                                 cr = input_rows[j][k + 2]; \
183                                 yuvtorgb(r, g, b, y, cb, cr); \
184                         } \
185                         else \
186                         { \
187                                 r = input_rows[j][k]; \
188                                 g = input_rows[j][k + 1]; \
189                                 b = input_rows[j][k + 2]; \
190                         } \
191  \
192                         r_n = plugin->r_lookup[r]; \
193                         g_n = plugin->g_lookup[g]; \
194                         b_n = plugin->b_lookup[b]; \
195  \
196                         if(plugin->config.preserve) \
197                         { \
198                                 HSV::rgb_to_hsv((float)r_n, (float)g_n, (float)b_n, h, s, v); \
199                                 HSV::rgb_to_hsv((float)r, (float)g, (float)b, h_old, s_old, v); \
200                                 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
201                                 r = (type)r_f; \
202                                 g = (type)g_f; \
203                                 b = (type)b_f; \
204                         } \
205                         else \
206                         { \
207                                 r = r_n; \
208                                 g = g_n; \
209                                 b = b_n; \
210                         } \
211  \
212                         if(do_yuv) \
213                         { \
214                                 rgbtoyuv(CLAMP(r, 0, max), CLAMP(g, 0, max), CLAMP(b, 0, max), y, cb, cr); \
215                                 output_rows[j][k] = y; \
216                                 output_rows[j][k + 1] = cb; \
217                                 output_rows[j][k + 2] = cr; \
218                         } \
219                         else \
220                         { \
221                                 output_rows[j][k] = CLAMP(r, 0, max); \
222                                 output_rows[j][k + 1] = CLAMP(g, 0, max); \
223                                 output_rows[j][k + 2] = CLAMP(b, 0, max); \
224                         } \
225                 } \
226         } \
227 }
228
229 #define PROCESS_F(components) \
230 { \
231         int j, k; \
232         float r, g, b, r_n, g_n, b_n; \
233         float h, s, v, h_old, s_old, r_f, g_f, b_f; \
234         float **input_rows, **output_rows; \
235         input_rows = (float**)input->get_rows(); \
236         output_rows = (float**)output->get_rows(); \
237         cyan_f = plugin->calculate_transfer(plugin->config.cyan); \
238         magenta_f = plugin->calculate_transfer(plugin->config.magenta); \
239         yellow_f = plugin->calculate_transfer(plugin->config.yellow); \
240  \
241 printf("PROCESS_F %f\n", cyan_f); \
242         for(j = row_start; j < row_end; j++) \
243         { \
244                 for(k = 0; k < input->get_w() * components; k += components) \
245                 { \
246                         r = input_rows[j][k]; \
247                         g = input_rows[j][k + 1]; \
248                         b = input_rows[j][k + 2]; \
249  \
250                         r_n = r * cyan_f; \
251                         g_n = g * magenta_f; \
252                         b_n = b * yellow_f; \
253  \
254                         if(plugin->config.preserve) \
255                         { \
256                                 HSV::rgb_to_hsv(r_n, g_n, b_n, h, s, v); \
257                                 HSV::rgb_to_hsv(r, g, b, h_old, s_old, v); \
258                                 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
259                                 r = (float)r_f; \
260                                 g = (float)g_f; \
261                                 b = (float)b_f; \
262                         } \
263                         else \
264                         { \
265                                 r = r_n; \
266                                 g = g_n; \
267                                 b = b_n; \
268                         } \
269  \
270                         output_rows[j][k] = r; \
271                         output_rows[j][k + 1] = g; \
272                         output_rows[j][k + 2] = b; \
273                 } \
274         } \
275 }
276
277                 switch(input->get_color_model())
278                 {
279                         case BC_RGB888:
280                                 PROCESS(YUV::yuv.yuv_to_rgb_8, YUV::yuv.rgb_to_yuv_8,
281                                         r_lookup_8, g_lookup_8, b_lookup_8,
282                                         unsigned char, 0xff, 3, 0);
283                                 break;
284
285                         case BC_RGB_FLOAT:
286                                 PROCESS_F(3);
287                                 break;
288
289                         case BC_YUV888:
290                                 PROCESS(YUV::yuv.yuv_to_rgb_8, YUV::yuv.rgb_to_yuv_8,
291                                         r_lookup_8, g_lookup_8, b_lookup_8,
292                                         unsigned char, 0xff, 3, 1);
293                                 break;
294
295                         case BC_RGBA_FLOAT:
296                                 PROCESS_F(4);
297                                 break;
298
299                         case BC_RGBA8888:
300                                 PROCESS(YUV::yuv.yuv_to_rgb_8, YUV::yuv.rgb_to_yuv_8,
301                                         r_lookup_8, g_lookup_8, b_lookup_8,
302                                         unsigned char, 0xff, 4, 0);
303                                 break;
304
305                         case BC_YUVA8888:
306                                 PROCESS(YUV::yuv.yuv_to_rgb_8, YUV::yuv.rgb_to_yuv_8,
307                                         r_lookup_8, g_lookup_8, b_lookup_8,
308                                         unsigned char, 0xff, 4, 1);
309                                 break;
310
311                         case BC_YUV161616:
312                                 PROCESS(YUV::yuv.yuv_to_rgb_16, YUV::yuv.rgb_to_yuv_16,
313                                         r_lookup_16, g_lookup_16, b_lookup_16,
314                                         u_int16_t, 0xffff, 3, 1);
315                                 break;
316
317                         case BC_YUVA16161616:
318                                 PROCESS(YUV::yuv.yuv_to_rgb_16, YUV::yuv.rgb_to_yuv_16,
319                                         r_lookup_16, g_lookup_16, b_lookup_16,
320                                         u_int16_t, 0xffff, 4, 1);
321                                 break;
322                 }
323
324
325
326                 output_lock.unlock();
327         }
328 }
329
330
331
332
333 ColorBalanceMain::ColorBalanceMain(PluginServer *server)
334  : PluginVClient(server)
335 {
336         need_reconfigure = 1;
337         engine = 0;
338
339 }
340
341 ColorBalanceMain::~ColorBalanceMain()
342 {
343
344
345
346         if(engine)
347         {
348                 for(int i = 0; i < total_engines; i++)
349                 {
350                         delete engine[i];
351                 }
352                 delete [] engine;
353         }
354 }
355
356 const char* ColorBalanceMain::plugin_title() { return N_("Color Balance"); }
357 int ColorBalanceMain::is_realtime() { return 1; }
358
359
360 int ColorBalanceMain::reconfigure()
361 {
362         float r_scale = calculate_transfer(config.cyan);
363         float g_scale = calculate_transfer(config.magenta);
364         float b_scale = calculate_transfer(config.yellow);
365
366 #define RECONFIGURE(r_lookup, g_lookup, b_lookup, max) \
367         for(int i = 0; i <= max; i++) \
368         { \
369                 r_lookup[i] = CLIP((int)(r_scale * i), 0, max); \
370                 g_lookup[i] = CLIP((int)(g_scale * i), 0, max); \
371                 b_lookup[i] = CLIP((int)(b_scale * i), 0, max); \
372         }
373
374         RECONFIGURE(r_lookup_8, g_lookup_8, b_lookup_8, 0xff);
375         RECONFIGURE(r_lookup_16, g_lookup_16, b_lookup_16, 0xffff);
376
377         return 0;
378 }
379
380 int64_t ColorBalanceMain::calculate_slider(float in)
381 {
382         if(in < 1.0)
383         {
384                 return (int64_t)(in * 1000 - 1000.0);
385         }
386         else
387         if(in > 1.0)
388         {
389                 return (int64_t)(1000 * (in - 1.0) / MAX_COLOR);
390         }
391         else
392                 return 0;
393 }
394
395 float ColorBalanceMain::calculate_transfer(float in)
396 {
397         if(in < 0)
398         {
399                 return (1000.0 + in) / 1000.0;
400         }
401         else
402         if(in > 0)
403         {
404                 return 1.0 + in / 1000.0 * MAX_COLOR;
405         }
406         else
407                 return 1.0;
408 }
409
410
411
412
413 int ColorBalanceMain::test_boundary(float &value)
414 {
415
416         if(value < -1000) value = -1000;
417     if(value > 1000) value = 1000;
418         return 0;
419 }
420
421 int ColorBalanceMain::synchronize_params(ColorBalanceSlider *slider, float difference)
422 {
423         if(thread && config.lock_params) {
424                 if(slider != ((ColorBalanceWindow*)thread->window)->cyan) {
425                         config.cyan += difference;
426                         test_boundary(config.cyan);
427                         ((ColorBalanceWindow*)thread->window)->cyan->update((int64_t)config.cyan);
428                 }
429                 if(slider != ((ColorBalanceWindow*)thread->window)->magenta) {
430                         config.magenta += difference;
431                         test_boundary(config.magenta);
432                         ((ColorBalanceWindow*)thread->window)->magenta->update((int64_t)config.magenta);
433                 }
434                 if(slider != ((ColorBalanceWindow*)thread->window)->yellow) {
435                         config.yellow += difference;
436                         test_boundary(config.yellow);
437                         ((ColorBalanceWindow*)thread->window)->yellow->update((int64_t)config.yellow);
438                 }
439         }
440         return 0;
441 }
442
443
444
445
446
447
448 LOAD_CONFIGURATION_MACRO(ColorBalanceMain, ColorBalanceConfig)
449 NEW_WINDOW_MACRO(ColorBalanceMain, ColorBalanceWindow)
450
451
452
453
454
455 int ColorBalanceMain::process_buffer(VFrame *frame,
456         int64_t start_position,
457         double frame_rate)
458 {
459         need_reconfigure |= load_configuration();
460
461 //printf("ColorBalanceMain::process_realtime 1 %d\n", need_reconfigure);
462         if(need_reconfigure)
463         {
464                 if(!engine)
465                 {
466                         total_engines = PluginClient::smp + 1;
467                         engine = new ColorBalanceEngine*[total_engines];
468                         for(int i = 0; i < total_engines; i++)
469                         {
470                                 engine[i] = new ColorBalanceEngine(this);
471                                 engine[i]->start();
472                         }
473                 }
474
475                 reconfigure();
476                 need_reconfigure = 0;
477         }
478
479         frame->get_params()->update("COLORBALANCE_PRESERVE", config.preserve);
480         frame->get_params()->update("COLORBALANCE_CYAN", calculate_transfer(config.cyan));
481         frame->get_params()->update("COLORBALANCE_MAGENTA", calculate_transfer(config.magenta));
482         frame->get_params()->update("COLORBALANCE_YELLOW", calculate_transfer(config.yellow));
483
484
485         read_frame(frame,
486                 0,
487                 get_source_position(),
488                 get_framerate(),
489                 get_use_opengl());
490
491         int aggregate_interpolate = 0;
492         int aggregate_gamma = 0;
493         get_aggregation(&aggregate_interpolate,
494                 &aggregate_gamma);
495
496         if(!EQUIV(config.cyan, 0) ||
497                 !EQUIV(config.magenta, 0) ||
498                 !EQUIV(config.yellow, 0) ||
499                 (get_use_opengl() &&
500                         (aggregate_interpolate ||
501                         aggregate_gamma)))
502         {
503                 if(get_use_opengl())
504                 {
505 //get_output()->dump_stacks();
506 // Aggregate
507                         if(next_effect_is(_("Histogram"))) return 0;
508                         return run_opengl();
509                 }
510
511                 for(int i = 0; i < total_engines; i++)
512                 {
513                         engine[i]->start_process_frame(frame,
514                                 frame,
515                                 frame->get_h() * i / total_engines,
516                                 frame->get_h() * (i + 1) / total_engines);
517                 }
518
519                 for(int i = 0; i < total_engines; i++)
520                 {
521                         engine[i]->wait_process_frame();
522                 }
523         }
524
525
526         return 0;
527 }
528
529
530 void ColorBalanceMain::update_gui()
531 {
532         if(thread)
533         {
534                 load_configuration();
535                 ((ColorBalanceWindow*)thread->window)->lock_window("ColorBalanceMain::update_gui");
536                 ((ColorBalanceWindow*)thread->window)->cyan->update((int64_t)config.cyan);
537                 ((ColorBalanceWindow*)thread->window)->magenta->update((int64_t)config.magenta);
538                 ((ColorBalanceWindow*)thread->window)->yellow->update((int64_t)config.yellow);
539                 ((ColorBalanceWindow*)thread->window)->preserve->update(config.preserve);
540                 ((ColorBalanceWindow*)thread->window)->lock_params->update(config.lock_params);
541                 ((ColorBalanceWindow*)thread->window)->unlock_window();
542         }
543 }
544
545
546
547
548 void ColorBalanceMain::save_data(KeyFrame *keyframe)
549 {
550         FileXML output;
551
552 // cause data to be stored directly in text
553         output.set_shared_output(keyframe->xbuf);
554         output.tag.set_title("COLORBALANCE");
555         output.tag.set_property("CYAN", config.cyan);
556         output.tag.set_property("MAGENTA",  config.magenta);
557         output.tag.set_property("YELLOW",  config.yellow);
558         output.tag.set_property("PRESERVELUMINOSITY",  config.preserve);
559         output.tag.set_property("LOCKPARAMS",  config.lock_params);
560         output.append_tag();
561         output.tag.set_title("/COLORBALANCE");
562         output.append_tag();
563         output.append_newline();
564         output.terminate_string();
565 }
566
567 void ColorBalanceMain::read_data(KeyFrame *keyframe)
568 {
569         FileXML input;
570
571         input.set_shared_input(keyframe->xbuf);
572
573         int result = 0;
574
575         while(!result)
576         {
577                 result = input.read_tag();
578
579                 if(!result)
580                 {
581                         if(input.tag.title_is("COLORBALANCE"))
582                         {
583                                 config.cyan = input.tag.get_property("CYAN", config.cyan);
584                                 config.magenta = input.tag.get_property("MAGENTA", config.magenta);
585                                 config.yellow = input.tag.get_property("YELLOW", config.yellow);
586                                 config.preserve = input.tag.get_property("PRESERVELUMINOSITY", config.preserve);
587                                 config.lock_params = input.tag.get_property("LOCKPARAMS", config.lock_params);
588                         }
589                 }
590         }
591 }
592
593 void ColorBalanceMain::get_aggregation(int *aggregate_interpolate,
594         int *aggregate_gamma)
595 {
596         if(!strcmp(get_output()->get_prev_effect(1), _("Interpolate Pixels")) &&
597                 !strcmp(get_output()->get_prev_effect(0), _("Gamma")))
598         {
599                 *aggregate_interpolate = 1;
600                 *aggregate_gamma = 1;
601         }
602         else
603         if(!strcmp(get_output()->get_prev_effect(0), _("Interpolate Pixels")))
604         {
605                 *aggregate_interpolate = 1;
606         }
607         else
608         if(!strcmp(get_output()->get_prev_effect(0), _("Gamma")))
609         {
610                 *aggregate_gamma = 1;
611         }
612 }
613
614 int ColorBalanceMain::handle_opengl()
615 {
616 #ifdef HAVE_GL
617
618         get_output()->to_texture();
619         get_output()->enable_opengl();
620
621         const char *shader_stack[16];
622         memset(shader_stack,0, sizeof(shader_stack));
623         int current_shader = 0;
624
625         int need_color_matrix = BC_CModels::is_yuv(get_output()->get_color_model()) ? 1 : 0;
626         if( need_color_matrix )
627                 shader_stack[current_shader++] = bc_gl_colors;
628
629         int aggregate_interpolate = 0;
630         int aggregate_gamma = 0;
631
632         get_aggregation(&aggregate_interpolate,
633                 &aggregate_gamma);
634
635 //printf("ColorBalanceMain::handle_opengl %d %d\n", aggregate_interpolate, aggregate_gamma);
636         if(aggregate_interpolate)
637                 INTERPOLATE_COMPILE(shader_stack, current_shader);
638
639         if(aggregate_gamma)
640                 GAMMA_COMPILE(shader_stack, current_shader,
641                         aggregate_interpolate);
642
643         COLORBALANCE_COMPILE(shader_stack, current_shader,
644                 aggregate_gamma || aggregate_interpolate);
645
646         shader_stack[current_shader] = 0;
647         unsigned int shader = VFrame::make_shader(shader_stack);
648         if( shader > 0 ) {
649                 glUseProgram(shader);
650                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
651
652                 if(aggregate_interpolate) INTERPOLATE_UNIFORMS(shader);
653                 if(aggregate_gamma) GAMMA_UNIFORMS(shader);
654
655                 COLORBALANCE_UNIFORMS(shader);
656                 if( need_color_matrix ) BC_GL_COLORS(shader);
657         }
658
659         get_output()->init_screen();
660         get_output()->bind_texture(0);
661         get_output()->draw_texture();
662         glUseProgram(0);
663         get_output()->set_opengl_state(VFrame::SCREEN);
664 #endif
665         return 0;
666 }
667
668
669
670
671
672
673