r123: Merged HEAD and TEST. New stuff shall be committed to HEAD from now on.
[cinelerra_cv/mob.git] / plugins / colorbalance / colorbalance.C
blob6db866f98dd1d301cfab653287909e4db9700b40
1 #include "filexml.h"
2 #include "colorbalance.h"
3 #include "defaults.h"
4 #include "picon_png.h"
6 #include <stdio.h>
7 #include <string.h>
9 #include <libintl.h>
10 #define _(String) gettext(String)
11 #define gettext_noop(String) String
12 #define N_(String) gettext_noop (String)
14 #define SQR(a) ((a) * (a))
16 REGISTER_PLUGIN(ColorBalanceMain)
20 ColorBalanceConfig::ColorBalanceConfig()
22         cyan = 0;
23         magenta = 0;
24         yellow = 0;
25         lock_params = 0;
26     preserve = 0;
29 int ColorBalanceConfig::equivalent(ColorBalanceConfig &that)
31         return (cyan == that.cyan && 
32                 magenta == that.magenta && 
33                 yellow == that.yellow && 
34                 lock_params == that.lock_params && 
35         preserve == that.preserve);
38 void ColorBalanceConfig::copy_from(ColorBalanceConfig &that)
40         cyan = that.cyan;
41         magenta = that.magenta;
42         yellow = that.yellow;
43         lock_params = that.lock_params;
44     preserve = that.preserve;
47 void ColorBalanceConfig::interpolate(ColorBalanceConfig &prev, 
48         ColorBalanceConfig &next, 
49         int64_t prev_frame, 
50         int64_t next_frame, 
51         int64_t current_frame)
53         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
54         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
56         this->cyan = prev.cyan * prev_scale + next.cyan * next_scale;
57         this->magenta = prev.magenta * prev_scale + next.magenta * next_scale;
58         this->yellow = prev.yellow * prev_scale + next.yellow * next_scale;
70 ColorBalanceEngine::ColorBalanceEngine(ColorBalanceMain *plugin)
71  : Thread()
73         this->plugin = plugin;
74         last_frame = 0;
75         input_lock.lock();
76         output_lock.lock();
77         set_synchronous(1);
80 ColorBalanceEngine::~ColorBalanceEngine()
82         last_frame = 1;
83         input_lock.unlock();
84         Thread::join();
88 int ColorBalanceEngine::start_process_frame(VFrame *output, VFrame *input, int row_start, int row_end)
90         this->output = output;
91         this->input = input;
92         this->row_start = row_start;
93         this->row_end = row_end;
94         input_lock.unlock();
95         return 0;
99 int ColorBalanceEngine::wait_process_frame()
101         output_lock.lock();
102         return 0;
106 void ColorBalanceEngine::run()
108         while(1)
109         {
110                 input_lock.lock();
111                 if(last_frame)
112                 {
113                         output_lock.unlock();
114                         return;
115                 }
116                 
117 #define PROCESS(yuvtorgb,  \
118         rgbtoyuv,  \
119         r_lookup,  \
120         g_lookup,  \
121         b_lookup,  \
122         type,  \
123         max,  \
124         components,  \
125         do_yuv) \
126 { \
127         int i, j, k; \
128         int y, cb, cr, r, g, b, r_n, g_n, b_n; \
129     float h, s, v, h_old, s_old, r_f, g_f, b_f; \
130         type **input_rows, **output_rows; \
131         input_rows = (type**)input->get_rows(); \
132         output_rows = (type**)output->get_rows(); \
134         for(j = row_start; j < row_end; j++) \
135         { \
136                 for(k = 0; k < input->get_w() * components; k += components) \
137                 { \
138                         if(do_yuv) \
139                         { \
140                                 y = input_rows[j][k]; \
141                                 cb = input_rows[j][k + 1]; \
142                                 cr = input_rows[j][k + 2]; \
143                                 yuvtorgb(r, g, b, y, cb, cr); \
144                         } \
145                         else \
146                         { \
147                 r = input_rows[j][k]; \
148                 g = input_rows[j][k + 1]; \
149                 b = input_rows[j][k + 2]; \
150                         } \
152             r_n = plugin->r_lookup[r]; \
153             g_n = plugin->g_lookup[g]; \
154             b_n = plugin->b_lookup[b]; \
156                         if(plugin->config.preserve) \
157             { \
158                                 HSV::rgb_to_hsv((float)r_n, (float)g_n, (float)b_n, h, s, v); \
159                                 HSV::rgb_to_hsv((float)r, (float)g, (float)b, h_old, s_old, v); \
160                 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
161                 r = (type)r_f; \
162                 g = (type)g_f; \
163                 b = (type)b_f; \
164                         } \
165             else \
166             { \
167                 r = r_n; \
168                 g = g_n; \
169                 b = b_n; \
170                         } \
172                         if(do_yuv) \
173                         { \
174                                 rgbtoyuv(CLAMP(r, 0, max), CLAMP(g, 0, max), CLAMP(b, 0, max), y, cb, cr); \
175                 output_rows[j][k] = y; \
176                 output_rows[j][k + 1] = cb; \
177                 output_rows[j][k + 2] = cr; \
178                         } \
179                         else \
180                         { \
181                 output_rows[j][k] = CLAMP(r, 0, max); \
182                 output_rows[j][k + 1] = CLAMP(g, 0, max); \
183                 output_rows[j][k + 2] = CLAMP(b, 0, max); \
184                         } \
185                 } \
186         } \
189                 switch(input->get_color_model())
190                 {
191                         case BC_RGB888:
192                                 PROCESS(yuv.yuv_to_rgb_8, 
193                                         yuv.rgb_to_yuv_8, 
194                                         r_lookup_8, 
195                                         g_lookup_8, 
196                                         b_lookup_8, 
197                                         unsigned char, 
198                                         0xff, 
199                                         3,
200                                         0);
201                                 break;
203                         case BC_YUV888:
204                                 PROCESS(yuv.yuv_to_rgb_8, 
205                                         yuv.rgb_to_yuv_8, 
206                                         r_lookup_8, 
207                                         g_lookup_8, 
208                                         b_lookup_8, 
209                                         unsigned char, 
210                                         0xff, 
211                                         3,
212                                         1);
213                                 break;
214                         
215                         case BC_RGBA8888:
216                                 PROCESS(yuv.yuv_to_rgb_8, 
217                                         yuv.rgb_to_yuv_8, 
218                                         r_lookup_8, 
219                                         g_lookup_8, 
220                                         b_lookup_8, 
221                                         unsigned char, 
222                                         0xff, 
223                                         4,
224                                         0);
225                                 break;
227                         case BC_YUVA8888:
228                                 PROCESS(yuv.yuv_to_rgb_8, 
229                                         yuv.rgb_to_yuv_8, 
230                                         r_lookup_8, 
231                                         g_lookup_8, 
232                                         b_lookup_8, 
233                                         unsigned char, 
234                                         0xff, 
235                                         4,
236                                         1);
237                                 break;
238                         
239                         case BC_RGB161616:
240                                 PROCESS(yuv.yuv_to_rgb_16, 
241                                         yuv.rgb_to_yuv_16, 
242                                         r_lookup_16, 
243                                         g_lookup_16, 
244                                         b_lookup_16, 
245                                         u_int16_t, 
246                                         0xffff, 
247                                         3,
248                                         0);
249                                 break;
251                         case BC_YUV161616:
252                                 PROCESS(yuv.yuv_to_rgb_16, 
253                                         yuv.rgb_to_yuv_16, 
254                                         r_lookup_16, 
255                                         g_lookup_16, 
256                                         b_lookup_16, 
257                                         u_int16_t, 
258                                         0xffff, 
259                                         3,
260                                         1);
261                                 break;
263                         case BC_RGBA16161616:
264                                 PROCESS(yuv.yuv_to_rgb_16, 
265                                         yuv.rgb_to_yuv_16, 
266                                         r_lookup_16, 
267                                         g_lookup_16, 
268                                         b_lookup_16, 
269                                         u_int16_t, 
270                                         0xffff, 
271                                         4,
272                                         0);
273                                 break;
275                         case BC_YUVA16161616:
276                                 PROCESS(yuv.yuv_to_rgb_16, 
277                                         yuv.rgb_to_yuv_16, 
278                                         r_lookup_16, 
279                                         g_lookup_16, 
280                                         b_lookup_16, 
281                                         u_int16_t, 
282                                         0xffff, 
283                                         4,
284                                         1);
285                                 break;
286                 }
288                 
289                 
290                 output_lock.unlock();
291         }
297 ColorBalanceMain::ColorBalanceMain(PluginServer *server)
298  : PluginVClient(server)
300         need_reconfigure = 1;
301         engine = 0;
302         PLUGIN_CONSTRUCTOR_MACRO
305 ColorBalanceMain::~ColorBalanceMain()
307         PLUGIN_DESTRUCTOR_MACRO
310         if(engine)
311         {
312                 for(int i = 0; i < total_engines; i++)
313                 {
314                         delete engine[i];
315                 }
316                 delete [] engine;
317         }
320 char* ColorBalanceMain::plugin_title() { return _("Color Balance"); }
321 int ColorBalanceMain::is_realtime() { return 1; }
324 int ColorBalanceMain::reconfigure()
326         int r_n, g_n, b_n;
327     double *cyan_red_transfer;
328     double *magenta_green_transfer;
329     double *yellow_blue_transfer;
332 #define RECONFIGURE(highlights_add, highlights_sub, r_lookup, g_lookup, b_lookup, max) \
333     cyan_red_transfer = config.cyan > 0 ? highlights_add : highlights_sub; \
334     magenta_green_transfer = config.magenta > 0 ? highlights_add : highlights_sub; \
335     yellow_blue_transfer = config.yellow > 0 ? highlights_add : highlights_sub; \
336         for(int i = 0; i < max; i++) \
337     { \
338         r_n = g_n = b_n = i; \
339             r_n += (int)(config.cyan / 100 * max * cyan_red_transfer[r_n]); \
340             g_n += (int)(config.magenta / 100 * max  * magenta_green_transfer[g_n]); \
341             b_n += (int)(config.yellow / 100 * max  * yellow_blue_transfer[b_n]); \
343         if(r_n > max) r_n = max; \
344         else \
345                 if(r_n < 0) r_n = 0; \
346         if(g_n > max) g_n = max; \
347         else \
348         if(g_n < 0) g_n = 0; \
349         if(b_n > max) b_n = max; \
350         else \
351         if(b_n < 0) b_n = 0; \
353         r_lookup[i] = r_n; \
354         g_lookup[i] = g_n; \
355         b_lookup[i] = b_n; \
356     }
359         RECONFIGURE(highlights_add_8, highlights_sub_8, r_lookup_8, g_lookup_8, b_lookup_8, 0xff);
360         RECONFIGURE(highlights_add_16, highlights_sub_16, r_lookup_16, g_lookup_16, b_lookup_16, 0xffff);
361         
362         return 0;
365 int ColorBalanceMain::test_boundary(float &value)
367         if(value < -100) value = -100;
368     if(value > 100) value = 100;
369         return 0;
372 int ColorBalanceMain::synchronize_params(ColorBalanceSlider *slider, float difference)
374         if(thread && config.lock_params)
375     {
376             if(slider != thread->window->cyan)
377         {
378                 config.cyan += difference;
379             test_boundary(config.cyan);
380                 thread->window->cyan->update((int64_t)config.cyan);
381         }
382             if(slider != thread->window->magenta)
383         {
384                 config.magenta += difference;
385             test_boundary(config.magenta);
386                 thread->window->magenta->update((int64_t)config.magenta);
387         }
388             if(slider != thread->window->yellow)
389         {
390                 config.yellow += difference;
391             test_boundary(config.yellow);
392                 thread->window->yellow->update((int64_t)config.yellow);
393         }
394     }
395         return 0;
403 NEW_PICON_MACRO(ColorBalanceMain)
404 LOAD_CONFIGURATION_MACRO(ColorBalanceMain, ColorBalanceConfig)
405 SHOW_GUI_MACRO(ColorBalanceMain, ColorBalanceThread)
406 RAISE_WINDOW_MACRO(ColorBalanceMain)
407 SET_STRING_MACRO(ColorBalanceMain)
413 int ColorBalanceMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
415         need_reconfigure |= load_configuration();
417 //printf("ColorBalanceMain::process_realtime 1 %d\n", need_reconfigure);
418         if(need_reconfigure)
419         {
420                 int i;
422                 if(!engine)
423                 {
424 #define CALCULATE_HIGHLIGHTS(add, sub, max) \
425 for(i = 0; i < max; i++) \
426 { \
427         add[i] = sub[i] = 0.667 * (1 - SQR(((double)i - (max / 2)) / (max / 2))); \
430                         CALCULATE_HIGHLIGHTS(highlights_add_8, highlights_sub_8, 0xff);
431                         CALCULATE_HIGHLIGHTS(highlights_add_16, highlights_sub_16, 0xffff);
433                         total_engines = PluginClient::smp > 1 ? 2 : 1;
434                         engine = new ColorBalanceEngine*[total_engines];
435                         for(int i = 0; i < total_engines; i++)
436                         {
437                                 engine[i] = new ColorBalanceEngine(this);
438                                 engine[i]->start();
439                         }
440                 }
442                 reconfigure();
443                 need_reconfigure = 0;
444         }
447         if(config.cyan != 0 || config.magenta != 0 || config.yellow != 0)
448         {
449                 int64_t row_step = input_ptr->get_h() / total_engines + 1;
450                 for(int i = 0; i < input_ptr->get_h(); i += row_step)
451                 {
452                         if(i + row_step > input_ptr->get_h()) 
453                                 row_step = input_ptr->get_h() - i;
454                         engine[i]->start_process_frame(output_ptr, 
455                                 input_ptr, 
456                                 i, 
457                                 i + row_step);
458                 }
460                 for(int i = 0; i < total_engines; i++)
461                 {
462                         engine[i]->wait_process_frame();
463                 }
464         }
465         else
466 // Data never processed so copy if necessary
467         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
468         {
469                 output_ptr->copy_from(input_ptr);
470         }
471 //printf("ColorBalanceMain::process_realtime 2\n");
472         return 0;
477 int ColorBalanceMain::load_defaults()
479         char directory[1024], string[1024];
480 // set the default directory
481         sprintf(directory, "%scolorbalance.rc", BCASTDIR);
483 // load the defaults
484         defaults = new Defaults(directory);
485         defaults->load();
487         config.cyan = defaults->get("CYAN", config.cyan);
488         config.magenta = defaults->get("MAGENTA", config.magenta);
489         config.yellow = defaults->get("YELLOW", config.yellow);
490         config.preserve = defaults->get("PRESERVELUMINOSITY", config.preserve);
491         config.lock_params = defaults->get("LOCKPARAMS", config.lock_params);
492         return 0;
495 int ColorBalanceMain::save_defaults()
497         defaults->update("CYAN", config.cyan);
498         defaults->update("MAGENTA", config.magenta);
499         defaults->update("YELLOW", config.yellow);
500         defaults->update("PRESERVELUMINOSITY", config.preserve);
501         defaults->update("LOCKPARAMS", config.lock_params);
502         defaults->save();
503         return 0;
506 void ColorBalanceMain::save_data(KeyFrame *keyframe)
508         FileXML output;
510 // cause data to be stored directly in text
511         output.set_shared_string(keyframe->data, MESSAGESIZE);
512         output.tag.set_title("COLORBALANCE");
513         output.tag.set_property("CYAN", config.cyan);
514         output.tag.set_property("MAGENTA",  config.magenta);
515         output.tag.set_property("YELLOW",  config.yellow);
516         output.tag.set_property("PRESERVELUMINOSITY",  config.preserve);
517         output.tag.set_property("LOCKPARAMS",  config.lock_params);
518         output.append_tag();
519         output.terminate_string();
522 void ColorBalanceMain::read_data(KeyFrame *keyframe)
524         FileXML input;
526 //printf("ColorBalanceMain::read_data 1\n");
527         input.set_shared_string(keyframe->data, strlen(keyframe->data));
528 //printf("ColorBalanceMain::read_data 1\n");
530         int result = 0;
531 //printf("ColorBalanceMain::read_data 1\n");
533         while(!result)
534         {
535                 result = input.read_tag();
537                 if(!result)
538                 {
539                         if(input.tag.title_is("COLORBALANCE"))
540                         {
541                                 config.cyan = input.tag.get_property("CYAN", config.cyan);
542                                 config.magenta = input.tag.get_property("MAGENTA", config.magenta);
543                                 config.yellow = input.tag.get_property("YELLOW", config.yellow);
544                                 config.preserve = input.tag.get_property("PRESERVELUMINOSITY", config.preserve);
545                                 config.lock_params = input.tag.get_property("LOCKPARAMS", config.lock_params);
546                         }
547                 }
548         }
549 //printf("ColorBalanceMain::read_data 2\n");