bbaf2ca6f96f505db0206a8fca7a5e036acd24fc
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / huesaturation / huesaturation.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 "bccolors.h"
23 #include "bcdisplayinfo.h"
24 #include "clip.h"
25 #include "bchash.h"
26 #include "filexml.h"
27 #include "huesaturation.h"
28 #include "guicast.h"
29 #include "language.h"
30 #include "loadbalance.h"
31 #include "bccolors.h"
32 #include "playback3d.h"
33 #include "pluginvclient.h"
34 #include "theme.h"
35 #include "vframe.h"
36
37 #include <stdint.h>
38 #include <string.h>
39
40
41
42 REGISTER_PLUGIN(HueEffect)
43
44
45
46
47
48 HueConfig::HueConfig()
49 {
50         reset(RESET_ALL);
51 }
52
53 void HueConfig::reset(int clear)
54 {
55         switch(clear) {
56                 case RESET_HUV : hue = 0;
57                         break;
58                 case RESET_SAT : saturation = 0;
59                         break;
60                 case RESET_VAL : value = 0;
61                         break;
62                 case RESET_ALL :
63                 default:
64                         hue = saturation = value = 0;
65                         break;
66         }
67 }
68
69 void HueConfig::copy_from(HueConfig &src)
70 {
71         hue = src.hue;
72         saturation = src.saturation;
73         value = src.value;
74 }
75 int HueConfig::equivalent(HueConfig &src)
76 {
77         return EQUIV(hue, src.hue) &&
78                 EQUIV(saturation, src.saturation) &&
79                 EQUIV(value, src.value);
80 }
81 void HueConfig::interpolate(HueConfig &prev,
82         HueConfig &next,
83         long prev_frame,
84         long next_frame,
85         long current_frame)
86 {
87         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
88         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
89
90         this->hue = prev.hue * prev_scale + next.hue * next_scale;
91         this->saturation = prev.saturation * prev_scale + next.saturation * next_scale;
92         this->value = prev.value * prev_scale + next.value * next_scale;
93 }
94
95
96
97
98
99
100
101
102 HueSlider::HueSlider(HueEffect *plugin, int x, int y, int w)
103  : BC_FSlider(x,
104                         y,
105                         0,
106                         w,
107                         w,
108                         (float)MINHUE,
109                         (float)MAXHUE,
110                         plugin->config.hue)
111 {
112         this->plugin = plugin;
113 }
114 int HueSlider::handle_event()
115 {
116         plugin->config.hue = get_value();
117         plugin->send_configure_change();
118         return 1;
119 }
120
121
122
123
124
125
126
127 SaturationSlider::SaturationSlider(HueEffect *plugin, int x, int y, int w)
128  : BC_FSlider(x,
129                         y,
130                         0,
131                         w,
132                         w,
133                         (float)MINSATURATION,
134                         (float)MAXSATURATION,
135                         plugin->config.saturation)
136 {
137         this->plugin = plugin;
138 }
139 int SaturationSlider::handle_event()
140 {
141         plugin->config.saturation = get_value();
142         plugin->send_configure_change();
143         return 1;
144 }
145
146 char* SaturationSlider::get_caption()
147 {
148         float fraction = ((float)plugin->config.saturation - MINSATURATION) /
149                 MAXSATURATION;;
150         sprintf(string, "%0.4f", fraction);
151         return string;
152 }
153
154
155
156
157
158
159
160 ValueSlider::ValueSlider(HueEffect *plugin, int x, int y, int w)
161  : BC_FSlider(x,
162                         y,
163                         0,
164                         w,
165                         w,
166                         (float)MINVALUE,
167                         (float)MAXVALUE,
168                         plugin->config.value)
169 {
170         this->plugin = plugin;
171 }
172 int ValueSlider::handle_event()
173 {
174         plugin->config.value = get_value();
175         plugin->send_configure_change();
176         return 1;
177 }
178
179 char* ValueSlider::get_caption()
180 {
181         float fraction = ((float)plugin->config.value - MINVALUE) / MAXVALUE;
182         sprintf(string, "%0.4f", fraction);
183         return string;
184 }
185
186
187 HueReset::HueReset(HueEffect *plugin, HueWindow *gui, int x, int y)
188  : BC_GenericButton(x, y, _("Reset"))
189 {
190         this->plugin = plugin;
191         this->gui = gui;
192 }
193 HueReset::~HueReset()
194 {
195 }
196 int HueReset::handle_event()
197 {
198         plugin->config.reset(RESET_ALL); // clear=0 ==> reset all
199         gui->update_gui(RESET_ALL);
200         plugin->send_configure_change();
201         return 1;
202 }
203
204
205 HueSliderClr::HueSliderClr(HueEffect *plugin, HueWindow *gui, int x, int y, int w, int clear)
206  : BC_Button(x, y, w, plugin->get_theme()->get_image_set("reset_button"))
207 {
208         this->plugin = plugin;
209         this->gui = gui;
210         this->clear = clear;
211 }
212 HueSliderClr::~HueSliderClr()
213 {
214 }
215 int HueSliderClr::handle_event()
216 {
217         // clear==1 ==> Hue slider
218         // clear==2 ==> Saturation slider
219         // clear==3 ==> Value slider
220         plugin->config.reset(clear);
221         gui->update_gui(clear);
222         plugin->send_configure_change();
223         return 1;
224 }
225
226
227
228
229 HueWindow::HueWindow(HueEffect *plugin)
230  : PluginClientWindow(plugin, xS(370), yS(140), xS(370), yS(140), 0)
231 {
232         this->plugin = plugin;
233 }
234 void HueWindow::create_objects()
235 {
236         int xs10 = xS(10), xs50 = xS(50), xs100 = xS(100), xs200 = xS(200);
237         int ys10 = yS(10), ys30 = yS(30), ys40 = yS(40);
238         int x = xs10, y = ys10, x1 = xs100;
239         int x2 = 0; int clrBtn_w = xs50;
240
241         add_subwindow(new BC_Title(x, y, _("Hue:")));
242         add_subwindow(hue = new HueSlider(plugin, x1, y, xs200));
243         x2 = x1 + hue->get_w() + xs10;
244         add_subwindow(hueClr = new HueSliderClr(plugin, this, x2, y, clrBtn_w, RESET_HUV));
245
246         y += ys30;
247         add_subwindow(new BC_Title(x, y, _("Saturation:")));
248         add_subwindow(saturation = new SaturationSlider(plugin, x1, y, xs200));
249         add_subwindow(satClr = new HueSliderClr(plugin, this, x2, y, clrBtn_w, RESET_SAT));
250
251         y += ys30;
252         add_subwindow(new BC_Title(x, y, _("Value:")));
253         add_subwindow(value = new ValueSlider(plugin, x1, y, xs200));
254         add_subwindow(valClr = new HueSliderClr(plugin, this, x2, y, clrBtn_w, RESET_VAL));
255
256         y += ys40;
257         add_subwindow(reset = new HueReset(plugin, this, x, y));
258         show_window();
259         flush();
260 }
261
262
263 // for Reset button
264 void HueWindow::update_gui(int clear)
265 {
266         switch(clear) {
267                 case RESET_HUV : hue->update(plugin->config.hue);
268                         break;
269                 case RESET_SAT : saturation->update(plugin->config.saturation);
270                         break;
271                 case RESET_VAL : value->update(plugin->config.value);
272                         break;
273                 case RESET_ALL :
274                 default:
275                         hue->update(plugin->config.hue);
276                         saturation->update(plugin->config.saturation);
277                         value->update(plugin->config.value);
278                         break;
279         }
280 }
281
282
283
284
285
286
287
288
289 HueEngine::HueEngine(HueEffect *plugin, int cpus)
290  : LoadServer(cpus, cpus)
291 {
292         this->plugin = plugin;
293 }
294 void HueEngine::init_packages()
295 {
296         for(int i = 0; i < LoadServer::get_total_packages(); i++)
297         {
298                 HuePackage *pkg = (HuePackage*)get_package(i);
299                 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
300                 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
301         }
302 }
303 LoadClient* HueEngine::new_client()
304 {
305         return new HueUnit(plugin, this);
306 }
307 LoadPackage* HueEngine::new_package()
308 {
309         return new HuePackage;
310 }
311
312
313
314
315
316
317
318
319 HuePackage::HuePackage()
320  : LoadPackage()
321 {
322 }
323
324 HueUnit::HueUnit(HueEffect *plugin, HueEngine *server)
325  : LoadClient(server)
326 {
327         this->plugin = plugin;
328 }
329
330
331
332
333
334
335
336 #define HUESATURATION(type, max, components, use_yuv) \
337 { \
338         float h_offset = plugin->config.hue; \
339         float s_offset = ((float)plugin->config.saturation - MINSATURATION) / MAXSATURATION; \
340         float v_offset = ((float)plugin->config.value - MINVALUE) / MAXVALUE; \
341         for(int i = pkg->row1; i < pkg->row2; i++) \
342         { \
343                 type* in_row = (type*)plugin->input->get_rows()[i]; \
344                 type* out_row = (type*)plugin->output->get_rows()[i]; \
345  \
346                 for(int j = 0; j < w; j++) \
347                 { \
348                         float h, s, va; \
349                         int y, u, v; \
350                         float r, g, b; \
351                         int r_i, g_i, b_i; \
352  \
353                         if(use_yuv) \
354                         { \
355                                 y = (int)in_row[0]; \
356                                 u = (int)in_row[1]; \
357                                 v = (int)in_row[2]; \
358                                 if(max == 0xffff) \
359                                         YUV::yuv.yuv_to_rgb_16(r_i, g_i, b_i, y, u, v); \
360                                 else \
361                                         YUV::yuv.yuv_to_rgb_8(r_i, g_i, b_i, y, u, v); \
362                                 HSV::rgb_to_hsv((float)r_i / max, \
363                                         (float)g_i / max, \
364                                         (float)b_i / max, \
365                                         h, \
366                                         s, \
367                                         va); \
368                         } \
369                         else \
370                         { \
371                                 r = (float)in_row[0] / max; \
372                                 g = (float)in_row[1] / max; \
373                                 b = (float)in_row[2] / max; \
374                                 HSV::rgb_to_hsv(r, g, b, h, s, va); \
375                         } \
376  \
377  \
378                         h += h_offset; \
379                         s *= s_offset; \
380                         va *= v_offset; \
381  \
382                         if(h >= 360) h -= 360; \
383                         if(h < 0) h += 360; \
384                         if(sizeof(type) < 4) \
385                         { \
386                                 if(s > 1) s = 1; \
387                                 if(va > 1) va = 1; \
388                                 if(s < 0) s = 0; \
389                                 if(va < 0) va = 0; \
390                         } \
391  \
392                         if(use_yuv) \
393                         { \
394                                 HSV::hsv_to_yuv(y, u, v, h, s, va, max); \
395                                 out_row[0] = y; \
396                                 out_row[1] = u; \
397                                 out_row[2] = v; \
398                         } \
399                         else \
400                         { \
401                                 HSV::hsv_to_rgb(r, g, b, h, s, va); \
402                                 if(sizeof(type) < 4) \
403                                 { \
404                                         r *= max; \
405                                         g *= max; \
406                                         b *= max; \
407                                         out_row[0] = (type)CLIP(r, 0, max); \
408                                         out_row[1] = (type)CLIP(g, 0, max); \
409                                         out_row[2] = (type)CLIP(b, 0, max); \
410                                 } \
411                                 else \
412                                 { \
413                                         out_row[0] = (type)r; \
414                                         out_row[1] = (type)g; \
415                                         out_row[2] = (type)b; \
416                                 } \
417                         } \
418  \
419                         in_row += components; \
420                         out_row += components; \
421                 } \
422         } \
423 }
424
425
426 void HueUnit::process_package(LoadPackage *package)
427 {
428         HuePackage *pkg = (HuePackage*)package;
429         int w = plugin->input->get_w();
430
431         switch(plugin->input->get_color_model())
432         {
433                 case BC_RGB888:
434                         HUESATURATION(unsigned char, 0xff, 3, 0)
435                         break;
436
437                 case BC_RGB_FLOAT:
438                         HUESATURATION(float, 1, 3, 0)
439                         break;
440
441                 case BC_YUV888:
442                         HUESATURATION(unsigned char, 0xff, 3, 1)
443                         break;
444
445                 case BC_RGB161616:
446                         HUESATURATION(uint16_t, 0xffff, 3, 0)
447                         break;
448
449                 case BC_YUV161616:
450                         HUESATURATION(uint16_t, 0xffff, 3, 1)
451                         break;
452
453                 case BC_RGBA_FLOAT:
454                         HUESATURATION(float, 1, 4, 0)
455                         break;
456
457                 case BC_RGBA8888:
458                         HUESATURATION(unsigned char, 0xff, 4, 0)
459                         break;
460
461                 case BC_YUVA8888:
462                         HUESATURATION(unsigned char, 0xff, 4, 1)
463                         break;
464
465                 case BC_RGBA16161616:
466                         HUESATURATION(uint16_t, 0xffff, 4, 0)
467                         break;
468
469                 case BC_YUVA16161616:
470                         HUESATURATION(uint16_t, 0xffff, 4, 1)
471                         break;
472
473         }
474 }
475
476
477
478
479
480
481 HueEffect::HueEffect(PluginServer *server)
482  : PluginVClient(server)
483 {
484         engine = 0;
485
486 }
487 HueEffect::~HueEffect()
488 {
489
490         if(engine) delete engine;
491 }
492
493 int HueEffect::process_buffer(VFrame *frame,
494         int64_t start_position,
495         double frame_rate)
496 {
497         load_configuration();
498
499         read_frame(frame,
500                 0,
501                 start_position,
502                 frame_rate,
503                 get_use_opengl());
504
505
506         this->input = frame;
507         this->output = frame;
508         if(EQUIV(config.hue, 0) && EQUIV(config.saturation, 0) && EQUIV(config.value, 0))
509         {
510                 return 0;
511         }
512         else
513         {
514                 if(get_use_opengl())
515                 {
516                         run_opengl();
517                         return 0;
518                 }
519
520                 if(!engine) engine = new HueEngine(this, PluginClient::smp + 1);
521
522                 engine->process_packages();
523         }
524         return 0;
525 }
526
527 const char* HueEffect::plugin_title() { return N_("Hue saturation"); }
528 int HueEffect::is_realtime() { return 1; }
529
530 NEW_WINDOW_MACRO(HueEffect, HueWindow)
531 LOAD_CONFIGURATION_MACRO(HueEffect, HueConfig)
532
533
534 void HueEffect::save_data(KeyFrame *keyframe)
535 {
536         FileXML output;
537         output.set_shared_output(keyframe->xbuf);
538         output.tag.set_title("HUESATURATION");
539         output.tag.set_property("HUE", config.hue);
540         output.tag.set_property("SATURATION", config.saturation);
541         output.tag.set_property("VALUE", config.value);
542         output.append_tag();
543         output.tag.set_title("/HUESATURATION");
544         output.append_tag();
545         output.append_newline();
546         output.terminate_string();
547 }
548 void HueEffect::read_data(KeyFrame *keyframe)
549 {
550         FileXML input;
551         input.set_shared_input(keyframe->xbuf);
552         while(!input.read_tag())
553         {
554                 if(input.tag.title_is("HUESATURATION"))
555                 {
556                         config.hue = input.tag.get_property("HUE", config.hue);
557                         config.saturation = input.tag.get_property("SATURATION", config.saturation);
558                         config.value = input.tag.get_property("VALUE", config.value);
559                 }
560         }
561 }
562 void HueEffect::update_gui()
563 {
564         if(thread)
565         {
566                 ((HueWindow*)thread->window)->lock_window();
567                 load_configuration();
568                 ((HueWindow*)thread->window)->hue->update(config.hue);
569                 ((HueWindow*)thread->window)->saturation->update(config.saturation);
570                 ((HueWindow*)thread->window)->value->update(config.value);
571                 ((HueWindow*)thread->window)->unlock_window();
572         }
573 }
574
575 int HueEffect::handle_opengl()
576 {
577 #ifdef HAVE_GL
578         const char *yuv_saturation_frag =
579                 "uniform sampler2D tex;\n"
580                 "uniform float s_offset;\n"
581                 "uniform float v_offset;\n"
582                 "void main()\n"
583                 "{\n"
584                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
585                 "       pixel.r *= v_offset;\n"
586                 "       pixel.gb -= vec2(0.5, 0.5);\n"
587                 "       pixel.g *= s_offset;\n"
588                 "       pixel.b *= s_offset;\n"
589                 "       pixel.gb += vec2(0.5, 0.5);\n"
590                 "       gl_FragColor = pixel;\n"
591                 "}\n";
592
593
594         const char *yuv_frag =
595                 "uniform sampler2D tex;\n"
596                 "uniform float h_offset;\n"
597                 "uniform float s_offset;\n"
598                 "uniform float v_offset;\n"
599                 "void main()\n"
600                 "{\n"
601                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
602                         YUV_TO_RGB_FRAG("pixel")
603                         RGB_TO_HSV_FRAG("pixel")
604                 "       pixel.r += h_offset;\n"
605                 "       pixel.g *= s_offset;\n"
606                 "       pixel.b *= v_offset;\n"
607                 "       if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
608                 "       if(pixel.r < 0.0) pixel.r += 360.0;\n"
609                         HSV_TO_RGB_FRAG("pixel")
610                         RGB_TO_YUV_FRAG("pixel")
611                 "       gl_FragColor = pixel;\n"
612                 "}\n";
613
614         const char *rgb_frag =
615                 "uniform sampler2D tex;\n"
616                 "uniform float h_offset;\n"
617                 "uniform float s_offset;\n"
618                 "uniform float v_offset;\n"
619                 "void main()\n"
620                 "{\n"
621                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
622                         RGB_TO_HSV_FRAG("pixel")
623                 "       pixel.r += h_offset;\n"
624                 "       pixel.g *= s_offset;\n"
625                 "       pixel.b *= v_offset;\n"
626                 "       if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
627                 "       if(pixel.r < 0.0) pixel.r += 360.0;\n"
628                         HSV_TO_RGB_FRAG("pixel")
629                 "       gl_FragColor = pixel;\n"
630                 "}\n";
631
632
633         get_output()->to_texture();
634         get_output()->enable_opengl();
635
636         const char *shader_stack[16];
637         memset(shader_stack,0, sizeof(shader_stack));
638         int current_shader = 0;
639
640         int need_color_matrix = BC_CModels::is_yuv(get_output()->get_color_model()) ? 1 : 0;
641         if( need_color_matrix ) shader_stack[current_shader++] = bc_gl_colors;
642         shader_stack[current_shader++] = !need_color_matrix ? rgb_frag :
643                 EQUIV(config.hue, 0) ? yuv_saturation_frag: yuv_frag ;
644
645         shader_stack[current_shader] = 0;
646         unsigned int shader = VFrame::make_shader(shader_stack);
647         if(shader > 0) {
648                 glUseProgram(shader);
649                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
650                 glUniform1f(glGetUniformLocation(shader, "h_offset"), config.hue);
651                 glUniform1f(glGetUniformLocation(shader, "s_offset"),
652                         ((float)config.saturation - MINSATURATION) / MAXSATURATION);
653                 glUniform1f(glGetUniformLocation(shader, "v_offset"),
654                         ((float)config.value - MINVALUE) / MAXVALUE);
655                 if( need_color_matrix ) BC_GL_COLORS(shader);
656         }
657
658         get_output()->init_screen();
659         get_output()->bind_texture(0);
660         get_output()->draw_texture();
661         glUseProgram(0);
662         get_output()->set_opengl_state(VFrame::SCREEN);
663 #endif
664         return 0;
665 }
666
667
668
669
670