r870: Merge 2.1:
[cinelerra_cv.git] / plugins / colorbalance / colorbalance.C
blob9808dc50f812ad59810ec9474b4938a79f00cb34
1 #include "filexml.h"
2 #include "colorbalance.h"
3 #include "bchash.h"
4 #include "language.h"
5 #include "picon_png.h"
6 #include "playback3d.h"
8 #include "aggregated.h"
9 #include "../interpolate/aggregated.h"
10 #include "../gamma/aggregated.h"
12 #include <stdio.h>
13 #include <string.h>
15 // 1000 corresponds to (1.0 + MAX_COLOR) * input
16 #define MAX_COLOR 1.0
17 #define SQR(a) ((a) * (a))
19 REGISTER_PLUGIN(ColorBalanceMain)
23 ColorBalanceConfig::ColorBalanceConfig()
25         cyan = 0;
26         magenta = 0;
27         yellow = 0;
28         lock_params = 0;
29     preserve = 0;
32 int ColorBalanceConfig::equivalent(ColorBalanceConfig &that)
34         return (cyan == that.cyan && 
35                 magenta == that.magenta && 
36                 yellow == that.yellow && 
37                 lock_params == that.lock_params && 
38         preserve == that.preserve);
41 void ColorBalanceConfig::copy_from(ColorBalanceConfig &that)
43         cyan = that.cyan;
44         magenta = that.magenta;
45         yellow = that.yellow;
46         lock_params = that.lock_params;
47     preserve = that.preserve;
50 void ColorBalanceConfig::interpolate(ColorBalanceConfig &prev, 
51         ColorBalanceConfig &next, 
52         int64_t prev_frame, 
53         int64_t next_frame, 
54         int64_t current_frame)
56         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
57         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
59         this->cyan = prev.cyan * prev_scale + next.cyan * next_scale;
60         this->magenta = prev.magenta * prev_scale + next.magenta * next_scale;
61         this->yellow = prev.yellow * prev_scale + next.yellow * next_scale;
62         this->preserve = prev.preserve;
63         this->lock_params = prev.lock_params;
75 ColorBalanceEngine::ColorBalanceEngine(ColorBalanceMain *plugin)
76  : Thread()
78         this->plugin = plugin;
79         last_frame = 0;
80         set_synchronous(1);
83 ColorBalanceEngine::~ColorBalanceEngine()
85         last_frame = 1;
86         input_lock.unlock();
87         Thread::join();
91 int ColorBalanceEngine::start_process_frame(VFrame *output, VFrame *input, int row_start, int row_end)
93         this->output = output;
94         this->input = input;
95         this->row_start = row_start;
96         this->row_end = row_end;
97         input_lock.unlock();
98         return 0;
102 int ColorBalanceEngine::wait_process_frame()
104         output_lock.lock("ColorBalanceEngine::wait_process_frame");
105         return 0;
108 void ColorBalanceEngine::run()
110         while(1)
111         {
112                 input_lock.lock("ColorBalanceEngine::run");
113                 if(last_frame)
114                 {
115                         output_lock.unlock();
116                         return;
117                 }
119 #define PROCESS(yuvtorgb,  \
120         rgbtoyuv,  \
121         r_lookup,  \
122         g_lookup,  \
123         b_lookup,  \
124         type,  \
125         max,  \
126         components,  \
127         do_yuv) \
128 { \
129         int i, j, k; \
130         int y, cb, cr, r, g, b, r_n, g_n, b_n; \
131     float h, s, v, h_old, s_old, r_f, g_f, b_f; \
132         type **input_rows, **output_rows; \
133         input_rows = (type**)input->get_rows(); \
134         output_rows = (type**)output->get_rows(); \
136         for(j = row_start; j < row_end; j++) \
137         { \
138                 for(k = 0; k < input->get_w() * components; k += components) \
139                 { \
140                         if(do_yuv) \
141                         { \
142                                 y = input_rows[j][k]; \
143                                 cb = input_rows[j][k + 1]; \
144                                 cr = input_rows[j][k + 2]; \
145                                 yuvtorgb(r, g, b, y, cb, cr); \
146                         } \
147                         else \
148                         { \
149                 r = input_rows[j][k]; \
150                 g = input_rows[j][k + 1]; \
151                 b = input_rows[j][k + 2]; \
152                         } \
154             r = CLAMP(r, 0, max-1); g = CLAMP(g, 0, max-1); b = CLAMP(b, 0, max-1); \
155             r_n = plugin->r_lookup[r]; \
156             g_n = plugin->g_lookup[g]; \
157             b_n = plugin->b_lookup[b]; \
159                         if(plugin->config.preserve) \
160             { \
161                                 HSV::rgb_to_hsv((float)r_n, (float)g_n, (float)b_n, h, s, v); \
162                                 HSV::rgb_to_hsv((float)r, (float)g, (float)b, h_old, s_old, v); \
163                 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
164                 r = (type)r_f; \
165                 g = (type)g_f; \
166                 b = (type)b_f; \
167                         } \
168             else \
169             { \
170                 r = r_n; \
171                 g = g_n; \
172                 b = b_n; \
173                         } \
175                         if(do_yuv) \
176                         { \
177                                 rgbtoyuv(CLAMP(r, 0, max), CLAMP(g, 0, max), CLAMP(b, 0, max), y, cb, cr); \
178                 output_rows[j][k] = y; \
179                 output_rows[j][k + 1] = cb; \
180                 output_rows[j][k + 2] = cr; \
181                         } \
182                         else \
183                         { \
184                 output_rows[j][k] = CLAMP(r, 0, max); \
185                 output_rows[j][k + 1] = CLAMP(g, 0, max); \
186                 output_rows[j][k + 2] = CLAMP(b, 0, max); \
187                         } \
188                 } \
189         } \
192 #define PROCESS_F(components) \
193 { \
194         int i, j, k; \
195         float y, cb, cr, r, g, b, r_n, g_n, b_n; \
196     float h, s, v, h_old, s_old, r_f, g_f, b_f; \
197         float **input_rows, **output_rows; \
198         input_rows = (float**)input->get_rows(); \
199         output_rows = (float**)output->get_rows(); \
200         cyan_f = plugin->calculate_transfer(plugin->config.cyan); \
201         magenta_f = plugin->calculate_transfer(plugin->config.magenta); \
202         yellow_f = plugin->calculate_transfer(plugin->config.yellow); \
204         for(j = row_start; j < row_end; j++) \
205         { \
206                 for(k = 0; k < input->get_w() * components; k += components) \
207                 { \
208             r = input_rows[j][k]; \
209             g = input_rows[j][k + 1]; \
210             b = input_rows[j][k + 2]; \
212             r_n = r * cyan_f; \
213             g_n = g * magenta_f; \
214             b_n = b * yellow_f; \
216                         if(plugin->config.preserve) \
217             { \
218                                 HSV::rgb_to_hsv(r_n, g_n, b_n, h, s, v); \
219                                 HSV::rgb_to_hsv(r, g, b, h_old, s_old, v); \
220                 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
221                 r = (float)r_f; \
222                 g = (float)g_f; \
223                 b = (float)b_f; \
224                         } \
225             else \
226             { \
227                 r = r_n; \
228                 g = g_n; \
229                 b = b_n; \
230                         } \
232             output_rows[j][k] = r; \
233             output_rows[j][k + 1] = g; \
234             output_rows[j][k + 2] = b; \
235                 } \
236         } \
239                 switch(input->get_color_model())
240                 {
241                         case BC_RGB888:
242                                 PROCESS(yuv.yuv_to_rgb_8, 
243                                         yuv.rgb_to_yuv_8, 
244                                         r_lookup_8, 
245                                         g_lookup_8, 
246                                         b_lookup_8, 
247                                         unsigned char, 
248                                         0xff, 
249                                         3,
250                                         0);
251                                 break;
253                         case BC_RGB_FLOAT:
254                                 PROCESS_F(3);
255                                 break;
257                         case BC_YUV888:
258                                 PROCESS(yuv.yuv_to_rgb_8, 
259                                         yuv.rgb_to_yuv_8, 
260                                         r_lookup_8, 
261                                         g_lookup_8, 
262                                         b_lookup_8, 
263                                         unsigned char, 
264                                         0xff, 
265                                         3,
266                                         1);
267                                 break;
268                         
269                         case BC_RGBA_FLOAT:
270                                 PROCESS_F(4);
271                                 break;
273                         case BC_RGBA8888:
274                                 PROCESS(yuv.yuv_to_rgb_8, 
275                                         yuv.rgb_to_yuv_8, 
276                                         r_lookup_8, 
277                                         g_lookup_8, 
278                                         b_lookup_8, 
279                                         unsigned char, 
280                                         0xff, 
281                                         4,
282                                         0);
283                                 break;
285                         case BC_YUVA8888:
286                                 PROCESS(yuv.yuv_to_rgb_8, 
287                                         yuv.rgb_to_yuv_8, 
288                                         r_lookup_8, 
289                                         g_lookup_8, 
290                                         b_lookup_8, 
291                                         unsigned char, 
292                                         0xff, 
293                                         4,
294                                         1);
295                                 break;
296                         
297                         case BC_YUV161616:
298                                 PROCESS(yuv.yuv_to_rgb_16, 
299                                         yuv.rgb_to_yuv_16, 
300                                         r_lookup_16, 
301                                         g_lookup_16, 
302                                         b_lookup_16, 
303                                         u_int16_t, 
304                                         0xffff, 
305                                         3,
306                                         1);
307                                 break;
309                         case BC_YUVA16161616:
310                                 PROCESS(yuv.yuv_to_rgb_16, 
311                                         yuv.rgb_to_yuv_16, 
312                                         r_lookup_16, 
313                                         g_lookup_16, 
314                                         b_lookup_16, 
315                                         u_int16_t, 
316                                         0xffff, 
317                                         4,
318                                         1);
319                                 break;
320                 }
322                 
323                 
324                 output_lock.unlock();
325         }
331 ColorBalanceMain::ColorBalanceMain(PluginServer *server)
332  : PluginVClient(server)
334         need_reconfigure = 1;
335         engine = 0;
336         PLUGIN_CONSTRUCTOR_MACRO
339 ColorBalanceMain::~ColorBalanceMain()
341         PLUGIN_DESTRUCTOR_MACRO
344         if(engine)
345         {
346                 for(int i = 0; i < total_engines; i++)
347                 {
348                         delete engine[i];
349                 }
350                 delete [] engine;
351         }
354 char* ColorBalanceMain::plugin_title() { return N_("Color Balance"); }
355 int ColorBalanceMain::is_realtime() { return 1; }
358 int ColorBalanceMain::reconfigure()
360         int r_n, g_n, b_n;
361         float r_scale = calculate_transfer(config.cyan);
362         float g_scale = calculate_transfer(config.magenta);
363         float b_scale = calculate_transfer(config.yellow);
367 #define RECONFIGURE(r_lookup, g_lookup, b_lookup, max) \
368         for(int i = 0; i <= max; i++) \
369     { \
370             r_lookup[i] = CLIP((int)(r_scale * i), 0, max); \
371             g_lookup[i] = CLIP((int)(g_scale * i), 0, max); \
372             b_lookup[i] = CLIP((int)(b_scale * i), 0, max); \
373     }
375         RECONFIGURE(r_lookup_8, g_lookup_8, b_lookup_8, 0xff);
376         RECONFIGURE(r_lookup_16, g_lookup_16, b_lookup_16, 0xffff);
377         
378         return 0;
381 int64_t ColorBalanceMain::calculate_slider(float in)
383         if(in < 1.0)
384         {
385                 return (int64_t)(in * 1000 - 1000.0);
386         }
387         else
388         if(in > 1.0)
389         {
390                 return (int64_t)(1000 * (in - 1.0) / MAX_COLOR);
391         }
392         else
393                 return 0;
396 float ColorBalanceMain::calculate_transfer(float in)
398         if(in < 0)
399         {
400                 return (1000.0 + in) / 1000.0;
401         }
402         else
403         if(in > 0)
404         {
405                 return 1.0 + in / 1000.0 * MAX_COLOR;
406         }
407         else
408                 return 1.0;
414 int ColorBalanceMain::test_boundary(float &value)
417         if(value < -1000) value = -1000;
418     if(value > 1000) value = 1000;
419         return 0;
422 int ColorBalanceMain::synchronize_params(ColorBalanceSlider *slider, float difference)
424         if(thread && config.lock_params)
425     {
426             if(slider != thread->window->cyan)
427         {
428                 config.cyan += difference;
429             test_boundary(config.cyan);
430                 thread->window->cyan->update((int64_t)config.cyan);
431         }
432             if(slider != thread->window->magenta)
433         {
434                 config.magenta += difference;
435             test_boundary(config.magenta);
436                 thread->window->magenta->update((int64_t)config.magenta);
437         }
438             if(slider != thread->window->yellow)
439         {
440                 config.yellow += difference;
441             test_boundary(config.yellow);
442                 thread->window->yellow->update((int64_t)config.yellow);
443         }
444     }
445         return 0;
453 NEW_PICON_MACRO(ColorBalanceMain)
454 LOAD_CONFIGURATION_MACRO(ColorBalanceMain, ColorBalanceConfig)
455 SHOW_GUI_MACRO(ColorBalanceMain, ColorBalanceThread)
456 RAISE_WINDOW_MACRO(ColorBalanceMain)
457 SET_STRING_MACRO(ColorBalanceMain)
463 int ColorBalanceMain::process_buffer(VFrame *frame,
464         int64_t start_position,
465         double frame_rate)
467         need_reconfigure |= load_configuration();
469 //printf("ColorBalanceMain::process_realtime 1 %d\n", need_reconfigure);
470         if(need_reconfigure)
471         {
472                 int i;
474                 if(!engine)
475                 {
476                         total_engines = PluginClient::smp > 1 ? 2 : 1;
477                         engine = new ColorBalanceEngine*[total_engines];
478                         for(int i = 0; i < total_engines; i++)
479                         {
480                                 engine[i] = new ColorBalanceEngine(this);
481                                 engine[i]->start();
482                         }
483                 }
485                 reconfigure();
486                 need_reconfigure = 0;
487         }
489         frame->get_params()->update("COLORBALANCE_PRESERVE", config.preserve);
490         frame->get_params()->update("COLORBALANCE_CYAN", calculate_transfer(config.cyan));
491         frame->get_params()->update("COLORBALANCE_MAGENTA", calculate_transfer(config.magenta));
492         frame->get_params()->update("COLORBALANCE_YELLOW", calculate_transfer(config.yellow));
495         read_frame(frame,
496                 0,
497                 get_source_position(),
498                 get_framerate(),
499                 get_use_opengl());
501         int aggregate_interpolate = 0;
502         int aggregate_gamma = 0;
503         get_aggregation(&aggregate_interpolate,
504                 &aggregate_gamma);
506         if(!EQUIV(config.cyan, 0) || 
507                 !EQUIV(config.magenta, 0) || 
508                 !EQUIV(config.yellow, 0) ||
509                 (get_use_opengl() &&
510                         (aggregate_interpolate ||
511                         aggregate_gamma)))
512         {
513                 if(get_use_opengl())
514                 {
515 get_output()->dump_stacks();
516 // Aggregate
517                         if(next_effect_is("Histogram")) return 0;
518                         return run_opengl();
519                 }
520         
521                 for(int i = 0; i < total_engines; i++)
522                 {
523                         engine[i]->start_process_frame(frame, 
524                                 frame, 
525                                 frame->get_h() * i / total_engines, 
526                                 frame->get_h() * (i + 1) / total_engines);
527                 }
529                 for(int i = 0; i < total_engines; i++)
530                 {
531                         engine[i]->wait_process_frame();
532                 }
533         }
536         return 0;
540 void ColorBalanceMain::update_gui()
542         if(thread)
543         {
544                 load_configuration();
545                 thread->window->lock_window("ColorBalanceMain::update_gui");
546                 thread->window->cyan->update((int64_t)config.cyan);
547                 thread->window->magenta->update((int64_t)config.magenta);
548                 thread->window->yellow->update((int64_t)config.yellow);
549                 thread->window->preserve->update(config.preserve);
550                 thread->window->lock_params->update(config.lock_params);
551                 thread->window->unlock_window();
552         }
557 int ColorBalanceMain::load_defaults()
559         char directory[1024], string[1024];
560 // set the default directory
561         sprintf(directory, "%scolorbalance.rc", BCASTDIR);
563 // load the defaults
564         defaults = new BC_Hash(directory);
565         defaults->load();
567         config.cyan = defaults->get("CYAN", config.cyan);
568         config.magenta = defaults->get("MAGENTA", config.magenta);
569         config.yellow = defaults->get("YELLOW", config.yellow);
570         config.preserve = defaults->get("PRESERVELUMINOSITY", config.preserve);
571         config.lock_params = defaults->get("LOCKPARAMS", config.lock_params);
572         return 0;
575 int ColorBalanceMain::save_defaults()
577         defaults->update("CYAN", config.cyan);
578         defaults->update("MAGENTA", config.magenta);
579         defaults->update("YELLOW", config.yellow);
580         defaults->update("PRESERVELUMINOSITY", config.preserve);
581         defaults->update("LOCKPARAMS", config.lock_params);
582         defaults->save();
583         return 0;
586 void ColorBalanceMain::save_data(KeyFrame *keyframe)
588         FileXML output;
590 // cause data to be stored directly in text
591         output.set_shared_string(keyframe->data, MESSAGESIZE);
592         output.tag.set_title("COLORBALANCE");
593         output.tag.set_property("CYAN", config.cyan);
594         output.tag.set_property("MAGENTA",  config.magenta);
595         output.tag.set_property("YELLOW",  config.yellow);
596         output.tag.set_property("PRESERVELUMINOSITY",  config.preserve);
597         output.tag.set_property("LOCKPARAMS",  config.lock_params);
598         output.append_tag();
599         output.terminate_string();
602 void ColorBalanceMain::read_data(KeyFrame *keyframe)
604         FileXML input;
606         input.set_shared_string(keyframe->data, strlen(keyframe->data));
608         int result = 0;
610         while(!result)
611         {
612                 result = input.read_tag();
614                 if(!result)
615                 {
616                         if(input.tag.title_is("COLORBALANCE"))
617                         {
618                                 config.cyan = input.tag.get_property("CYAN", config.cyan);
619                                 config.magenta = input.tag.get_property("MAGENTA", config.magenta);
620                                 config.yellow = input.tag.get_property("YELLOW", config.yellow);
621                                 config.preserve = input.tag.get_property("PRESERVELUMINOSITY", config.preserve);
622                                 config.lock_params = input.tag.get_property("LOCKPARAMS", config.lock_params);
623                         }
624                 }
625         }
628 void ColorBalanceMain::get_aggregation(int *aggregate_interpolate,
629         int *aggregate_gamma)
631         if(!strcmp(get_output()->get_prev_effect(1), "Interpolate Pixels") &&
632                 !strcmp(get_output()->get_prev_effect(0), "Gamma"))
633         {
634                 *aggregate_interpolate = 1;
635                 *aggregate_gamma = 1;
636         }
637         else
638         if(!strcmp(get_output()->get_prev_effect(0), "Interpolate Pixels"))
639         {
640                 *aggregate_interpolate = 1;
641         }
642         else
643         if(!strcmp(get_output()->get_prev_effect(0), "Gamma"))
644         {
645                 *aggregate_gamma = 1;
646         }
649 int ColorBalanceMain::handle_opengl()
651 #ifdef HAVE_GL
653         get_output()->to_texture();
654         get_output()->enable_opengl();
656         unsigned int shader = 0;
657         char *shader_stack[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
658         int current_shader = 0;
659         int aggregate_interpolate = 0;
660         int aggregate_gamma = 0;
662         get_aggregation(&aggregate_interpolate,
663                 &aggregate_gamma);
665 printf("ColorBalanceMain::handle_opengl %d %d\n", aggregate_interpolate, aggregate_gamma);
666         if(aggregate_interpolate)
667                 INTERPOLATE_COMPILE(shader_stack, current_shader)
669         if(aggregate_gamma)
670                 GAMMA_COMPILE(shader_stack, current_shader, aggregate_interpolate)
672         COLORBALANCE_COMPILE(shader_stack, 
673                 current_shader, 
674                 aggregate_gamma || aggregate_interpolate)
676         shader = VFrame::make_shader(0, 
677                 shader_stack[0], 
678                 shader_stack[1], 
679                 shader_stack[2], 
680                 shader_stack[3], 
681                 shader_stack[4], 
682                 shader_stack[5], 
683                 shader_stack[6], 
684                 shader_stack[7], 
685                 0);
687         if(shader > 0)
688         {
689                 glUseProgram(shader);
690                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
692                 if(aggregate_interpolate) INTERPOLATE_UNIFORMS(shader);
693                 if(aggregate_gamma) GAMMA_UNIFORMS(shader);
695                 COLORBALANCE_UNIFORMS(shader);
697         }
699         get_output()->init_screen();
700         get_output()->bind_texture(0);
701         get_output()->draw_texture();
702         glUseProgram(0);
703         get_output()->set_opengl_state(VFrame::SCREEN);
704 #endif