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