r105: This commit was manufactured by cvs2svn to create tag
[cinelerra_cv/mob.git] / hvirtual / plugins / chromakey / chromakey.C
blobbbc96e4ec982fbe1f44097ecca2fe4d1e9822cb8
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 "loadbalance.h"
9 #include "picon_png.h"
10 #include "plugincolors.h"
11 #include "pluginvclient.h"
12 #include "vframe.h"
14 #include <stdint.h>
15 #include <string.h>
17 #include <libintl.h>
18 #define _(String) gettext(String)
19 #define gettext_noop(String) String
20 #define N_(String) gettext_noop (String)
25 ChromaKeyConfig::ChromaKeyConfig()
27         hue = 0.0;
28         threshold = 60.0;
29         value = 0.1;
30         use_value = 0;
31         slope = 100;
35 void ChromaKeyConfig::copy_from(ChromaKeyConfig &src)
37         hue = src.hue;
38         threshold = src.threshold;
39         value = src.value;
40         use_value = src.use_value;
41         slope = src.slope;
44 int ChromaKeyConfig::equivalent(ChromaKeyConfig &src)
46         return (EQUIV(hue, src.hue) &&
47                 EQUIV(threshold, src.threshold) &&
48                 EQUIV(value, src.value) &&
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->hue = prev.hue * prev_scale + next.hue * next_scale;
63         this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
64         this->value = prev.value * prev_scale + next.value * next_scale;
65         this->use_value = prev.use_value;
66         this->slope = prev.slope * prev_scale + next.slope * next_scale;
70 ChromaKeyWindow::ChromaKeyWindow(ChromaKey *plugin, int x, int y)
71  : BC_Window(plugin->gui_string, 
72         x, 
73         y, 
74         320, 
75         230, 
76         320, 
77         230, 
78         0, 
79         0,
80         1)
82         this->plugin = plugin;
85 ChromaKeyWindow::~ChromaKeyWindow()
89 void ChromaKeyWindow::create_objects()
91         int x = 10, y = 10, x1 = 100;
93         for(int i = 0; i < 200; i++)
94         {
95                 float r, g, b;
96                 HSV::hsv_to_rgb(r, g, b, i * 360 / 200, 1, 1);
97                 int color = (((int)(r * 0xff)) << 16) | (((int)(g * 0xff)) << 8)  | ((int)(b * 0xff));
98                 set_color(color);
99                 draw_line(x1 + i, y, x1 + i, y + 25);
100         }
101         y += 30;
102         add_subwindow(new BC_Title(x, y, _("Hue:")));
103         add_subwindow(hue = new ChromaKeyHue(plugin, x1, y));
104         y += 30;
106         for(int i = 0; i < 200; i++)
107         {
108                 int r = i * 0xff / 200;
109                 set_color((r << 16) | (r << 8) | r);
110                 draw_line(x1 + i, y, x1 + i, y + 25);
111         }
113         y += 30;
114         add_subwindow(new BC_Title(x, y, _("Value:")));
115         add_subwindow(value = new ChromaKeyValue(plugin, x1, y));
117         y += 30;
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         show_window();
130         flush();
133 int ChromaKeyWindow::close_event()
135         set_done(1);
136         return 1;
139 ChromaKeyHue::ChromaKeyHue(ChromaKey *plugin, int x, int y)
140  : BC_FSlider(x, 
141                         y,
142                         0,
143                         200, 
144                         200, 
145                         (float)0, 
146                         (float)360, 
147                         plugin->config.hue)
149         this->plugin = plugin;
150         set_precision(0.1);
152 int ChromaKeyHue::handle_event()
154         plugin->config.hue = get_value();
155         plugin->send_configure_change();
156         return 1;
159 ChromaKeyThreshold::ChromaKeyThreshold(ChromaKey *plugin, int x, int y)
160  : BC_FSlider(x, 
161                         y,
162                         0,
163                         200, 
164                         200, 
165                         (float)0, 
166                         (float)100, 
167                         plugin->config.threshold)
169         this->plugin = plugin;
170         set_precision(0.01);
172 int ChromaKeyThreshold::handle_event()
174         plugin->config.threshold = get_value();
175         plugin->send_configure_change();
176         return 1;
179 ChromaKeyValue::ChromaKeyValue(ChromaKey *plugin, int x, int y)
180  : BC_FSlider(x, 
181                         y,
182                         0,
183                         200, 
184                         200, 
185                         (float)0, 
186                         (float)100, 
187                         plugin->config.value)
189         this->plugin = plugin;
190         set_precision(0.01);
192 int ChromaKeyValue::handle_event()
194         plugin->config.value = get_value();
195         plugin->send_configure_change();
196         return 1;
201 ChromaKeySlope::ChromaKeySlope(ChromaKey *plugin, int x, int y)
202  : BC_FSlider(x, 
203                         y,
204                         0,
205                         200, 
206                         200, 
207                         (float)0, 
208                         (float)100, 
209                         plugin->config.slope)
211         this->plugin = plugin;
212         set_precision(0.01);
214 int ChromaKeySlope::handle_event()
216         plugin->config.slope = get_value();
217         plugin->send_configure_change();
218         return 1;
222 ChromaKeyUseValue::ChromaKeyUseValue(ChromaKey *plugin, int x, int y)
223  : BC_CheckBox(x, y, plugin->config.use_value, _("Use value"))
225         this->plugin = plugin;
227 int ChromaKeyUseValue::handle_event()
229         plugin->config.use_value = get_value();
230         plugin->send_configure_change();
231         return 1;
242 PLUGIN_THREAD_OBJECT(ChromaKey, ChromaKeyThread, ChromaKeyWindow)
245 ChromaKeyServer::ChromaKeyServer(ChromaKey *plugin)
246  : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
248         this->plugin = plugin;
250 void ChromaKeyServer::init_packages()
252         int increment = plugin->input->get_h() / get_total_packages() + 1;
253         int y = 0;
254         for(int i = 0; i < get_total_packages(); i++)
255         {
256                 ChromaKeyPackage *pkg = (ChromaKeyPackage*)get_package(i);
257                 pkg->y1 = y;
258                 pkg->y2 = y + increment;
259                 y += increment;
260                 if(pkg->y2 > plugin->input->get_h())
261                 {
262                         y = pkg->y2 = plugin->input->get_h();
263                 }
264                 if(pkg->y1 > plugin->input->get_h())
265                 {
266                         y = pkg->y1 = plugin->input->get_h();
267                 }
268         }
269         
271 LoadClient* ChromaKeyServer::new_client()
273         return new ChromaKeyUnit(plugin, this);
275 LoadPackage* ChromaKeyServer::new_package()
277         return new ChromaKeyPackage();
282 ChromaKeyPackage::ChromaKeyPackage()
283  : LoadPackage()
287 ChromaKeyUnit::ChromaKeyUnit(ChromaKey *plugin, ChromaKeyServer *server)
288  : LoadClient(server)
290         this->plugin = plugin;
292 void ChromaKeyUnit::process_package(LoadPackage *package)
294         ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
296         int w = plugin->input->get_w();
297         float min_h = plugin->config.hue - plugin->config.threshold * 3.60 / 2;
298         float max_h = plugin->config.hue + plugin->config.threshold * 3.60 / 2;
299         float min_v = (plugin->config.value - plugin->config.threshold / 2) / 100;
300         float max_v = (plugin->config.value + plugin->config.threshold / 2) / 100;
301         int a;
303         float run;
304         if(plugin->config.use_value)
305                 run = (max_v - min_v) / 2 * (1 - plugin->config.slope / 100);
306         else
307                 run = (max_h - min_h) / 2 * (1 - plugin->config.slope / 100);
309 // printf("ChromaKeyUnit::process_package %f %f %f %f\n", 
310 //      min_h, max_h, min_v, max_v);
312 #define CHROMAKEY(type, max, components, use_yuv) \
313 { \
314         int chroma_offset; \
315         if(use_yuv) \
316                 chroma_offset = max / 2 + 1; \
317         else \
318                 chroma_offset = 0; \
319         for(int i = pkg->y1; i < pkg->y2; i++) \
320         { \
321                 float h, s, va; \
322                 type *row = (type*)plugin->input->get_rows()[i]; \
324                 for(int j = 0; j < w; j++) \
325                 { \
326                         if(use_yuv) \
327                         { \
328                                 HSV::yuv_to_hsv(row[0],  \
329                                         row[1],  \
330                                         row[2],  \
331                                         h,  \
332                                         s,  \
333                                         va,  \
334                                         max); \
335                         } \
336                         else \
337                         { \
338                                 HSV::rgb_to_hsv((float)row[0] / max,  \
339                                         (float)row[1] / max,  \
340                                         (float)row[2] / max,  \
341                                         h, s, va); \
342                         } \
344                         if(plugin->config.use_value) \
345                         { \
346                                 if(va >= min_v && va <= max_v) \
347                                 { \
348                                         if(va - min_v < max_v - va) \
349                                         { \
350                                                 if(va - min_v < run) \
351                                                         a = (int)(max - (va - min_v) / run * max); \
352                                                 else \
353                                                         a = 0; \
354                                         } \
355                                         else \
356                                         { \
357                                                 if(max_v - va < run) \
358                                                         a = (int)(max - (max_v - va) / run * max); \
359                                                 else \
360                                                         a = 0; \
361                                         } \
363                                         if(components == 4) \
364                                                 row[3] = MIN(a, row[3]); \
365                                         else \
366                                         { \
367                                                 row[0] = (a * row[0]) / max; \
368                                                 row[1] = (a * row[1] + (max - a) * chroma_offset) / max; \
369                                                 row[2] = (a * row[2] + (max - a) * chroma_offset) / max; \
370                                         } \
371                                 } \
372                         } \
373                         else \
374                         { \
375                                 if(h >= min_h && h <= max_h) \
376                                 { \
377                                         if(h - min_h < max_h - h) \
378                                         { \
379                                                 if(h - min_h < run) \
380                                                         a = (int)(max - (h - min_h) / run * max); \
381                                                 else \
382                                                         a = 0; \
383                                         } \
384                                         else \
385                                         { \
386                                                 if(max_h - h < run) \
387                                                         a = (int)(max - (max_h - h) / run * max); \
388                                                 else \
389                                                         a = 0; \
390                                         } \
392                                         if(components == 4) \
393                                                 row[3] = MIN(a, row[3]); \
394                                         else \
395                                         { \
396                                                 row[0] = a * row[0] / max; \
397                                                 row[1] = (a * row[1] + (max - a) * chroma_offset) / max; \
398                                                 row[2] = (a * row[2] + (max - a) * chroma_offset) / max; \
399                                         } \
400                                 } \
401                         } \
403                         row += components; \
404                 } \
405         } \
411         switch(plugin->input->get_color_model())
412         {
413                 case BC_RGB888:
414                         CHROMAKEY(unsigned char, 0xff, 3, 0);
415                         break;
416                 case BC_YUV888:
417                         CHROMAKEY(unsigned char, 0xff, 3, 1);
418                         break;
419                 case BC_RGBA8888:
420                         CHROMAKEY(unsigned char, 0xff, 4, 0);
421                         break;
422                 case BC_YUVA8888:
423                         CHROMAKEY(unsigned char, 0xff, 4, 1);
424                         break;
425                 case BC_RGB161616:
426                         CHROMAKEY(uint16_t, 0xffff, 3, 0);
427                         break;
428                 case BC_YUV161616:
429                         CHROMAKEY(uint16_t, 0xffff, 3, 1);
430                         break;
431                 case BC_RGBA16161616:
432                         CHROMAKEY(uint16_t, 0xffff, 4, 0);
433                         break;
434                 case BC_YUVA16161616:
435                         CHROMAKEY(uint16_t, 0xffff, 4, 1);
436                         break;
437         }
445 REGISTER_PLUGIN(ChromaKey)
449 ChromaKey::ChromaKey(PluginServer *server)
450  : PluginVClient(server)
452         PLUGIN_CONSTRUCTOR_MACRO
453         engine = 0;
456 ChromaKey::~ChromaKey()
458         PLUGIN_DESTRUCTOR_MACRO
459         if(engine) delete engine;
463 int ChromaKey::process_realtime(VFrame *input, VFrame *output)
465         load_configuration();
466         this->input = input;
467         this->output = output;
469         if(EQUIV(config.threshold, 0))
470         {
471                 if(input->get_rows()[0] != output->get_rows()[0])
472                         output->copy_from(input);
473         }
474         else
475         {
476                 if(!engine) engine = new ChromaKeyServer(this);
477                 engine->process_packages();
478         }
480         return 0;
483 int ChromaKey::is_realtime()
485         return 1;
487 char* ChromaKey::plugin_title()
489         return _("Chroma key");
492 NEW_PICON_MACRO(ChromaKey)
494 LOAD_CONFIGURATION_MACRO(ChromaKey, ChromaKeyConfig)
496 int ChromaKey::load_defaults()
498         char directory[BCTEXTLEN];
499 // set the default directory
500         sprintf(directory, "%schromakey.rc", BCASTDIR);
502 // load the defaults
503         defaults = new Defaults(directory);
504         defaults->load();
506         config.hue = defaults->get("HUE", config.hue);
507         config.value = defaults->get("VALUE", config.value);
508         config.threshold = defaults->get("THRESHOLD", config.threshold);
509         config.threshold = defaults->get("SLOPE", config.slope);
510         config.use_value = defaults->get("USE_VALUE", config.use_value);
511         return 0;
514 int ChromaKey::save_defaults()
516         defaults->update("HUE", config.hue);
517         defaults->update("VALUE", config.value);
518     defaults->update("THRESHOLD", config.threshold);
519     defaults->update("SLOPE", config.slope);
520     defaults->update("USE_VALUE", config.use_value);
521         defaults->save();
522         return 0;
525 void ChromaKey::save_data(KeyFrame *keyframe)
527         FileXML output;
528         output.set_shared_string(keyframe->data, MESSAGESIZE);
529         output.tag.set_title("CHROMAKEY");
530         output.tag.set_property("HUE", config.hue);
531         output.tag.set_property("VALUE", config.value);
532         output.tag.set_property("THRESHOLD", config.threshold);
533         output.tag.set_property("SLOPE", config.slope);
534         output.tag.set_property("USE_VALUE", config.use_value);
535         output.append_tag();
536         output.terminate_string();
539 void ChromaKey::read_data(KeyFrame *keyframe)
541         FileXML input;
543         input.set_shared_string(keyframe->data, strlen(keyframe->data));
545         while(!input.read_tag())
546         {
547                 if(input.tag.title_is("CHROMAKEY"))
548                 {
549                         config.hue = input.tag.get_property("HUE", config.hue);
550                         config.value = input.tag.get_property("VALUE", config.value);
551                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
552                         config.slope = input.tag.get_property("SLOPE", config.slope);
553                         config.use_value = input.tag.get_property("USE_VALUE", config.use_value);
554                 }
555         }
559 SHOW_GUI_MACRO(ChromaKey, ChromaKeyThread)
561 SET_STRING_MACRO(ChromaKey)
563 RAISE_WINDOW_MACRO(ChromaKey)
565 void ChromaKey::update_gui()
567         if(thread)
568         {
569                 load_configuration();
570                 thread->window->lock_window();
571                 thread->window->hue->update(config.hue);
572                 thread->window->value->update(config.value);
573                 thread->window->threshold->update(config.threshold);
574                 thread->window->slope->update(config.slope);
575                 thread->window->use_value->update(config.use_value);
576                 thread->window->unlock_window();
577         }