r665: Merged the official release 2.0.
[cinelerra_cv.git] / plugins / colorbalance / colorbalance.C
blob21abd91d556db6097187e5ae248523af25208a58
1 #include "filexml.h"
2 #include "colorbalance.h"
3 #include "defaults.h"
4 #include "language.h"
5 #include "picon_png.h"
7 #include <stdio.h>
8 #include <string.h>
10 // 100 corresponds to (1.0 + MAX_COLOR) * input
11 #define MAX_COLOR 4.0
12 #define SQR(a) ((a) * (a))
14 REGISTER_PLUGIN(ColorBalanceMain)
18 ColorBalanceConfig::ColorBalanceConfig()
20         cyan = 0;
21         magenta = 0;
22         yellow = 0;
23         lock_params = 0;
24     preserve = 0;
27 int ColorBalanceConfig::equivalent(ColorBalanceConfig &that)
29         return (cyan == that.cyan && 
30                 magenta == that.magenta && 
31                 yellow == that.yellow && 
32                 lock_params == that.lock_params && 
33         preserve == that.preserve);
36 void ColorBalanceConfig::copy_from(ColorBalanceConfig &that)
38         cyan = that.cyan;
39         magenta = that.magenta;
40         yellow = that.yellow;
41         lock_params = that.lock_params;
42     preserve = that.preserve;
45 void ColorBalanceConfig::interpolate(ColorBalanceConfig &prev, 
46         ColorBalanceConfig &next, 
47         int64_t prev_frame, 
48         int64_t next_frame, 
49         int64_t current_frame)
51         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
52         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
54         this->cyan = prev.cyan * prev_scale + next.cyan * next_scale;
55         this->magenta = prev.magenta * prev_scale + next.magenta * next_scale;
56         this->yellow = prev.yellow * prev_scale + next.yellow * next_scale;
57         this->preserve = prev.preserve;
58         this->lock_params = prev.lock_params;
70 ColorBalanceEngine::ColorBalanceEngine(ColorBalanceMain *plugin)
71  : Thread()
73         this->plugin = plugin;
74         last_frame = 0;
75         set_synchronous(1);
78 ColorBalanceEngine::~ColorBalanceEngine()
80         last_frame = 1;
81         input_lock.unlock();
82         Thread::join();
86 int ColorBalanceEngine::start_process_frame(VFrame *output, VFrame *input, int row_start, int row_end)
88         this->output = output;
89         this->input = input;
90         this->row_start = row_start;
91         this->row_end = row_end;
92         input_lock.unlock();
93         return 0;
97 int ColorBalanceEngine::wait_process_frame()
99         output_lock.lock("ColorBalanceEngine::wait_process_frame");
100         return 0;
103 void ColorBalanceEngine::run()
105         while(1)
106         {
107                 input_lock.lock("ColorBalanceEngine::run");
108                 if(last_frame)
109                 {
110                         output_lock.unlock();
111                         return;
112                 }
114 #define PROCESS(yuvtorgb,  \
115         rgbtoyuv,  \
116         r_lookup,  \
117         g_lookup,  \
118         b_lookup,  \
119         type,  \
120         max,  \
121         components,  \
122         do_yuv) \
123 { \
124         int i, j, k; \
125         int y, cb, cr, r, g, b, r_n, g_n, b_n; \
126     float h, s, v, h_old, s_old, r_f, g_f, b_f; \
127         type **input_rows, **output_rows; \
128         input_rows = (type**)input->get_rows(); \
129         output_rows = (type**)output->get_rows(); \
131         for(j = row_start; j < row_end; j++) \
132         { \
133                 for(k = 0; k < input->get_w() * components; k += components) \
134                 { \
135                         if(do_yuv) \
136                         { \
137                                 y = input_rows[j][k]; \
138                                 cb = input_rows[j][k + 1]; \
139                                 cr = input_rows[j][k + 2]; \
140                                 yuvtorgb(r, g, b, y, cb, cr); \
141                         } \
142                         else \
143                         { \
144                 r = input_rows[j][k]; \
145                 g = input_rows[j][k + 1]; \
146                 b = input_rows[j][k + 2]; \
147                         } \
149             r = CLAMP(r, 0, max-1); g = CLAMP(g, 0, max-1); b = CLAMP(b, 0, max-1); \
150             r_n = plugin->r_lookup[r]; \
151             g_n = plugin->g_lookup[g]; \
152             b_n = plugin->b_lookup[b]; \
154                         if(plugin->config.preserve) \
155             { \
156                                 HSV::rgb_to_hsv((float)r_n, (float)g_n, (float)b_n, h, s, v); \
157                                 HSV::rgb_to_hsv((float)r, (float)g, (float)b, h_old, s_old, v); \
158                 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
159                 r = (type)r_f; \
160                 g = (type)g_f; \
161                 b = (type)b_f; \
162                         } \
163             else \
164             { \
165                 r = r_n; \
166                 g = g_n; \
167                 b = b_n; \
168                         } \
170                         if(do_yuv) \
171                         { \
172                                 rgbtoyuv(CLAMP(r, 0, max), CLAMP(g, 0, max), CLAMP(b, 0, max), y, cb, cr); \
173                 output_rows[j][k] = y; \
174                 output_rows[j][k + 1] = cb; \
175                 output_rows[j][k + 2] = cr; \
176                         } \
177                         else \
178                         { \
179                 output_rows[j][k] = CLAMP(r, 0, max); \
180                 output_rows[j][k + 1] = CLAMP(g, 0, max); \
181                 output_rows[j][k + 2] = CLAMP(b, 0, max); \
182                         } \
183                 } \
184         } \
187 #define PROCESS_F(components) \
188 { \
189         int i, j, k; \
190         float y, cb, cr, r, g, b, r_n, g_n, b_n; \
191     float h, s, v, h_old, s_old, r_f, g_f, b_f; \
192         float **input_rows, **output_rows; \
193         input_rows = (float**)input->get_rows(); \
194         output_rows = (float**)output->get_rows(); \
195         cyan_f = plugin->calculate_transfer(plugin->config.cyan); \
196         magenta_f = plugin->calculate_transfer(plugin->config.magenta); \
197         yellow_f = plugin->calculate_transfer(plugin->config.yellow); \
199         for(j = row_start; j < row_end; j++) \
200         { \
201                 for(k = 0; k < input->get_w() * components; k += components) \
202                 { \
203             r = input_rows[j][k]; \
204             g = input_rows[j][k + 1]; \
205             b = input_rows[j][k + 2]; \
207             r_n = r * cyan_f; \
208             g_n = g * magenta_f; \
209             b_n = b * yellow_f; \
211                         if(plugin->config.preserve) \
212             { \
213                                 HSV::rgb_to_hsv(r_n, g_n, b_n, h, s, v); \
214                                 HSV::rgb_to_hsv(r, g, b, h_old, s_old, v); \
215                 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
216                 r = (float)r_f; \
217                 g = (float)g_f; \
218                 b = (float)b_f; \
219                         } \
220             else \
221             { \
222                 r = r_n; \
223                 g = g_n; \
224                 b = b_n; \
225                         } \
227             output_rows[j][k] = r; \
228             output_rows[j][k + 1] = g; \
229             output_rows[j][k + 2] = b; \
230                 } \
231         } \
234                 switch(input->get_color_model())
235                 {
236                         case BC_RGB888:
237                                 PROCESS(yuv.yuv_to_rgb_8, 
238                                         yuv.rgb_to_yuv_8, 
239                                         r_lookup_8, 
240                                         g_lookup_8, 
241                                         b_lookup_8, 
242                                         unsigned char, 
243                                         0xff, 
244                                         3,
245                                         0);
246                                 break;
248                         case BC_RGB_FLOAT:
249                                 PROCESS_F(3);
250                                 break;
252                         case BC_YUV888:
253                                 PROCESS(yuv.yuv_to_rgb_8, 
254                                         yuv.rgb_to_yuv_8, 
255                                         r_lookup_8, 
256                                         g_lookup_8, 
257                                         b_lookup_8, 
258                                         unsigned char, 
259                                         0xff, 
260                                         3,
261                                         1);
262                                 break;
263                         
264                         case BC_RGBA_FLOAT:
265                                 PROCESS_F(4);
266                                 break;
268                         case BC_RGBA8888:
269                                 PROCESS(yuv.yuv_to_rgb_8, 
270                                         yuv.rgb_to_yuv_8, 
271                                         r_lookup_8, 
272                                         g_lookup_8, 
273                                         b_lookup_8, 
274                                         unsigned char, 
275                                         0xff, 
276                                         4,
277                                         0);
278                                 break;
280                         case BC_YUVA8888:
281                                 PROCESS(yuv.yuv_to_rgb_8, 
282                                         yuv.rgb_to_yuv_8, 
283                                         r_lookup_8, 
284                                         g_lookup_8, 
285                                         b_lookup_8, 
286                                         unsigned char, 
287                                         0xff, 
288                                         4,
289                                         1);
290                                 break;
291                         
292                         case BC_YUV161616:
293                                 PROCESS(yuv.yuv_to_rgb_16, 
294                                         yuv.rgb_to_yuv_16, 
295                                         r_lookup_16, 
296                                         g_lookup_16, 
297                                         b_lookup_16, 
298                                         u_int16_t, 
299                                         0xffff, 
300                                         3,
301                                         1);
302                                 break;
304                         case BC_YUVA16161616:
305                                 PROCESS(yuv.yuv_to_rgb_16, 
306                                         yuv.rgb_to_yuv_16, 
307                                         r_lookup_16, 
308                                         g_lookup_16, 
309                                         b_lookup_16, 
310                                         u_int16_t, 
311                                         0xffff, 
312                                         4,
313                                         1);
314                                 break;
315                 }
317                 
318                 
319                 output_lock.unlock();
320         }
326 ColorBalanceMain::ColorBalanceMain(PluginServer *server)
327  : PluginVClient(server)
329         need_reconfigure = 1;
330         engine = 0;
331         PLUGIN_CONSTRUCTOR_MACRO
334 ColorBalanceMain::~ColorBalanceMain()
336         PLUGIN_DESTRUCTOR_MACRO
339         if(engine)
340         {
341                 for(int i = 0; i < total_engines; i++)
342                 {
343                         delete engine[i];
344                 }
345                 delete [] engine;
346         }
349 char* ColorBalanceMain::plugin_title() { return N_("Color Balance"); }
350 int ColorBalanceMain::is_realtime() { return 1; }
353 int ColorBalanceMain::reconfigure()
355         int r_n, g_n, b_n;
356         float r_scale = calculate_transfer(config.cyan);
357         float g_scale = calculate_transfer(config.magenta);
358         float b_scale = calculate_transfer(config.yellow);
362 #define RECONFIGURE(r_lookup, g_lookup, b_lookup, max) \
363         for(int i = 0; i <= max; i++) \
364     { \
365             r_lookup[i] = CLIP((int)(r_scale * i), 0, max); \
366             g_lookup[i] = CLIP((int)(g_scale * i), 0, max); \
367             b_lookup[i] = CLIP((int)(b_scale * i), 0, max); \
368     }
370         RECONFIGURE(r_lookup_8, g_lookup_8, b_lookup_8, 0xff);
371         RECONFIGURE(r_lookup_16, g_lookup_16, b_lookup_16, 0xffff);
372         
373         return 0;
376 int64_t ColorBalanceMain::calculate_slider(float in)
378         if(in < 1.0)
379         {
380                 return (int64_t)(in * 100 - 100.0);
381         }
382         else
383         if(in > 1.0)
384         {
385                 return (int64_t)(100 * (in - 1.0) / MAX_COLOR);
386         }
387         else
388                 return 0;
391 float ColorBalanceMain::calculate_transfer(float in)
393         if(in < 0)
394         {
395                 return (100.0 + in) / 100.0;
396         }
397         else
398         if(in > 0)
399         {
400                 return 1.0 + in / 100.0 * MAX_COLOR;
401         }
402         else
403                 return 1.0;
409 int ColorBalanceMain::test_boundary(float &value)
412         if(value < -100) value = -100;
413     if(value > 100) value = 100;
414         return 0;
417 int ColorBalanceMain::synchronize_params(ColorBalanceSlider *slider, float difference)
419         if(thread && config.lock_params)
420     {
421             if(slider != thread->window->cyan)
422         {
423                 config.cyan += difference;
424             test_boundary(config.cyan);
425                 thread->window->cyan->update((int64_t)config.cyan);
426         }
427             if(slider != thread->window->magenta)
428         {
429                 config.magenta += difference;
430             test_boundary(config.magenta);
431                 thread->window->magenta->update((int64_t)config.magenta);
432         }
433             if(slider != thread->window->yellow)
434         {
435                 config.yellow += difference;
436             test_boundary(config.yellow);
437                 thread->window->yellow->update((int64_t)config.yellow);
438         }
439     }
440         return 0;
448 NEW_PICON_MACRO(ColorBalanceMain)
449 LOAD_CONFIGURATION_MACRO(ColorBalanceMain, ColorBalanceConfig)
450 SHOW_GUI_MACRO(ColorBalanceMain, ColorBalanceThread)
451 RAISE_WINDOW_MACRO(ColorBalanceMain)
452 SET_STRING_MACRO(ColorBalanceMain)
458 int ColorBalanceMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
460         need_reconfigure |= load_configuration();
462 //printf("ColorBalanceMain::process_realtime 1 %d\n", need_reconfigure);
463         if(need_reconfigure)
464         {
465                 int i;
467                 if(!engine)
468                 {
469                         total_engines = PluginClient::smp > 1 ? 2 : 1;
470                         engine = new ColorBalanceEngine*[total_engines];
471                         for(int i = 0; i < total_engines; i++)
472                         {
473                                 engine[i] = new ColorBalanceEngine(this);
474                                 engine[i]->start();
475                         }
476                 }
478                 reconfigure();
479                 need_reconfigure = 0;
480         }
483         if(!EQUIV(config.cyan, 0) || !EQUIV(config.magenta, 0) || !EQUIV(config.yellow, 0))
484         {
485                 for(int i = 0; i < total_engines; i++)
486                 {
487                         engine[i]->start_process_frame(output_ptr, 
488                                 input_ptr, 
489                                 input_ptr->get_h() * i / total_engines, 
490                                 input_ptr->get_h() * (i + 1) / total_engines);
491                 }
493                 for(int i = 0; i < total_engines; i++)
494                 {
495                         engine[i]->wait_process_frame();
496                 }
497         }
498         else
499 // Data never processed so copy if necessary
500         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
501         {
502                 output_ptr->copy_from(input_ptr);
503         }
505         return 0;
509 void ColorBalanceMain::update_gui()
511         if(thread)
512         {
513                 load_configuration();
514                 thread->window->lock_window("ColorBalanceMain::update_gui");
515                 thread->window->cyan->update((int64_t)config.cyan);
516                 thread->window->magenta->update((int64_t)config.magenta);
517                 thread->window->yellow->update((int64_t)config.yellow);
518                 thread->window->preserve->update(config.preserve);
519                 thread->window->lock_params->update(config.lock_params);
520                 thread->window->unlock_window();
521         }
527 int ColorBalanceMain::load_defaults()
529         char directory[1024], string[1024];
530 // set the default directory
531         sprintf(directory, "%scolorbalance.rc", BCASTDIR);
533 // load the defaults
534         defaults = new Defaults(directory);
535         defaults->load();
537         config.cyan = defaults->get("CYAN", config.cyan);
538         config.magenta = defaults->get("MAGENTA", config.magenta);
539         config.yellow = defaults->get("YELLOW", config.yellow);
540         config.preserve = defaults->get("PRESERVELUMINOSITY", config.preserve);
541         config.lock_params = defaults->get("LOCKPARAMS", config.lock_params);
542         return 0;
545 int ColorBalanceMain::save_defaults()
547         defaults->update("CYAN", config.cyan);
548         defaults->update("MAGENTA", config.magenta);
549         defaults->update("YELLOW", config.yellow);
550         defaults->update("PRESERVELUMINOSITY", config.preserve);
551         defaults->update("LOCKPARAMS", config.lock_params);
552         defaults->save();
553         return 0;
556 void ColorBalanceMain::save_data(KeyFrame *keyframe)
558         FileXML output;
560 // cause data to be stored directly in text
561         output.set_shared_string(keyframe->data, MESSAGESIZE);
562         output.tag.set_title("COLORBALANCE");
563         output.tag.set_property("CYAN", config.cyan);
564         output.tag.set_property("MAGENTA",  config.magenta);
565         output.tag.set_property("YELLOW",  config.yellow);
566         output.tag.set_property("PRESERVELUMINOSITY",  config.preserve);
567         output.tag.set_property("LOCKPARAMS",  config.lock_params);
568         output.append_tag();
569         output.terminate_string();
572 void ColorBalanceMain::read_data(KeyFrame *keyframe)
574         FileXML input;
576         input.set_shared_string(keyframe->data, strlen(keyframe->data));
578         int result = 0;
580         while(!result)
581         {
582                 result = input.read_tag();
584                 if(!result)
585                 {
586                         if(input.tag.title_is("COLORBALANCE"))
587                         {
588                                 config.cyan = input.tag.get_property("CYAN", config.cyan);
589                                 config.magenta = input.tag.get_property("MAGENTA", config.magenta);
590                                 config.yellow = input.tag.get_property("YELLOW", config.yellow);
591                                 config.preserve = input.tag.get_property("PRESERVELUMINOSITY", config.preserve);
592                                 config.lock_params = input.tag.get_property("LOCKPARAMS", config.lock_params);
593                         }
594                 }
595         }