r373: Merged the official release 1.2.1.
[cinelerra_cv.git] / plugins / colorbalance / colorbalance.C
blob5ecd05086792eacf088a2a57e789fa34beebd752
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 #define SQR(a) ((a) * (a))
12 REGISTER_PLUGIN(ColorBalanceMain)
16 ColorBalanceConfig::ColorBalanceConfig()
18         cyan = 0;
19         magenta = 0;
20         yellow = 0;
21         lock_params = 0;
22     preserve = 0;
25 int ColorBalanceConfig::equivalent(ColorBalanceConfig &that)
27         return (cyan == that.cyan && 
28                 magenta == that.magenta && 
29                 yellow == that.yellow && 
30                 lock_params == that.lock_params && 
31         preserve == that.preserve);
34 void ColorBalanceConfig::copy_from(ColorBalanceConfig &that)
36         cyan = that.cyan;
37         magenta = that.magenta;
38         yellow = that.yellow;
39         lock_params = that.lock_params;
40     preserve = that.preserve;
43 void ColorBalanceConfig::interpolate(ColorBalanceConfig &prev, 
44         ColorBalanceConfig &next, 
45         int64_t prev_frame, 
46         int64_t next_frame, 
47         int64_t current_frame)
49         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
50         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
52         this->cyan = prev.cyan * prev_scale + next.cyan * next_scale;
53         this->magenta = prev.magenta * prev_scale + next.magenta * next_scale;
54         this->yellow = prev.yellow * prev_scale + next.yellow * next_scale;
66 ColorBalanceEngine::ColorBalanceEngine(ColorBalanceMain *plugin)
67  : Thread()
69         this->plugin = plugin;
70         last_frame = 0;
71         input_lock.lock();
72         output_lock.lock();
73         set_synchronous(1);
76 ColorBalanceEngine::~ColorBalanceEngine()
78         last_frame = 1;
79         input_lock.unlock();
80         Thread::join();
84 int ColorBalanceEngine::start_process_frame(VFrame *output, VFrame *input, int row_start, int row_end)
86         this->output = output;
87         this->input = input;
88         this->row_start = row_start;
89         this->row_end = row_end;
90         input_lock.unlock();
91         return 0;
95 int ColorBalanceEngine::wait_process_frame()
97         output_lock.lock();
98         return 0;
101 float ColorBalanceEngine::calculate_highlight(float in)
103         return 0.667 * (1.0 - SQR((in - 0.5) / 0.5));
106 float ColorBalanceEngine::calculate_r(float r)
108         return r + cyan_f * calculate_highlight(r);
111 float ColorBalanceEngine::calculate_g(float g)
113         return g + magenta_f * calculate_highlight(g);
116 float ColorBalanceEngine::calculate_b(float b)
118         return b + yellow_f * calculate_highlight(b);
121 void ColorBalanceEngine::run()
123         while(1)
124         {
125                 input_lock.lock();
126                 if(last_frame)
127                 {
128                         output_lock.unlock();
129                         return;
130                 }
131                 
132 #define PROCESS(yuvtorgb,  \
133         rgbtoyuv,  \
134         r_lookup,  \
135         g_lookup,  \
136         b_lookup,  \
137         type,  \
138         max,  \
139         components,  \
140         do_yuv) \
141 { \
142         int i, j, k; \
143         int y, cb, cr, r, g, b, r_n, g_n, b_n; \
144     float h, s, v, h_old, s_old, r_f, g_f, b_f; \
145         type **input_rows, **output_rows; \
146         input_rows = (type**)input->get_rows(); \
147         output_rows = (type**)output->get_rows(); \
149         for(j = row_start; j < row_end; j++) \
150         { \
151                 for(k = 0; k < input->get_w() * components; k += components) \
152                 { \
153                         if(do_yuv) \
154                         { \
155                                 y = input_rows[j][k]; \
156                                 cb = input_rows[j][k + 1]; \
157                                 cr = input_rows[j][k + 2]; \
158                                 yuvtorgb(r, g, b, y, cb, cr); \
159                         } \
160                         else \
161                         { \
162                 r = input_rows[j][k]; \
163                 g = input_rows[j][k + 1]; \
164                 b = input_rows[j][k + 2]; \
165                         } \
167             r = CLAMP(r, 0, max-1); g = CLAMP(g, 0, max-1); b = CLAMP(b, 0, max-1); \
168             r_n = plugin->r_lookup[r]; \
169             g_n = plugin->g_lookup[g]; \
170             b_n = plugin->b_lookup[b]; \
172                         if(plugin->config.preserve) \
173             { \
174                                 HSV::rgb_to_hsv((float)r_n, (float)g_n, (float)b_n, h, s, v); \
175                                 HSV::rgb_to_hsv((float)r, (float)g, (float)b, h_old, s_old, v); \
176                 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
177                 r = (type)r_f; \
178                 g = (type)g_f; \
179                 b = (type)b_f; \
180                         } \
181             else \
182             { \
183                 r = r_n; \
184                 g = g_n; \
185                 b = b_n; \
186                         } \
188                         if(do_yuv) \
189                         { \
190                                 rgbtoyuv(CLAMP(r, 0, max), CLAMP(g, 0, max), CLAMP(b, 0, max), y, cb, cr); \
191                 output_rows[j][k] = y; \
192                 output_rows[j][k + 1] = cb; \
193                 output_rows[j][k + 2] = cr; \
194                         } \
195                         else \
196                         { \
197                 output_rows[j][k] = CLAMP(r, 0, max); \
198                 output_rows[j][k + 1] = CLAMP(g, 0, max); \
199                 output_rows[j][k + 2] = CLAMP(b, 0, max); \
200                         } \
201                 } \
202         } \
205 #define PROCESS_F(components) \
206 { \
207         int i, j, k; \
208         float y, cb, cr, r, g, b, r_n, g_n, b_n; \
209     float h, s, v, h_old, s_old, r_f, g_f, b_f; \
210         float **input_rows, **output_rows; \
211         input_rows = (float**)input->get_rows(); \
212         output_rows = (float**)output->get_rows(); \
213         cyan_f = (float)plugin->config.cyan / 100; \
214         magenta_f = (float)plugin->config.magenta / 100; \
215         yellow_f = (float)plugin->config.yellow / 100; \
217         for(j = row_start; j < row_end; j++) \
218         { \
219                 for(k = 0; k < input->get_w() * components; k += components) \
220                 { \
221             r = input_rows[j][k]; \
222             g = input_rows[j][k + 1]; \
223             b = input_rows[j][k + 2]; \
225             r_n = calculate_r(r); \
226             g_n = calculate_g(g); \
227             b_n = calculate_b(b); \
229                         if(plugin->config.preserve) \
230             { \
231                                 HSV::rgb_to_hsv(r_n, g_n, b_n, h, s, v); \
232                                 HSV::rgb_to_hsv(r, g, b, h_old, s_old, v); \
233                 HSV::hsv_to_rgb(r_f, g_f, b_f, h, s, v); \
234                 r = (float)r_f; \
235                 g = (float)g_f; \
236                 b = (float)b_f; \
237                         } \
238             else \
239             { \
240                 r = r_n; \
241                 g = g_n; \
242                 b = b_n; \
243                         } \
245             output_rows[j][k] = r; \
246             output_rows[j][k + 1] = g; \
247             output_rows[j][k + 2] = b; \
248                 } \
249         } \
252                 switch(input->get_color_model())
253                 {
254                         case BC_RGB888:
255                                 PROCESS(yuv.yuv_to_rgb_8, 
256                                         yuv.rgb_to_yuv_8, 
257                                         r_lookup_8, 
258                                         g_lookup_8, 
259                                         b_lookup_8, 
260                                         unsigned char, 
261                                         0xff, 
262                                         3,
263                                         0);
264                                 break;
266                         case BC_RGB_FLOAT:
267                                 PROCESS_F(3);
268                                 break;
270                         case BC_YUV888:
271                                 PROCESS(yuv.yuv_to_rgb_8, 
272                                         yuv.rgb_to_yuv_8, 
273                                         r_lookup_8, 
274                                         g_lookup_8, 
275                                         b_lookup_8, 
276                                         unsigned char, 
277                                         0xff, 
278                                         3,
279                                         1);
280                                 break;
281                         
282                         case BC_RGBA_FLOAT:
283                                 PROCESS_F(4);
284                                 break;
286                         case BC_RGBA8888:
287                                 PROCESS(yuv.yuv_to_rgb_8, 
288                                         yuv.rgb_to_yuv_8, 
289                                         r_lookup_8, 
290                                         g_lookup_8, 
291                                         b_lookup_8, 
292                                         unsigned char, 
293                                         0xff, 
294                                         4,
295                                         0);
296                                 break;
298                         case BC_YUVA8888:
299                                 PROCESS(yuv.yuv_to_rgb_8, 
300                                         yuv.rgb_to_yuv_8, 
301                                         r_lookup_8, 
302                                         g_lookup_8, 
303                                         b_lookup_8, 
304                                         unsigned char, 
305                                         0xff, 
306                                         4,
307                                         1);
308                                 break;
309                         
310                         case BC_RGB161616:
311                                 PROCESS(yuv.yuv_to_rgb_16, 
312                                         yuv.rgb_to_yuv_16, 
313                                         r_lookup_16, 
314                                         g_lookup_16, 
315                                         b_lookup_16, 
316                                         u_int16_t, 
317                                         0xffff, 
318                                         3,
319                                         0);
320                                 break;
322                         case BC_YUV161616:
323                                 PROCESS(yuv.yuv_to_rgb_16, 
324                                         yuv.rgb_to_yuv_16, 
325                                         r_lookup_16, 
326                                         g_lookup_16, 
327                                         b_lookup_16, 
328                                         u_int16_t, 
329                                         0xffff, 
330                                         3,
331                                         1);
332                                 break;
334                         case BC_RGBA16161616:
335                                 PROCESS(yuv.yuv_to_rgb_16, 
336                                         yuv.rgb_to_yuv_16, 
337                                         r_lookup_16, 
338                                         g_lookup_16, 
339                                         b_lookup_16, 
340                                         u_int16_t, 
341                                         0xffff, 
342                                         4,
343                                         0);
344                                 break;
346                         case BC_YUVA16161616:
347                                 PROCESS(yuv.yuv_to_rgb_16, 
348                                         yuv.rgb_to_yuv_16, 
349                                         r_lookup_16, 
350                                         g_lookup_16, 
351                                         b_lookup_16, 
352                                         u_int16_t, 
353                                         0xffff, 
354                                         4,
355                                         1);
356                                 break;
357                 }
359                 
360                 
361                 output_lock.unlock();
362         }
368 ColorBalanceMain::ColorBalanceMain(PluginServer *server)
369  : PluginVClient(server)
371         need_reconfigure = 1;
372         engine = 0;
373         PLUGIN_CONSTRUCTOR_MACRO
376 ColorBalanceMain::~ColorBalanceMain()
378         PLUGIN_DESTRUCTOR_MACRO
381         if(engine)
382         {
383                 for(int i = 0; i < total_engines; i++)
384                 {
385                         delete engine[i];
386                 }
387                 delete [] engine;
388         }
391 char* ColorBalanceMain::plugin_title() { return N_("Color Balance"); }
392 int ColorBalanceMain::is_realtime() { return 1; }
395 int ColorBalanceMain::reconfigure()
397         int r_n, g_n, b_n;
398     double *cyan_red_transfer;
399     double *magenta_green_transfer;
400     double *yellow_blue_transfer;
403 #define RECONFIGURE(highlights_add, highlights_sub, r_lookup, g_lookup, b_lookup, max) \
404     cyan_red_transfer = config.cyan > 0 ? highlights_add : highlights_sub; \
405     magenta_green_transfer = config.magenta > 0 ? highlights_add : highlights_sub; \
406     yellow_blue_transfer = config.yellow > 0 ? highlights_add : highlights_sub; \
407         for(int i = 0; i <= max; i++) \
408     { \
409         r_n = g_n = b_n = i; \
410             r_n += (int)(config.cyan / 100 * max * cyan_red_transfer[r_n]); \
411             g_n += (int)(config.magenta / 100 * max  * magenta_green_transfer[g_n]); \
412             b_n += (int)(config.yellow / 100 * max  * yellow_blue_transfer[b_n]); \
414         if(r_n > max) r_n = max; \
415         else \
416                 if(r_n < 0) r_n = 0; \
417         if(g_n > max) g_n = max; \
418         else \
419         if(g_n < 0) g_n = 0; \
420         if(b_n > max) b_n = max; \
421         else \
422         if(b_n < 0) b_n = 0; \
424         r_lookup[i] = r_n; \
425         g_lookup[i] = g_n; \
426         b_lookup[i] = b_n; \
427     }
430         RECONFIGURE(highlights_add_8, highlights_sub_8, r_lookup_8, g_lookup_8, b_lookup_8, 0xff);
431         RECONFIGURE(highlights_add_16, highlights_sub_16, r_lookup_16, g_lookup_16, b_lookup_16, 0xffff);
432         
433         return 0;
436 int ColorBalanceMain::test_boundary(float &value)
438         if(value < -100) value = -100;
439     if(value > 100) value = 100;
440         return 0;
443 int ColorBalanceMain::synchronize_params(ColorBalanceSlider *slider, float difference)
445         if(thread && config.lock_params)
446     {
447             if(slider != thread->window->cyan)
448         {
449                 config.cyan += difference;
450             test_boundary(config.cyan);
451                 thread->window->cyan->update((int64_t)config.cyan);
452         }
453             if(slider != thread->window->magenta)
454         {
455                 config.magenta += difference;
456             test_boundary(config.magenta);
457                 thread->window->magenta->update((int64_t)config.magenta);
458         }
459             if(slider != thread->window->yellow)
460         {
461                 config.yellow += difference;
462             test_boundary(config.yellow);
463                 thread->window->yellow->update((int64_t)config.yellow);
464         }
465     }
466         return 0;
474 NEW_PICON_MACRO(ColorBalanceMain)
475 LOAD_CONFIGURATION_MACRO(ColorBalanceMain, ColorBalanceConfig)
476 SHOW_GUI_MACRO(ColorBalanceMain, ColorBalanceThread)
477 RAISE_WINDOW_MACRO(ColorBalanceMain)
478 SET_STRING_MACRO(ColorBalanceMain)
484 int ColorBalanceMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
486         need_reconfigure |= load_configuration();
488 //printf("ColorBalanceMain::process_realtime 1 %d\n", need_reconfigure);
489         if(need_reconfigure)
490         {
491                 int i;
493                 if(!engine)
494                 {
495 #define CALCULATE_HIGHLIGHTS(add, sub, max) \
496 for(i = 0; i < max; i++) \
497 { \
498         add[i] = sub[i] = 0.667 * (1 - SQR(((double)i - (max / 2)) / (max / 2))); \
501                         CALCULATE_HIGHLIGHTS(highlights_add_8, highlights_sub_8, 0xff);
502                         CALCULATE_HIGHLIGHTS(highlights_add_16, highlights_sub_16, 0xffff);
504                         total_engines = PluginClient::smp > 1 ? 2 : 1;
505                         engine = new ColorBalanceEngine*[total_engines];
506                         for(int i = 0; i < total_engines; i++)
507                         {
508                                 engine[i] = new ColorBalanceEngine(this);
509                                 engine[i]->start();
510                         }
511                 }
513                 reconfigure();
514                 need_reconfigure = 0;
515         }
518         if(config.cyan != 0 || config.magenta != 0 || config.yellow != 0)
519         {
520                 int64_t row_step = input_ptr->get_h() / total_engines + 1;
521                 for(int i = 0; i < input_ptr->get_h(); i += row_step)
522                 {
523                         if(i + row_step > input_ptr->get_h()) 
524                                 row_step = input_ptr->get_h() - i;
525                         engine[i]->start_process_frame(output_ptr, 
526                                 input_ptr, 
527                                 i, 
528                                 i + row_step);
529                 }
531                 for(int i = 0; i < total_engines; i++)
532                 {
533                         engine[i]->wait_process_frame();
534                 }
535         }
536         else
537 // Data never processed so copy if necessary
538         if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
539         {
540                 output_ptr->copy_from(input_ptr);
541         }
542 //printf("ColorBalanceMain::process_realtime 2\n");
543         return 0;
548 int ColorBalanceMain::load_defaults()
550         char directory[1024], string[1024];
551 // set the default directory
552         sprintf(directory, "%scolorbalance.rc", BCASTDIR);
554 // load the defaults
555         defaults = new Defaults(directory);
556         defaults->load();
558         config.cyan = defaults->get("CYAN", config.cyan);
559         config.magenta = defaults->get("MAGENTA", config.magenta);
560         config.yellow = defaults->get("YELLOW", config.yellow);
561         config.preserve = defaults->get("PRESERVELUMINOSITY", config.preserve);
562         config.lock_params = defaults->get("LOCKPARAMS", config.lock_params);
563         return 0;
566 int ColorBalanceMain::save_defaults()
568         defaults->update("CYAN", config.cyan);
569         defaults->update("MAGENTA", config.magenta);
570         defaults->update("YELLOW", config.yellow);
571         defaults->update("PRESERVELUMINOSITY", config.preserve);
572         defaults->update("LOCKPARAMS", config.lock_params);
573         defaults->save();
574         return 0;
577 void ColorBalanceMain::save_data(KeyFrame *keyframe)
579         FileXML output;
581 // cause data to be stored directly in text
582         output.set_shared_string(keyframe->data, MESSAGESIZE);
583         output.tag.set_title("COLORBALANCE");
584         output.tag.set_property("CYAN", config.cyan);
585         output.tag.set_property("MAGENTA",  config.magenta);
586         output.tag.set_property("YELLOW",  config.yellow);
587         output.tag.set_property("PRESERVELUMINOSITY",  config.preserve);
588         output.tag.set_property("LOCKPARAMS",  config.lock_params);
589         output.append_tag();
590         output.terminate_string();
593 void ColorBalanceMain::read_data(KeyFrame *keyframe)
595         FileXML input;
597 //printf("ColorBalanceMain::read_data 1\n");
598         input.set_shared_string(keyframe->data, strlen(keyframe->data));
599 //printf("ColorBalanceMain::read_data 1\n");
601         int result = 0;
602 //printf("ColorBalanceMain::read_data 1\n");
604         while(!result)
605         {
606                 result = input.read_tag();
608                 if(!result)
609                 {
610                         if(input.tag.title_is("COLORBALANCE"))
611                         {
612                                 config.cyan = input.tag.get_property("CYAN", config.cyan);
613                                 config.magenta = input.tag.get_property("MAGENTA", config.magenta);
614                                 config.yellow = input.tag.get_property("YELLOW", config.yellow);
615                                 config.preserve = input.tag.get_property("PRESERVELUMINOSITY", config.preserve);
616                                 config.lock_params = input.tag.get_property("LOCKPARAMS", config.lock_params);
617                         }
618                 }
619         }
620 //printf("ColorBalanceMain::read_data 2\n");