r665: Merged the official release 2.0.
[cinelerra_cv.git] / plugins / chromakey / chromakey.C
blobacc47f40d434d87eefc31f6f31bf83d8811857df
1 #include "bcdisplayinfo.h"
2 #include "chromakey.h"
3 #include "clip.h"
4 #include "defaults.h"
5 #include "filexml.h"
6 #include "guicast.h"
7 #include "keyframe.h"
8 #include "language.h"
9 #include "loadbalance.h"
10 #include "picon_png.h"
11 #include "plugincolors.h"
12 #include "pluginvclient.h"
13 #include "vframe.h"
15 #include <stdint.h>
16 #include <string.h>
22 ChromaKeyConfig::ChromaKeyConfig()
24         red = 0.0;
25         green = 0.0;
26         blue = 0.0;
27         threshold = 60.0;
28         use_value = 0;
29         slope = 100;
33 void ChromaKeyConfig::copy_from(ChromaKeyConfig &src)
35         red = src.red;
36         green = src.green;
37         blue = src.blue;
38         threshold = src.threshold;
39         use_value = src.use_value;
40         slope = src.slope;
43 int ChromaKeyConfig::equivalent(ChromaKeyConfig &src)
45         return (EQUIV(red, src.red) &&
46                 EQUIV(green, src.green) &&
47                 EQUIV(blue, src.blue) &&
48                 EQUIV(threshold, src.threshold) &&
49                 EQUIV(slope, src.slope) &&
50                 use_value == src.use_value);
53 void ChromaKeyConfig::interpolate(ChromaKeyConfig &prev, 
54         ChromaKeyConfig &next, 
55         int64_t prev_frame, 
56         int64_t next_frame, 
57         int64_t current_frame)
59         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
60         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
62         this->red = prev.red * prev_scale + next.red * next_scale;
63         this->green = prev.green * prev_scale + next.green * next_scale;
64         this->blue = prev.blue * prev_scale + next.blue * next_scale;
65         this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
66         this->slope = prev.slope * prev_scale + next.slope * next_scale;
67         this->use_value = prev.use_value;
70 int ChromaKeyConfig::get_color()
72         int red = (int)(CLIP(this->red, 0, 1) * 0xff);
73         int green = (int)(CLIP(this->green, 0, 1) * 0xff);
74         int blue = (int)(CLIP(this->blue, 0, 1) * 0xff);
75         return (red << 16) | (green << 8) | blue;
84 ChromaKeyWindow::ChromaKeyWindow(ChromaKey *plugin, int x, int y)
85  : BC_Window(plugin->gui_string, 
86         x, 
87         y, 
88         320, 
89         220, 
90         320, 
91         220, 
92         0, 
93         0,
94         1)
96         this->plugin = plugin;
97         color_thread = 0;
100 ChromaKeyWindow::~ChromaKeyWindow()
102         delete color_thread;
105 void ChromaKeyWindow::create_objects()
107         int x = 10, y = 10, x1 = 100;
109         BC_Title *title;
110         add_subwindow(title = new BC_Title(x, y, _("Color:")));
111         x += title->get_w() + 10;
112         add_subwindow(color = new ChromaKeyColor(plugin, this, x, y));
113         x += color->get_w() + 10;
114         add_subwindow(sample = new BC_SubWindow(x, y, 100, 50));
115         y += sample->get_h() + 10;
116         x = 10;
118         add_subwindow(new BC_Title(x, y, _("Slope:")));
119         add_subwindow(slope = new ChromaKeySlope(plugin, x1, y));
121         y += 30;
122         add_subwindow(new BC_Title(x, y, _("Threshold:")));
123         add_subwindow(threshold = new ChromaKeyThreshold(plugin, x1, y));
126         y += 30;
127         add_subwindow(use_value = new ChromaKeyUseValue(plugin, x1, y));
129         y += 30;
130         add_subwindow(use_colorpicker = new ChromaKeyUseColorPicker(plugin, this, x1, y));
132         color_thread = new ChromaKeyColorThread(plugin, this);
134         update_sample();
135         show_window();
136         flush();
139 void ChromaKeyWindow::update_sample()
141         sample->set_color(plugin->config.get_color());
142         sample->draw_box(0, 
143                 0, 
144                 sample->get_w(), 
145                 sample->get_h());
146         sample->set_color(BLACK);
147         sample->draw_rectangle(0, 
148                 0, 
149                 sample->get_w(), 
150                 sample->get_h());
151         sample->flash();
156 WINDOW_CLOSE_EVENT(ChromaKeyWindow)
163 ChromaKeyColor::ChromaKeyColor(ChromaKey *plugin, 
164         ChromaKeyWindow *gui, 
165         int x, 
166         int y)
167  : BC_GenericButton(x, 
168         y,
169         _("Color..."))
171         this->plugin = plugin;
172         this->gui = gui;
174 int ChromaKeyColor::handle_event()
176         gui->color_thread->start_window(
177                 plugin->config.get_color(),
178                 0xff);
179         return 1;
185 ChromaKeyThreshold::ChromaKeyThreshold(ChromaKey *plugin, int x, int y)
186  : BC_FSlider(x, 
187                         y,
188                         0,
189                         200, 
190                         200, 
191                         (float)0, 
192                         (float)100, 
193                         plugin->config.threshold)
195         this->plugin = plugin;
196         set_precision(0.01);
198 int ChromaKeyThreshold::handle_event()
200         plugin->config.threshold = get_value();
201         plugin->send_configure_change();
202         return 1;
206 ChromaKeySlope::ChromaKeySlope(ChromaKey *plugin, int x, int y)
207  : BC_FSlider(x, 
208                         y,
209                         0,
210                         200, 
211                         200, 
212                         (float)0, 
213                         (float)100, 
214                         plugin->config.slope)
216         this->plugin = plugin;
217         set_precision(0.01);
220 int ChromaKeySlope::handle_event()
222         plugin->config.slope = get_value();
223         plugin->send_configure_change();
224         return 1;
228 ChromaKeyUseValue::ChromaKeyUseValue(ChromaKey *plugin, int x, int y)
229  : BC_CheckBox(x, y, plugin->config.use_value, _("Use value"))
231         this->plugin = plugin;
233 int ChromaKeyUseValue::handle_event()
235         plugin->config.use_value = get_value();
236         plugin->send_configure_change();
237         return 1;
241 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker(ChromaKey *plugin, 
242         ChromaKeyWindow *gui,
243         int x, 
244         int y)
245  : BC_GenericButton(x, y, _("Use color picker"))
247         this->plugin = plugin;
248         this->gui = gui;
251 int ChromaKeyUseColorPicker::handle_event()
253         plugin->config.red = plugin->get_red();
254         plugin->config.green = plugin->get_green();
255         plugin->config.blue = plugin->get_blue();
256         gui->update_sample();
257         plugin->send_configure_change();
258         return 1;
264 ChromaKeyColorThread::ChromaKeyColorThread(ChromaKey *plugin, ChromaKeyWindow *gui)
265  : ColorThread(1, _("Inner color"))
267         this->plugin = plugin;
268         this->gui = gui;
271 int ChromaKeyColorThread::handle_new_color(int output, int alpha)
273         plugin->config.red = (float)(output & 0xff0000) / 0xff0000;
274         plugin->config.green = (float)(output & 0xff00) / 0xff00;
275         plugin->config.blue = (float)(output & 0xff) / 0xff;
276         gui->update_sample();
277         plugin->send_configure_change();
278         return 1;
288 PLUGIN_THREAD_OBJECT(ChromaKey, ChromaKeyThread, ChromaKeyWindow)
291 ChromaKeyServer::ChromaKeyServer(ChromaKey *plugin)
292  : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
294         this->plugin = plugin;
296 void ChromaKeyServer::init_packages()
298         for(int i = 0; i < get_total_packages(); i++)
299         {
300                 ChromaKeyPackage *pkg = (ChromaKeyPackage*)get_package(i);
301                 pkg->y1 = plugin->input->get_h() * i / get_total_packages();
302                 pkg->y2 = plugin->input->get_h() * (i + 1) / get_total_packages();
303         }
304         
306 LoadClient* ChromaKeyServer::new_client()
308         return new ChromaKeyUnit(plugin, this);
310 LoadPackage* ChromaKeyServer::new_package()
312         return new ChromaKeyPackage;
317 ChromaKeyPackage::ChromaKeyPackage()
318  : LoadPackage()
322 ChromaKeyUnit::ChromaKeyUnit(ChromaKey *plugin, ChromaKeyServer *server)
323  : LoadClient(server)
325         this->plugin = plugin;
329 void ChromaKeyUnit::process_package(LoadPackage *package)
331         ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
333         int w = plugin->input->get_w();
335         float h, s, v;
336         HSV::rgb_to_hsv(plugin->config.red, 
337                 plugin->config.green,
338                 plugin->config.blue,
339                 h,
340                 s,
341                 v);
342         float min_hue = h - plugin->config.threshold * 360 / 100;
343         float max_hue = h + plugin->config.threshold * 360 / 100;
346 #define RGB_TO_VALUE(r, g, b) \
347 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
349 #define SQR(x) ((x) * (x))
351         float value = RGB_TO_VALUE(plugin->config.red,
352                 plugin->config.green,
353                 plugin->config.blue);
354         float min_v = value - plugin->config.threshold / 100;
355         float max_v = value + plugin->config.threshold / 100;
356         float threshold = plugin->config.threshold / 100;
357         float red = plugin->config.red;
358         float green = plugin->config.green;
359         float blue = plugin->config.blue;
361         float run = plugin->config.slope / 100;
362         float threshold_run = threshold + run;
365 #define CHROMAKEY(type, components, max, use_yuv) \
366 { \
367         for(int i = pkg->y1; i < pkg->y2; i++) \
368         { \
369                 type *row = (type*)plugin->input->get_rows()[i]; \
371                 for(int j = 0; j < w; j++) \
372                 { \
373                         float a = 1; \
375 /* Test for value in range */ \
376                         if(plugin->config.use_value) \
377                         { \
378                                 float current_value; \
379                                 if(use_yuv) \
380                                 { \
381                                         float r = (float)row[0] / max; \
382                                         current_value = r; \
383                                 } \
384                                 else \
385                                 { \
386                                         float r = (float)row[0] / max; \
387                                         float g = (float)row[1] / max; \
388                                         float b = (float)row[2] / max; \
389                                         current_value = RGB_TO_VALUE(r, g, b); \
390                                 } \
392 /* Full transparency if in range */ \
393                                 if(current_value >= min_v && current_value < max_v) \
394                                 { \
395                                         a = 0; \
396                                 } \
397                                 else \
398 /* Phased out if below or above range */ \
399                                 if(current_value < min_v) \
400                                 { \
401                                         if(min_v - current_value < run) \
402                                                 a = (min_v - current_value) / run; \
403                                 } \
404                                 else \
405                                 if(current_value - max_v < run) \
406                                         a = (current_value - max_v) / run; \
407                         } \
408                         else \
409 /* Use color cube */ \
410                         { \
411                                 float r = (float)row[0] / max; \
412                                 float g = (float)row[1] / max; \
413                                 float b = (float)row[2] / max; \
414                                 if(use_yuv) \
415                                 { \
416 /* Convert pixel to RGB float */ \
417                                         float y = r; \
418                                         float u = g; \
419                                         float v = b; \
420                                         YUV::yuv_to_rgb_f(r, g, b, y, u - 0.5, v - 0.5); \
421                                 } \
423                                 float difference = sqrt(SQR(r - red) +  \
424                                         SQR(g - green) + \
425                                         SQR(b - blue)); \
427                                 if(difference < threshold) \
428                                 { \
429                                         a = 0; \
430                                 } \
431                                 else \
432                                 if(difference < threshold_run) \
433                                 { \
434                                         a = (difference - threshold) / run; \
435                                 } \
437                         } \
439 /* Multiply alpha and put back in frame */ \
440                         if(components == 4) \
441                         { \
442                                 row[3] = MIN((type)(a * max), row[3]); \
443                         } \
444                         else \
445                         if(use_yuv) \
446                         { \
447                                 row[0] = (type)(a * row[0]); \
448                                 row[1] = (type)(a * (row[1] - (max / 2 + 1)) + max / 2 + 1); \
449                                 row[2] = (type)(a * (row[2] - (max / 2 + 1)) + max / 2 + 1); \
450                         } \
451                         else \
452                         { \
453                                 row[0] = (type)(a * row[0]); \
454                                 row[1] = (type)(a * row[1]); \
455                                 row[2] = (type)(a * row[2]); \
456                         } \
458                         row += components; \
459                 } \
460         } \
466         switch(plugin->input->get_color_model())
467         {
468                 case BC_RGB_FLOAT:
469                         CHROMAKEY(float, 3, 1.0, 0);
470                         break;
471                 case BC_RGBA_FLOAT:
472                         CHROMAKEY(float, 4, 1.0, 0);
473                         break;
474                 case BC_RGB888:
475                         CHROMAKEY(unsigned char, 3, 0xff, 0);
476                         break;
477                 case BC_RGBA8888:
478                         CHROMAKEY(unsigned char, 4, 0xff, 0);
479                         break;
480                 case BC_YUV888:
481                         CHROMAKEY(unsigned char, 3, 0xff, 1);
482                         break;
483                 case BC_YUVA8888:
484                         CHROMAKEY(unsigned char, 4, 0xff, 1);
485                         break;
486                 case BC_YUV161616:
487                         CHROMAKEY(uint16_t, 3, 0xffff, 1);
488                         break;
489                 case BC_YUVA16161616:
490                         CHROMAKEY(uint16_t, 4, 0xffff, 1);
491                         break;
492         }
500 REGISTER_PLUGIN(ChromaKey)
504 ChromaKey::ChromaKey(PluginServer *server)
505  : PluginVClient(server)
507         PLUGIN_CONSTRUCTOR_MACRO
508         engine = 0;
511 ChromaKey::~ChromaKey()
513         PLUGIN_DESTRUCTOR_MACRO
514         if(engine) delete engine;
518 int ChromaKey::process_realtime(VFrame *input, VFrame *output)
520         load_configuration();
521         this->input = input;
522         this->output = output;
524         if(EQUIV(config.threshold, 0))
525         {
526                 if(input->get_rows()[0] != output->get_rows()[0])
527                         output->copy_from(input);
528         }
529         else
530         {
531                 if(!engine) engine = new ChromaKeyServer(this);
532                 engine->process_packages();
533         }
535         return 0;
538 char* ChromaKey::plugin_title() { return N_("Chroma key"); }
539 int ChromaKey::is_realtime() { return 1; }
541 NEW_PICON_MACRO(ChromaKey)
543 LOAD_CONFIGURATION_MACRO(ChromaKey, ChromaKeyConfig)
545 int ChromaKey::load_defaults()
547         char directory[BCTEXTLEN];
548 // set the default directory
549         sprintf(directory, "%schromakey.rc", BCASTDIR);
551 // load the defaults
552         defaults = new Defaults(directory);
553         defaults->load();
555         config.red = defaults->get("RED", config.red);
556         config.green = defaults->get("GREEN", config.green);
557         config.blue = defaults->get("BLUE", config.blue);
558         config.threshold = defaults->get("THRESHOLD", config.threshold);
559         config.slope = defaults->get("SLOPE", config.slope);
560         config.use_value = defaults->get("USE_VALUE", config.use_value);
561         return 0;
564 int ChromaKey::save_defaults()
566         defaults->update("RED", config.red);
567         defaults->update("GREEN", config.green);
568         defaults->update("BLUE", config.blue);
569     defaults->update("THRESHOLD", config.threshold);
570     defaults->update("SLOPE", config.slope);
571     defaults->update("USE_VALUE", config.use_value);
572         defaults->save();
573         return 0;
576 void ChromaKey::save_data(KeyFrame *keyframe)
578         FileXML output;
579         output.set_shared_string(keyframe->data, MESSAGESIZE);
580         output.tag.set_title("CHROMAKEY");
581         output.tag.set_property("RED", config.red);
582         output.tag.set_property("GREEN", config.green);
583         output.tag.set_property("BLUE", config.blue);
584         output.tag.set_property("THRESHOLD", config.threshold);
585         output.tag.set_property("SLOPE", config.slope);
586         output.tag.set_property("USE_VALUE", config.use_value);
587         output.append_tag();
588         output.terminate_string();
591 void ChromaKey::read_data(KeyFrame *keyframe)
593         FileXML input;
595         input.set_shared_string(keyframe->data, strlen(keyframe->data));
597         while(!input.read_tag())
598         {
599                 if(input.tag.title_is("CHROMAKEY"))
600                 {
601                         config.red = input.tag.get_property("RED", config.red);
602                         config.green = input.tag.get_property("GREEN", config.green);
603                         config.blue = input.tag.get_property("BLUE", config.blue);
604                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
605                         config.slope = input.tag.get_property("SLOPE", config.slope);
606                         config.use_value = input.tag.get_property("USE_VALUE", config.use_value);
607                 }
608         }
612 SHOW_GUI_MACRO(ChromaKey, ChromaKeyThread)
614 SET_STRING_MACRO(ChromaKey)
616 RAISE_WINDOW_MACRO(ChromaKey)
618 void ChromaKey::update_gui()
620         if(thread)
621         {
622                 load_configuration();
623                 thread->window->lock_window();
624                 thread->window->threshold->update(config.threshold);
625                 thread->window->slope->update(config.slope);
626                 thread->window->use_value->update(config.use_value);
627                 thread->window->update_sample();
629                 thread->window->unlock_window();
630         }