rework keyframe hide popup, keyframe auto render, textbox set_selection wide text
[goodguy/history.git] / cinelerra-5.1 / 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.tag.set_title("/DIFFKEY");
344         output.append_tag();
345         output.append_newline();
346         output.terminate_string();
347 }
348
349 void DiffKey::read_data(KeyFrame *keyframe)
350 {
351         FileXML input;
352
353         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
354
355         while(!input.read_tag())
356         {
357                 if(input.tag.title_is("DIFFKEY"))
358                 {
359                         config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
360                         config.slope = input.tag.get_property("SLOPE", config.slope);
361                         config.do_value = input.tag.get_property("DO_VALUE", config.do_value);
362                 }
363         }
364 }
365
366 void DiffKey::update_gui()
367 {
368         if(thread)
369         {
370                 if(load_configuration())
371                 {
372                         thread->window->lock_window("DiffKey::update_gui");
373                         ((DiffKeyGUI*)thread->window)->threshold->update(config.threshold);
374                         ((DiffKeyGUI*)thread->window)->slope->update(config.slope);
375                         ((DiffKeyGUI*)thread->window)->do_value->update(config.do_value);
376                         thread->window->unlock_window();
377                 }
378         }
379 }
380
381 int DiffKey::process_buffer(VFrame **frame,
382         int64_t start_position,
383         double frame_rate)
384 {
385         load_configuration();
386
387 // Don't process if only 1 layer.
388         if(get_total_buffers() < 2) 
389         {
390                 read_frame(frame[0], 
391                         0, 
392                         start_position, 
393                         frame_rate,
394                         get_use_opengl());
395                 return 0;
396         }
397
398 // Read frames from 2 layers
399         read_frame(frame[0], 
400                 0, 
401                 start_position, 
402                 frame_rate,
403                 get_use_opengl());
404         read_frame(frame[1], 
405                 1, 
406                 start_position, 
407                 frame_rate,
408                 get_use_opengl());
409
410         top_frame = frame[0];
411         bottom_frame = frame[1];
412
413         if(get_use_opengl())
414                 return run_opengl();
415
416         if(!engine)
417         {
418                 engine = new DiffKeyEngine(this);
419         }
420
421         engine->process_packages();
422
423         return 0;
424 }
425
426 #define DIFFKEY_VARS(plugin) \
427         float threshold = plugin->config.threshold / 100; \
428         float pad = plugin->config.slope / 100; \
429         float threshold_pad = threshold + pad; \
430
431
432 int DiffKey::handle_opengl()
433 {
434 #ifdef HAVE_GL
435         static const char *diffkey_head = 
436                 "uniform sampler2D tex_bg;\n"
437                 "uniform sampler2D tex_fg;\n"
438                 "uniform float threshold;\n"
439                 "uniform float pad;\n"
440                 "uniform float threshold_pad;\n"
441                 "void main()\n"
442                 "{\n"
443                 "       vec4 foreground = texture2D(tex_fg, gl_TexCoord[0].st);\n"
444                 "       vec4 background = texture2D(tex_bg, gl_TexCoord[0].st);\n";
445
446         static const char *colorcube = 
447                 "       float difference = length(foreground.rgb - background.rgb);\n";
448
449         static const char *yuv_value = 
450                 "       float difference = abs(foreground.r - background.r);\n";
451
452         static const char *rgb_value = 
453                 "       float difference = abs(dot(foreground.rgb, vec3(0.29900, 0.58700, 0.11400)) - \n"
454                 "                                               dot(background.rgb, vec3(0.29900, 0.58700, 0.11400)));\n";
455
456         static const char *diffkey_tail = 
457                 "       vec4 result;\n"
458                 "       if(difference < threshold)\n"
459                 "               result.a = 0.0;\n"
460                 "       else\n"
461                 "       if(difference < threshold_pad)\n"
462                 "               result.a = (difference - threshold) / pad;\n"
463                 "       else\n"
464                 "               result.a = 1.0;\n"
465                 "       result.rgb = foreground.rgb;\n"
466                 "       gl_FragColor = result;\n"
467                 "}\n";
468
469
470
471
472
473         top_frame->enable_opengl();
474         top_frame->init_screen();
475
476         top_frame->to_texture();
477         bottom_frame->to_texture();
478
479         top_frame->enable_opengl();
480         top_frame->init_screen();
481
482         unsigned int shader_id = 0;
483         if(config.do_value)
484         {
485                 if(BC_CModels::is_yuv(top_frame->get_color_model()))
486                         shader_id = VFrame::make_shader(0, 
487                                 diffkey_head,
488                                 yuv_value,
489                                 diffkey_tail,
490                                 0);
491                 else
492                         shader_id = VFrame::make_shader(0, 
493                                 diffkey_head,
494                                 rgb_value,
495                                 diffkey_tail,
496                                 0);
497         }
498         else
499         {
500                         shader_id = VFrame::make_shader(0, 
501                                 diffkey_head,
502                                 colorcube,
503                                 diffkey_tail,
504                                 0);
505         }
506
507
508
509         DIFFKEY_VARS(this)
510
511         bottom_frame->bind_texture(1);
512         top_frame->bind_texture(0);
513
514         if(shader_id > 0)
515         {
516                 glUseProgram(shader_id);
517                 glUniform1i(glGetUniformLocation(shader_id, "tex_fg"), 0);
518                 glUniform1i(glGetUniformLocation(shader_id, "tex_bg"), 1);
519                 glUniform1f(glGetUniformLocation(shader_id, "threshold"), threshold);
520                 glUniform1f(glGetUniformLocation(shader_id, "pad"), pad);
521                 glUniform1f(glGetUniformLocation(shader_id, "threshold_pad"), threshold_pad);
522         }
523
524         if(BC_CModels::components(get_output()->get_color_model()) == 3)
525         {
526                 glEnable(GL_BLEND);
527                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
528                 top_frame->clear_pbuffer();
529         }
530
531         top_frame->draw_texture();
532         glUseProgram(0);
533         top_frame->set_opengl_state(VFrame::SCREEN);
534 // Fastest way to discard output
535         bottom_frame->set_opengl_state(VFrame::TEXTURE);
536         glDisable(GL_BLEND);
537
538
539 #endif
540         return 0;
541 }
542
543
544
545
546 DiffKeyEngine::DiffKeyEngine(DiffKey *plugin) 
547  : LoadServer(plugin->get_project_smp() + 1, plugin->get_project_smp() + 1)
548 {
549         this->plugin = plugin;
550 }
551
552 void DiffKeyEngine::init_packages()
553 {
554         int increment = plugin->top_frame->get_h() / get_total_packages() + 1;
555         int y = 0;
556         for(int i = 0; i < get_total_packages(); i++)
557         {
558                 DiffKeyPackage *pkg = (DiffKeyPackage*)get_package(i);
559                 pkg->row1 = y;
560                 pkg->row2 = MIN(y + increment, plugin->top_frame->get_h()); 
561                 y  += increment;
562         }
563 }
564
565 LoadClient* DiffKeyEngine::new_client()
566 {
567         return new DiffKeyClient(this);
568 }
569
570 LoadPackage* DiffKeyEngine::new_package()
571 {
572         return new DiffKeyPackage;
573 }
574
575
576
577
578
579
580
581
582
583
584
585 DiffKeyClient::DiffKeyClient(DiffKeyEngine *engine)
586  : LoadClient(engine)
587 {
588         this->engine = engine;
589 }
590
591 DiffKeyClient::~DiffKeyClient()
592 {
593 }
594
595 void DiffKeyClient::process_package(LoadPackage *ptr)
596 {
597         DiffKeyPackage *pkg = (DiffKeyPackage*)ptr;
598         DiffKey *plugin = engine->plugin;
599         int w = plugin->top_frame->get_w();
600
601 #define RGB_TO_VALUE(r, g, b) \
602 ((r) * R_TO_Y + (g) * G_TO_Y + (b) * B_TO_Y)
603
604
605 #define DIFFKEY_MACRO(type, components, max, chroma_offset) \
606 { \
607  \
608         for(int i = pkg->row1; i < pkg->row2; i++) \
609         { \
610                 type *top_row = (type*)plugin->top_frame->get_rows()[i]; \
611                 type *bottom_row = (type*)plugin->bottom_frame->get_rows()[i]; \
612  \
613                 for(int j = 0; j < w; j++) \
614                 { \
615                         float a = 1.0; \
616  \
617 /* Test for value in range */ \
618                         if(plugin->config.do_value) \
619                         { \
620                                 float top_value; \
621                                 float bottom_value; \
622  \
623 /* Convert pixel data into floating point value */ \
624                                 if(chroma_offset) \
625                                 { \
626                                         float top_r = (float)top_row[0] / max; \
627                                         float bottom_r = (float)bottom_row[0] / max; \
628                                         top_value = top_r; \
629                                         bottom_value = bottom_r; \
630                                 } \
631                                 else \
632                                 { \
633                                         float top_r = (float)top_row[0] / max; \
634                                         float top_g = (float)top_row[1] / max; \
635                                         float top_b = (float)top_row[2] / max; \
636                                         top_g -= (float)chroma_offset / max; \
637                                         top_b -= (float)chroma_offset / max; \
638  \
639                                         float bottom_r = (float)bottom_row[0] / max; \
640                                         float bottom_g = (float)bottom_row[1] / max; \
641                                         float bottom_b = (float)bottom_row[2] / max; \
642                                         bottom_g -= (float)chroma_offset / max; \
643                                         bottom_b -= (float)chroma_offset / max; \
644  \
645                                         top_value = RGB_TO_VALUE(top_r, top_g, top_b); \
646                                         bottom_value = RGB_TO_VALUE(bottom_r, bottom_g, bottom_b); \
647                                 } \
648  \
649                                 float min_v = bottom_value - threshold; \
650                                 float max_v = bottom_value + threshold; \
651  \
652 /* Full transparency if in range */ \
653                                 if(top_value >= min_v && top_value < max_v) \
654                                 { \
655                                         a = 0; \
656                                 } \
657                                 else \
658 /* Phased out if below or above range */ \
659                                 if(top_value < min_v) \
660                                 { \
661                                         if(min_v - top_value < pad) \
662                                                 a = (min_v - top_value) / pad; \
663                                 } \
664                                 else \
665                                 if(top_value - max_v < pad) \
666                                         a = (top_value - max_v) / pad; \
667                         } \
668                         else \
669 /* Use color cube */ \
670                         { \
671                                 float top_r = (float)top_row[0] / max; \
672                                 float top_g = (float)top_row[1] / max; \
673                                 float top_b = (float)top_row[2] / max; \
674                                 top_g -= (float)chroma_offset / max; \
675                                 top_b -= (float)chroma_offset / max; \
676  \
677                                 float bottom_r = (float)bottom_row[0] / max; \
678                                 float bottom_g = (float)bottom_row[1] / max; \
679                                 float bottom_b = (float)bottom_row[2] / max; \
680                                 bottom_g -= (float)chroma_offset / max; \
681                                 bottom_b -= (float)chroma_offset / max; \
682  \
683  \
684                                 float difference = sqrt(SQR(top_r - bottom_r) +  \
685                                         SQR(top_g - bottom_g) + \
686                                         SQR(top_b - bottom_b)); \
687  \
688                                 if(difference < threshold) \
689                                 { \
690                                         a = 0; \
691                                 } \
692                                 else \
693                                 if(difference < threshold_pad) \
694                                 { \
695                                         a = (difference - threshold) / pad; \
696                                 } \
697                         } \
698  \
699 /* multiply alpha */ \
700                         if(components == 4) \
701                         { \
702                                 top_row[3] = MIN((type)(a * max), top_row[3]); \
703                         } \
704                         else \
705                         { \
706                                 top_row[0] = (type)(a * top_row[0]); \
707                                 top_row[1] = (type)(a * (top_row[1] - chroma_offset) + chroma_offset); \
708                                 top_row[2] = (type)(a * (top_row[2] - chroma_offset) + chroma_offset); \
709                         } \
710  \
711                         top_row += components; \
712                         bottom_row += components; \
713                 } \
714         } \
715 }
716
717         DIFFKEY_VARS(plugin)
718
719         switch(plugin->top_frame->get_color_model())
720         {
721                 case BC_RGB_FLOAT:
722                         DIFFKEY_MACRO(float, 3, 1.0, 0);
723                         break;
724                 case BC_RGBA_FLOAT:
725                         DIFFKEY_MACRO(float, 4, 1.0, 0);
726                         break;
727                 case BC_RGB888:
728                         DIFFKEY_MACRO(unsigned char, 3, 0xff, 0);
729                         break;
730                 case BC_RGBA8888:
731                         DIFFKEY_MACRO(unsigned char, 4, 0xff, 0);
732                         break;
733                 case BC_YUV888:
734                         DIFFKEY_MACRO(unsigned char, 3, 0xff, 0x80);
735                         break;
736                 case BC_YUVA8888:
737                         DIFFKEY_MACRO(unsigned char, 4, 0xff, 0x80);
738                         break;
739         }
740
741
742
743 }
744
745
746
747
748 DiffKeyPackage::DiffKeyPackage()
749  : LoadPackage()
750 {
751 }
752
753
754
755
756
757
758 #endif