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