update internationalization data
[goodguy/history.git] / cinelerra-5.0 / plugins / diffkey / diffkey.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  * 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22 #ifndef DIFFKEY_H
23 #define DIFFKEY_H
24
25 #include "bcdisplayinfo.h"
26 #include "clip.h"
27 #include "bchash.h"
28 #include "filexml.h"
29 #include "guicast.h"
30 #include "language.h"
31 #include "loadbalance.h"
32 #include "cicolors.h"
33 #include "pluginvclient.h"
34
35
36 #include <string.h>
37
38
39
40 class DiffKeyGUI;
41 class DiffKey;
42
43
44
45 class DiffKeyConfig
46 {
47 public:
48         DiffKeyConfig();
49         void copy_from(DiffKeyConfig &src);
50         int equivalent(DiffKeyConfig &src);
51         void interpolate(DiffKeyConfig &prev, 
52                 DiffKeyConfig &next, 
53                 int64_t prev_frame, 
54                 int64_t next_frame, 
55                 int64_t current_frame);
56
57         float threshold;
58         float slope;
59         int do_value;
60 };
61
62
63 class DiffKeyThreshold : public BC_FSlider
64 {
65 public:
66         DiffKeyThreshold(DiffKey *plugin, int x, int y);
67         int handle_event();
68         DiffKey *plugin;
69 };
70
71 class DiffKeySlope : public BC_FSlider
72 {
73 public:
74         DiffKeySlope(DiffKey *plugin, int x, int y);
75         int handle_event();
76         DiffKey *plugin;
77 };
78
79 class DiffKeyDoValue : public BC_CheckBox
80 {
81 public:
82         DiffKeyDoValue(DiffKey *plugin, int x, int y);
83         int handle_event();
84         DiffKey *plugin;
85 };
86
87
88
89 class DiffKeyGUI : public PluginClientWindow
90 {
91 public:
92         DiffKeyGUI(DiffKey *plugin);
93         ~DiffKeyGUI();
94
95
96         void create_objects();
97
98
99         DiffKeyThreshold *threshold;
100         DiffKeySlope *slope;
101         DiffKeyDoValue *do_value;
102         DiffKey *plugin;
103 };
104
105
106
107
108 class DiffKeyEngine : public LoadServer
109 {
110 public:
111         DiffKeyEngine(DiffKey *plugin);
112         void init_packages();
113         LoadClient* new_client();
114         LoadPackage* new_package();
115         DiffKey *plugin;
116 };
117
118
119 class DiffKeyClient : public LoadClient
120 {
121 public:
122         DiffKeyClient(DiffKeyEngine *engine);
123         ~DiffKeyClient();
124
125         void process_package(LoadPackage *pkg);
126         DiffKeyEngine *engine;
127 };
128
129 class DiffKeyPackage : public LoadPackage
130 {
131 public:
132         DiffKeyPackage();
133         int row1;
134         int row2;
135 };
136
137
138
139 class DiffKey : public PluginVClient
140 {
141 public:
142         DiffKey(PluginServer *server);
143         ~DiffKey();
144
145         int process_buffer(VFrame **frame,
146                 int64_t start_position,
147                 double frame_rate);
148         int is_realtime();
149         int is_multichannel();
150         void save_data(KeyFrame *keyframe);
151         void read_data(KeyFrame *keyframe);
152         void update_gui();
153         int handle_opengl();
154
155
156
157         PLUGIN_CLASS_MEMBERS(DiffKeyConfig)
158
159         DiffKeyEngine *engine;
160         VFrame *top_frame;
161         VFrame *bottom_frame;
162 };
163
164
165
166
167
168
169
170
171 REGISTER_PLUGIN(DiffKey)
172
173
174 DiffKeyConfig::DiffKeyConfig()
175 {
176         threshold = 0.1;
177         slope = 0;
178         do_value = 0;
179 }
180
181 void DiffKeyConfig::copy_from(DiffKeyConfig &src)
182 {
183         this->threshold = src.threshold;
184         this->slope = src.slope;
185         this->do_value = src.do_value;
186 }
187
188
189 int DiffKeyConfig::equivalent(DiffKeyConfig &src)
190 {
191         return EQUIV(threshold, src.threshold) &&
192                 EQUIV(slope, src.slope) &&
193                 do_value == src.do_value;
194 }
195
196 void DiffKeyConfig::interpolate(DiffKeyConfig &prev, 
197         DiffKeyConfig &next, 
198         int64_t prev_frame, 
199         int64_t next_frame, 
200         int64_t current_frame)
201 {
202         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
203         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
204
205         this->threshold = prev.threshold * prev_scale + next.threshold * next_scale;
206         this->slope = prev.slope * prev_scale + next.slope * next_scale;
207         this->do_value = prev.do_value;
208 }
209
210
211
212
213
214
215
216
217
218
219 DiffKeyThreshold::DiffKeyThreshold(DiffKey *plugin, int x, int y)
220  : BC_FSlider(x, y, 0, 200, 200, 0, 100, plugin->config.threshold)
221 {
222         this->plugin = plugin;
223 }
224
225 int DiffKeyThreshold::handle_event()
226 {
227         plugin->config.threshold = get_value();
228         plugin->send_configure_change();
229         return 1;
230 }
231
232
233
234
235
236
237
238
239 DiffKeySlope::DiffKeySlope(DiffKey *plugin, int x, int y)
240  : BC_FSlider(x, y, 0, 200, 200, 0, 100, plugin->config.slope)
241 {
242         this->plugin = plugin;
243 }
244
245 int DiffKeySlope::handle_event()
246 {
247         plugin->config.slope = get_value();
248         plugin->send_configure_change();
249         return 1;
250 }
251
252
253
254 DiffKeyDoValue::DiffKeyDoValue(DiffKey *plugin, int x, int y)
255  : BC_CheckBox(x, y, plugin->config.do_value, _("Use Value"))
256 {
257         this->plugin = plugin;
258 }
259
260 int DiffKeyDoValue::handle_event()
261 {
262         plugin->config.do_value = get_value();
263         plugin->send_configure_change();
264         return 1;
265 }
266
267
268
269
270
271
272
273 DiffKeyGUI::DiffKeyGUI(DiffKey *plugin)
274  : PluginClientWindow(plugin,
275         320,
276         100,
277         320,
278         100,
279         0)
280 {
281         this->plugin = plugin;
282 }
283
284 DiffKeyGUI::~DiffKeyGUI()
285 {
286 }
287
288
289 void DiffKeyGUI::create_objects()
290 {
291         int x = 10, y = 10;
292         BC_Title *title;
293         add_subwindow(title = new BC_Title(x, y, _("Threshold:")));
294         x += title->get_w() + 10;
295         add_subwindow(threshold = new DiffKeyThreshold(plugin, x, y));
296         x = 10;
297         y += threshold->get_h() + 10;
298         add_subwindow(title = new BC_Title(x, y, _("Slope:")));
299         x += title->get_w() + 10;
300         add_subwindow(slope = new DiffKeySlope(plugin, x, y));
301         x = 10;
302         y += slope->get_h() + 10;
303         add_subwindow(do_value = new DiffKeyDoValue(plugin, x, y));
304
305
306
307         show_window();
308 }
309
310
311
312
313 DiffKey::DiffKey(PluginServer *server)
314  : PluginVClient(server)
315 {
316         
317         engine = 0;
318 }
319
320 DiffKey::~DiffKey()
321 {
322         
323         delete engine;
324 }
325
326 NEW_WINDOW_MACRO(DiffKey, DiffKeyGUI)
327 LOAD_CONFIGURATION_MACRO(DiffKey, DiffKeyConfig)
328
329 const char* DiffKey::plugin_title() { return _("Difference key"); }
330 int DiffKey::is_realtime() { return 1; }
331 int DiffKey::is_multichannel() { return 1; }
332
333
334 void DiffKey::save_data(KeyFrame *keyframe)
335 {
336         FileXML output;
337         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
338         output.tag.set_title("DIFFKEY");
339         output.tag.set_property("THRESHOLD", config.threshold);
340         output.tag.set_property("SLOPE", config.slope);
341         output.tag.set_property("DO_VALUE", config.do_value);
342         output.append_tag();
343         output.terminate_string();
344 }
345
346 void DiffKey::read_data(KeyFrame *keyframe)
347 {
348         FileXML input;
349
350         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
351
352         while(!input.read_tag())
353         {
354                 if(input.tag.title_is("DIFFKEY"))
355                 {
356                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
357                         config.slope = input.tag.get_property("SLOPE", config.slope);
358                         config.do_value = input.tag.get_property("DO_VALUE", config.do_value);
359                 }
360         }
361 }
362
363 void DiffKey::update_gui()
364 {
365         if(thread)
366         {
367                 if(load_configuration())
368                 {
369                         thread->window->lock_window("DiffKey::update_gui");
370                         ((DiffKeyGUI*)thread->window)->threshold->update(config.threshold);
371                         ((DiffKeyGUI*)thread->window)->slope->update(config.slope);
372                         ((DiffKeyGUI*)thread->window)->do_value->update(config.do_value);
373                         thread->window->unlock_window();
374                 }
375         }
376 }
377
378 int DiffKey::process_buffer(VFrame **frame,
379         int64_t start_position,
380         double frame_rate)
381 {
382         load_configuration();
383
384 // Don't process if only 1 layer.
385         if(get_total_buffers() < 2) 
386         {
387                 read_frame(frame[0], 
388                         0, 
389                         start_position, 
390                         frame_rate,
391                         get_use_opengl());
392                 return 0;
393         }
394
395 // Read frames from 2 layers
396         read_frame(frame[0], 
397                 0, 
398                 start_position, 
399                 frame_rate,
400                 get_use_opengl());
401         read_frame(frame[1], 
402                 1, 
403                 start_position, 
404                 frame_rate,
405                 get_use_opengl());
406
407         top_frame = frame[0];
408         bottom_frame = frame[1];
409
410         if(get_use_opengl())
411                 return run_opengl();
412
413         if(!engine)
414         {
415                 engine = new DiffKeyEngine(this);
416         }
417
418         engine->process_packages();
419
420         return 0;
421 }
422
423 #define DIFFKEY_VARS(plugin) \
424         float threshold = plugin->config.threshold / 100; \
425         float pad = plugin->config.slope / 100; \
426         float threshold_pad = threshold + pad; \
427
428
429 int DiffKey::handle_opengl()
430 {
431 #ifdef HAVE_GL
432         static const char *diffkey_head = 
433                 "uniform sampler2D tex_bg;\n"
434                 "uniform sampler2D tex_fg;\n"
435                 "uniform float threshold;\n"
436                 "uniform float pad;\n"
437                 "uniform float threshold_pad;\n"
438                 "void main()\n"
439                 "{\n"
440                 "       vec4 foreground = texture2D(tex_fg, gl_TexCoord[0].st);\n"
441                 "       vec4 background = texture2D(tex_bg, gl_TexCoord[0].st);\n";
442
443         static const char *colorcube = 
444                 "       float difference = length(foreground.rgb - background.rgb);\n";
445
446         static const char *yuv_value = 
447                 "       float difference = abs(foreground.r - background.r);\n";
448
449         static const char *rgb_value = 
450                 "       float difference = abs(dot(foreground.rgb, vec3(0.29900, 0.58700, 0.11400)) - \n"
451                 "                                               dot(background.rgb, vec3(0.29900, 0.58700, 0.11400)));\n";
452
453         static const char *diffkey_tail = 
454                 "       vec4 result;\n"
455                 "       if(difference < threshold)\n"
456                 "               result.a = 0.0;\n"
457                 "       else\n"
458                 "       if(difference < threshold_pad)\n"
459                 "               result.a = (difference - threshold) / pad;\n"
460                 "       else\n"
461                 "               result.a = 1.0;\n"
462                 "       result.rgb = foreground.rgb;\n"
463                 "       gl_FragColor = result;\n"
464                 "}\n";
465
466
467
468
469
470         top_frame->enable_opengl();
471         top_frame->init_screen();
472
473         top_frame->to_texture();
474         bottom_frame->to_texture();
475
476         top_frame->enable_opengl();
477         top_frame->init_screen();
478
479         unsigned int shader_id = 0;
480         if(config.do_value)
481         {
482                 if(BC_CModels::is_yuv(top_frame->get_color_model()))
483                         shader_id = VFrame::make_shader(0, 
484                                 diffkey_head,
485                                 yuv_value,
486                                 diffkey_tail,
487                                 0);
488                 else
489                         shader_id = VFrame::make_shader(0, 
490                                 diffkey_head,
491                                 rgb_value,
492                                 diffkey_tail,
493                                 0);
494         }
495         else
496         {
497                         shader_id = VFrame::make_shader(0, 
498                                 diffkey_head,
499                                 colorcube,
500                                 diffkey_tail,
501                                 0);
502         }
503
504
505
506         DIFFKEY_VARS(this)
507
508         bottom_frame->bind_texture(1);
509         top_frame->bind_texture(0);
510
511         if(shader_id > 0)
512         {
513                 glUseProgram(shader_id);
514                 glUniform1i(glGetUniformLocation(shader_id, "tex_fg"), 0);
515                 glUniform1i(glGetUniformLocation(shader_id, "tex_bg"), 1);
516                 glUniform1f(glGetUniformLocation(shader_id, "threshold"), threshold);
517                 glUniform1f(glGetUniformLocation(shader_id, "pad"), pad);
518                 glUniform1f(glGetUniformLocation(shader_id, "threshold_pad"), threshold_pad);
519         }
520
521         if(BC_CModels::components(get_output()->get_color_model()) == 3)
522         {
523                 glEnable(GL_BLEND);
524                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
525                 top_frame->clear_pbuffer();
526         }
527
528         top_frame->draw_texture();
529         glUseProgram(0);
530         top_frame->set_opengl_state(VFrame::SCREEN);
531 // Fastest way to discard output
532         bottom_frame->set_opengl_state(VFrame::TEXTURE);
533         glDisable(GL_BLEND);
534
535
536 #endif
537         return 0;
538 }
539
540
541
542
543 DiffKeyEngine::DiffKeyEngine(DiffKey *plugin) 
544  : LoadServer(plugin->get_project_smp() + 1, plugin->get_project_smp() + 1)
545 {
546         this->plugin = plugin;
547 }
548
549 void DiffKeyEngine::init_packages()
550 {
551         int increment = plugin->top_frame->get_h() / get_total_packages() + 1;
552         int y = 0;
553         for(int i = 0; i < get_total_packages(); i++)
554         {
555                 DiffKeyPackage *pkg = (DiffKeyPackage*)get_package(i);
556                 pkg->row1 = y;
557                 pkg->row2 = MIN(y + increment, plugin->top_frame->get_h()); 
558                 y  += increment;
559         }
560 }
561
562 LoadClient* DiffKeyEngine::new_client()
563 {
564         return new DiffKeyClient(this);
565 }
566
567 LoadPackage* DiffKeyEngine::new_package()
568 {
569         return new DiffKeyPackage;
570 }
571
572
573
574
575
576
577
578
579
580
581
582 DiffKeyClient::DiffKeyClient(DiffKeyEngine *engine)
583  : LoadClient(engine)
584 {
585         this->engine = engine;
586 }
587
588 DiffKeyClient::~DiffKeyClient()
589 {
590 }
591
592 void DiffKeyClient::process_package(LoadPackage *ptr)
593 {
594         DiffKeyPackage *pkg = (DiffKeyPackage*)ptr;
595         DiffKey *plugin = engine->plugin;
596         int w = plugin->top_frame->get_w();
597
598 #define RGB_TO_VALUE(r, g, b) \
599 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
600
601
602 #define DIFFKEY_MACRO(type, components, max, chroma_offset) \
603 { \
604  \
605         for(int i = pkg->row1; i < pkg->row2; i++) \
606         { \
607                 type *top_row = (type*)plugin->top_frame->get_rows()[i]; \
608                 type *bottom_row = (type*)plugin->bottom_frame->get_rows()[i]; \
609  \
610                 for(int j = 0; j < w; j++) \
611                 { \
612                         float a = 1.0; \
613  \
614 /* Test for value in range */ \
615                         if(plugin->config.do_value) \
616                         { \
617                                 float top_value; \
618                                 float bottom_value; \
619  \
620 /* Convert pixel data into floating point value */ \
621                                 if(chroma_offset) \
622                                 { \
623                                         float top_r = (float)top_row[0] / max; \
624                                         float bottom_r = (float)bottom_row[0] / max; \
625                                         top_value = top_r; \
626                                         bottom_value = bottom_r; \
627                                 } \
628                                 else \
629                                 { \
630                                         float top_r = (float)top_row[0] / max; \
631                                         float top_g = (float)top_row[1] / max; \
632                                         float top_b = (float)top_row[2] / max; \
633                                         top_g -= (float)chroma_offset / max; \
634                                         top_b -= (float)chroma_offset / max; \
635  \
636                                         float bottom_r = (float)bottom_row[0] / max; \
637                                         float bottom_g = (float)bottom_row[1] / max; \
638                                         float bottom_b = (float)bottom_row[2] / max; \
639                                         bottom_g -= (float)chroma_offset / max; \
640                                         bottom_b -= (float)chroma_offset / max; \
641  \
642                                         top_value = RGB_TO_VALUE(top_r, top_g, top_b); \
643                                         bottom_value = RGB_TO_VALUE(bottom_r, bottom_g, bottom_b); \
644                                 } \
645  \
646                                 float min_v = bottom_value - threshold; \
647                                 float max_v = bottom_value + threshold; \
648  \
649 /* Full transparency if in range */ \
650                                 if(top_value >= min_v && top_value < max_v) \
651                                 { \
652                                         a = 0; \
653                                 } \
654                                 else \
655 /* Phased out if below or above range */ \
656                                 if(top_value < min_v) \
657                                 { \
658                                         if(min_v - top_value < pad) \
659                                                 a = (min_v - top_value) / pad; \
660                                 } \
661                                 else \
662                                 if(top_value - max_v < pad) \
663                                         a = (top_value - max_v) / pad; \
664                         } \
665                         else \
666 /* Use color cube */ \
667                         { \
668                                 float top_r = (float)top_row[0] / max; \
669                                 float top_g = (float)top_row[1] / max; \
670                                 float top_b = (float)top_row[2] / max; \
671                                 top_g -= (float)chroma_offset / max; \
672                                 top_b -= (float)chroma_offset / max; \
673  \
674                                 float bottom_r = (float)bottom_row[0] / max; \
675                                 float bottom_g = (float)bottom_row[1] / max; \
676                                 float bottom_b = (float)bottom_row[2] / max; \
677                                 bottom_g -= (float)chroma_offset / max; \
678                                 bottom_b -= (float)chroma_offset / max; \
679  \
680  \
681                                 float difference = sqrt(SQR(top_r - bottom_r) +  \
682                                         SQR(top_g - bottom_g) + \
683                                         SQR(top_b - bottom_b)); \
684  \
685                                 if(difference < threshold) \
686                                 { \
687                                         a = 0; \
688                                 } \
689                                 else \
690                                 if(difference < threshold_pad) \
691                                 { \
692                                         a = (difference - threshold) / pad; \
693                                 } \
694                         } \
695  \
696 /* multiply alpha */ \
697                         if(components == 4) \
698                         { \
699                                 top_row[3] = MIN((type)(a * max), top_row[3]); \
700                         } \
701                         else \
702                         { \
703                                 top_row[0] = (type)(a * top_row[0]); \
704                                 top_row[1] = (type)(a * (top_row[1] - chroma_offset) + chroma_offset); \
705                                 top_row[2] = (type)(a * (top_row[2] - chroma_offset) + chroma_offset); \
706                         } \
707  \
708                         top_row += components; \
709                         bottom_row += components; \
710                 } \
711         } \
712 }
713
714         DIFFKEY_VARS(plugin)
715
716         switch(plugin->top_frame->get_color_model())
717         {
718                 case BC_RGB_FLOAT:
719                         DIFFKEY_MACRO(float, 3, 1.0, 0);
720                         break;
721                 case BC_RGBA_FLOAT:
722                         DIFFKEY_MACRO(float, 4, 1.0, 0);
723                         break;
724                 case BC_RGB888:
725                         DIFFKEY_MACRO(unsigned char, 3, 0xff, 0);
726                         break;
727                 case BC_RGBA8888:
728                         DIFFKEY_MACRO(unsigned char, 4, 0xff, 0);
729                         break;
730                 case BC_YUV888:
731                         DIFFKEY_MACRO(unsigned char, 3, 0xff, 0x80);
732                         break;
733                 case BC_YUVA8888:
734                         DIFFKEY_MACRO(unsigned char, 4, 0xff, 0x80);
735                         break;
736         }
737
738
739
740 }
741
742
743
744
745 DiffKeyPackage::DiffKeyPackage()
746  : LoadPackage()
747 {
748 }
749
750
751
752
753
754
755 #endif