r955: Fix the Diffkey icon.
[cinelerra_cv/ct.git] / plugins / chromakey / chromakey.C
blob320c4303dfb59eedd926fd6fe99070c47132fe2e
1 #include "bcdisplayinfo.h"
2 #include "bcsignals.h"
3 #include "chromakey.h"
4 #include "clip.h"
5 #include "bchash.h"
6 #include "filexml.h"
7 #include "guicast.h"
8 #include "keyframe.h"
9 #include "language.h"
10 #include "loadbalance.h"
11 #include "picon_png.h"
12 #include "playback3d.h"
13 #include "plugincolors.h"
14 #include "pluginvclient.h"
15 #include "vframe.h"
17 #include <stdint.h>
18 #include <string.h>
24 ChromaKeyConfig::ChromaKeyConfig()
26         red = 0.0;
27         green = 0.0;
28         blue = 0.0;
29         threshold = 60.0;
30         use_value = 0;
31         slope = 100;
35 void ChromaKeyConfig::copy_from(ChromaKeyConfig &src)
37         red = src.red;
38         green = src.green;
39         blue = src.blue;
40         threshold = src.threshold;
41         use_value = src.use_value;
42         slope = src.slope;
45 int ChromaKeyConfig::equivalent(ChromaKeyConfig &src)
47         return (EQUIV(red, src.red) &&
48                 EQUIV(green, src.green) &&
49                 EQUIV(blue, src.blue) &&
50                 EQUIV(threshold, src.threshold) &&
51                 EQUIV(slope, src.slope) &&
52                 use_value == src.use_value);
55 void ChromaKeyConfig::interpolate(ChromaKeyConfig &prev, 
56         ChromaKeyConfig &next, 
57         int64_t prev_frame, 
58         int64_t next_frame, 
59         int64_t current_frame)
61         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
62         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
64         this->red = prev.red * prev_scale + next.red * next_scale;
65         this->green = prev.green * prev_scale + next.green * next_scale;
66         this->blue = prev.blue * prev_scale + next.blue * next_scale;
67         this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
68         this->slope = prev.slope * prev_scale + next.slope * next_scale;
69         this->use_value = prev.use_value;
72 int ChromaKeyConfig::get_color()
74         int red = (int)(CLIP(this->red, 0, 1) * 0xff);
75         int green = (int)(CLIP(this->green, 0, 1) * 0xff);
76         int blue = (int)(CLIP(this->blue, 0, 1) * 0xff);
77         return (red << 16) | (green << 8) | blue;
86 ChromaKeyWindow::ChromaKeyWindow(ChromaKey *plugin, int x, int y)
87  : BC_Window(plugin->gui_string, 
88         x, 
89         y, 
90         320, 
91         220, 
92         320, 
93         220, 
94         0, 
95         0,
96         1)
98         this->plugin = plugin;
99         color_thread = 0;
102 ChromaKeyWindow::~ChromaKeyWindow()
104         delete color_thread;
107 void ChromaKeyWindow::create_objects()
109         int x = 10, y = 10, x1 = 100;
111         BC_Title *title;
112         add_subwindow(title = new BC_Title(x, y, _("Color:")));
113         x += title->get_w() + 10;
114         add_subwindow(color = new ChromaKeyColor(plugin, this, x, y));
115         x += color->get_w() + 10;
116         add_subwindow(sample = new BC_SubWindow(x, y, 100, 50));
117         y += sample->get_h() + 10;
118         x = 10;
120         add_subwindow(new BC_Title(x, y, _("Slope:")));
121         add_subwindow(slope = new ChromaKeySlope(plugin, x1, y));
123         y += 30;
124         add_subwindow(new BC_Title(x, y, _("Threshold:")));
125         add_subwindow(threshold = new ChromaKeyThreshold(plugin, x1, y));
128         y += 30;
129         add_subwindow(use_value = new ChromaKeyUseValue(plugin, x1, y));
131         y += 30;
132         add_subwindow(use_colorpicker = new ChromaKeyUseColorPicker(plugin, this, x1, y));
134         color_thread = new ChromaKeyColorThread(plugin, this);
136         update_sample();
137         show_window();
138         flush();
141 void ChromaKeyWindow::update_sample()
143         sample->set_color(plugin->config.get_color());
144         sample->draw_box(0, 
145                 0, 
146                 sample->get_w(), 
147                 sample->get_h());
148         sample->set_color(BLACK);
149         sample->draw_rectangle(0, 
150                 0, 
151                 sample->get_w(), 
152                 sample->get_h());
153         sample->flash();
158 WINDOW_CLOSE_EVENT(ChromaKeyWindow)
165 ChromaKeyColor::ChromaKeyColor(ChromaKey *plugin, 
166         ChromaKeyWindow *gui, 
167         int x, 
168         int y)
169  : BC_GenericButton(x, 
170         y,
171         _("Color..."))
173         this->plugin = plugin;
174         this->gui = gui;
176 int ChromaKeyColor::handle_event()
178         gui->color_thread->start_window(
179                 plugin->config.get_color(),
180                 0xff);
181         return 1;
187 ChromaKeyThreshold::ChromaKeyThreshold(ChromaKey *plugin, int x, int y)
188  : BC_FSlider(x, 
189                         y,
190                         0,
191                         200, 
192                         200, 
193                         (float)0, 
194                         (float)100, 
195                         plugin->config.threshold)
197         this->plugin = plugin;
198         set_precision(0.01);
200 int ChromaKeyThreshold::handle_event()
202         plugin->config.threshold = get_value();
203         plugin->send_configure_change();
204         return 1;
208 ChromaKeySlope::ChromaKeySlope(ChromaKey *plugin, int x, int y)
209  : BC_FSlider(x, 
210                         y,
211                         0,
212                         200, 
213                         200, 
214                         (float)0, 
215                         (float)100, 
216                         plugin->config.slope)
218         this->plugin = plugin;
219         set_precision(0.01);
222 int ChromaKeySlope::handle_event()
224         plugin->config.slope = get_value();
225         plugin->send_configure_change();
226         return 1;
230 ChromaKeyUseValue::ChromaKeyUseValue(ChromaKey *plugin, int x, int y)
231  : BC_CheckBox(x, y, plugin->config.use_value, _("Use value"))
233         this->plugin = plugin;
235 int ChromaKeyUseValue::handle_event()
237         plugin->config.use_value = get_value();
238         plugin->send_configure_change();
239         return 1;
243 ChromaKeyUseColorPicker::ChromaKeyUseColorPicker(ChromaKey *plugin, 
244         ChromaKeyWindow *gui,
245         int x, 
246         int y)
247  : BC_GenericButton(x, y, _("Use color picker"))
249         this->plugin = plugin;
250         this->gui = gui;
253 int ChromaKeyUseColorPicker::handle_event()
255         plugin->config.red = plugin->get_red();
256         plugin->config.green = plugin->get_green();
257         plugin->config.blue = plugin->get_blue();
258         gui->update_sample();
259         plugin->send_configure_change();
260         return 1;
266 ChromaKeyColorThread::ChromaKeyColorThread(ChromaKey *plugin, ChromaKeyWindow *gui)
267  : ColorThread(1, _("Inner color"))
269         this->plugin = plugin;
270         this->gui = gui;
273 int ChromaKeyColorThread::handle_new_color(int output, int alpha)
275         plugin->config.red = (float)(output & 0xff0000) / 0xff0000;
276         plugin->config.green = (float)(output & 0xff00) / 0xff00;
277         plugin->config.blue = (float)(output & 0xff) / 0xff;
278         gui->update_sample();
279         plugin->send_configure_change();
280         return 1;
290 PLUGIN_THREAD_OBJECT(ChromaKey, ChromaKeyThread, ChromaKeyWindow)
293 ChromaKeyServer::ChromaKeyServer(ChromaKey *plugin)
294  : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
296         this->plugin = plugin;
298 void ChromaKeyServer::init_packages()
300         for(int i = 0; i < get_total_packages(); i++)
301         {
302                 ChromaKeyPackage *pkg = (ChromaKeyPackage*)get_package(i);
303                 pkg->y1 = plugin->input->get_h() * i / get_total_packages();
304                 pkg->y2 = plugin->input->get_h() * (i + 1) / get_total_packages();
305         }
306         
308 LoadClient* ChromaKeyServer::new_client()
310         return new ChromaKeyUnit(plugin, this);
312 LoadPackage* ChromaKeyServer::new_package()
314         return new ChromaKeyPackage;
319 ChromaKeyPackage::ChromaKeyPackage()
320  : LoadPackage()
324 ChromaKeyUnit::ChromaKeyUnit(ChromaKey *plugin, ChromaKeyServer *server)
325  : LoadClient(server)
327         this->plugin = plugin;
331 void ChromaKeyUnit::process_package(LoadPackage *package)
333         ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
335         int w = plugin->input->get_w();
337         float h, s, v;
338         HSV::rgb_to_hsv(plugin->config.red, 
339                 plugin->config.green,
340                 plugin->config.blue,
341                 h,
342                 s,
343                 v);
344         float min_hue = h - plugin->config.threshold * 360 / 100;
345         float max_hue = h + plugin->config.threshold * 360 / 100;
348 #define RGB_TO_VALUE(r, g, b) \
349 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
351 #define SQR(x) ((x) * (x))
353 #define OUTER_VARIABLES(plugin) \
354         YUV yuv; \
355         float value = RGB_TO_VALUE(plugin->config.red, \
356                 plugin->config.green, \
357                 plugin->config.blue); \
358         float threshold = plugin->config.threshold / 100; \
359         float min_v = value - threshold; \
360         float max_v = value + threshold; \
361         float r_key = plugin->config.red; \
362         float g_key = plugin->config.green; \
363         float b_key = plugin->config.blue; \
364         int y_key, u_key, v_key; \
365         yuv.rgb_to_yuv_8((int)(r_key * 0xff), (int)(g_key * 0xff), (int)(b_key * 0xff), y_key, u_key, v_key); \
366         float run = plugin->config.slope / 100; \
367         float threshold_run = threshold + run;
369         OUTER_VARIABLES(plugin)
373 #define CHROMAKEY(type, components, max, use_yuv) \
374 { \
375         for(int i = pkg->y1; i < pkg->y2; i++) \
376         { \
377                 type *row = (type*)plugin->input->get_rows()[i]; \
379                 for(int j = 0; j < w; j++) \
380                 { \
381                         float a = 1; \
383 /* Test for value in range */ \
384                         if(plugin->config.use_value) \
385                         { \
386                                 float current_value; \
387                                 if(use_yuv) \
388                                 { \
389                                         float r = (float)row[0] / max; \
390                                         current_value = r; \
391                                 } \
392                                 else \
393                                 { \
394                                         float r = (float)row[0] / max; \
395                                         float g = (float)row[1] / max; \
396                                         float b = (float)row[2] / max; \
397                                         current_value = RGB_TO_VALUE(r, g, b); \
398                                 } \
400 /* Full transparency if in range */ \
401                                 if(current_value >= min_v && current_value < max_v) \
402                                 { \
403                                         a = 0; \
404                                 } \
405                                 else \
406 /* Phased out if below or above range */ \
407                                 if(current_value < min_v) \
408                                 { \
409                                         if(min_v - current_value < run) \
410                                                 a = (min_v - current_value) / run; \
411                                 } \
412                                 else \
413                                 if(current_value - max_v < run) \
414                                         a = (current_value - max_v) / run; \
415                         } \
416                         else \
417 /* Use color cube */ \
418                         { \
419                                 float difference; \
420                                 if(use_yuv) \
421                                 { \
422                                         type y = row[0]; \
423                                         type u = row[1]; \
424                                         type v = row[2]; \
425                                         difference = sqrt(SQR(y - y_key) + \
426                                                 SQR(u - u_key) + \
427                                                 SQR(v - v_key)) / max; \
428                                 } \
429                                 else \
430                                 { \
431                                         float r = (float)row[0] / max; \
432                                         float g = (float)row[1] / max; \
433                                         float b = (float)row[2] / max; \
434                                         difference = sqrt(SQR(r - r_key) +  \
435                                                 SQR(g - g_key) + \
436                                                 SQR(b - b_key)); \
437                                 } \
438                                 if(difference < threshold) \
439                                 { \
440                                         a = 0; \
441                                 } \
442                                 else \
443                                 if(difference < threshold_run) \
444                                 { \
445                                         a = (difference - threshold) / run; \
446                                 } \
448                         } \
450 /* Multiply alpha and put back in frame */ \
451                         if(components == 4) \
452                         { \
453                                 row[3] = MIN((type)(a * max), row[3]); \
454                         } \
455                         else \
456                         if(use_yuv) \
457                         { \
458                                 row[0] = (type)(a * row[0]); \
459                                 row[1] = (type)(a * (row[1] - (max / 2 + 1)) + max / 2 + 1); \
460                                 row[2] = (type)(a * (row[2] - (max / 2 + 1)) + max / 2 + 1); \
461                         } \
462                         else \
463                         { \
464                                 row[0] = (type)(a * row[0]); \
465                                 row[1] = (type)(a * row[1]); \
466                                 row[2] = (type)(a * row[2]); \
467                         } \
469                         row += components; \
470                 } \
471         } \
477         switch(plugin->input->get_color_model())
478         {
479                 case BC_RGB_FLOAT:
480                         CHROMAKEY(float, 3, 1.0, 0);
481                         break;
482                 case BC_RGBA_FLOAT:
483                         CHROMAKEY(float, 4, 1.0, 0);
484                         break;
485                 case BC_RGB888:
486                         CHROMAKEY(unsigned char, 3, 0xff, 0);
487                         break;
488                 case BC_RGBA8888:
489                         CHROMAKEY(unsigned char, 4, 0xff, 0);
490                         break;
491                 case BC_YUV888:
492                         CHROMAKEY(unsigned char, 3, 0xff, 1);
493                         break;
494                 case BC_YUVA8888:
495                         CHROMAKEY(unsigned char, 4, 0xff, 1);
496                         break;
497                 case BC_YUV161616:
498                         CHROMAKEY(uint16_t, 3, 0xffff, 1);
499                         break;
500                 case BC_YUVA16161616:
501                         CHROMAKEY(uint16_t, 4, 0xffff, 1);
502                         break;
503         }
511 REGISTER_PLUGIN(ChromaKey)
515 ChromaKey::ChromaKey(PluginServer *server)
516  : PluginVClient(server)
518         PLUGIN_CONSTRUCTOR_MACRO
519         engine = 0;
522 ChromaKey::~ChromaKey()
524         PLUGIN_DESTRUCTOR_MACRO
525         delete engine;
529 int ChromaKey::process_buffer(VFrame *frame,
530                 int64_t start_position,
531                 double frame_rate)
533 SET_TRACE
535         load_configuration();
536         this->input = frame;
537         this->output = frame;
539         read_frame(frame, 
540                 0, 
541                 start_position, 
542                 frame_rate,
543                 get_use_opengl());
545         if(EQUIV(config.threshold, 0))
546         {
547                 return 1;
548         }
549         else
550         {
551                 if(get_use_opengl()) return run_opengl();
553                 if(!engine) engine = new ChromaKeyServer(this);
554                 engine->process_packages();
555         }
556 SET_TRACE
558         return 1;
561 char* ChromaKey::plugin_title() { return N_("Chroma key"); }
562 int ChromaKey::is_realtime() { return 1; }
564 NEW_PICON_MACRO(ChromaKey)
566 LOAD_CONFIGURATION_MACRO(ChromaKey, ChromaKeyConfig)
568 int ChromaKey::load_defaults()
570 SET_TRACE
571         char directory[BCTEXTLEN];
572 // set the default directory
573         sprintf(directory, "%schromakey.rc", BCASTDIR);
575 // load the defaults
576         defaults = new BC_Hash(directory);
577         defaults->load();
579         config.red = defaults->get("RED", config.red);
580         config.green = defaults->get("GREEN", config.green);
581         config.blue = defaults->get("BLUE", config.blue);
582         config.threshold = defaults->get("THRESHOLD", config.threshold);
583         config.slope = defaults->get("SLOPE", config.slope);
584         config.use_value = defaults->get("USE_VALUE", config.use_value);
585 SET_TRACE
586         return 0;
589 int ChromaKey::save_defaults()
591 SET_TRACE
592         defaults->update("RED", config.red);
593         defaults->update("GREEN", config.green);
594         defaults->update("BLUE", config.blue);
595     defaults->update("THRESHOLD", config.threshold);
596     defaults->update("SLOPE", config.slope);
597     defaults->update("USE_VALUE", config.use_value);
598         defaults->save();
599 SET_TRACE
600         return 0;
603 void ChromaKey::save_data(KeyFrame *keyframe)
605         FileXML output;
606         output.set_shared_string(keyframe->data, MESSAGESIZE);
607         output.tag.set_title("CHROMAKEY");
608         output.tag.set_property("RED", config.red);
609         output.tag.set_property("GREEN", config.green);
610         output.tag.set_property("BLUE", config.blue);
611         output.tag.set_property("THRESHOLD", config.threshold);
612         output.tag.set_property("SLOPE", config.slope);
613         output.tag.set_property("USE_VALUE", config.use_value);
614         output.append_tag();
615         output.terminate_string();
618 void ChromaKey::read_data(KeyFrame *keyframe)
620         FileXML input;
622         input.set_shared_string(keyframe->data, strlen(keyframe->data));
624         while(!input.read_tag())
625         {
626                 if(input.tag.title_is("CHROMAKEY"))
627                 {
628                         config.red = input.tag.get_property("RED", config.red);
629                         config.green = input.tag.get_property("GREEN", config.green);
630                         config.blue = input.tag.get_property("BLUE", config.blue);
631                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
632                         config.slope = input.tag.get_property("SLOPE", config.slope);
633                         config.use_value = input.tag.get_property("USE_VALUE", config.use_value);
634                 }
635         }
639 SHOW_GUI_MACRO(ChromaKey, ChromaKeyThread)
641 SET_STRING_MACRO(ChromaKey)
643 RAISE_WINDOW_MACRO(ChromaKey)
645 void ChromaKey::update_gui()
647         if(thread)
648         {
649                 load_configuration();
650                 thread->window->lock_window();
651                 thread->window->threshold->update(config.threshold);
652                 thread->window->slope->update(config.slope);
653                 thread->window->use_value->update(config.use_value);
654                 thread->window->update_sample();
656                 thread->window->unlock_window();
657         }
660 int ChromaKey::handle_opengl()
662 #ifdef HAVE_GL
663         OUTER_VARIABLES(this)
664         
667         static char *uniform_frag =
668                 "uniform sampler2D tex;\n"
669                 "uniform float min_v;\n"
670                 "uniform float max_v;\n"
671                 "uniform float run;\n"
672                 "uniform float threshold;\n"
673                 "uniform float threshold_run;\n"
674                 "uniform vec3 key;\n";
676         static char *get_yuvvalue_frag =
677                 "float get_value(vec4 color)\n"
678                 "{\n"
679                 "       return abs(color.r);\n"
680                 "}\n";
681                 
682         static char *get_rgbvalue_frag = 
683                 "float get_value(vec4 color)\n"
684                 "{\n"
685                 "       return dot(color.rgb, vec3(0.29900, 0.58700, 0.11400));\n"
686                 "}\n";
688         static char *value_frag =
689                 "void main()\n"
690                 "{\n"
691                 "       vec4 color = texture2D(tex, gl_TexCoord[0].st);\n"
692                 "       float value = get_value(color);\n"
693                 "       float alpha = 1.0;\n"
694                 "\n"
695                 "       if(value >= min_v && value < max_v)\n"
696                 "               alpha = 0.0;\n"
697                 "       else\n"
698                 "       if(value < min_v)\n"
699                 "       {\n"
700                 "               if(min_v - value < run)\n"
701                 "                       alpha = (min_v - value) / run;\n"
702                 "       }\n"
703                 "       else\n"
704                 "       if(value - max_v < run)\n"
705                 "               alpha = (value - max_v) / run;\n"
706                 "\n"
707                 "       gl_FragColor = vec4(color.rgb, alpha);\n"
708                 "}\n";
710         static char *cube_frag = 
711                 "void main()\n"
712                 "{\n"
713                 "       vec4 color = texture2D(tex, gl_TexCoord[0].st);\n"
714                 "       float difference = length(color.rgb - key);\n"
715                 "       float alpha = 1.0;\n"
716                 "       if(difference < threshold)\n"
717                 "               alpha = 0.0;\n"
718                 "       else\n"
719                 "       if(difference < threshold_run)\n"
720                 "               alpha = (difference - threshold) / run;\n"
721                 "       gl_FragColor = vec4(color.rgb, min(color.a, alpha));\n"
722                 "}\n";
724         get_output()->to_texture();
725         get_output()->enable_opengl();
726         get_output()->init_screen();
727         char *shader_stack[] = { 0, 0, 0, 0, 0 };
728         int current_shader = 0;
730         shader_stack[current_shader++] = uniform_frag;
731         switch(get_output()->get_color_model())
732         {
733                 case BC_YUV888:
734                 case BC_YUVA8888:
735                         if(config.use_value)
736                         {
737                                 shader_stack[current_shader++] = get_yuvvalue_frag;
738                                 shader_stack[current_shader++] = value_frag;
739                         }
740                         else
741                         {
742                                 shader_stack[current_shader++] = cube_frag;
743                         }
744                         break;
746                 default:
747                         if(config.use_value)
748                         {
749                                 shader_stack[current_shader++] = get_rgbvalue_frag;
750                                 shader_stack[current_shader++] = value_frag;
751                         }
752                         else
753                         {
754                                 shader_stack[current_shader++] = cube_frag;
755                         }
756                         break;
757         }
758 SET_TRACE
760         unsigned int frag = VFrame::make_shader(0, 
761                 shader_stack[0], 
762                 shader_stack[1], 
763                 shader_stack[2], 
764                 shader_stack[3], 
765                 0);
766         get_output()->bind_texture(0);
768         if(frag)
769         {
770                 glUseProgram(frag);
771                 glUniform1i(glGetUniformLocation(frag, "tex"), 0);
772                 glUniform1f(glGetUniformLocation(frag, "min_v"), min_v);
773                 glUniform1f(glGetUniformLocation(frag, "max_v"), max_v);
774                 glUniform1f(glGetUniformLocation(frag, "run"), run);
775                 glUniform1f(glGetUniformLocation(frag, "threshold"), threshold);
776                 glUniform1f(glGetUniformLocation(frag, "threshold_run"), threshold_run);
777                 if(get_output()->get_color_model() != BC_YUV888 &&
778                         get_output()->get_color_model() != BC_YUVA8888)
779                         glUniform3f(glGetUniformLocation(frag, "key"), 
780                                 r_key, g_key, b_key);
781                 else
782                         glUniform3f(glGetUniformLocation(frag, "key"), 
783                                 (float)y_key / 0xff, (float)u_key / 0xff, (float)v_key / 0xff);
784                 
785         }
786 SET_TRACE
788         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
789         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
791         if(cmodel_components(get_output()->get_color_model()) == 3)
792         {
793                 glEnable(GL_BLEND);
794                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
795                 get_output()->clear_pbuffer();
796         }
797 SET_TRACE
799         get_output()->draw_texture();
801         glUseProgram(0);
802         get_output()->set_opengl_state(VFrame::SCREEN);
803         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
804         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
805         glDisable(GL_BLEND);
806 SET_TRACE
807 #endif