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