improved plugins with added Tumbler box and visible values
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / brightness / brightness.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 "brightness.h"
23 #include "bccolors.h"
24 #include "bchash.h"
25 #include "clip.h"
26 #include "filexml.h"
27 #include "language.h"
28 #include "playback3d.h"
29
30 #include <stdio.h>
31 #include <stdint.h>
32 #include <string.h>
33
34
35 REGISTER_PLUGIN(BrightnessMain)
36
37
38 BrightnessConfig::BrightnessConfig()
39 {
40         reset(0);
41 }
42
43 void BrightnessConfig::reset(int clear)
44 {
45         switch(clear) {
46                 case RESET_CONTRAST : contrast = 0;
47                         break;
48                 case RESET_BRIGHTNESS : brightness = 0;
49                         break;
50                 case RESET_ALL :
51                 default:
52                         brightness = 0;
53                         contrast = 0;
54                         luma = 1;
55                         break;
56         }
57 }
58
59 int BrightnessConfig::equivalent(BrightnessConfig &that)
60 {
61         return (brightness == that.brightness &&
62                 contrast == that.contrast &&
63                 luma == that.luma);
64 }
65
66 void BrightnessConfig::copy_from(BrightnessConfig &that)
67 {
68         brightness = that.brightness;
69         contrast = that.contrast;
70         luma = that.luma;
71 }
72
73 void BrightnessConfig::interpolate(BrightnessConfig &prev,
74         BrightnessConfig &next,
75         int64_t prev_frame,
76         int64_t next_frame,
77         int64_t current_frame)
78 {
79         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
80         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
81
82         this->brightness = prev.brightness * prev_scale + next.brightness * next_scale;
83         this->contrast = prev.contrast * prev_scale + next.contrast * next_scale;
84         this->luma = (int)(prev.luma * prev_scale + next.luma * next_scale);
85 }
86
87
88
89
90
91
92
93
94
95 BrightnessMain::BrightnessMain(PluginServer *server)
96  : PluginVClient(server)
97 {
98     redo_buffers = 1;
99         engine = 0;
100
101 }
102
103 BrightnessMain::~BrightnessMain()
104 {
105
106         if(engine) delete engine;
107 }
108
109 const char* BrightnessMain::plugin_title() { return N_("Brightness/Contrast"); }
110 int BrightnessMain::is_realtime() { return 1; }
111
112 NEW_WINDOW_MACRO(BrightnessMain, BrightnessWindow)
113 LOAD_CONFIGURATION_MACRO(BrightnessMain, BrightnessConfig)
114
115 int BrightnessMain::process_buffer(VFrame *frame,
116         int64_t start_position,
117         double frame_rate)
118 {
119         load_configuration();
120
121         read_frame(frame,
122                 0,
123                 start_position,
124                 frame_rate,
125                 get_use_opengl());
126
127
128 // Use hardware
129         if(get_use_opengl())
130         {
131                 run_opengl();
132                 return 0;
133         }
134
135
136
137
138         if(!engine) engine = new BrightnessEngine(this, PluginClient::smp + 1);
139
140         this->input = frame;
141         this->output = frame;
142
143         if(!EQUIV(config.brightness, 0) || !EQUIV(config.contrast, 0))
144         {
145                 engine->process_packages();
146         }
147
148         return 0;
149 }
150
151 int BrightnessMain::handle_opengl()
152 {
153 #ifdef HAVE_GL
154         static const char *brightness_yuvluma_frag =
155                 "uniform sampler2D tex;\n"
156                 "uniform float brightness;\n"
157                 "uniform float contrast;\n"
158                 "uniform float offset;\n"
159                 "void main()\n"
160                 "{\n"
161                 "       vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n"
162                 "       yuva.r += brightness;\n"
163                 "       yuva.r = yuva.r * contrast + offset;\n"
164                 "       gl_FragColor = yuva;\n"
165                 "}\n";
166
167         static const char *brightness_yuv_frag =
168                 "uniform sampler2D tex;\n"
169                 "uniform float brightness;\n"
170                 "uniform float contrast;\n"
171                 "uniform float offset;\n"
172                 "void main()\n"
173                 "{\n"
174                 "       vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n"
175                 "       yuva.r += brightness;\n"
176                 "       yuva.rgb *= vec3(contrast, contrast, contrast);\n"
177                 "       yuva.rgb += vec3(offset, offset, offset);\n"
178                 "       gl_FragColor = yuva;\n"
179                 "}\n";
180
181         static const char *brightness_rgb_frag =
182                 "uniform sampler2D tex;\n"
183                 "uniform float brightness;\n"
184                 "uniform float contrast;\n"
185                 "uniform float offset;\n"
186                 "void main()\n"
187                 "{\n"
188                 "       vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n"
189                 "       rgba.rgb += vec3(brightness, brightness, brightness);\n"
190                 "       rgba.rgb *= vec3(contrast, contrast, contrast);\n"
191                 "       rgba.rgb += vec3(offset, offset, offset);\n"
192                 "       gl_FragColor = rgba;\n"
193                 "}\n";
194
195         static const char *brightness_rgbluma_frag =
196                 "uniform sampler2D tex;\n"
197                 "uniform float brightness;\n"
198                 "uniform float contrast;\n"
199                 "uniform float offset;\n"
200                 "uniform mat3 yuv_to_rgb_matrix;\n"
201                 "uniform mat3 rgb_to_yuv_matrix;\n"
202
203                 "void main()\n"
204                 "{\n"
205                 "       vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n"
206                 "       rgba.rgb = rgb_to_yuv_matrix * rgba.rgb;\n"
207                 "       rgba.r += brightness;\n"
208                 "       rgba.r = rgba.r * contrast + offset;\n"
209                 "       rgba.rgb = yuv_to_rgb_matrix * rgba.rgb;\n"
210                 "       gl_FragColor = rgba;\n"
211                 "}\n";
212
213         get_output()->to_texture();
214         get_output()->enable_opengl();
215         int need_matrix = 0;
216         const char *brightness_frag = BC_CModels::is_yuv(get_output()->get_color_model()) ?
217                 (config.luma ? (need_matrix = 0, brightness_yuvluma_frag) : brightness_yuv_frag) :
218                 (config.luma ? (need_matrix = 1, brightness_rgbluma_frag) : brightness_rgb_frag) ;
219
220         unsigned int shader_id = VFrame::make_shader(0, brightness_frag, 0);
221         if( shader_id > 0 ) {
222                 glUseProgram(shader_id);
223                 glUniform1i(glGetUniformLocation(shader_id, "tex"), 0);
224                 glUniform1f(glGetUniformLocation(shader_id, "brightness"), config.brightness / 100);
225                 float contrast = (config.contrast < 0) ?
226                         (config.contrast + 100) / 100 :
227                         (config.contrast + 25) / 25;
228                 glUniform1f(glGetUniformLocation(shader_id, "contrast"), contrast);
229                 float offset = 0.5 - contrast / 2;
230                 glUniform1f(glGetUniformLocation(shader_id, "offset"), offset);
231                 if( need_matrix ) {
232                         BC_GL_MATRIX(shader_id, yuv_to_rgb_matrix);
233                         BC_GL_MATRIX(shader_id, rgb_to_yuv_matrix);
234                 }
235         }
236
237         get_output()->init_screen();
238         get_output()->bind_texture(0);
239
240
241
242         get_output()->draw_texture();
243         glUseProgram(0);
244         get_output()->set_opengl_state(VFrame::SCREEN);
245 //printf("BrightnessMain::handle_opengl 100 %x\n", glGetError());
246 #endif
247         return 0;
248 }
249
250
251 void BrightnessMain::update_gui()
252 {
253         if(thread)
254         {
255                 if(load_configuration())
256                 {
257                         ((BrightnessWindow*)thread->window)->lock_window("BrightnessMain::update_gui");
258                         ((BrightnessWindow*)thread->window)->brightness_text->update(config.brightness);
259                         ((BrightnessWindow*)thread->window)->brightness_slider->update(config.brightness);
260                         ((BrightnessWindow*)thread->window)->contrast_text->update(config.contrast);
261                         ((BrightnessWindow*)thread->window)->contrast_slider->update(config.contrast);
262                         ((BrightnessWindow*)thread->window)->luma->update(config.luma);
263                         ((BrightnessWindow*)thread->window)->unlock_window();
264                 }
265         }
266 }
267
268
269 void BrightnessMain::save_data(KeyFrame *keyframe)
270 {
271         FileXML output;
272
273 // cause data to be stored directly in text
274         output.set_shared_output(keyframe->xbuf);
275         output.tag.set_title("BRIGHTNESS");
276         output.tag.set_property("BRIGHTNESS", config.brightness);
277         output.tag.set_property("CONTRAST",  config.contrast);
278         output.tag.set_property("LUMA",  config.luma);
279         output.append_tag();
280         output.tag.set_title("/BRIGHTNESS");
281         output.append_tag();
282         output.append_newline();
283         output.terminate_string();
284 }
285
286 void BrightnessMain::read_data(KeyFrame *keyframe)
287 {
288         FileXML input;
289
290         input.set_shared_input(keyframe->xbuf);
291
292         int result = 0;
293
294         while(!result)
295         {
296                 result = input.read_tag();
297
298                 if(!result)
299                 {
300                         if(input.tag.title_is("BRIGHTNESS"))
301                         {
302                                 config.brightness = input.tag.get_property("BRIGHTNESS", config.brightness);
303                                 config.contrast = input.tag.get_property("CONTRAST", config.contrast);
304                                 config.luma = input.tag.get_property("LUMA", config.luma);
305                         }
306                 }
307         }
308 }
309
310
311
312
313
314
315
316
317
318
319
320
321
322 BrightnessPackage::BrightnessPackage()
323  : LoadPackage()
324 {
325 }
326
327
328
329
330 BrightnessUnit::BrightnessUnit(BrightnessEngine *server, BrightnessMain *plugin)
331  : LoadClient(server)
332 {
333         this->plugin = plugin;
334 }
335
336 BrightnessUnit::~BrightnessUnit()
337 {
338 }
339
340 void BrightnessUnit::process_package(LoadPackage *package)
341 {
342         BrightnessPackage *pkg = (BrightnessPackage*)package;
343
344
345         VFrame *output = plugin->output;
346         VFrame *input = plugin->input;
347
348
349
350
351
352 #define DO_BRIGHTNESS(max, type, components, is_yuv) \
353 { \
354         type **input_rows = (type**)input->get_rows(); \
355         type **output_rows = (type**)output->get_rows(); \
356         int row1 = pkg->row1; \
357         int row2 = pkg->row2; \
358         int width = output->get_w(); \
359         int r, g, b; \
360  \
361         if(!EQUIV(plugin->config.brightness, 0)) \
362         { \
363                 int offset = (int)(plugin->config.brightness / 100 * max); \
364 /*printf("DO_BRIGHTNESS offset=%d\n", offset);*/ \
365  \
366                 for(int i = row1; i < row2; i++) \
367                 { \
368                         type *input_row = input_rows[i]; \
369                         type *output_row = output_rows[i]; \
370  \
371                         for(int j = 0; j < width; j++) \
372                         { \
373                                 r = input_row[j * components] + offset; \
374  \
375                                 if(!is_yuv) \
376                                 { \
377                                         g = input_row[j * components + 1] + offset; \
378                                         b = input_row[j * components + 2] + offset; \
379                                 } \
380  \
381                                 CLAMP(r, 0, max); \
382                                 if(!is_yuv) \
383                                 { \
384                                         CLAMP(g, 0, max); \
385                                         CLAMP(b, 0, max); \
386                                 } \
387  \
388                                 output_row[j * components] = r; \
389  \
390                                 if(!is_yuv) \
391                                 { \
392                                         output_row[j * components + 1] = g; \
393                                         output_row[j * components + 2] = b; \
394                                 } \
395                                 else \
396                                 { \
397                                         output_row[j * components + 1] = input_row[j * components + 1]; \
398                                         output_row[j * components + 2] = input_row[j * components + 2]; \
399                                 } \
400  \
401                                 if(components == 4)  \
402                                         output_row[j * components + 3] = input_row[j * components + 3]; \
403                         } \
404                 } \
405  \
406 /* Data to be processed is now in the output buffer */ \
407                 input_rows = output_rows; \
408         } \
409  \
410         if(!EQUIV(plugin->config.contrast, 0)) \
411         { \
412                 float contrast = (plugin->config.contrast < 0) ?  \
413                         (plugin->config.contrast + 100) / 100 :  \
414                         (plugin->config.contrast + 25) / 25; \
415 /*printf("DO_BRIGHTNESS contrast=%f\n", contrast);*/ \
416  \
417                 int scalar = (int)(contrast * 0x100); \
418                 int offset = (max << 8) / 2 - max * scalar / 2; \
419                 int y, u, v; \
420  \
421                 for(int i = row1; i < row2; i++) \
422                 { \
423                         type *input_row = input_rows[i]; \
424                         type *output_row = output_rows[i]; \
425  \
426                         if(plugin->config.luma) \
427                         { \
428                                 for(int j = 0; j < width; j++) \
429                                 { \
430                                         if(is_yuv) \
431                                         { \
432                                                 y = input_row[j * components]; \
433                                         } \
434                                         else \
435                                         { \
436                                                 r = input_row[j * components]; \
437                                                 g = input_row[j * components + 1]; \
438                                                 b = input_row[j * components + 2]; \
439                                                 if(max == 0xff) \
440                                                 { \
441                                                         YUV::yuv.rgb_to_yuv_8( \
442                                                                 r,  \
443                                                                 g,  \
444                                                                 b,  \
445                                                                 y,  \
446                                                                 u,  \
447                                                                 v); \
448                                                 } \
449                                                 else \
450                                                 { \
451                                                         YUV::yuv.rgb_to_yuv_16( \
452                                                                 r,  \
453                                                                 g,  \
454                                                                 b,  \
455                                                                 y,  \
456                                                                 u,  \
457                                                                 v); \
458                                                 } \
459          \
460                                         } \
461          \
462                                         y = (y * scalar + offset) >> 8; \
463                                         CLAMP(y, 0, max); \
464          \
465          \
466                                         if(is_yuv) \
467                                         { \
468                                                 output_row[j * components] = y; \
469                                                 output_row[j * components + 1] = input_row[j * components + 1]; \
470                                                 output_row[j * components + 2] = input_row[j * components + 2]; \
471                                         } \
472                                         else \
473                                         { \
474                                                 if(max == 0xff) \
475                                                 { \
476                                                         YUV::yuv.yuv_to_rgb_8( \
477                                                                 r,  \
478                                                                 g,  \
479                                                                 b,  \
480                                                                 y,  \
481                                                                 u,  \
482                                                                 v); \
483                                                 } \
484                                                 else \
485                                                 { \
486                                                         YUV::yuv.yuv_to_rgb_16( \
487                                                                 r,  \
488                                                                 g,  \
489                                                                 b,  \
490                                                                 y,  \
491                                                                 u,  \
492                                                                 v); \
493                                                 } \
494                                                 input_row[j * components] = r; \
495                                                 input_row[j * components + 1] = g; \
496                                                 input_row[j * components + 2] = b; \
497                                         } \
498          \
499                                         if(components == 4)  \
500                                                 output_row[j * components + 3] = input_row[j * components + 3]; \
501                                 } \
502                         } \
503                         else \
504                         { \
505                                 for(int j = 0; j < width; j++) \
506                                 { \
507                                         r = input_row[j * components]; \
508                                         g = input_row[j * components + 1]; \
509                                         b = input_row[j * components + 2]; \
510  \
511                                         r = (r * scalar + offset) >> 8; \
512                                         g = (g * scalar + offset) >> 8; \
513                                         b = (b * scalar + offset) >> 8; \
514  \
515                                         CLAMP(r, 0, max); \
516                                         CLAMP(g, 0, max); \
517                                         CLAMP(b, 0, max); \
518  \
519                                         output_row[j * components] = r; \
520                                         output_row[j * components + 1] = g; \
521                                         output_row[j * components + 2] = b; \
522  \
523                                         if(components == 4)  \
524                                                 output_row[j * components + 3] = input_row[j * components + 3]; \
525                                 } \
526                         } \
527                 } \
528         } \
529 }
530
531
532
533 #define DO_BRIGHTNESS_F(components) \
534 { \
535         float **input_rows = (float**)input->get_rows(); \
536         float **output_rows = (float**)output->get_rows(); \
537         int row1 = pkg->row1; \
538         int row2 = pkg->row2; \
539         int width = output->get_w(); \
540         float r, g, b; \
541  \
542         if(!EQUIV(plugin->config.brightness, 0)) \
543         { \
544                 float offset = plugin->config.brightness / 100; \
545  \
546                 for(int i = row1; i < row2; i++) \
547                 { \
548                         float *input_row = input_rows[i]; \
549                         float *output_row = output_rows[i]; \
550  \
551                         for(int j = 0; j < width; j++) \
552                         { \
553                                 r = input_row[j * components] + offset; \
554                                 g = input_row[j * components + 1] + offset; \
555                                 b = input_row[j * components + 2] + offset; \
556  \
557                                 output_row[j * components] = r; \
558                                 output_row[j * components + 1] = g; \
559                                 output_row[j * components + 2] = b; \
560                                 if(components == 4)  \
561                                         output_row[j * components + 3] = input_row[j * components + 3]; \
562                         } \
563                 } \
564  \
565 /* Data to be processed is now in the output buffer */ \
566                 input_rows = output_rows; \
567         } \
568  \
569         if(!EQUIV(plugin->config.contrast, 0)) \
570         { \
571                 float contrast = (plugin->config.contrast < 0) ?  \
572                         (plugin->config.contrast + 100) / 100 :  \
573                         (plugin->config.contrast + 25) / 25; \
574  \
575 /* Shift black level down so shadows get darker instead of lighter */ \
576                 float offset = 0.5 - contrast / 2; \
577                 float y, u, v; \
578  \
579                 for(int i = row1; i < row2; i++) \
580                 { \
581                         float *input_row = input_rows[i]; \
582                         float *output_row = output_rows[i]; \
583  \
584                         if(plugin->config.luma) \
585                         { \
586                                 for(int j = 0; j < width; j++) \
587                                 { \
588                                         r = input_row[j * components]; \
589                                         g = input_row[j * components + 1]; \
590                                         b = input_row[j * components + 2]; \
591                                         YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v); \
592                                         y = y * contrast + offset; \
593                                         YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v); \
594                                         input_row[j * components] = r; \
595                                         input_row[j * components + 1] = g; \
596                                         input_row[j * components + 2] = b; \
597  \
598                                         if(components == 4)  \
599                                                 output_row[j * components + 3] = input_row[j * components + 3]; \
600                                 } \
601                         } \
602                         else \
603                         { \
604                                 for(int j = 0; j < width; j++) \
605                                 { \
606                                         r = input_row[j * components]; \
607                                         g = input_row[j * components + 1]; \
608                                         b = input_row[j * components + 2]; \
609  \
610                                         r = r * contrast + offset; \
611                                         g = g * contrast + offset; \
612                                         b = b * contrast + offset; \
613  \
614                                         output_row[j * components] = r; \
615                                         output_row[j * components + 1] = g; \
616                                         output_row[j * components + 2] = b; \
617  \
618                                         if(components == 4)  \
619                                                 output_row[j * components + 3] = input_row[j * components + 3]; \
620                                 } \
621                         } \
622                 } \
623         } \
624 }
625
626
627         switch(input->get_color_model())
628         {
629                 case BC_RGB888:
630                         DO_BRIGHTNESS(0xff, unsigned char, 3, 0)
631                         break;
632
633                 case BC_RGB_FLOAT:
634                         DO_BRIGHTNESS_F(3)
635                         break;
636
637                 case BC_YUV888:
638                         DO_BRIGHTNESS(0xff, unsigned char, 3, 1)
639                         break;
640
641                 case BC_RGBA8888:
642                         DO_BRIGHTNESS(0xff, unsigned char, 4, 0)
643                         break;
644
645                 case BC_RGBA_FLOAT:
646                         DO_BRIGHTNESS_F(4)
647                         break;
648
649                 case BC_YUVA8888:
650                         DO_BRIGHTNESS(0xff, unsigned char, 4, 1)
651                         break;
652
653                 case BC_RGB161616:
654                         DO_BRIGHTNESS(0xffff, uint16_t, 3, 0)
655                         break;
656
657                 case BC_YUV161616:
658                         DO_BRIGHTNESS(0xffff, uint16_t, 3, 1)
659                         break;
660
661                 case BC_RGBA16161616:
662                         DO_BRIGHTNESS(0xffff, uint16_t, 4, 0)
663                         break;
664
665                 case BC_YUVA16161616:
666                         DO_BRIGHTNESS(0xffff, uint16_t, 4, 1)
667                         break;
668         }
669
670
671
672
673
674
675
676
677
678 }
679
680
681
682
683
684
685 BrightnessEngine::BrightnessEngine(BrightnessMain *plugin, int cpus)
686  : LoadServer(cpus, cpus)
687 {
688         this->plugin = plugin;
689 }
690
691 BrightnessEngine::~BrightnessEngine()
692 {
693 }
694
695
696 void BrightnessEngine::init_packages()
697 {
698         for(int i = 0; i < LoadServer::get_total_packages(); i++)
699         {
700                 BrightnessPackage *package = (BrightnessPackage*)LoadServer::get_package(i);
701                 package->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
702                 package->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
703         }
704 }
705
706 LoadClient* BrightnessEngine::new_client()
707 {
708         return new BrightnessUnit(this, plugin);
709 }
710
711 LoadPackage* BrightnessEngine::new_package()
712 {
713         return new BrightnessPackage;
714 }
715
716
717
718
719