modify clr btn 16 plugins, add regdmp for sigtraps, rework mask_engine, mask rotate...
[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, 370, 140, 370, 140, 0)
231 {
232         this->plugin = plugin;
233 }
234 void HueWindow::create_objects()
235 {
236         int x = 10, y = 10, x1 = 100;
237         int x2 = 0; int clrBtn_w = 50;
238
239         add_subwindow(new BC_Title(x, y, _("Hue:")));
240         add_subwindow(hue = new HueSlider(plugin, x1, y, 200));
241         x2 = x1 + hue->get_w() + 10;
242         add_subwindow(hueClr = new HueSliderClr(plugin, this, x2, y, clrBtn_w, RESET_HUV));
243
244         y += 30;
245         add_subwindow(new BC_Title(x, y, _("Saturation:")));
246         add_subwindow(saturation = new SaturationSlider(plugin, x1, y, 200));
247         add_subwindow(satClr = new HueSliderClr(plugin, this, x2, y, clrBtn_w, RESET_SAT));
248
249         y += 30;
250         add_subwindow(new BC_Title(x, y, _("Value:")));
251         add_subwindow(value = new ValueSlider(plugin, x1, y, 200));
252         add_subwindow(valClr = new HueSliderClr(plugin, this, x2, y, clrBtn_w, RESET_VAL));
253
254         y += 40;
255         add_subwindow(reset = new HueReset(plugin, this, x, y));
256         show_window();
257         flush();
258 }
259
260
261 // for Reset button
262 void HueWindow::update_gui(int clear)
263 {
264         switch(clear) {
265                 case RESET_HUV : hue->update(plugin->config.hue);
266                         break;
267                 case RESET_SAT : saturation->update(plugin->config.saturation);
268                         break;
269                 case RESET_VAL : value->update(plugin->config.value);
270                         break;
271                 case RESET_ALL :
272                 default:
273                         hue->update(plugin->config.hue);
274                         saturation->update(plugin->config.saturation);
275                         value->update(plugin->config.value);
276                         break;
277         }
278 }
279
280
281
282
283
284
285
286
287 HueEngine::HueEngine(HueEffect *plugin, int cpus)
288  : LoadServer(cpus, cpus)
289 {
290         this->plugin = plugin;
291 }
292 void HueEngine::init_packages()
293 {
294         for(int i = 0; i < LoadServer::get_total_packages(); i++)
295         {
296                 HuePackage *pkg = (HuePackage*)get_package(i);
297                 pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
298                 pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
299         }
300 }
301 LoadClient* HueEngine::new_client()
302 {
303         return new HueUnit(plugin, this);
304 }
305 LoadPackage* HueEngine::new_package()
306 {
307         return new HuePackage;
308 }
309
310
311
312
313
314
315
316
317 HuePackage::HuePackage()
318  : LoadPackage()
319 {
320 }
321
322 HueUnit::HueUnit(HueEffect *plugin, HueEngine *server)
323  : LoadClient(server)
324 {
325         this->plugin = plugin;
326 }
327
328
329
330
331
332
333
334 #define HUESATURATION(type, max, components, use_yuv) \
335 { \
336         float h_offset = plugin->config.hue; \
337         float s_offset = ((float)plugin->config.saturation - MINSATURATION) / MAXSATURATION; \
338         float v_offset = ((float)plugin->config.value - MINVALUE) / MAXVALUE; \
339         for(int i = pkg->row1; i < pkg->row2; i++) \
340         { \
341                 type* in_row = (type*)plugin->input->get_rows()[i]; \
342                 type* out_row = (type*)plugin->output->get_rows()[i]; \
343  \
344                 for(int j = 0; j < w; j++) \
345                 { \
346                         float h, s, va; \
347                         int y, u, v; \
348                         float r, g, b; \
349                         int r_i, g_i, b_i; \
350  \
351                         if(use_yuv) \
352                         { \
353                                 y = (int)in_row[0]; \
354                                 u = (int)in_row[1]; \
355                                 v = (int)in_row[2]; \
356                                 if(max == 0xffff) \
357                                         YUV::yuv.yuv_to_rgb_16(r_i, g_i, b_i, y, u, v); \
358                                 else \
359                                         YUV::yuv.yuv_to_rgb_8(r_i, g_i, b_i, y, u, v); \
360                                 HSV::rgb_to_hsv((float)r_i / max, \
361                                         (float)g_i / max, \
362                                         (float)b_i / max, \
363                                         h, \
364                                         s, \
365                                         va); \
366                         } \
367                         else \
368                         { \
369                                 r = (float)in_row[0] / max; \
370                                 g = (float)in_row[1] / max; \
371                                 b = (float)in_row[2] / max; \
372                                 HSV::rgb_to_hsv(r, g, b, h, s, va); \
373                         } \
374  \
375  \
376                         h += h_offset; \
377                         s *= s_offset; \
378                         va *= v_offset; \
379  \
380                         if(h >= 360) h -= 360; \
381                         if(h < 0) h += 360; \
382                         if(sizeof(type) < 4) \
383                         { \
384                                 if(s > 1) s = 1; \
385                                 if(va > 1) va = 1; \
386                                 if(s < 0) s = 0; \
387                                 if(va < 0) va = 0; \
388                         } \
389  \
390                         if(use_yuv) \
391                         { \
392                                 HSV::hsv_to_yuv(y, u, v, h, s, va, max); \
393                                 out_row[0] = y; \
394                                 out_row[1] = u; \
395                                 out_row[2] = v; \
396                         } \
397                         else \
398                         { \
399                                 HSV::hsv_to_rgb(r, g, b, h, s, va); \
400                                 if(sizeof(type) < 4) \
401                                 { \
402                                         r *= max; \
403                                         g *= max; \
404                                         b *= max; \
405                                         out_row[0] = (type)CLIP(r, 0, max); \
406                                         out_row[1] = (type)CLIP(g, 0, max); \
407                                         out_row[2] = (type)CLIP(b, 0, max); \
408                                 } \
409                                 else \
410                                 { \
411                                         out_row[0] = (type)r; \
412                                         out_row[1] = (type)g; \
413                                         out_row[2] = (type)b; \
414                                 } \
415                         } \
416  \
417                         in_row += components; \
418                         out_row += components; \
419                 } \
420         } \
421 }
422
423
424 void HueUnit::process_package(LoadPackage *package)
425 {
426         HuePackage *pkg = (HuePackage*)package;
427         int w = plugin->input->get_w();
428
429         switch(plugin->input->get_color_model())
430         {
431                 case BC_RGB888:
432                         HUESATURATION(unsigned char, 0xff, 3, 0)
433                         break;
434
435                 case BC_RGB_FLOAT:
436                         HUESATURATION(float, 1, 3, 0)
437                         break;
438
439                 case BC_YUV888:
440                         HUESATURATION(unsigned char, 0xff, 3, 1)
441                         break;
442
443                 case BC_RGB161616:
444                         HUESATURATION(uint16_t, 0xffff, 3, 0)
445                         break;
446
447                 case BC_YUV161616:
448                         HUESATURATION(uint16_t, 0xffff, 3, 1)
449                         break;
450
451                 case BC_RGBA_FLOAT:
452                         HUESATURATION(float, 1, 4, 0)
453                         break;
454
455                 case BC_RGBA8888:
456                         HUESATURATION(unsigned char, 0xff, 4, 0)
457                         break;
458
459                 case BC_YUVA8888:
460                         HUESATURATION(unsigned char, 0xff, 4, 1)
461                         break;
462
463                 case BC_RGBA16161616:
464                         HUESATURATION(uint16_t, 0xffff, 4, 0)
465                         break;
466
467                 case BC_YUVA16161616:
468                         HUESATURATION(uint16_t, 0xffff, 4, 1)
469                         break;
470
471         }
472 }
473
474
475
476
477
478
479 HueEffect::HueEffect(PluginServer *server)
480  : PluginVClient(server)
481 {
482         engine = 0;
483
484 }
485 HueEffect::~HueEffect()
486 {
487
488         if(engine) delete engine;
489 }
490
491 int HueEffect::process_buffer(VFrame *frame,
492         int64_t start_position,
493         double frame_rate)
494 {
495         load_configuration();
496
497         read_frame(frame,
498                 0,
499                 start_position,
500                 frame_rate,
501                 get_use_opengl());
502
503
504         this->input = frame;
505         this->output = frame;
506         if(EQUIV(config.hue, 0) && EQUIV(config.saturation, 0) && EQUIV(config.value, 0))
507         {
508                 return 0;
509         }
510         else
511         {
512                 if(get_use_opengl())
513                 {
514                         run_opengl();
515                         return 0;
516                 }
517
518                 if(!engine) engine = new HueEngine(this, PluginClient::smp + 1);
519
520                 engine->process_packages();
521         }
522         return 0;
523 }
524
525 const char* HueEffect::plugin_title() { return N_("Hue saturation"); }
526 int HueEffect::is_realtime() { return 1; }
527
528 NEW_WINDOW_MACRO(HueEffect, HueWindow)
529 LOAD_CONFIGURATION_MACRO(HueEffect, HueConfig)
530
531
532 void HueEffect::save_data(KeyFrame *keyframe)
533 {
534         FileXML output;
535         output.set_shared_output(keyframe->xbuf);
536         output.tag.set_title("HUESATURATION");
537         output.tag.set_property("HUE", config.hue);
538         output.tag.set_property("SATURATION", config.saturation);
539         output.tag.set_property("VALUE", config.value);
540         output.append_tag();
541         output.tag.set_title("/HUESATURATION");
542         output.append_tag();
543         output.append_newline();
544         output.terminate_string();
545 }
546 void HueEffect::read_data(KeyFrame *keyframe)
547 {
548         FileXML input;
549         input.set_shared_input(keyframe->xbuf);
550         while(!input.read_tag())
551         {
552                 if(input.tag.title_is("HUESATURATION"))
553                 {
554                         config.hue = input.tag.get_property("HUE", config.hue);
555                         config.saturation = input.tag.get_property("SATURATION", config.saturation);
556                         config.value = input.tag.get_property("VALUE", config.value);
557                 }
558         }
559 }
560 void HueEffect::update_gui()
561 {
562         if(thread)
563         {
564                 ((HueWindow*)thread->window)->lock_window();
565                 load_configuration();
566                 ((HueWindow*)thread->window)->hue->update(config.hue);
567                 ((HueWindow*)thread->window)->saturation->update(config.saturation);
568                 ((HueWindow*)thread->window)->value->update(config.value);
569                 ((HueWindow*)thread->window)->unlock_window();
570         }
571 }
572
573 int HueEffect::handle_opengl()
574 {
575 #ifdef HAVE_GL
576         const char *yuv_saturation_frag =
577                 "uniform sampler2D tex;\n"
578                 "uniform float s_offset;\n"
579                 "uniform float v_offset;\n"
580                 "void main()\n"
581                 "{\n"
582                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
583                 "       pixel.r *= v_offset;\n"
584                 "       pixel.gb -= vec2(0.5, 0.5);\n"
585                 "       pixel.g *= s_offset;\n"
586                 "       pixel.b *= s_offset;\n"
587                 "       pixel.gb += vec2(0.5, 0.5);\n"
588                 "       gl_FragColor = pixel;\n"
589                 "}\n";
590
591
592         const char *yuv_frag =
593                 "uniform sampler2D tex;\n"
594                 "uniform float h_offset;\n"
595                 "uniform float s_offset;\n"
596                 "uniform float v_offset;\n"
597                 "void main()\n"
598                 "{\n"
599                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
600                         YUV_TO_RGB_FRAG("pixel")
601                         RGB_TO_HSV_FRAG("pixel")
602                 "       pixel.r += h_offset;\n"
603                 "       pixel.g *= s_offset;\n"
604                 "       pixel.b *= v_offset;\n"
605                 "       if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
606                 "       if(pixel.r < 0.0) pixel.r += 360.0;\n"
607                         HSV_TO_RGB_FRAG("pixel")
608                         RGB_TO_YUV_FRAG("pixel")
609                 "       gl_FragColor = pixel;\n"
610                 "}\n";
611
612         const char *rgb_frag =
613                 "uniform sampler2D tex;\n"
614                 "uniform float h_offset;\n"
615                 "uniform float s_offset;\n"
616                 "uniform float v_offset;\n"
617                 "void main()\n"
618                 "{\n"
619                 "       vec4 pixel = texture2D(tex, gl_TexCoord[0].st);\n"
620                         RGB_TO_HSV_FRAG("pixel")
621                 "       pixel.r += h_offset;\n"
622                 "       pixel.g *= s_offset;\n"
623                 "       pixel.b *= v_offset;\n"
624                 "       if(pixel.r >= 360.0) pixel.r -= 360.0;\n"
625                 "       if(pixel.r < 0.0) pixel.r += 360.0;\n"
626                         HSV_TO_RGB_FRAG("pixel")
627                 "       gl_FragColor = pixel;\n"
628                 "}\n";
629
630
631         get_output()->to_texture();
632         get_output()->enable_opengl();
633
634         const char *shader_stack[16];
635         memset(shader_stack,0, sizeof(shader_stack));
636         int current_shader = 0;
637
638         int need_color_matrix = BC_CModels::is_yuv(get_output()->get_color_model()) ? 1 : 0;
639         if( need_color_matrix ) shader_stack[current_shader++] = bc_gl_colors;
640         shader_stack[current_shader++] = !need_color_matrix ? rgb_frag :
641                 EQUIV(config.hue, 0) ? yuv_saturation_frag: yuv_frag ;
642
643         shader_stack[current_shader] = 0;
644         unsigned int shader = VFrame::make_shader(shader_stack);
645         if(shader > 0) {
646                 glUseProgram(shader);
647                 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
648                 glUniform1f(glGetUniformLocation(shader, "h_offset"), config.hue);
649                 glUniform1f(glGetUniformLocation(shader, "s_offset"),
650                         ((float)config.saturation - MINSATURATION) / MAXSATURATION);
651                 glUniform1f(glGetUniformLocation(shader, "v_offset"),
652                         ((float)config.value - MINVALUE) / MAXVALUE);
653                 if( need_color_matrix ) BC_GL_COLORS(shader);
654         }
655
656         get_output()->init_screen();
657         get_output()->bind_texture(0);
658         get_output()->draw_texture();
659         glUseProgram(0);
660         get_output()->set_opengl_state(VFrame::SCREEN);
661 #endif
662         return 0;
663 }
664
665
666
667
668