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