r1009: Move the dependencies to newer package names
[cinelerra_cv/mob.git] / plugins / huesaturation / huesaturation.C
blob55e4785fe5056d5c2150cceb1731884c81e4ffbe
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "bchash.h"
4 #include "filexml.h"
5 #include "guicast.h"
6 #include "language.h"
7 #include "loadbalance.h"
8 #include "picon_png.h"
9 #include "plugincolors.h"
10 #include "playback3d.h"
11 #include "pluginvclient.h"
12 #include "vframe.h"
14 #include <stdint.h>
15 #include <string.h>
18 class HueEffect;
20 #define MINHUE -180
21 #define MAXHUE 180
22 #define MINSATURATION -100
23 #define MAXSATURATION 100
24 #define MINVALUE -100
25 #define MAXVALUE 100
32 class HueConfig
34 public:
35         HueConfig();
36         
37         void copy_from(HueConfig &src);
38         int equivalent(HueConfig &src);
39         void interpolate(HueConfig &prev, 
40                 HueConfig &next, 
41                 long prev_frame, 
42                 long next_frame, 
43                 long current_frame);
44         float hue, saturation, value;
47 class HueSlider : public BC_FSlider
49 public:
50         HueSlider(HueEffect *plugin, int x, int y, int w);
51         int handle_event();
52         HueEffect *plugin;
55 class SaturationSlider : public BC_FSlider
57 public:
58         SaturationSlider(HueEffect *plugin, int x, int y, int w);
59         int handle_event();
60         char* get_caption();
61         HueEffect *plugin;
62         char string[BCTEXTLEN];
65 class ValueSlider : public BC_FSlider
67 public:
68         ValueSlider(HueEffect *plugin, int x, int y, int w);
69         int handle_event();
70         char* get_caption();
71         HueEffect *plugin;
72         char string[BCTEXTLEN];
75 class HueWindow : public BC_Window
77 public:
78         HueWindow(HueEffect *plugin, int x, int y);
79         void create_objects();
80         int close_event();
81         HueEffect *plugin;
82         HueSlider *hue;
83         SaturationSlider *saturation;
84         ValueSlider *value;
87 PLUGIN_THREAD_HEADER(HueEffect, HueThread, HueWindow)
89 class HueEngine : public LoadServer
91 public:
92         HueEngine(HueEffect *plugin, int cpus);
93         void init_packages();
94         LoadClient* new_client();
95         LoadPackage* new_package();
96         HueEffect *plugin;
99 class HuePackage : public LoadPackage
101 public:
102         HuePackage();
103         int row1, row2;
106 class HueUnit : public LoadClient
108 public:
109         HueUnit(HueEffect *plugin, HueEngine *server);
110         void process_package(LoadPackage *package);
111         HueEffect *plugin;
112         YUV yuv;
115 class HueEffect : public PluginVClient
117 public:
118         HueEffect(PluginServer *server);
119         ~HueEffect();
120         
121         int process_buffer(VFrame *frame,
122                 int64_t start_position,
123                 double frame_rate);
124         int is_realtime();
125         char* plugin_title();
126         VFrame* new_picon();
127         int load_configuration();
128         int load_defaults();
129         int save_defaults();
130         void save_data(KeyFrame *keyframe);
131         void read_data(KeyFrame *keyframe);
132         int show_gui();
133         int set_string();
134         void raise_window();
135         void update_gui();
136         int handle_opengl();
138         HueConfig config;
139         VFrame *input, *output;
140         BC_Hash *defaults;
141         HueThread *thread;
142         HueEngine *engine;
164 HueConfig::HueConfig()
166         hue = saturation = value = 0;
168         
169 void HueConfig::copy_from(HueConfig &src)
171         hue = src.hue;
172         saturation = src.saturation;
173         value = src.value;
175 int HueConfig::equivalent(HueConfig &src)
177         return EQUIV(hue, src.hue) && 
178                 EQUIV(saturation, src.saturation) && 
179                 EQUIV(value, src.value);
181 void HueConfig::interpolate(HueConfig &prev, 
182         HueConfig &next, 
183         long prev_frame, 
184         long next_frame, 
185         long current_frame)
187         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
188         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
190         this->hue = prev.hue * prev_scale + next.hue * next_scale;
191         this->saturation = prev.saturation * prev_scale + next.saturation * next_scale;
192         this->value = prev.value * prev_scale + next.value * next_scale;
202 HueSlider::HueSlider(HueEffect *plugin, int x, int y, int w)
203  : BC_FSlider(x, 
204                         y,
205                         0,
206                         w, 
207                         w, 
208                         (float)MINHUE, 
209                         (float)MAXHUE, 
210                         plugin->config.hue)
212         this->plugin = plugin;
214 int HueSlider::handle_event()
216         plugin->config.hue = get_value();
217         plugin->send_configure_change();
218         return 1;
227 SaturationSlider::SaturationSlider(HueEffect *plugin, int x, int y, int w)
228  : BC_FSlider(x, 
229                         y,
230                         0,
231                         w, 
232                         w, 
233                         (float)MINSATURATION, 
234                         (float)MAXSATURATION, 
235                         plugin->config.saturation)
237         this->plugin = plugin;
239 int SaturationSlider::handle_event()
241         plugin->config.saturation = get_value();
242         plugin->send_configure_change();
243         return 1;
246 char* SaturationSlider::get_caption()
248         float fraction = ((float)plugin->config.saturation - MINSATURATION) / 
249                 MAXSATURATION;;
250         sprintf(string, "%0.4f", fraction);
251         return string;
260 ValueSlider::ValueSlider(HueEffect *plugin, int x, int y, int w)
261  : BC_FSlider(x, 
262                         y,
263                         0,
264                         w, 
265                         w, 
266                         (float)MINVALUE, 
267                         (float)MAXVALUE, 
268                         plugin->config.value)
270         this->plugin = plugin;
272 int ValueSlider::handle_event()
274         plugin->config.value = get_value();
275         plugin->send_configure_change();
276         return 1;
279 char* ValueSlider::get_caption()
281         float fraction = ((float)plugin->config.value - MINVALUE) / MAXVALUE;
282         sprintf(string, "%0.4f", fraction);
283         return string;
292 HueWindow::HueWindow(HueEffect *plugin, int x, int y)
293  : BC_Window(plugin->gui_string, 
294                         x,
295                         y,
296                         310, 
297                         100, 
298                         310, 
299                         100, 
300                         0,
301                         0, 
302                         1)
304         this->plugin = plugin;
306 void HueWindow::create_objects()
308         int x = 10, y = 10, x1 = 100;
309         add_subwindow(new BC_Title(x, y, _("Hue:")));
310         add_subwindow(hue = new HueSlider(plugin, x1, y, 200));
311         y += 30;
312         add_subwindow(new BC_Title(x, y, _("Saturation:")));
313         add_subwindow(saturation = new SaturationSlider(plugin, x1, y, 200));
314         y += 30;
315         add_subwindow(new BC_Title(x, y, _("Value:")));
316         add_subwindow(value = new ValueSlider(plugin, x1, y, 200));
317         show_window();
318         flush();
322 WINDOW_CLOSE_EVENT(HueWindow)
331 PLUGIN_THREAD_OBJECT(HueEffect, HueThread, HueWindow)
333 HueEngine::HueEngine(HueEffect *plugin, int cpus)
334  : LoadServer(cpus, cpus)
336         this->plugin = plugin;
338 void HueEngine::init_packages()
340         for(int i = 0; i < LoadServer::get_total_packages(); i++)
341         {
342                 HuePackage *pkg = (HuePackage*)get_package(i);
343                 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
344                 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
345         }
347 LoadClient* HueEngine::new_client()
349         return new HueUnit(plugin, this);
351 LoadPackage* HueEngine::new_package()
353         return new HuePackage;
363 HuePackage::HuePackage()
364  : LoadPackage()
368 HueUnit::HueUnit(HueEffect *plugin, HueEngine *server)
369  : LoadClient(server)
371         this->plugin = plugin;
380 #define HUESATURATION(type, max, components, use_yuv) \
381 { \
382         float h_offset = plugin->config.hue; \
383         float s_offset = ((float)plugin->config.saturation - MINSATURATION) / MAXSATURATION; \
384         float v_offset = ((float)plugin->config.value - MINVALUE) / MAXVALUE; \
385         for(int i = pkg->row1; i < pkg->row2; i++) \
386         { \
387                 type* in_row = (type*)plugin->input->get_rows()[i]; \
388                 type* out_row = (type*)plugin->output->get_rows()[i]; \
390                 for(int j = 0; j < w; j++) \
391                 { \
392                         float h, s, va; \
393                         int y, u, v; \
394                         float r, g, b; \
395                         int r_i, g_i, b_i; \
397                         if(use_yuv) \
398                         { \
399                                 y = (int)in_row[0]; \
400                                 u = (int)in_row[1]; \
401                                 v = (int)in_row[2]; \
402                                 if(max == 0xffff) \
403                                         yuv.yuv_to_rgb_16(r_i, g_i, b_i, y, u, v); \
404                                 else \
405                                         yuv.yuv_to_rgb_8(r_i, g_i, b_i, y, u, v); \
406                                 HSV::rgb_to_hsv((float)r_i / max, \
407                                         (float)g_i / max, \
408                                         (float)b_i / max, \
409                                         h, \
410                                         s, \
411                                         va); \
412                         } \
413                         else \
414                         { \
415                                 r = (float)in_row[0] / max; \
416                                 g = (float)in_row[1] / max; \
417                                 b = (float)in_row[2] / max; \
418                                 HSV::rgb_to_hsv(r, g, b, h, s, va); \
419                         } \
422                         h += h_offset; \
423                         s *= s_offset; \
424                         va *= v_offset; \
426                         if(h >= 360) h -= 360; \
427                         if(h < 0) h += 360; \
428                         if(sizeof(type) < 4) \
429                         { \
430                                 if(s > 1) s = 1; \
431                                 if(va > 1) va = 1; \
432                                 if(s < 0) s = 0; \
433                                 if(va < 0) va = 0; \
434                         } \
436                         if(use_yuv) \
437                         { \
438                                 HSV::hsv_to_yuv(y, u, v, h, s, va, max); \
439                                 out_row[0] = y; \
440                                 out_row[1] = u; \
441                                 out_row[2] = v; \
442                         } \
443                         else \
444                         { \
445                                 HSV::hsv_to_rgb(r, g, b, h, s, va); \
446                                 if(sizeof(type) < 4) \
447                                 { \
448                                         r *= max; \
449                                         g *= max; \
450                                         b *= max; \
451                                         out_row[0] = (type)CLIP(r, 0, max); \
452                                         out_row[1] = (type)CLIP(g, 0, max); \
453                                         out_row[2] = (type)CLIP(b, 0, max); \
454                                 } \
455                                 else \
456                                 { \
457                                         out_row[0] = (type)r; \
458                                         out_row[1] = (type)g; \
459                                         out_row[2] = (type)b; \
460                                 } \
461                         } \
463                         in_row += components; \
464                         out_row += components; \
465                 } \
466         } \
470 void HueUnit::process_package(LoadPackage *package)
472         HuePackage *pkg = (HuePackage*)package;
473         int w = plugin->input->get_w();
475         switch(plugin->input->get_color_model())
476         {
477                 case BC_RGB888:
478                         HUESATURATION(unsigned char, 0xff, 3, 0)
479                         break;
481                 case BC_RGB_FLOAT:
482                         HUESATURATION(float, 1, 3, 0)
483                         break;
485                 case BC_YUV888:
486                         HUESATURATION(unsigned char, 0xff, 3, 1)
487                         break;
489                 case BC_RGB161616:
490                         HUESATURATION(uint16_t, 0xffff, 3, 0)
491                         break;
493                 case BC_YUV161616:
494                         HUESATURATION(uint16_t, 0xffff, 3, 1)
495                         break;
497                 case BC_RGBA_FLOAT:
498                         HUESATURATION(float, 1, 4, 0)
499                         break;
501                 case BC_RGBA8888:
502                         HUESATURATION(unsigned char, 0xff, 4, 0)
503                         break;
505                 case BC_YUVA8888:
506                         HUESATURATION(unsigned char, 0xff, 4, 1)
507                         break;
509                 case BC_RGBA16161616:
510                         HUESATURATION(uint16_t, 0xffff, 4, 0)
511                         break;
513                 case BC_YUVA16161616:
514                         HUESATURATION(uint16_t, 0xffff, 4, 1)
515                         break;
517         }
523 REGISTER_PLUGIN(HueEffect)
526 HueEffect::HueEffect(PluginServer *server)
527  : PluginVClient(server)
529         engine = 0;
530         PLUGIN_CONSTRUCTOR_MACRO
532 HueEffect::~HueEffect()
534         PLUGIN_DESTRUCTOR_MACRO
535         if(engine) delete engine;
538 int HueEffect::process_buffer(VFrame *frame,
539         int64_t start_position,
540         double frame_rate)
542         load_configuration();
544         read_frame(frame, 
545                 0, 
546                 start_position, 
547                 frame_rate,
548                 get_use_opengl());
549         
551         this->input = frame;
552         this->output = frame;
553         if(EQUIV(config.hue, 0) && EQUIV(config.saturation, 0) && EQUIV(config.value, 0))
554         {
555                 return 0;
556         }
557         else
558         {
559                 if(get_use_opengl())
560                 {
561                         run_opengl();
562                         return 0;
563                 }
565                 if(!engine) engine = new HueEngine(this, PluginClient::smp + 1);
566                 
567                 engine->process_packages();
568         }
569         return 0;
572 char* HueEffect::plugin_title() { return N_("Hue saturation"); }
573 int HueEffect::is_realtime() { return 1; }
575 NEW_PICON_MACRO(HueEffect)
576 SHOW_GUI_MACRO(HueEffect, HueThread)
577 SET_STRING_MACRO(HueEffect)
578 RAISE_WINDOW_MACRO(HueEffect)
579 LOAD_CONFIGURATION_MACRO(HueEffect, HueConfig)
581 int HueEffect::load_defaults()
583         char directory[BCTEXTLEN];
584         sprintf(directory, "%shuesaturation.rc", BCASTDIR);
585         defaults = new BC_Hash(directory);
586         defaults->load();
587         config.hue = defaults->get("HUE", config.hue);
588         config.saturation = defaults->get("SATURATION", config.saturation);
589         config.value = defaults->get("VALUE", config.value);
590         return 0;
592 int HueEffect::save_defaults()
594         defaults->update("HUE", config.hue);
595         defaults->update("SATURATION", config.saturation);
596         defaults->update("VALUE", config.value);
597         defaults->save();
598         return 0;
600 void HueEffect::save_data(KeyFrame *keyframe)
602         FileXML output;
603         output.set_shared_string(keyframe->data, MESSAGESIZE);
604         output.tag.set_title("HUESATURATION");
605         output.tag.set_property("HUE", config.hue);
606         output.tag.set_property("SATURATION", config.saturation);
607         output.tag.set_property("VALUE", config.value);
608         output.append_tag();
609         output.terminate_string();
611 void HueEffect::read_data(KeyFrame *keyframe)
613         FileXML input;
614         input.set_shared_string(keyframe->data, strlen(keyframe->data));
615         while(!input.read_tag())
616         {
617                 if(input.tag.title_is("HUESATURATION"))
618                 {
619                         config.hue = input.tag.get_property("HUE", config.hue);
620                         config.saturation = input.tag.get_property("SATURATION", config.saturation);
621                         config.value = input.tag.get_property("VALUE", config.value);
622                 }
623         }
625 void HueEffect::update_gui()
627         if(thread)
628         {
629                 thread->window->lock_window();
630                 load_configuration();
631                 thread->window->hue->update(config.hue);
632                 thread->window->saturation->update(config.saturation);
633                 thread->window->value->update(config.value);
634                 thread->window->unlock_window();
635         }
638 int HueEffect::handle_opengl()
640 #ifdef HAVE_GL
641         static char *yuv_saturation_frag = 
642                 "uniform sampler2D tex;\n"
643                 "uniform float s_offset;\n"
644                 "uniform float v_offset;\n"
645                 "void main()\n"
646                 "{\n"
647                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
648                 "       pixel.r *= v_offset;\n"
649                 "       pixel.gb -= vec2(0.5, 0.5);\n"
650                 "       pixel.g *= s_offset;\n"
651                 "       pixel.b *= s_offset;\n"
652                 "       pixel.gb += vec2(0.5, 0.5);\n"
653                 "       gl_FragColor = pixel;\n"
654                 "}\n";
657         static char *yuv_frag = 
658                 "uniform sampler2D tex;\n"
659                 "uniform float h_offset;\n"
660                 "uniform float s_offset;\n"
661                 "uniform float v_offset;\n"
662                 "void main()\n"
663                 "{\n"
664                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
665                         YUV_TO_RGB_FRAG("pixel")
666                         RGB_TO_HSV_FRAG("pixel")
667                 "       pixel.r += h_offset;\n"
668                 "       pixel.g *= s_offset;\n"
669                 "       pixel.b *= v_offset;\n"
670                 "       if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
671                 "       if(pixel.r < 0.0) pixel.r += 360.0;\n"
672                         HSV_TO_RGB_FRAG("pixel")
673                         RGB_TO_YUV_FRAG("pixel")
674                 "       gl_FragColor = pixel;\n"
675                 "}\n";
677         static char *rgb_frag = 
678                 "uniform sampler2D tex;\n"
679                 "uniform float h_offset;\n"
680                 "uniform float s_offset;\n"
681                 "uniform float v_offset;\n"
682                 "void main()\n"
683                 "{\n"
684                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
685                         RGB_TO_HSV_FRAG("pixel")
686                 "       pixel.r += h_offset;\n"
687                 "       pixel.g *= s_offset;\n"
688                 "       pixel.b *= v_offset;\n"
689                 "       if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
690                 "       if(pixel.r < 0.0) pixel.r += 360.0;\n"
691                         HSV_TO_RGB_FRAG("pixel")
692                 "       gl_FragColor = pixel;\n"
693                 "}\n";
696         get_output()->to_texture();
697         get_output()->enable_opengl();
699         unsigned int frag_shader = 0;
700         switch(get_output()->get_color_model())
701         {
702                 case BC_YUV888:
703                 case BC_YUVA8888:
704 // This is a lousy approximation but good enough for the masker.
705                         if(EQUIV(config.hue, 0))
706                                 frag_shader = VFrame::make_shader(0,
707                                         yuv_saturation_frag,
708                                         0);
709                         else
710                                 frag_shader = VFrame::make_shader(0,
711                                         yuv_frag,
712                                         0);
713                         break;
714                 default:
715                         frag_shader = VFrame::make_shader(0,
716                                 rgb_frag,
717                                 0);
718                         break;
719         }
722         if(frag_shader > 0) 
723         {
724                 glUseProgram(frag_shader);
725                 glUniform1i(glGetUniformLocation(frag_shader, "tex"), 0);
726                 glUniform1f(glGetUniformLocation(frag_shader, "h_offset"), config.hue);
727                 glUniform1f(glGetUniformLocation(frag_shader, "s_offset"), 
728                         ((float)config.saturation - MINSATURATION) / MAXSATURATION);
729                 glUniform1f(glGetUniformLocation(frag_shader, "v_offset"), 
730                         ((float)config.value - MINVALUE) / MAXVALUE);
731         }
733         get_output()->init_screen();
734         get_output()->bind_texture(0);
735         get_output()->draw_texture();
736         glUseProgram(0);
737         get_output()->set_opengl_state(VFrame::SCREEN);
738 #endif