rework ffmpeg a/v filter setup, rework ffmpeg hw decode with vid filters, rework...
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / threshold / threshold.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 #include <string>
23 #include <string.h>
24
25 #include "threshold.h"
26 #include "bccolors.h"
27 #include "clip.h"
28 #include "bchash.h"
29 #include "filexml.h"
30 #include "histogramengine.h"
31 #include "language.h"
32 #include "playback3d.h"
33 #include "thresholdwindow.h"
34 #include "vframe.h"
35
36 using ::std::string;
37
38
39
40 ThresholdConfig::ThresholdConfig()
41 {
42         reset();
43 }
44
45 int ThresholdConfig::equivalent(ThresholdConfig &that)
46 {
47         return EQUIV(min, that.min) &&
48                 EQUIV(max, that.max) &&
49                 plot == that.plot &&
50                 low_color == that.low_color &&
51                 mid_color == that.mid_color &&
52                 high_color == that.high_color;
53 }
54
55 void ThresholdConfig::copy_from(ThresholdConfig &that)
56 {
57         min = that.min;
58         max = that.max;
59         plot = that.plot;
60         low_color = that.low_color;
61         mid_color = that.mid_color;
62         high_color = that.high_color;
63 }
64
65 // General purpose scale function.
66 template<typename T>
67 T interpolate(const T & prev, const double & prev_scale, const T & next, const double & next_scale)
68 {
69         return static_cast<T>(prev * prev_scale + next * next_scale);
70 }
71
72 void ThresholdConfig::interpolate(ThresholdConfig &prev,
73         ThresholdConfig &next,
74         int64_t prev_frame,
75         int64_t next_frame,
76         int64_t current_frame)
77 {
78         double next_scale = (double)(current_frame - prev_frame) /
79                 (next_frame - prev_frame);
80         double prev_scale = (double)(next_frame - current_frame) /
81                 (next_frame - prev_frame);
82
83         min = ::interpolate(prev.min, prev_scale, next.min, next_scale);
84         max = ::interpolate(prev.max, prev_scale, next.max, next_scale);
85         plot = prev.plot;
86
87         low_color =  ::interpolate(prev.low_color,  prev_scale, next.low_color,  next_scale);
88         mid_color =  ::interpolate(prev.mid_color,  prev_scale, next.mid_color,  next_scale);
89         high_color = ::interpolate(prev.high_color, prev_scale, next.high_color, next_scale);
90 }
91
92 void ThresholdConfig::reset()
93 {
94         min = 0.0;
95         max = 1.0;
96         plot = 1;
97         low_color.set (0x0,  0x0,  0x0,  0xff);
98         mid_color.set (0xff, 0xff, 0xff, 0xff);
99         high_color.set(0x0,  0x0,  0x0,  0xff);
100 }
101
102 void ThresholdConfig::boundaries()
103 {
104         CLAMP(min, HISTOGRAM_MIN, max);
105         CLAMP(max, min, HISTOGRAM_MAX);
106 }
107
108
109
110
111
112
113
114
115 REGISTER_PLUGIN(ThresholdMain)
116
117 ThresholdMain::ThresholdMain(PluginServer *server)
118  : PluginVClient(server)
119 {
120         engine = 0;
121         threshold_engine = 0;
122 }
123
124 ThresholdMain::~ThresholdMain()
125 {
126         delete engine;
127         delete threshold_engine;
128 }
129
130 const char* ThresholdMain::plugin_title() { return N_("Threshold"); }
131 int ThresholdMain::is_realtime() { return 1; }
132
133
134
135 LOAD_CONFIGURATION_MACRO(ThresholdMain, ThresholdConfig)
136
137
138
139
140
141
142
143 int ThresholdMain::process_buffer(VFrame *frame,
144         int64_t start_position,
145         double frame_rate)
146 {
147         load_configuration();
148
149         int use_opengl = get_use_opengl() &&
150                 (!config.plot || !gui_open());
151
152         read_frame(frame,
153                 0,
154                 get_source_position(),
155                 get_framerate(),
156                 use_opengl);
157
158         send_render_gui(frame);
159
160         if(use_opengl) return run_opengl();
161
162         if(!threshold_engine)
163                 threshold_engine = new ThresholdEngine(this);
164         threshold_engine->process_packages(frame);
165
166         return 0;
167 }
168
169 void ThresholdMain::save_data(KeyFrame *keyframe)
170 {
171         FileXML output;
172         output.set_shared_output(keyframe->xbuf);
173         output.tag.set_title("THRESHOLD");
174         output.tag.set_property("MIN", config.min);
175         output.tag.set_property("MAX", config.max);
176         output.tag.set_property("PLOT", config.plot);
177         config.low_color.set_property(output.tag,  "LOW_COLOR");
178         config.mid_color.set_property(output.tag,  "MID_COLOR");
179         config.high_color.set_property(output.tag, "HIGH_COLOR");
180         output.append_tag();
181         output.tag.set_title("/THRESHOLD");
182         output.append_tag();
183         output.append_newline();
184         output.terminate_string();
185 }
186
187 void ThresholdMain::read_data(KeyFrame *keyframe)
188 {
189         FileXML input;
190         input.set_shared_input(keyframe->xbuf);
191         int result = 0;
192         while(!result)
193         {
194                 result = input.read_tag();
195                 if(!result)
196                 {
197                         config.min = input.tag.get_property("MIN", config.min);
198                         config.max = input.tag.get_property("MAX", config.max);
199                         config.plot = input.tag.get_property("PLOT", config.plot);
200                         config.low_color = config.low_color.get_property(input.tag, "LOW_COLOR");
201                         config.mid_color = config.mid_color.get_property(input.tag, "MID_COLOR");
202                         config.high_color = config.high_color.get_property(input.tag, "HIGH_COLOR");
203                 }
204         }
205         config.boundaries();
206 }
207
208 NEW_WINDOW_MACRO(ThresholdMain,ThresholdWindow);
209
210 void ThresholdMain::update_gui()
211 {
212         if(thread)
213         {
214                 thread->window->lock_window("ThresholdMain::update_gui");
215                 if(load_configuration())
216                 {
217                         ThresholdWindow *window = (ThresholdWindow*) thread->window;
218                         window->min->update(config.min);
219                         window->max->update(config.max);
220                         window->plot->update(config.plot);
221                         window->update_low_color();
222                         window->update_mid_color();
223                         window->update_high_color();
224                         window->low_color_thread->update_gui(config.low_color.getRGB(), config.low_color.a);
225                         window->mid_color_thread->update_gui(config.mid_color.getRGB(), config.mid_color.a);
226                         window->high_color_thread->update_gui(config.high_color.getRGB(), config.high_color.a);
227                 }
228                 thread->window->unlock_window();
229         }
230 }
231
232 void ThresholdMain::render_gui(void *data)
233 {
234         if(thread)
235         {
236                 calculate_histogram((VFrame*)data);
237                 ThresholdWindow *window = (ThresholdWindow*) thread->window;
238                 window->lock_window("ThresholdMain::render_gui");
239                 window->canvas->draw();
240                 window->unlock_window();
241         }
242 }
243
244 void ThresholdMain::calculate_histogram(VFrame *frame)
245 {
246         if(!engine) engine = new HistogramEngine(get_project_smp() + 1,
247                 get_project_smp() + 1);
248         engine->process_packages(frame);
249 }
250
251 int ThresholdMain::handle_opengl()
252 {
253 #ifdef HAVE_GL
254         static const char *rgb_shader =
255                 "uniform sampler2D tex;\n"
256                 "uniform float min;\n"
257                 "uniform float max;\n"
258                 "uniform vec4 low_color;\n"
259                 "uniform vec4 mid_color;\n"
260                 "uniform vec4 high_color;\n"
261                 "uniform vec3 rgb_to_y_vector;\n"
262                 "uniform float yminf;\n"
263                 "void main()\n"
264                 "{\n"
265                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
266                 "       float v = dot(pixel.rgb, rgb_to_y_vector) + yminf;\n"
267                 "       if(v < min)\n"
268                 "               pixel = low_color;\n"
269                 "       else if(v < max)\n"
270                 "               pixel = mid_color;\n"
271                 "       else\n"
272                 "               pixel = high_color;\n"
273                 "       gl_FragColor = pixel;\n"
274                 "}\n";
275
276         static const char *yuv_shader =
277                 "uniform sampler2D tex;\n"
278                 "uniform float min;\n"
279                 "uniform float max;\n"
280                 "uniform vec4 low_color;\n"
281                 "uniform vec4 mid_color;\n"
282                 "uniform vec4 high_color;\n"
283                 "void main()\n"
284                 "{\n"
285                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
286                 "       if(pixel.r < min)\n"
287                 "               pixel = low_color;\n"
288                 "       else if(pixel.r < max)\n"
289                 "               pixel = mid_color;\n"
290                 "       else\n"
291                 "               pixel = high_color;\n"
292                 "       gl_FragColor = pixel;\n"
293                 "}\n";
294
295         get_output()->to_texture();
296         get_output()->enable_opengl();
297
298         int color_model = get_output()->get_color_model();
299         bool is_yuv = BC_CModels::is_yuv(color_model);
300         bool has_alpha = BC_CModels::has_alpha(color_model);
301         unsigned int shader = VFrame::make_shader(0, is_yuv ? yuv_shader : rgb_shader, 0);
302         if( shader > 0 ) {
303                 glUseProgram(shader);
304                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
305                 glUniform1f(glGetUniformLocation(shader, "min"), config.min);
306                 glUniform1f(glGetUniformLocation(shader, "max"), config.max);
307
308                 if (is_yuv) {
309                         float y_low,  u_low,  v_low;
310                         float y_mid,  u_mid,  v_mid;
311                         float y_high, u_high, v_high;
312
313                         YUV::yuv.rgb_to_yuv_f(
314                                         (float)config.low_color.r / 0xff,
315                                         (float)config.low_color.g / 0xff,
316                                         (float)config.low_color.b / 0xff,
317                                         y_low, u_low, v_low);
318                         u_low += 0.5;  v_low += 0.5;
319                         YUV::yuv.rgb_to_yuv_f(
320                                         (float)config.mid_color.r / 0xff,
321                                         (float)config.mid_color.g / 0xff,
322                                         (float)config.mid_color.b / 0xff,
323                                         y_mid, u_mid, v_mid);
324                         u_mid += 0.5;  v_mid += 0.5;
325                         YUV::yuv.rgb_to_yuv_f(
326                                         (float)config.high_color.r / 0xff,
327                                         (float)config.high_color.g / 0xff,
328                                         (float)config.high_color.b / 0xff,
329                                         y_high, u_high, v_high);
330                         u_high += 0.5;  v_high += 0.5;
331
332                         glUniform4f(glGetUniformLocation(shader, "low_color"),
333                                         y_low, u_low, v_low,
334                                         has_alpha ? (float)config.low_color.a / 0xff : 1.0);
335                         glUniform4f(glGetUniformLocation(shader, "mid_color"),
336                                         y_mid, u_mid, v_mid,
337                                         has_alpha ? (float)config.mid_color.a / 0xff : 1.0);
338                         glUniform4f(glGetUniformLocation(shader, "high_color"),
339                                         y_high, u_high, v_high,
340                                         has_alpha ? (float)config.high_color.a / 0xff : 1.0);
341                 }
342                 else {
343                         glUniform4f(glGetUniformLocation(shader, "low_color"),
344                                         (float)config.low_color.r / 0xff,
345                                         (float)config.low_color.g / 0xff,
346                                         (float)config.low_color.b / 0xff,
347                                         has_alpha ? (float)config.low_color.a / 0xff : 1.0);
348                         glUniform4f(glGetUniformLocation(shader, "mid_color"),
349                                         (float)config.mid_color.r / 0xff,
350                                         (float)config.mid_color.g / 0xff,
351                                         (float)config.mid_color.b / 0xff,
352                                         has_alpha ? (float)config.mid_color.a / 0xff : 1.0);
353                         glUniform4f(glGetUniformLocation(shader, "high_color"),
354                                         (float)config.high_color.r / 0xff,
355                                         (float)config.high_color.g / 0xff,
356                                         (float)config.high_color.b / 0xff,
357                                         has_alpha ? (float)config.high_color.a / 0xff : 1.0);
358                         BC_GL_RGB_TO_Y(shader);
359                 }
360         }
361
362         get_output()->init_screen();
363         get_output()->bind_texture(0);
364         get_output()->draw_texture();
365         glUseProgram(0);
366         get_output()->set_opengl_state(VFrame::SCREEN);
367 #endif
368         return 0;
369 }
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392 ThresholdPackage::ThresholdPackage()
393  : LoadPackage()
394 {
395         start = end = 0;
396 }
397
398
399
400
401
402
403
404
405
406
407
408 ThresholdUnit::ThresholdUnit(ThresholdEngine *server)
409  : LoadClient(server)
410 {
411         this->server = server;
412 }
413
414 // Coerces pixel component to int.
415 static inline int get_component(unsigned char v)
416 {
417         return (v << 8) | v;
418 }
419
420 static inline int get_component(float v)
421 {
422         return (int)(v * 0xffff);
423 }
424
425 static inline int get_component(uint16_t v)
426 {
427         return v;
428 }
429
430 // Rescales value in range [0, 255] to range appropriate to TYPE.
431 template<typename TYPE>
432 static TYPE scale_to_range(int v)
433 {
434         return v;  // works for unsigned char, override for the rest.
435 }
436
437 template<>
438 inline float scale_to_range(int v)
439 {
440         return (float) v / 0xff;
441 }
442
443 template<>
444 inline uint16_t scale_to_range(int v)
445 {
446         return v << 8 | v;
447 }
448
449 static inline void rgb_to_yuv(unsigned char   r, unsigned char   g, unsigned char   b,
450                               unsigned char & y, unsigned char & u, unsigned char & v)
451 {
452         YUV::yuv.rgb_to_yuv_8(r, g, b, y, u, v);
453 }
454
455 static inline void rgb_to_yuv(float   r, float   g, float   b,
456                               float & y, float & u, float & v)
457 {
458         YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v);
459 }
460
461 static inline void rgb_to_yuv(uint16_t   r, uint16_t   g, uint16_t   b,
462                               uint16_t & y, uint16_t & u, uint16_t & v)
463 {
464         YUV::yuv.rgb_to_yuv_16(r, g, b, y, u, v);
465 }
466
467 template<typename TYPE, int COMPONENTS, bool USE_YUV>
468 void ThresholdUnit::render_data(LoadPackage *package)
469 {
470         const ThresholdPackage *pkg = (ThresholdPackage*)package;
471         const ThresholdConfig *config = & server->plugin->config;
472         VFrame *data = server->data;
473         const int min = (int)(config->min * 0xffff);
474         const int max = (int)(config->max * 0xffff);
475         const int w = data->get_w();
476         //const int h = data->get_h();
477
478         const TYPE r_low = scale_to_range<TYPE>(config->low_color.r);
479         const TYPE g_low = scale_to_range<TYPE>(config->low_color.g);
480         const TYPE b_low = scale_to_range<TYPE>(config->low_color.b);
481         const TYPE a_low = scale_to_range<TYPE>(config->low_color.a);
482
483         const TYPE r_mid = scale_to_range<TYPE>(config->mid_color.r);
484         const TYPE g_mid = scale_to_range<TYPE>(config->mid_color.g);
485         const TYPE b_mid = scale_to_range<TYPE>(config->mid_color.b);
486         const TYPE a_mid = scale_to_range<TYPE>(config->mid_color.a);
487
488         const TYPE r_high = scale_to_range<TYPE>(config->high_color.r);
489         const TYPE g_high = scale_to_range<TYPE>(config->high_color.g);
490         const TYPE b_high = scale_to_range<TYPE>(config->high_color.b);
491         const TYPE a_high = scale_to_range<TYPE>(config->high_color.a);
492
493         TYPE y_low,  u_low,  v_low;
494         TYPE y_mid,  u_mid,  v_mid;
495         TYPE y_high, u_high, v_high;
496
497         if (USE_YUV)
498         {
499                 rgb_to_yuv(r_low,  g_low,  b_low,  y_low,  u_low,  v_low);
500                 rgb_to_yuv(r_mid,  g_mid,  b_mid,  y_mid,  u_mid,  v_mid);
501                 rgb_to_yuv(r_high, g_high, b_high, y_high, u_high, v_high);
502         }
503
504         for(int i = pkg->start; i < pkg->end; i++)
505         {
506                 TYPE *in_row = (TYPE*)data->get_rows()[i];
507                 TYPE *out_row = in_row;
508                 for(int j = 0; j < w; j++)
509                 {
510                         if (USE_YUV)
511                         {
512                                 const int y = get_component(in_row[0]);
513                                 if (y < min)
514                                 {
515                                         *out_row++ = y_low;
516                                         *out_row++ = u_low;
517                                         *out_row++ = v_low;
518                                         if(COMPONENTS == 4) *out_row++ = a_low;
519                                 }
520                                 else if (y < max)
521                                 {
522                                         *out_row++ = y_mid;
523                                         *out_row++ = u_mid;
524                                         *out_row++ = v_mid;
525                                         if(COMPONENTS == 4) *out_row++ = a_mid;
526                                 }
527                                 else
528                                 {
529                                         *out_row++ = y_high;
530                                         *out_row++ = u_high;
531                                         *out_row++ = v_high;
532                                         if(COMPONENTS == 4) *out_row++ = a_high;
533                                 }
534                         }
535                         else
536                         {
537                                 const int r = get_component(in_row[0]);
538                                 const int g = get_component(in_row[1]);
539                                 const int b = get_component(in_row[2]);
540                                 const int y = (r * 76 + g * 150 + b * 29) >> 8;
541                                 if (y < min)
542                                 {
543                                         *out_row++ = r_low;
544                                         *out_row++ = g_low;
545                                         *out_row++ = b_low;
546                                         if(COMPONENTS == 4) *out_row++ = a_low;
547                                 }
548                                 else if (y < max)
549                                 {
550                                         *out_row++ = r_mid;
551                                         *out_row++ = g_mid;
552                                         *out_row++ = b_mid;
553                                         if(COMPONENTS == 4) *out_row++ = a_mid;
554                                 }
555                                 else
556                                 {
557                                         *out_row++ = r_high;
558                                         *out_row++ = g_high;
559                                         *out_row++ = b_high;
560                                         if(COMPONENTS == 4) *out_row++ = a_high;
561                                 }
562                         }
563                         in_row += COMPONENTS;
564                 }
565         }
566 }
567
568 void ThresholdUnit::process_package(LoadPackage *package)
569 {
570         switch(server->data->get_color_model())
571         {
572                 case BC_RGB888:
573                         render_data<unsigned char, 3, false>(package);
574                         break;
575
576                 case BC_RGB_FLOAT:
577                         render_data<float, 3, false>(package);
578                         break;
579
580                 case BC_RGBA8888:
581                         render_data<unsigned char, 4, false>(package);
582                         break;
583
584                 case BC_RGBA_FLOAT:
585                         render_data<float, 4, false>(package);
586                         break;
587
588                 case BC_YUV888:
589                         render_data<unsigned char, 3, true>(package);
590                         break;
591
592                 case BC_YUVA8888:
593                         render_data<unsigned char, 4, true>(package);
594                         break;
595
596                 case BC_YUV161616:
597                         render_data<uint16_t, 3, true>(package);
598                         break;
599
600                 case BC_YUVA16161616:
601                         render_data<uint16_t, 4, true>(package);
602                         break;
603         }
604 }
605
606
607
608
609
610
611
612
613
614
615
616 ThresholdEngine::ThresholdEngine(ThresholdMain *plugin)
617  : LoadServer(plugin->get_project_smp() + 1,
618         plugin->get_project_smp() + 1)
619 {
620         this->plugin = plugin;
621 }
622
623 ThresholdEngine::~ThresholdEngine()
624 {
625 }
626
627 void ThresholdEngine::process_packages(VFrame *data)
628 {
629         this->data = data;
630         LoadServer::process_packages();
631 }
632
633 void ThresholdEngine::init_packages()
634 {
635         for(int i = 0; i < get_total_packages(); i++)
636         {
637                 ThresholdPackage *package = (ThresholdPackage*)get_package(i);
638                 package->start = data->get_h() * i / get_total_packages();
639                 package->end = data->get_h() * (i + 1) / get_total_packages();
640         }
641 }
642
643 LoadClient* ThresholdEngine::new_client()
644 {
645         return (LoadClient*)new ThresholdUnit(this);
646 }
647
648 LoadPackage* ThresholdEngine::new_package()
649 {
650         return (LoadPackage*)new HistogramPackage;
651 }
652
653
654
655
656
657
658
659
660 RGBA::RGBA()
661 {
662         r = g = b = a = 0;
663 }
664
665 RGBA::RGBA(int r, int g, int b, int a)
666 {
667         this->r = r;
668         this->g = g;
669         this->b = b;
670         this->a = a;
671 }
672
673 void RGBA::set(int r, int g, int b, int a)
674 {
675         this->r = r;
676         this->g = g;
677         this->b = b;
678         this->a = a;
679 }
680
681 void RGBA::set(int rgb, int alpha)
682 {
683         r = (rgb & 0xff0000) >> 16;
684         g = (rgb & 0xff00)   >>  8;
685         b = (rgb & 0xff);
686         a = alpha;
687 }
688
689 int RGBA::getRGB() const
690 {
691         return r << 16 | g << 8 | b;
692 }
693
694 static void init_RGBA_keys(const char * prefix,
695                            string & r_s,
696                            string & g_s,
697                            string & b_s,
698                            string & a_s)
699 {
700         r_s = prefix;
701         g_s = prefix;
702         b_s = prefix;
703         a_s = prefix;
704
705         r_s += "_R";
706         g_s += "_G";
707         b_s += "_B";
708         a_s += "_A";
709 }
710
711 void RGBA::set_property(XMLTag & tag, const char * prefix) const
712 {
713         string r_s, g_s, b_s, a_s;
714         init_RGBA_keys(prefix, r_s, g_s, b_s, a_s);
715
716         tag.set_property(const_cast<char *>(r_s.c_str()), r);
717         tag.set_property(const_cast<char *>(g_s.c_str()), g);
718         tag.set_property(const_cast<char *>(b_s.c_str()), b);
719         tag.set_property(const_cast<char *>(a_s.c_str()), a);
720 }
721
722 RGBA RGBA::get_property(XMLTag & tag, const char * prefix) const
723 {
724         string r_s, g_s, b_s, a_s;
725         init_RGBA_keys(prefix, r_s, g_s, b_s, a_s);
726
727         return RGBA(tag.get_property(const_cast<char *>(r_s.c_str()), r),
728                     tag.get_property(const_cast<char *>(g_s.c_str()), g),
729                     tag.get_property(const_cast<char *>(b_s.c_str()), b),
730                     tag.get_property(const_cast<char *>(a_s.c_str()), a));
731 }
732
733 bool operator==(const RGBA & a, const RGBA & b)
734 {
735         return  a.r == b.r &&
736                 a.g == b.g &&
737                 a.b == b.b &&
738                 a.a == b.a;
739 }
740
741 template<>
742 RGBA interpolate(const RGBA & prev_color, const double & prev_scale, const RGBA &next_color, const double & next_scale)
743 {
744         return RGBA(interpolate(prev_color.r, prev_scale, next_color.r, next_scale),
745                     interpolate(prev_color.g, prev_scale, next_color.g, next_scale),
746                     interpolate(prev_color.b, prev_scale, next_color.b, next_scale),
747                     interpolate(prev_color.a, prev_scale, next_color.a, next_scale));
748 }