yuv colorspace/range + prefs, ffmpeg colorrange probe, x11 direct force colormodel...
[goodguy/history.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 "clip.h"
26 #include "bchash.h"
27 #include "filexml.h"
28 #include "histogramengine.h"
29 #include "language.h"
30 #include "bccolors.h"
31 #include "threshold.h"
32 #include "thresholdwindow.h"
33 #include "vframe.h"
34
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 _("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         if(use_opengl) return run_opengl();
159
160         send_render_gui(frame);
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->get_data(), MESSAGESIZE);
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         const char *data = keyframe->get_data();
191         input.set_shared_input((char *)data, strlen(data));
192         int result = 0;
193         while(!result)
194         {
195                 result = input.read_tag();
196                 if(!result)
197                 {
198                         config.min = input.tag.get_property("MIN", config.min);
199                         config.max = input.tag.get_property("MAX", config.max);
200                         config.plot = input.tag.get_property("PLOT", config.plot);
201                         config.low_color = config.low_color.get_property(input.tag, "LOW_COLOR");
202                         config.mid_color = config.mid_color.get_property(input.tag, "MID_COLOR");
203                         config.high_color = config.high_color.get_property(input.tag, "HIGH_COLOR");
204                 }
205         }
206         config.boundaries();
207 }
208
209 NEW_WINDOW_MACRO(ThresholdMain,ThresholdWindow);
210
211 void ThresholdMain::update_gui()
212 {
213         if(thread)
214         {
215                 thread->window->lock_window("ThresholdMain::update_gui");
216                 if(load_configuration())
217                 {
218                         ThresholdWindow *window = (ThresholdWindow*) thread->window;
219                         window->min->update(config.min);
220                         window->max->update(config.max);
221                         window->plot->update(config.plot);
222                         window->update_low_color();
223                         window->update_mid_color();
224                         window->update_high_color();
225                         window->low_color_thread->update_gui(config.low_color.getRGB(), config.low_color.a);
226                         window->mid_color_thread->update_gui(config.mid_color.getRGB(), config.mid_color.a);
227                         window->high_color_thread->update_gui(config.high_color.getRGB(), config.high_color.a);
228                 }
229                 thread->window->unlock_window();
230         }
231 }
232
233 void ThresholdMain::render_gui(void *data)
234 {
235         if(thread)
236         {
237                 calculate_histogram((VFrame*)data);
238                 ThresholdWindow *window = (ThresholdWindow*) thread->window;
239                 window->lock_window("ThresholdMain::render_gui");
240                 window->canvas->draw();
241                 window->unlock_window();
242         }
243 }
244
245 void ThresholdMain::calculate_histogram(VFrame *frame)
246 {
247         if(!engine) engine = new HistogramEngine(get_project_smp() + 1,
248                 get_project_smp() + 1);
249         engine->process_packages(frame);
250 }
251
252 int ThresholdMain::handle_opengl()
253 {
254 #ifdef HAVE_GL
255         static const char *rgb_shader =
256                 "uniform sampler2D tex;\n"
257                 "uniform float min;\n"
258                 "uniform float max;\n"
259                 "uniform vec4 low_color;\n"
260                 "uniform vec4 mid_color;\n"
261                 "uniform vec4 high_color;\n"
262                 "void main()\n"
263                 "{\n"
264                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
265                 "       float v = dot(pixel.rgb, vec3(0.299, 0.587, 0.114));\n"
266                 "       if(v < min)\n"
267                 "               pixel = low_color;\n"
268                 "       else if(v < max)\n"
269                 "               pixel = mid_color;\n"
270                 "       else\n"
271                 "               pixel = high_color;\n"
272                 "       gl_FragColor = pixel;\n"
273                 "}\n";
274
275         static const char *yuv_shader =
276                 "uniform sampler2D tex;\n"
277                 "uniform float min;\n"
278                 "uniform float max;\n"
279                 "uniform vec4 low_color;\n"
280                 "uniform vec4 mid_color;\n"
281                 "uniform vec4 high_color;\n"
282                 "void main()\n"
283                 "{\n"
284                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
285                 "       if(pixel.r < min)\n"
286                 "               pixel = low_color;\n"
287                 "       else if(pixel.r < max)\n"
288                 "               pixel = mid_color;\n"
289                 "       else\n"
290                 "               pixel = high_color;\n"
291                 "       gl_FragColor = pixel;\n"
292                 "}\n";
293
294         get_output()->to_texture();
295         get_output()->enable_opengl();
296
297         unsigned int shader = 0;
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         if(is_yuv)
302                 shader = VFrame::make_shader(0, yuv_shader, 0);
303         else
304                 shader = VFrame::make_shader(0, rgb_shader, 0);
305
306         if(shader > 0)
307         {
308                 glUseProgram(shader);
309                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
310                 glUniform1f(glGetUniformLocation(shader, "min"), config.min);
311                 glUniform1f(glGetUniformLocation(shader, "max"), config.max);
312
313                 if (is_yuv)
314                 {
315                         float y_low,  u_low,  v_low;
316                         float y_mid,  u_mid,  v_mid;
317                         float y_high, u_high, v_high;
318
319                         YUV::yuv.rgb_to_yuv_f((float)config.low_color.r / 0xff,
320                                           (float)config.low_color.g / 0xff,
321                                           (float)config.low_color.b / 0xff,
322                                           y_low,
323                                           u_low,
324                                           v_low);
325                         u_low += 0.5;
326                         v_low += 0.5;
327                         YUV::yuv.rgb_to_yuv_f((float)config.mid_color.r / 0xff,
328                                           (float)config.mid_color.g / 0xff,
329                                           (float)config.mid_color.b / 0xff,
330                                           y_mid,
331                                           u_mid,
332                                           v_mid);
333                         u_mid += 0.5;
334                         v_mid += 0.5;
335                         YUV::yuv.rgb_to_yuv_f((float)config.high_color.r / 0xff,
336                                           (float)config.high_color.g / 0xff,
337                                           (float)config.high_color.b / 0xff,
338                                           y_high,
339                                           u_high,
340                                           v_high);
341                         u_high += 0.5;
342                         v_high += 0.5;
343
344                         glUniform4f(glGetUniformLocation(shader, "low_color"),
345                                     y_low, u_low, v_low,
346                                     has_alpha ? (float)config.low_color.a / 0xff : 1.0);
347                         glUniform4f(glGetUniformLocation(shader, "mid_color"),
348                                     y_mid, u_mid, v_mid,
349                                     has_alpha ? (float)config.mid_color.a / 0xff : 1.0);
350                         glUniform4f(glGetUniformLocation(shader, "high_color"),
351                                     y_high, u_high, v_high,
352                                     has_alpha ? (float)config.high_color.a / 0xff : 1.0);
353                 } else {
354                         glUniform4f(glGetUniformLocation(shader, "low_color"),
355                                     (float)config.low_color.r / 0xff,
356                                     (float)config.low_color.g / 0xff,
357                                     (float)config.low_color.b / 0xff,
358                                     has_alpha ? (float)config.low_color.a / 0xff : 1.0);
359                         glUniform4f(glGetUniformLocation(shader, "mid_color"),
360                                     (float)config.mid_color.r / 0xff,
361                                     (float)config.mid_color.g / 0xff,
362                                     (float)config.mid_color.b / 0xff,
363                                     has_alpha ? (float)config.mid_color.a / 0xff : 1.0);
364                         glUniform4f(glGetUniformLocation(shader, "high_color"),
365                                     (float)config.high_color.r / 0xff,
366                                     (float)config.high_color.g / 0xff,
367                                     (float)config.high_color.b / 0xff,
368                                     has_alpha ? (float)config.high_color.a / 0xff : 1.0);
369                 }
370         }
371
372         get_output()->init_screen();
373         get_output()->bind_texture(0);
374         get_output()->draw_texture();
375         glUseProgram(0);
376         get_output()->set_opengl_state(VFrame::SCREEN);
377 #endif
378         return 0;
379 }
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402 ThresholdPackage::ThresholdPackage()
403  : LoadPackage()
404 {
405         start = end = 0;
406 }
407
408
409
410
411
412
413
414
415
416
417
418 ThresholdUnit::ThresholdUnit(ThresholdEngine *server)
419  : LoadClient(server)
420 {
421         this->server = server;
422 }
423
424 // Coerces pixel component to int.
425 static inline int get_component(unsigned char v)
426 {
427         return (v << 8) | v;
428 }
429
430 static inline int get_component(float v)
431 {
432         return (int)(v * 0xffff);
433 }
434
435 static inline int get_component(uint16_t v)
436 {
437         return v;
438 }
439
440 // Rescales value in range [0, 255] to range appropriate to TYPE.
441 template<typename TYPE>
442 static TYPE scale_to_range(int v)
443 {
444         return v;  // works for unsigned char, override for the rest.
445 }
446
447 template<>
448 inline float scale_to_range(int v)
449 {
450         return (float) v / 0xff;
451 }
452
453 template<>
454 inline uint16_t scale_to_range(int v)
455 {
456         return v << 8 | v;
457 }
458
459 static inline void rgb_to_yuv(unsigned char   r, unsigned char   g, unsigned char   b,
460                               unsigned char & y, unsigned char & u, unsigned char & v)
461 {
462         YUV::yuv.rgb_to_yuv_8(r, g, b, y, u, v);
463 }
464
465 static inline void rgb_to_yuv(float   r, float   g, float   b,
466                               float & y, float & u, float & v)
467 {
468         YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v);
469 }
470
471 static inline void rgb_to_yuv(uint16_t   r, uint16_t   g, uint16_t   b,
472                               uint16_t & y, uint16_t & u, uint16_t & v)
473 {
474         YUV::yuv.rgb_to_yuv_16(r, g, b, y, u, v);
475 }
476
477 template<typename TYPE, int COMPONENTS, bool USE_YUV>
478 void ThresholdUnit::render_data(LoadPackage *package)
479 {
480         const ThresholdPackage *pkg = (ThresholdPackage*)package;
481         const ThresholdConfig *config = & server->plugin->config;
482         VFrame *data = server->data;
483         const int min = (int)(config->min * 0xffff);
484         const int max = (int)(config->max * 0xffff);
485         const int w = data->get_w();
486         //const int h = data->get_h();
487
488         const TYPE r_low = scale_to_range<TYPE>(config->low_color.r);
489         const TYPE g_low = scale_to_range<TYPE>(config->low_color.g);
490         const TYPE b_low = scale_to_range<TYPE>(config->low_color.b);
491         const TYPE a_low = scale_to_range<TYPE>(config->low_color.a);
492
493         const TYPE r_mid = scale_to_range<TYPE>(config->mid_color.r);
494         const TYPE g_mid = scale_to_range<TYPE>(config->mid_color.g);
495         const TYPE b_mid = scale_to_range<TYPE>(config->mid_color.b);
496         const TYPE a_mid = scale_to_range<TYPE>(config->mid_color.a);
497
498         const TYPE r_high = scale_to_range<TYPE>(config->high_color.r);
499         const TYPE g_high = scale_to_range<TYPE>(config->high_color.g);
500         const TYPE b_high = scale_to_range<TYPE>(config->high_color.b);
501         const TYPE a_high = scale_to_range<TYPE>(config->high_color.a);
502
503         TYPE y_low,  u_low,  v_low;
504         TYPE y_mid,  u_mid,  v_mid;
505         TYPE y_high, u_high, v_high;
506
507         if (USE_YUV)
508         {
509                 rgb_to_yuv(r_low,  g_low,  b_low,  y_low,  u_low,  v_low);
510                 rgb_to_yuv(r_mid,  g_mid,  b_mid,  y_mid,  u_mid,  v_mid);
511                 rgb_to_yuv(r_high, g_high, b_high, y_high, u_high, v_high);
512         }
513
514         for(int i = pkg->start; i < pkg->end; i++)
515         {
516                 TYPE *in_row = (TYPE*)data->get_rows()[i];
517                 TYPE *out_row = in_row;
518                 for(int j = 0; j < w; j++)
519                 {
520                         if (USE_YUV)
521                         {
522                                 const int y = get_component(in_row[0]);
523                                 if (y < min)
524                                 {
525                                         *out_row++ = y_low;
526                                         *out_row++ = u_low;
527                                         *out_row++ = v_low;
528                                         if(COMPONENTS == 4) *out_row++ = a_low;
529                                 }
530                                 else if (y < max)
531                                 {
532                                         *out_row++ = y_mid;
533                                         *out_row++ = u_mid;
534                                         *out_row++ = v_mid;
535                                         if(COMPONENTS == 4) *out_row++ = a_mid;
536                                 }
537                                 else
538                                 {
539                                         *out_row++ = y_high;
540                                         *out_row++ = u_high;
541                                         *out_row++ = v_high;
542                                         if(COMPONENTS == 4) *out_row++ = a_high;
543                                 }
544                         }
545                         else
546                         {
547                                 const int r = get_component(in_row[0]);
548                                 const int g = get_component(in_row[1]);
549                                 const int b = get_component(in_row[2]);
550                                 const int y = (r * 76 + g * 150 + b * 29) >> 8;
551                                 if (y < min)
552                                 {
553                                         *out_row++ = r_low;
554                                         *out_row++ = g_low;
555                                         *out_row++ = b_low;
556                                         if(COMPONENTS == 4) *out_row++ = a_low;
557                                 }
558                                 else if (y < max)
559                                 {
560                                         *out_row++ = r_mid;
561                                         *out_row++ = g_mid;
562                                         *out_row++ = b_mid;
563                                         if(COMPONENTS == 4) *out_row++ = a_mid;
564                                 }
565                                 else
566                                 {
567                                         *out_row++ = r_high;
568                                         *out_row++ = g_high;
569                                         *out_row++ = b_high;
570                                         if(COMPONENTS == 4) *out_row++ = a_high;
571                                 }
572                         }
573                         in_row += COMPONENTS;
574                 }
575         }
576 }
577
578 void ThresholdUnit::process_package(LoadPackage *package)
579 {
580         switch(server->data->get_color_model())
581         {
582                 case BC_RGB888:
583                         render_data<unsigned char, 3, false>(package);
584                         break;
585
586                 case BC_RGB_FLOAT:
587                         render_data<float, 3, false>(package);
588                         break;
589
590                 case BC_RGBA8888:
591                         render_data<unsigned char, 4, false>(package);
592                         break;
593
594                 case BC_RGBA_FLOAT:
595                         render_data<float, 4, false>(package);
596                         break;
597
598                 case BC_YUV888:
599                         render_data<unsigned char, 3, true>(package);
600                         break;
601
602                 case BC_YUVA8888:
603                         render_data<unsigned char, 4, true>(package);
604                         break;
605
606                 case BC_YUV161616:
607                         render_data<uint16_t, 3, true>(package);
608                         break;
609
610                 case BC_YUVA16161616:
611                         render_data<uint16_t, 4, true>(package);
612                         break;
613         }
614 }
615
616
617
618
619
620
621
622
623
624
625
626 ThresholdEngine::ThresholdEngine(ThresholdMain *plugin)
627  : LoadServer(plugin->get_project_smp() + 1,
628         plugin->get_project_smp() + 1)
629 {
630         this->plugin = plugin;
631 }
632
633 ThresholdEngine::~ThresholdEngine()
634 {
635 }
636
637 void ThresholdEngine::process_packages(VFrame *data)
638 {
639         this->data = data;
640         LoadServer::process_packages();
641 }
642
643 void ThresholdEngine::init_packages()
644 {
645         for(int i = 0; i < get_total_packages(); i++)
646         {
647                 ThresholdPackage *package = (ThresholdPackage*)get_package(i);
648                 package->start = data->get_h() * i / get_total_packages();
649                 package->end = data->get_h() * (i + 1) / get_total_packages();
650         }
651 }
652
653 LoadClient* ThresholdEngine::new_client()
654 {
655         return (LoadClient*)new ThresholdUnit(this);
656 }
657
658 LoadPackage* ThresholdEngine::new_package()
659 {
660         return (LoadPackage*)new HistogramPackage;
661 }
662
663
664
665
666
667
668
669
670 RGBA::RGBA()
671 {
672         r = g = b = a = 0;
673 }
674
675 RGBA::RGBA(int r, int g, int b, int a)
676 {
677         this->r = r;
678         this->g = g;
679         this->b = b;
680         this->a = a;
681 }
682
683 void RGBA::set(int r, int g, int b, int a)
684 {
685         this->r = r;
686         this->g = g;
687         this->b = b;
688         this->a = a;
689 }
690
691 void RGBA::set(int rgb, int alpha)
692 {
693         r = (rgb & 0xff0000) >> 16;
694         g = (rgb & 0xff00)   >>  8;
695         b = (rgb & 0xff);
696         a = alpha;
697 }
698
699 int RGBA::getRGB() const
700 {
701         return r << 16 | g << 8 | b;
702 }
703
704 static void init_RGBA_keys(const char * prefix,
705                            string & r_s,
706                            string & g_s,
707                            string & b_s,
708                            string & a_s)
709 {
710         r_s = prefix;
711         g_s = prefix;
712         b_s = prefix;
713         a_s = prefix;
714
715         r_s += "_R";
716         g_s += "_G";
717         b_s += "_B";
718         a_s += "_A";
719 }
720
721 void RGBA::set_property(XMLTag & tag, const char * prefix) const
722 {
723         string r_s, g_s, b_s, a_s;
724         init_RGBA_keys(prefix, r_s, g_s, b_s, a_s);
725
726         tag.set_property(const_cast<char *>(r_s.c_str()), r);
727         tag.set_property(const_cast<char *>(g_s.c_str()), g);
728         tag.set_property(const_cast<char *>(b_s.c_str()), b);
729         tag.set_property(const_cast<char *>(a_s.c_str()), a);
730 }
731
732 RGBA RGBA::get_property(XMLTag & tag, const char * prefix) const
733 {
734         string r_s, g_s, b_s, a_s;
735         init_RGBA_keys(prefix, r_s, g_s, b_s, a_s);
736
737         return RGBA(tag.get_property(const_cast<char *>(r_s.c_str()), r),
738                     tag.get_property(const_cast<char *>(g_s.c_str()), g),
739                     tag.get_property(const_cast<char *>(b_s.c_str()), b),
740                     tag.get_property(const_cast<char *>(a_s.c_str()), a));
741 }
742
743 bool operator==(const RGBA & a, const RGBA & b)
744 {
745         return  a.r == b.r &&
746                 a.g == b.g &&
747                 a.b == b.b &&
748                 a.a == b.a;
749 }
750
751 template<>
752 RGBA interpolate(const RGBA & prev_color, const double & prev_scale, const RGBA &next_color, const double & next_scale)
753 {
754         return RGBA(interpolate(prev_color.r, prev_scale, next_color.r, next_scale),
755                     interpolate(prev_color.g, prev_scale, next_color.g, next_scale),
756                     interpolate(prev_color.b, prev_scale, next_color.b, next_scale),
757                     interpolate(prev_color.a, prev_scale, next_color.a, next_scale));
758 }