r665: Merged the official release 2.0.
[cinelerra_cv.git] / plugins / diffkey / diffkey.C
blob149c56008b25fbb609e95701949883ba36fae209
1 #ifndef DIFFKEY_H
2 #define DIFFKEY_H
4 #include "bcdisplayinfo.h"
5 #include "clip.h"
6 #include "defaults.h"
7 #include "filexml.h"
8 #include "guicast.h"
9 #include "language.h"
10 #include "loadbalance.h"
11 #include "plugincolors.h"
12 #include "pluginvclient.h"
15 #include <string.h>
19 class DiffKeyGUI;
20 class DiffKey;
24 class DiffKeyConfig
26 public:
27         DiffKeyConfig();
28         void copy_from(DiffKeyConfig &src);
29         int equivalent(DiffKeyConfig &src);
30         void interpolate(DiffKeyConfig &prev, 
31                 DiffKeyConfig &next, 
32                 int64_t prev_frame, 
33                 int64_t next_frame, 
34                 int64_t current_frame);
36         float threshold;
37         float slope;
38         int do_value;
42 class DiffKeyThreshold : public BC_FSlider
44 public:
45         DiffKeyThreshold(DiffKey *plugin, int x, int y);
46         int handle_event();
47         DiffKey *plugin;
50 class DiffKeySlope : public BC_FSlider
52 public:
53         DiffKeySlope(DiffKey *plugin, int x, int y);
54         int handle_event();
55         DiffKey *plugin;
58 class DiffKeyDoValue : public BC_CheckBox
60 public:
61         DiffKeyDoValue(DiffKey *plugin, int x, int y);
62         int handle_event();
63         DiffKey *plugin;
68 class DiffKeyGUI : public BC_Window
70 public:
71         DiffKeyGUI(DiffKey *plugin, int x, int y);
72         ~DiffKeyGUI();
75         void create_objects();
76         int close_event();
79         DiffKeyThreshold *threshold;
80         DiffKeySlope *slope;
81         DiffKeyDoValue *do_value;
82         DiffKey *plugin;
86 PLUGIN_THREAD_HEADER(DiffKey, DiffKeyThread, DiffKeyGUI)
90 class DiffKeyEngine : public LoadServer
92 public:
93         DiffKeyEngine(DiffKey *plugin);
94         void init_packages();
95         LoadClient* new_client();
96         LoadPackage* new_package();
97         DiffKey *plugin;
101 class DiffKeyClient : public LoadClient
103 public:
104         DiffKeyClient(DiffKeyEngine *engine);
105         ~DiffKeyClient();
107         void process_package(LoadPackage *pkg);
108         DiffKeyEngine *engine;
111 class DiffKeyPackage : public LoadPackage
113 public:
114         DiffKeyPackage();
115         int row1;
116         int row2;
121 class DiffKey : public PluginVClient
123 public:
124         DiffKey(PluginServer *server);
125         ~DiffKey();
127         int process_buffer(VFrame **frame,
128                 int64_t start_position,
129                 double frame_rate);
130         int is_realtime();
131         int is_multichannel();
132         int load_defaults();
133         int save_defaults();
134         void save_data(KeyFrame *keyframe);
135         void read_data(KeyFrame *keyframe);
136         void update_gui();
140         PLUGIN_CLASS_MEMBERS(DiffKeyConfig, DiffKeyThread)
142         DiffKeyEngine *engine;
143         VFrame *top_frame;
144         VFrame *bottom_frame;
154 REGISTER_PLUGIN(DiffKey)
157 DiffKeyConfig::DiffKeyConfig()
159         threshold = 0.1;
160         slope = 0;
161         do_value = 0;
164 void DiffKeyConfig::copy_from(DiffKeyConfig &src)
166         this->threshold = src.threshold;
167         this->slope = src.slope;
168         this->do_value = src.do_value;
172 int DiffKeyConfig::equivalent(DiffKeyConfig &src)
174         return EQUIV(threshold, src.threshold) &&
175                 EQUIV(slope, src.slope) &&
176                 do_value == src.do_value;
179 void DiffKeyConfig::interpolate(DiffKeyConfig &prev, 
180         DiffKeyConfig &next, 
181         int64_t prev_frame, 
182         int64_t next_frame, 
183         int64_t current_frame)
185         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
186         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
188         this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
189         this->slope = prev.slope * prev_scale + next.slope * next_scale;
190         this->do_value = prev.do_value;
202 DiffKeyThreshold::DiffKeyThreshold(DiffKey *plugin, int x, int y)
203  : BC_FSlider(x, y, 0, 200, 200, 0, 100, plugin->config.threshold)
205         this->plugin = plugin;
208 int DiffKeyThreshold::handle_event()
210         plugin->config.threshold = get_value();
211         plugin->send_configure_change();
212         return 1;
222 DiffKeySlope::DiffKeySlope(DiffKey *plugin, int x, int y)
223  : BC_FSlider(x, y, 0, 200, 200, 0, 100, plugin->config.slope)
225         this->plugin = plugin;
228 int DiffKeySlope::handle_event()
230         plugin->config.slope = get_value();
231         plugin->send_configure_change();
232         return 1;
237 DiffKeyDoValue::DiffKeyDoValue(DiffKey *plugin, int x, int y)
238  : BC_CheckBox(x, y, plugin->config.do_value, _("Use Value"))
240         this->plugin = plugin;
243 int DiffKeyDoValue::handle_event()
245         plugin->config.do_value = get_value();
246         plugin->send_configure_change();
247         return 1;
256 DiffKeyGUI::DiffKeyGUI(DiffKey *plugin, int x, int y)
257  : BC_Window(plugin->gui_string,
258         x,
259         y,
260         320,
261         100,
262         320,
263         100,
264         0,
265         0,
266         1)
268         this->plugin = plugin;
271 DiffKeyGUI::~DiffKeyGUI()
276 void DiffKeyGUI::create_objects()
278         int x = 10, y = 10, x2;
279         BC_Title *title;
280         add_subwindow(title = new BC_Title(x, y, "Threshold:"));
281         x += title->get_w() + 10;
282         add_subwindow(threshold = new DiffKeyThreshold(plugin, x, y));
283         x = 10;
284         y += threshold->get_h() + 10;
285         add_subwindow(title = new BC_Title(x, y, "Slope:"));
286         x += title->get_w() + 10;
287         add_subwindow(slope = new DiffKeySlope(plugin, x, y));
288         x = 10;
289         y += slope->get_h() + 10;
290         add_subwindow(do_value = new DiffKeyDoValue(plugin, x, y));
294         show_window();
297 WINDOW_CLOSE_EVENT(DiffKeyGUI)
300 PLUGIN_THREAD_OBJECT(DiffKey, DiffKeyThread, DiffKeyGUI)
304 DiffKey::DiffKey(PluginServer *server)
305  : PluginVClient(server)
307         PLUGIN_CONSTRUCTOR_MACRO
308         engine = 0;
311 DiffKey::~DiffKey()
313         PLUGIN_DESTRUCTOR_MACRO
314         delete engine;
317 SHOW_GUI_MACRO(DiffKey, DiffKeyThread)
318 RAISE_WINDOW_MACRO(DiffKey)
319 SET_STRING_MACRO(DiffKey)
320 #include "picon_png.h"
321 NEW_PICON_MACRO(DiffKey)
322 LOAD_CONFIGURATION_MACRO(DiffKey, DiffKeyConfig)
324 char* DiffKey::plugin_title() { return N_("Difference key"); }
325 int DiffKey::is_realtime() { return 1; }
326 int DiffKey::is_multichannel() { return 1; }
328 int DiffKey::load_defaults()
330         char directory[BCTEXTLEN];
331 // set the default directory
332         sprintf(directory, "%sdiffkey.rc", BCASTDIR);
334 // load the defaults
335         defaults = new Defaults(directory);
336         defaults->load();
338         config.threshold = defaults->get("THRESHOLD", config.threshold);
339         config.slope = defaults->get("SLOPE", config.slope);
340         config.do_value = defaults->get("DO_VALUE", config.do_value);
341         return 0;
344 int DiffKey::save_defaults()
346         defaults->update("THRESHOLD", config.threshold);
347         defaults->update("SLOPE", config.slope);
348         defaults->update("DO_VALUE", config.do_value);
349         defaults->save();
350         return 0;
353 void DiffKey::save_data(KeyFrame *keyframe)
355         FileXML output;
356         output.set_shared_string(keyframe->data, MESSAGESIZE);
357         output.tag.set_title("DIFFKEY");
358         output.tag.set_property("THRESHOLD", config.threshold);
359         output.tag.set_property("SLOPE", config.slope);
360         output.tag.set_property("DO_VALUE", config.do_value);
361         output.append_tag();
362         output.terminate_string();
365 void DiffKey::read_data(KeyFrame *keyframe)
367         FileXML input;
369         input.set_shared_string(keyframe->data, strlen(keyframe->data));
371         while(!input.read_tag())
372         {
373                 if(input.tag.title_is("DIFFKEY"))
374                 {
375                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
376                         config.slope = input.tag.get_property("SLOPE", config.slope);
377                         config.do_value = input.tag.get_property("DO_VALUE", config.do_value);
378                 }
379         }
382 void DiffKey::update_gui()
384         if(thread)
385         {
386                 if(load_configuration())
387                 {
388                         thread->window->lock_window("DiffKey::update_gui");
389                         thread->window->threshold->update(config.threshold);
390                         thread->window->slope->update(config.slope);
391                         thread->window->do_value->update(config.do_value);
392                         thread->window->unlock_window();
393                 }
394         }
397 int DiffKey::process_buffer(VFrame **frame,
398         int64_t start_position,
399         double frame_rate)
401         load_configuration();
403 // Don't process if only 1 layer.
404         if(get_total_buffers() < 2) 
405         {
406                 read_frame(frame[0], 0, start_position, frame_rate);
407                 return 0;
408         }
410 // Read frames from 2 layers
411         read_frame(frame[0], 0, start_position, frame_rate);
412         read_frame(frame[1], 1, start_position, frame_rate);
414         top_frame = frame[0];
415         bottom_frame = frame[1];
417         if(!engine)
418         {
419                 engine = new DiffKeyEngine(this);
420         }
422         engine->process_packages();
424         return 0;
433 DiffKeyEngine::DiffKeyEngine(DiffKey *plugin) 
434  : LoadServer(plugin->get_project_smp() + 1, plugin->get_project_smp() + 1)
436         this->plugin = plugin;
439 void DiffKeyEngine::init_packages()
441         int increment = plugin->top_frame->get_h() / get_total_packages() + 1;
442         int y = 0;
443         for(int i = 0; i < get_total_packages(); i++)
444         {
445                 DiffKeyPackage *pkg = (DiffKeyPackage*)get_package(i);
446                 pkg->row1 = y;
447                 pkg->row2 = MIN(y + increment, plugin->top_frame->get_h()); 
448                 y  += increment;
449         }
452 LoadClient* DiffKeyEngine::new_client()
454         return new DiffKeyClient(this);
457 LoadPackage* DiffKeyEngine::new_package()
459         return new DiffKeyPackage;
472 DiffKeyClient::DiffKeyClient(DiffKeyEngine *engine)
473  : LoadClient(engine)
475         this->engine = engine;
478 DiffKeyClient::~DiffKeyClient()
482 void DiffKeyClient::process_package(LoadPackage *ptr)
484         DiffKeyPackage *pkg = (DiffKeyPackage*)ptr;
485         DiffKey *plugin = engine->plugin;
486         int w = plugin->top_frame->get_w();
488 #define RGB_TO_VALUE(r, g, b) \
489 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
491 #define SQR(x) ((x) * (x))
493 #define DIFFKEY_MACRO(type, components, max, chroma_offset) \
494 { \
495         float threshold = plugin->config.threshold / 100; \
496         float run = plugin->config.slope / 100; \
497         float threshold_run = threshold + run; \
499         for(int i = pkg->row1; i < pkg->row2; i++) \
500         { \
501                 type *top_row = (type*)plugin->top_frame->get_rows()[i]; \
502                 type *bottom_row = (type*)plugin->bottom_frame->get_rows()[i]; \
504                 for(int j = 0; j < w; j++) \
505                 { \
506                         float a = 1.0; \
508 /* Test for value in range */ \
509                         if(plugin->config.do_value) \
510                         { \
511                                 float top_value; \
512                                 float bottom_value; \
514 /* Convert pixel data into floating point value */ \
515                                 if(chroma_offset) \
516                                 { \
517                                         float top_r = (float)top_row[0] / max; \
518                                         float bottom_r = (float)bottom_row[0] / max; \
519                                         top_value = top_r; \
520                                         bottom_value = bottom_r; \
521                                 } \
522                                 else \
523                                 { \
524                                         float top_r = (float)top_row[0] / max; \
525                                         float top_g = (float)top_row[1] / max; \
526                                         float top_b = (float)top_row[2] / max; \
527                                         top_g -= (float)chroma_offset / max; \
528                                         top_b -= (float)chroma_offset / max; \
530                                         float bottom_r = (float)bottom_row[0] / max; \
531                                         float bottom_g = (float)bottom_row[1] / max; \
532                                         float bottom_b = (float)bottom_row[2] / max; \
533                                         bottom_g -= (float)chroma_offset / max; \
534                                         bottom_b -= (float)chroma_offset / max; \
536                                         top_value = RGB_TO_VALUE(top_r, top_g, top_b); \
537                                         bottom_value = RGB_TO_VALUE(bottom_r, bottom_g, bottom_b); \
538                                 } \
540                                 float min_v = bottom_value - threshold; \
541                                 float max_v = bottom_value + threshold; \
543 /* Full transparency if in range */ \
544                                 if(top_value >= min_v && top_value < max_v) \
545                                 { \
546                                         a = 0; \
547                                 } \
548                                 else \
549 /* Phased out if below or above range */ \
550                                 if(top_value < min_v) \
551                                 { \
552                                         if(min_v - top_value < run) \
553                                                 a = (min_v - top_value) / run; \
554                                 } \
555                                 else \
556                                 if(top_value - max_v < run) \
557                                         a = (top_value - max_v) / run; \
558                         } \
559                         else \
560 /* Use color cube */ \
561                         { \
562                                 float top_r = (float)top_row[0] / max; \
563                                 float top_g = (float)top_row[1] / max; \
564                                 float top_b = (float)top_row[2] / max; \
565                                 top_g -= (float)chroma_offset / max; \
566                                 top_b -= (float)chroma_offset / max; \
568                                 float bottom_r = (float)bottom_row[0] / max; \
569                                 float bottom_g = (float)bottom_row[1] / max; \
570                                 float bottom_b = (float)bottom_row[2] / max; \
571                                 bottom_g -= (float)chroma_offset / max; \
572                                 bottom_b -= (float)chroma_offset / max; \
574 /* Convert pixel values to RGB float */ \
575                                 if(chroma_offset) \
576                                 { \
577                                         float y = bottom_r; \
578                                         float u = bottom_g; \
579                                         float v = bottom_b; \
580                                         YUV::yuv_to_rgb_f(bottom_r, \
581                                                 bottom_g, \
582                                                 bottom_b, \
583                                                 y, \
584                                                 u, \
585                                                 v); \
586                                         y = top_r; \
587                                         u = top_g; \
588                                         v = top_b; \
589                                         YUV::yuv_to_rgb_f(top_r, \
590                                                 top_g, \
591                                                 top_b, \
592                                                 y, \
593                                                 u, \
594                                                 v); \
595                                 } \
597                                 float difference = sqrt(SQR(top_r - bottom_r) +  \
598                                         SQR(top_g - bottom_g) + \
599                                         SQR(top_b - bottom_b)); \
601                                 if(difference < threshold) \
602                                 { \
603                                         a = 0; \
604                                 } \
605                                 else \
606                                 if(difference < threshold_run) \
607                                 { \
608                                         a = (difference - threshold) / run; \
609                                 } \
610                         } \
612 /* multiply alpha */ \
613                         if(components == 4) \
614                         { \
615                                 top_row[3] = MIN((type)(a * max), top_row[3]); \
616                         } \
617                         else \
618                         { \
619                                 top_row[0] = (type)(a * top_row[0]); \
620                                 top_row[1] = (type)(a * (top_row[1] - chroma_offset) + chroma_offset); \
621                                 top_row[2] = (type)(a * (top_row[2] - chroma_offset) + chroma_offset); \
622                         } \
624                         top_row += components; \
625                         bottom_row += components; \
626                 } \
627         } \
632         switch(plugin->top_frame->get_color_model())
633         {
634                 case BC_RGB_FLOAT:
635                         DIFFKEY_MACRO(float, 3, 1.0, 0);
636                         break;
637                 case BC_RGBA_FLOAT:
638                         DIFFKEY_MACRO(float, 4, 1.0, 0);
639                         break;
640                 case BC_RGB888:
641                         DIFFKEY_MACRO(unsigned char, 3, 0xff, 0);
642                         break;
643                 case BC_RGBA8888:
644                         DIFFKEY_MACRO(unsigned char, 4, 0xff, 0);
645                         break;
646                 case BC_YUV888:
647                         DIFFKEY_MACRO(unsigned char, 3, 0xff, 0x80);
648                         break;
649                 case BC_YUVA8888:
650                         DIFFKEY_MACRO(unsigned char, 4, 0xff, 0x80);
651                         break;
652         }
661 DiffKeyPackage::DiffKeyPackage()
662  : LoadPackage()
671 #endif