r370: Heroine Virutal's official release 1.2.1
[cinelerra_cv/mob.git] / hvirtual / plugins / chromakey / chromakey.C
blob78a099e7e6e4b2af3e8f89907a376ba4f27740dc
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         hue = 0.0;
25         threshold = 60.0;
26         value = 0.1;
27         use_value = 0;
28         slope = 100;
32 void ChromaKeyConfig::copy_from(ChromaKeyConfig &src)
34         hue = src.hue;
35         threshold = src.threshold;
36         value = src.value;
37         use_value = src.use_value;
38         slope = src.slope;
41 int ChromaKeyConfig::equivalent(ChromaKeyConfig &src)
43         return (EQUIV(hue, src.hue) &&
44                 EQUIV(threshold, src.threshold) &&
45                 EQUIV(value, src.value) &&
46                 EQUIV(slope, src.slope) &&
47                 use_value == src.use_value);
50 void ChromaKeyConfig::interpolate(ChromaKeyConfig &prev, 
51         ChromaKeyConfig &next, 
52         int64_t prev_frame, 
53         int64_t next_frame, 
54         int64_t current_frame)
56         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
57         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
59         this->hue = prev.hue * prev_scale + next.hue * next_scale;
60         this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
61         this->value = prev.value * prev_scale + next.value * next_scale;
62         this->use_value = prev.use_value;
63         this->slope = prev.slope * prev_scale + next.slope * next_scale;
67 ChromaKeyWindow::ChromaKeyWindow(ChromaKey *plugin, int x, int y)
68  : BC_Window(plugin->gui_string, 
69         x, 
70         y, 
71         320, 
72         230, 
73         320, 
74         230, 
75         0, 
76         0,
77         1)
79         this->plugin = plugin;
82 ChromaKeyWindow::~ChromaKeyWindow()
86 void ChromaKeyWindow::create_objects()
88         int x = 10, y = 10, x1 = 100;
90         for(int i = 0; i < 200; i++)
91         {
92                 float r, g, b;
93                 HSV::hsv_to_rgb(r, g, b, i * 360 / 200, 1, 1);
94                 int color = (((int)(r * 0xff)) << 16) | (((int)(g * 0xff)) << 8)  | ((int)(b * 0xff));
95                 set_color(color);
96                 draw_line(x1 + i, y, x1 + i, y + 25);
97         }
98         y += 30;
99         add_subwindow(new BC_Title(x, y, _("Hue:")));
100         add_subwindow(hue = new ChromaKeyHue(plugin, x1, y));
101         y += 30;
103         for(int i = 0; i < 200; i++)
104         {
105                 int r = i * 0xff / 200;
106                 set_color((r << 16) | (r << 8) | r);
107                 draw_line(x1 + i, y, x1 + i, y + 25);
108         }
110         y += 30;
111         add_subwindow(new BC_Title(x, y, _("Value:")));
112         add_subwindow(value = new ChromaKeyValue(plugin, x1, y));
114         y += 30;
115         add_subwindow(new BC_Title(x, y, _("Slope:")));
116         add_subwindow(slope = new ChromaKeySlope(plugin, x1, y));
118         y += 30;
119         add_subwindow(new BC_Title(x, y, _("Threshold:")));
120         add_subwindow(threshold = new ChromaKeyThreshold(plugin, x1, y));
123         y += 30;
124         add_subwindow(use_value = new ChromaKeyUseValue(plugin, x1, y));
126         show_window();
127         flush();
130 int ChromaKeyWindow::close_event()
132         set_done(1);
133         return 1;
136 ChromaKeyHue::ChromaKeyHue(ChromaKey *plugin, int x, int y)
137  : BC_FSlider(x, 
138                         y,
139                         0,
140                         200, 
141                         200, 
142                         (float)0, 
143                         (float)360, 
144                         plugin->config.hue)
146         this->plugin = plugin;
147         set_precision(0.1);
149 int ChromaKeyHue::handle_event()
151         plugin->config.hue = get_value();
152         plugin->send_configure_change();
153         return 1;
156 ChromaKeyThreshold::ChromaKeyThreshold(ChromaKey *plugin, int x, int y)
157  : BC_FSlider(x, 
158                         y,
159                         0,
160                         200, 
161                         200, 
162                         (float)0, 
163                         (float)100, 
164                         plugin->config.threshold)
166         this->plugin = plugin;
167         set_precision(0.01);
169 int ChromaKeyThreshold::handle_event()
171         plugin->config.threshold = get_value();
172         plugin->send_configure_change();
173         return 1;
176 ChromaKeyValue::ChromaKeyValue(ChromaKey *plugin, int x, int y)
177  : BC_FSlider(x, 
178                         y,
179                         0,
180                         200, 
181                         200, 
182                         (float)0, 
183                         (float)100, 
184                         plugin->config.value)
186         this->plugin = plugin;
187         set_precision(0.01);
189 int ChromaKeyValue::handle_event()
191         plugin->config.value = get_value();
192         plugin->send_configure_change();
193         return 1;
198 ChromaKeySlope::ChromaKeySlope(ChromaKey *plugin, int x, int y)
199  : BC_FSlider(x, 
200                         y,
201                         0,
202                         200, 
203                         200, 
204                         (float)0, 
205                         (float)100, 
206                         plugin->config.slope)
208         this->plugin = plugin;
209         set_precision(0.01);
211 int ChromaKeySlope::handle_event()
213         plugin->config.slope = get_value();
214         plugin->send_configure_change();
215         return 1;
219 ChromaKeyUseValue::ChromaKeyUseValue(ChromaKey *plugin, int x, int y)
220  : BC_CheckBox(x, y, plugin->config.use_value, _("Use value"))
222         this->plugin = plugin;
224 int ChromaKeyUseValue::handle_event()
226         plugin->config.use_value = get_value();
227         plugin->send_configure_change();
228         return 1;
239 PLUGIN_THREAD_OBJECT(ChromaKey, ChromaKeyThread, ChromaKeyWindow)
242 ChromaKeyServer::ChromaKeyServer(ChromaKey *plugin)
243  : LoadServer(plugin->PluginClient::smp + 1, plugin->PluginClient::smp + 1)
245         this->plugin = plugin;
247 void ChromaKeyServer::init_packages()
249         int increment = plugin->input->get_h() / get_total_packages() + 1;
250         int y = 0;
251         for(int i = 0; i < get_total_packages(); i++)
252         {
253                 ChromaKeyPackage *pkg = (ChromaKeyPackage*)get_package(i);
254                 pkg->y1 = y;
255                 pkg->y2 = y + increment;
256                 y += increment;
257                 if(pkg->y2 > plugin->input->get_h())
258                 {
259                         y = pkg->y2 = plugin->input->get_h();
260                 }
261                 if(pkg->y1 > plugin->input->get_h())
262                 {
263                         y = pkg->y1 = plugin->input->get_h();
264                 }
265         }
266         
268 LoadClient* ChromaKeyServer::new_client()
270         return new ChromaKeyUnit(plugin, this);
272 LoadPackage* ChromaKeyServer::new_package()
274         return new ChromaKeyPackage();
279 ChromaKeyPackage::ChromaKeyPackage()
280  : LoadPackage()
284 ChromaKeyUnit::ChromaKeyUnit(ChromaKey *plugin, ChromaKeyServer *server)
285  : LoadClient(server)
287         this->plugin = plugin;
289 void ChromaKeyUnit::process_package(LoadPackage *package)
291         ChromaKeyPackage *pkg = (ChromaKeyPackage*)package;
293         int w = plugin->input->get_w();
294         float min_h = plugin->config.hue - plugin->config.threshold * 3.60 / 2;
295         float max_h = plugin->config.hue + plugin->config.threshold * 3.60 / 2;
296         float min_v = (plugin->config.value - plugin->config.threshold / 2) / 100;
297         float max_v = (plugin->config.value + plugin->config.threshold / 2) / 100;
299         float run;
300         if(plugin->config.use_value)
301                 run = (max_v - min_v) / 2 * (1 - plugin->config.slope / 100);
302         else
303                 run = (max_h - min_h) / 2 * (1 - plugin->config.slope / 100);
305 // printf("ChromaKeyUnit::process_package %f %f %f %f\n", 
306 //      min_h, max_h, min_v, max_v);
308 #define CHROMAKEY(type, temp, max, components, use_yuv) \
309 { \
310         temp chroma_offset; \
311         temp a; \
313         if(use_yuv) \
314                 chroma_offset = max / 2 + 1; \
315         else \
316                 chroma_offset = 0; \
317         for(int i = pkg->y1; i < pkg->y2; i++) \
318         { \
319                 float h, s, va; \
320                 type *row = (type*)plugin->input->get_rows()[i]; \
322                 for(int j = 0; j < w; j++) \
323                 { \
324                         if(use_yuv) \
325                         { \
326                                 HSV::yuv_to_hsv(row[0],  \
327                                         row[1],  \
328                                         row[2],  \
329                                         h,  \
330                                         s,  \
331                                         va,  \
332                                         max); \
333                         } \
334                         else \
335                         { \
336                                 HSV::rgb_to_hsv((float)row[0] / max,  \
337                                         (float)row[1] / max,  \
338                                         (float)row[2] / max,  \
339                                         h, s, va); \
340                         } \
342                         if(plugin->config.use_value) \
343                         { \
344                                 if(va >= min_v && va <= max_v) \
345                                 { \
346                                         if(va - min_v < max_v - va) \
347                                         { \
348                                                 if(va - min_v < run) \
349                                                         a = (temp)(max - (va - min_v) / run * max); \
350                                                 else \
351                                                         a = 0; \
352                                         } \
353                                         else \
354                                         { \
355                                                 if(max_v - va < run) \
356                                                         a = (temp)(max - (max_v - va) / run * max); \
357                                                 else \
358                                                         a = 0; \
359                                         } \
361                                         if(components == 4) \
362                                                 row[3] = MIN(a, row[3]); \
363                                         else \
364                                         { \
365                                                 row[0] = (type)((a * row[0]) / max); \
366                                                 row[1] = (type)((a * row[1] + (max - a) * chroma_offset) / max); \
367                                                 row[2] = (type)((a * row[2] + (max - a) * chroma_offset) / max); \
368                                         } \
369                                 } \
370                         } \
371                         else \
372                         { \
373                                 if(h >= min_h && h <= max_h) \
374                                 { \
375                                         if(h - min_h < max_h - h) \
376                                         { \
377                                                 if(h - min_h < run) \
378                                                         a = (temp)(max - (h - min_h) / run * max); \
379                                                 else \
380                                                         a = 0; \
381                                         } \
382                                         else \
383                                         { \
384                                                 if(max_h - h < run) \
385                                                         a = (temp)(max - (max_h - h) / run * max); \
386                                                 else \
387                                                         a = 0; \
388                                         } \
390                                         if(components == 4) \
391                                                 row[3] = MIN(a, row[3]); \
392                                         else \
393                                         { \
394                                                 row[0] = (type)(a * row[0] / max); \
395                                                 row[1] = (type)((a * row[1] + (max - a) * chroma_offset) / max); \
396                                                 row[2] = (type)((a * row[2] + (max - a) * chroma_offset) / max); \
397                                         } \
398                                 } \
399                         } \
401                         row += components; \
402                 } \
403         } \
409         switch(plugin->input->get_color_model())
410         {
411                 case BC_RGB888:
412                         CHROMAKEY(unsigned char, int, 0xff, 3, 0);
413                         break;
414                 case BC_RGB_FLOAT:
415                         CHROMAKEY(float, float, 1.0, 3, 0);
416                         break;
417                 case BC_YUV888:
418                         CHROMAKEY(unsigned char, int, 0xff, 3, 1);
419                         break;
420                 case BC_RGBA8888:
421                         CHROMAKEY(unsigned char, int, 0xff, 4, 0);
422                         break;
423                 case BC_RGBA_FLOAT:
424                         CHROMAKEY(float, float, 1.0, 4, 0);
425                         break;
426                 case BC_YUVA8888:
427                         CHROMAKEY(unsigned char, int, 0xff, 4, 1);
428                         break;
429                 case BC_RGB161616:
430                         CHROMAKEY(uint16_t, int, 0xffff, 3, 0);
431                         break;
432                 case BC_YUV161616:
433                         CHROMAKEY(uint16_t, int, 0xffff, 3, 1);
434                         break;
435                 case BC_RGBA16161616:
436                         CHROMAKEY(uint16_t, int, 0xffff, 4, 0);
437                         break;
438                 case BC_YUVA16161616:
439                         CHROMAKEY(uint16_t, int, 0xffff, 4, 1);
440                         break;
441         }
449 REGISTER_PLUGIN(ChromaKey)
453 ChromaKey::ChromaKey(PluginServer *server)
454  : PluginVClient(server)
456         PLUGIN_CONSTRUCTOR_MACRO
457         engine = 0;
460 ChromaKey::~ChromaKey()
462         PLUGIN_DESTRUCTOR_MACRO
463         if(engine) delete engine;
467 int ChromaKey::process_realtime(VFrame *input, VFrame *output)
469         load_configuration();
470         this->input = input;
471         this->output = output;
473         if(EQUIV(config.threshold, 0))
474         {
475                 if(input->get_rows()[0] != output->get_rows()[0])
476                         output->copy_from(input);
477         }
478         else
479         {
480                 if(!engine) engine = new ChromaKeyServer(this);
481                 engine->process_packages();
482         }
484         return 0;
487 char* ChromaKey::plugin_title() { return N_("Chroma key"); }
488 int ChromaKey::is_realtime() { return 1; }
490 NEW_PICON_MACRO(ChromaKey)
492 LOAD_CONFIGURATION_MACRO(ChromaKey, ChromaKeyConfig)
494 int ChromaKey::load_defaults()
496         char directory[BCTEXTLEN];
497 // set the default directory
498         sprintf(directory, "%schromakey.rc", BCASTDIR);
500 // load the defaults
501         defaults = new Defaults(directory);
502         defaults->load();
504         config.hue = defaults->get("HUE", config.hue);
505         config.value = defaults->get("VALUE", config.value);
506         config.threshold = defaults->get("THRESHOLD", config.threshold);
507         config.threshold = defaults->get("SLOPE", config.slope);
508         config.use_value = defaults->get("USE_VALUE", config.use_value);
509         return 0;
512 int ChromaKey::save_defaults()
514         defaults->update("HUE", config.hue);
515         defaults->update("VALUE", config.value);
516     defaults->update("THRESHOLD", config.threshold);
517     defaults->update("SLOPE", config.slope);
518     defaults->update("USE_VALUE", config.use_value);
519         defaults->save();
520         return 0;
523 void ChromaKey::save_data(KeyFrame *keyframe)
525         FileXML output;
526         output.set_shared_string(keyframe->data, MESSAGESIZE);
527         output.tag.set_title("CHROMAKEY");
528         output.tag.set_property("HUE", config.hue);
529         output.tag.set_property("VALUE", config.value);
530         output.tag.set_property("THRESHOLD", config.threshold);
531         output.tag.set_property("SLOPE", config.slope);
532         output.tag.set_property("USE_VALUE", config.use_value);
533         output.append_tag();
534         output.terminate_string();
537 void ChromaKey::read_data(KeyFrame *keyframe)
539         FileXML input;
541         input.set_shared_string(keyframe->data, strlen(keyframe->data));
543         while(!input.read_tag())
544         {
545                 if(input.tag.title_is("CHROMAKEY"))
546                 {
547                         config.hue = input.tag.get_property("HUE", config.hue);
548                         config.value = input.tag.get_property("VALUE", config.value);
549                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
550                         config.slope = input.tag.get_property("SLOPE", config.slope);
551                         config.use_value = input.tag.get_property("USE_VALUE", config.use_value);
552                 }
553         }
557 SHOW_GUI_MACRO(ChromaKey, ChromaKeyThread)
559 SET_STRING_MACRO(ChromaKey)
561 RAISE_WINDOW_MACRO(ChromaKey)
563 void ChromaKey::update_gui()
565         if(thread)
566         {
567                 load_configuration();
568                 thread->window->lock_window();
569                 thread->window->hue->update(config.hue);
570                 thread->window->value->update(config.value);
571                 thread->window->threshold->update(config.threshold);
572                 thread->window->slope->update(config.slope);
573                 thread->window->use_value->update(config.use_value);
574                 thread->window->unlock_window();
575         }