r936: Sane default sample zoom...
[cinelerra_cv/mob.git] / plugins / diffkey / diffkey.C
blob217b13f749386f11c722581d22a2d9f03e24884d
1 #ifndef DIFFKEY_H
2 #define DIFFKEY_H
4 #include "bcdisplayinfo.h"
5 #include "clip.h"
6 #include "bchash.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();
137         int handle_opengl();
141         PLUGIN_CLASS_MEMBERS(DiffKeyConfig, DiffKeyThread)
143         DiffKeyEngine *engine;
144         VFrame *top_frame;
145         VFrame *bottom_frame;
155 REGISTER_PLUGIN(DiffKey)
158 DiffKeyConfig::DiffKeyConfig()
160         threshold = 0.1;
161         slope = 0;
162         do_value = 0;
165 void DiffKeyConfig::copy_from(DiffKeyConfig &src)
167         this->threshold = src.threshold;
168         this->slope = src.slope;
169         this->do_value = src.do_value;
173 int DiffKeyConfig::equivalent(DiffKeyConfig &src)
175         return EQUIV(threshold, src.threshold) &&
176                 EQUIV(slope, src.slope) &&
177                 do_value == src.do_value;
180 void DiffKeyConfig::interpolate(DiffKeyConfig &prev, 
181         DiffKeyConfig &next, 
182         int64_t prev_frame, 
183         int64_t next_frame, 
184         int64_t current_frame)
186         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
187         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
189         this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
190         this->slope = prev.slope * prev_scale + next.slope * next_scale;
191         this->do_value = prev.do_value;
203 DiffKeyThreshold::DiffKeyThreshold(DiffKey *plugin, int x, int y)
204  : BC_FSlider(x, y, 0, 200, 200, 0, 100, plugin->config.threshold)
206         this->plugin = plugin;
209 int DiffKeyThreshold::handle_event()
211         plugin->config.threshold = get_value();
212         plugin->send_configure_change();
213         return 1;
223 DiffKeySlope::DiffKeySlope(DiffKey *plugin, int x, int y)
224  : BC_FSlider(x, y, 0, 200, 200, 0, 100, plugin->config.slope)
226         this->plugin = plugin;
229 int DiffKeySlope::handle_event()
231         plugin->config.slope = get_value();
232         plugin->send_configure_change();
233         return 1;
238 DiffKeyDoValue::DiffKeyDoValue(DiffKey *plugin, int x, int y)
239  : BC_CheckBox(x, y, plugin->config.do_value, _("Use Value"))
241         this->plugin = plugin;
244 int DiffKeyDoValue::handle_event()
246         plugin->config.do_value = get_value();
247         plugin->send_configure_change();
248         return 1;
257 DiffKeyGUI::DiffKeyGUI(DiffKey *plugin, int x, int y)
258  : BC_Window(plugin->gui_string,
259         x,
260         y,
261         320,
262         100,
263         320,
264         100,
265         0,
266         0,
267         1)
269         this->plugin = plugin;
272 DiffKeyGUI::~DiffKeyGUI()
277 void DiffKeyGUI::create_objects()
279         int x = 10, y = 10, x2;
280         BC_Title *title;
281         add_subwindow(title = new BC_Title(x, y, _("Threshold:")));
282         x += title->get_w() + 10;
283         add_subwindow(threshold = new DiffKeyThreshold(plugin, x, y));
284         x = 10;
285         y += threshold->get_h() + 10;
286         add_subwindow(title = new BC_Title(x, y, _("Slope:")));
287         x += title->get_w() + 10;
288         add_subwindow(slope = new DiffKeySlope(plugin, x, y));
289         x = 10;
290         y += slope->get_h() + 10;
291         add_subwindow(do_value = new DiffKeyDoValue(plugin, x, y));
295         show_window();
298 WINDOW_CLOSE_EVENT(DiffKeyGUI)
301 PLUGIN_THREAD_OBJECT(DiffKey, DiffKeyThread, DiffKeyGUI)
305 DiffKey::DiffKey(PluginServer *server)
306  : PluginVClient(server)
308         PLUGIN_CONSTRUCTOR_MACRO
309         engine = 0;
312 DiffKey::~DiffKey()
314         PLUGIN_DESTRUCTOR_MACRO
315         delete engine;
318 SHOW_GUI_MACRO(DiffKey, DiffKeyThread)
319 RAISE_WINDOW_MACRO(DiffKey)
320 SET_STRING_MACRO(DiffKey)
321 #include "picon_png.h"
322 NEW_PICON_MACRO(DiffKey)
323 LOAD_CONFIGURATION_MACRO(DiffKey, DiffKeyConfig)
325 char* DiffKey::plugin_title() { return N_("Difference key"); }
326 int DiffKey::is_realtime() { return 1; }
327 int DiffKey::is_multichannel() { return 1; }
329 int DiffKey::load_defaults()
331         char directory[BCTEXTLEN];
332 // set the default directory
333         sprintf(directory, "%sdiffkey.rc", BCASTDIR);
335 // load the defaults
336         defaults = new BC_Hash(directory);
337         defaults->load();
339         config.threshold = defaults->get("THRESHOLD", config.threshold);
340         config.slope = defaults->get("SLOPE", config.slope);
341         config.do_value = defaults->get("DO_VALUE", config.do_value);
342         return 0;
345 int DiffKey::save_defaults()
347         defaults->update("THRESHOLD", config.threshold);
348         defaults->update("SLOPE", config.slope);
349         defaults->update("DO_VALUE", config.do_value);
350         defaults->save();
351         return 0;
354 void DiffKey::save_data(KeyFrame *keyframe)
356         FileXML output;
357         output.set_shared_string(keyframe->data, MESSAGESIZE);
358         output.tag.set_title("DIFFKEY");
359         output.tag.set_property("THRESHOLD", config.threshold);
360         output.tag.set_property("SLOPE", config.slope);
361         output.tag.set_property("DO_VALUE", config.do_value);
362         output.append_tag();
363         output.terminate_string();
366 void DiffKey::read_data(KeyFrame *keyframe)
368         FileXML input;
370         input.set_shared_string(keyframe->data, strlen(keyframe->data));
372         while(!input.read_tag())
373         {
374                 if(input.tag.title_is("DIFFKEY"))
375                 {
376                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
377                         config.slope = input.tag.get_property("SLOPE", config.slope);
378                         config.do_value = input.tag.get_property("DO_VALUE", config.do_value);
379                 }
380         }
383 void DiffKey::update_gui()
385         if(thread)
386         {
387                 if(load_configuration())
388                 {
389                         thread->window->lock_window("DiffKey::update_gui");
390                         thread->window->threshold->update(config.threshold);
391                         thread->window->slope->update(config.slope);
392                         thread->window->do_value->update(config.do_value);
393                         thread->window->unlock_window();
394                 }
395         }
398 int DiffKey::process_buffer(VFrame **frame,
399         int64_t start_position,
400         double frame_rate)
402         load_configuration();
404 // Don't process if only 1 layer.
405         if(get_total_buffers() < 2) 
406         {
407                 read_frame(frame[0], 
408                         0, 
409                         start_position, 
410                         frame_rate,
411                         get_use_opengl());
412                 return 0;
413         }
415 // Read frames from 2 layers
416         read_frame(frame[0], 
417                 0, 
418                 start_position, 
419                 frame_rate,
420                 get_use_opengl());
421         read_frame(frame[1], 
422                 1, 
423                 start_position, 
424                 frame_rate,
425                 get_use_opengl());
427         top_frame = frame[0];
428         bottom_frame = frame[1];
430         if(get_use_opengl())
431                 return run_opengl();
433         if(!engine)
434         {
435                 engine = new DiffKeyEngine(this);
436         }
438         engine->process_packages();
440         return 0;
443 #define DIFFKEY_VARS(plugin) \
444         float threshold = plugin->config.threshold / 100; \
445         float pad = plugin->config.slope / 100; \
446         float threshold_pad = threshold + pad; \
449 int DiffKey::handle_opengl()
451 #ifdef HAVE_GL
452         static char *diffkey_head = 
453                 "uniform sampler2D tex_bg;\n"
454                 "uniform sampler2D tex_fg;\n"
455                 "uniform float threshold;\n"
456                 "uniform float pad;\n"
457                 "uniform float threshold_pad;\n"
458                 "void main()\n"
459                 "{\n"
460                 "       vec4 foreground = texture2D(tex_fg, gl_TexCoord[0].st);\n"
461                 "       vec4 background = texture2D(tex_bg, gl_TexCoord[0].st);\n";
463         static char *colorcube = 
464                 "       float difference = length(foreground.rgb - background.rgb);\n";
466         static char *yuv_value = 
467                 "       float difference = abs(foreground.r - background.r);\n";
469         static char *rgb_value = 
470                 "       float difference = abs(dot(foreground.rgb, vec3(0.29900, 0.58700, 0.11400)) - \n"
471                 "                                               dot(background.rgb, vec3(0.29900, 0.58700, 0.11400)));\n";
473         static char *diffkey_tail = 
474                 "       vec4 result;\n"
475                 "       if(difference < threshold)\n"
476                 "               result.a = 0.0;\n"
477                 "       else\n"
478                 "       if(difference < threshold_pad)\n"
479                 "               result.a = (difference - threshold) / pad;\n"
480                 "       else\n"
481                 "               result.a = 1.0;\n"
482                 "       result.rgb = foreground.rgb;\n"
483                 "       gl_FragColor = result;\n"
484                 "}\n";
490         top_frame->enable_opengl();
491         top_frame->init_screen();
493         top_frame->to_texture();
494         bottom_frame->to_texture();
496         top_frame->enable_opengl();
497         top_frame->init_screen();
499         unsigned int shader_id = 0;
500         if(config.do_value)
501         {
502                 if(cmodel_is_yuv(top_frame->get_color_model()))
503                         shader_id = VFrame::make_shader(0, 
504                                 diffkey_head,
505                                 yuv_value,
506                                 diffkey_tail,
507                                 0);
508                 else
509                         shader_id = VFrame::make_shader(0, 
510                                 diffkey_head,
511                                 rgb_value,
512                                 diffkey_tail,
513                                 0);
514         }
515         else
516         {
517                         shader_id = VFrame::make_shader(0, 
518                                 diffkey_head,
519                                 colorcube,
520                                 diffkey_tail,
521                                 0);
522         }
526         DIFFKEY_VARS(this)
528         bottom_frame->bind_texture(1);
529         top_frame->bind_texture(0);
531         if(shader_id > 0)
532         {
533                 glUseProgram(shader_id);
534                 glUniform1i(glGetUniformLocation(shader_id, "tex_fg"), 0);
535                 glUniform1i(glGetUniformLocation(shader_id, "tex_bg"), 1);
536                 glUniform1f(glGetUniformLocation(shader_id, "threshold"), threshold);
537                 glUniform1f(glGetUniformLocation(shader_id, "pad"), pad);
538                 glUniform1f(glGetUniformLocation(shader_id, "threshold_pad"), threshold_pad);
539         }
541         if(cmodel_components(get_output()->get_color_model()) == 3)
542         {
543                 glEnable(GL_BLEND);
544                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
545                 top_frame->clear_pbuffer();
546         }
548         top_frame->draw_texture();
549         glUseProgram(0);
550         top_frame->set_opengl_state(VFrame::SCREEN);
551 // Fastest way to discard output
552         bottom_frame->set_opengl_state(VFrame::TEXTURE);
553         glDisable(GL_BLEND);
556 #endif
557         return 0;
563 DiffKeyEngine::DiffKeyEngine(DiffKey *plugin) 
564  : LoadServer(plugin->get_project_smp() + 1, plugin->get_project_smp() + 1)
566         this->plugin = plugin;
569 void DiffKeyEngine::init_packages()
571         int increment = plugin->top_frame->get_h() / get_total_packages() + 1;
572         int y = 0;
573         for(int i = 0; i < get_total_packages(); i++)
574         {
575                 DiffKeyPackage *pkg = (DiffKeyPackage*)get_package(i);
576                 pkg->row1 = y;
577                 pkg->row2 = MIN(y + increment, plugin->top_frame->get_h()); 
578                 y  += increment;
579         }
582 LoadClient* DiffKeyEngine::new_client()
584         return new DiffKeyClient(this);
587 LoadPackage* DiffKeyEngine::new_package()
589         return new DiffKeyPackage;
602 DiffKeyClient::DiffKeyClient(DiffKeyEngine *engine)
603  : LoadClient(engine)
605         this->engine = engine;
608 DiffKeyClient::~DiffKeyClient()
612 void DiffKeyClient::process_package(LoadPackage *ptr)
614         DiffKeyPackage *pkg = (DiffKeyPackage*)ptr;
615         DiffKey *plugin = engine->plugin;
616         int w = plugin->top_frame->get_w();
618 #define RGB_TO_VALUE(r, g, b) \
619 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
621 #define SQR(x) ((x) * (x))
623 #define DIFFKEY_MACRO(type, components, max, chroma_offset) \
624 { \
626         for(int i = pkg->row1; i < pkg->row2; i++) \
627         { \
628                 type *top_row = (type*)plugin->top_frame->get_rows()[i]; \
629                 type *bottom_row = (type*)plugin->bottom_frame->get_rows()[i]; \
631                 for(int j = 0; j < w; j++) \
632                 { \
633                         float a = 1.0; \
635 /* Test for value in range */ \
636                         if(plugin->config.do_value) \
637                         { \
638                                 float top_value; \
639                                 float bottom_value; \
641 /* Convert pixel data into floating point value */ \
642                                 if(chroma_offset) \
643                                 { \
644                                         float top_r = (float)top_row[0] / max; \
645                                         float bottom_r = (float)bottom_row[0] / max; \
646                                         top_value = top_r; \
647                                         bottom_value = bottom_r; \
648                                 } \
649                                 else \
650                                 { \
651                                         float top_r = (float)top_row[0] / max; \
652                                         float top_g = (float)top_row[1] / max; \
653                                         float top_b = (float)top_row[2] / max; \
654                                         top_g -= (float)chroma_offset / max; \
655                                         top_b -= (float)chroma_offset / max; \
657                                         float bottom_r = (float)bottom_row[0] / max; \
658                                         float bottom_g = (float)bottom_row[1] / max; \
659                                         float bottom_b = (float)bottom_row[2] / max; \
660                                         bottom_g -= (float)chroma_offset / max; \
661                                         bottom_b -= (float)chroma_offset / max; \
663                                         top_value = RGB_TO_VALUE(top_r, top_g, top_b); \
664                                         bottom_value = RGB_TO_VALUE(bottom_r, bottom_g, bottom_b); \
665                                 } \
667                                 float min_v = bottom_value - threshold; \
668                                 float max_v = bottom_value + threshold; \
670 /* Full transparency if in range */ \
671                                 if(top_value >= min_v && top_value < max_v) \
672                                 { \
673                                         a = 0; \
674                                 } \
675                                 else \
676 /* Phased out if below or above range */ \
677                                 if(top_value < min_v) \
678                                 { \
679                                         if(min_v - top_value < pad) \
680                                                 a = (min_v - top_value) / pad; \
681                                 } \
682                                 else \
683                                 if(top_value - max_v < pad) \
684                                         a = (top_value - max_v) / pad; \
685                         } \
686                         else \
687 /* Use color cube */ \
688                         { \
689                                 float top_r = (float)top_row[0] / max; \
690                                 float top_g = (float)top_row[1] / max; \
691                                 float top_b = (float)top_row[2] / max; \
692                                 top_g -= (float)chroma_offset / max; \
693                                 top_b -= (float)chroma_offset / max; \
695                                 float bottom_r = (float)bottom_row[0] / max; \
696                                 float bottom_g = (float)bottom_row[1] / max; \
697                                 float bottom_b = (float)bottom_row[2] / max; \
698                                 bottom_g -= (float)chroma_offset / max; \
699                                 bottom_b -= (float)chroma_offset / max; \
702                                 float difference = sqrt(SQR(top_r - bottom_r) +  \
703                                         SQR(top_g - bottom_g) + \
704                                         SQR(top_b - bottom_b)); \
706                                 if(difference < threshold) \
707                                 { \
708                                         a = 0; \
709                                 } \
710                                 else \
711                                 if(difference < threshold_pad) \
712                                 { \
713                                         a = (difference - threshold) / pad; \
714                                 } \
715                         } \
717 /* multiply alpha */ \
718                         if(components == 4) \
719                         { \
720                                 top_row[3] = MIN((type)(a * max), top_row[3]); \
721                         } \
722                         else \
723                         { \
724                                 top_row[0] = (type)(a * top_row[0]); \
725                                 top_row[1] = (type)(a * (top_row[1] - chroma_offset) + chroma_offset); \
726                                 top_row[2] = (type)(a * (top_row[2] - chroma_offset) + chroma_offset); \
727                         } \
729                         top_row += components; \
730                         bottom_row += components; \
731                 } \
732         } \
735         DIFFKEY_VARS(plugin)
737         switch(plugin->top_frame->get_color_model())
738         {
739                 case BC_RGB_FLOAT:
740                         DIFFKEY_MACRO(float, 3, 1.0, 0);
741                         break;
742                 case BC_RGBA_FLOAT:
743                         DIFFKEY_MACRO(float, 4, 1.0, 0);
744                         break;
745                 case BC_RGB888:
746                         DIFFKEY_MACRO(unsigned char, 3, 0xff, 0);
747                         break;
748                 case BC_RGBA8888:
749                         DIFFKEY_MACRO(unsigned char, 4, 0xff, 0);
750                         break;
751                 case BC_YUV888:
752                         DIFFKEY_MACRO(unsigned char, 3, 0xff, 0x80);
753                         break;
754                 case BC_YUVA8888:
755                         DIFFKEY_MACRO(unsigned char, 4, 0xff, 0x80);
756                         break;
757         }
766 DiffKeyPackage::DiffKeyPackage()
767  : LoadPackage()
776 #endif