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