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