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