no longer need ffmpeg patch0 which was for Termux
[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->update(config.brightness);
259                         ((BrightnessWindow*)thread->window)->contrast->update(config.contrast);
260                         ((BrightnessWindow*)thread->window)->luma->update(config.luma);
261                         ((BrightnessWindow*)thread->window)->unlock_window();
262                 }
263         }
264 }
265
266
267 void BrightnessMain::save_data(KeyFrame *keyframe)
268 {
269         FileXML output;
270
271 // cause data to be stored directly in text
272         output.set_shared_output(keyframe->xbuf);
273         output.tag.set_title("BRIGHTNESS");
274         output.tag.set_property("BRIGHTNESS", config.brightness);
275         output.tag.set_property("CONTRAST",  config.contrast);
276         output.tag.set_property("LUMA",  config.luma);
277         output.append_tag();
278         output.tag.set_title("/BRIGHTNESS");
279         output.append_tag();
280         output.append_newline();
281         output.terminate_string();
282 }
283
284 void BrightnessMain::read_data(KeyFrame *keyframe)
285 {
286         FileXML input;
287
288         input.set_shared_input(keyframe->xbuf);
289
290         int result = 0;
291
292         while(!result)
293         {
294                 result = input.read_tag();
295
296                 if(!result)
297                 {
298                         if(input.tag.title_is("BRIGHTNESS"))
299                         {
300                                 config.brightness = input.tag.get_property("BRIGHTNESS", config.brightness);
301                                 config.contrast = input.tag.get_property("CONTRAST", config.contrast);
302                                 config.luma = input.tag.get_property("LUMA", config.luma);
303                         }
304                 }
305         }
306 }
307
308
309
310
311
312
313
314
315
316
317
318
319
320 BrightnessPackage::BrightnessPackage()
321  : LoadPackage()
322 {
323 }
324
325
326
327
328 BrightnessUnit::BrightnessUnit(BrightnessEngine *server, BrightnessMain *plugin)
329  : LoadClient(server)
330 {
331         this->plugin = plugin;
332 }
333
334 BrightnessUnit::~BrightnessUnit()
335 {
336 }
337
338 void BrightnessUnit::process_package(LoadPackage *package)
339 {
340         BrightnessPackage *pkg = (BrightnessPackage*)package;
341
342
343         VFrame *output = plugin->output;
344         VFrame *input = plugin->input;
345
346
347
348
349
350 #define DO_BRIGHTNESS(max, type, components, is_yuv) \
351 { \
352         type **input_rows = (type**)input->get_rows(); \
353         type **output_rows = (type**)output->get_rows(); \
354         int row1 = pkg->row1; \
355         int row2 = pkg->row2; \
356         int width = output->get_w(); \
357         int r, g, b; \
358  \
359         if(!EQUIV(plugin->config.brightness, 0)) \
360         { \
361                 int offset = (int)(plugin->config.brightness / 100 * max); \
362 /*printf("DO_BRIGHTNESS offset=%d\n", offset);*/ \
363  \
364                 for(int i = row1; i < row2; i++) \
365                 { \
366                         type *input_row = input_rows[i]; \
367                         type *output_row = output_rows[i]; \
368  \
369                         for(int j = 0; j < width; j++) \
370                         { \
371                                 r = input_row[j * components] + offset; \
372  \
373                                 if(!is_yuv) \
374                                 { \
375                                         g = input_row[j * components + 1] + offset; \
376                                         b = input_row[j * components + 2] + offset; \
377                                 } \
378  \
379                                 CLAMP(r, 0, max); \
380                                 if(!is_yuv) \
381                                 { \
382                                         CLAMP(g, 0, max); \
383                                         CLAMP(b, 0, max); \
384                                 } \
385  \
386                                 output_row[j * components] = r; \
387  \
388                                 if(!is_yuv) \
389                                 { \
390                                         output_row[j * components + 1] = g; \
391                                         output_row[j * components + 2] = b; \
392                                 } \
393                                 else \
394                                 { \
395                                         output_row[j * components + 1] = input_row[j * components + 1]; \
396                                         output_row[j * components + 2] = input_row[j * components + 2]; \
397                                 } \
398  \
399                                 if(components == 4)  \
400                                         output_row[j * components + 3] = input_row[j * components + 3]; \
401                         } \
402                 } \
403  \
404 /* Data to be processed is now in the output buffer */ \
405                 input_rows = output_rows; \
406         } \
407  \
408         if(!EQUIV(plugin->config.contrast, 0)) \
409         { \
410                 float contrast = (plugin->config.contrast < 0) ?  \
411                         (plugin->config.contrast + 100) / 100 :  \
412                         (plugin->config.contrast + 25) / 25; \
413 /*printf("DO_BRIGHTNESS contrast=%f\n", contrast);*/ \
414  \
415                 int scalar = (int)(contrast * 0x100); \
416                 int offset = (max << 8) / 2 - max * scalar / 2; \
417                 int y, u, v; \
418  \
419                 for(int i = row1; i < row2; i++) \
420                 { \
421                         type *input_row = input_rows[i]; \
422                         type *output_row = output_rows[i]; \
423  \
424                         if(plugin->config.luma) \
425                         { \
426                                 for(int j = 0; j < width; j++) \
427                                 { \
428                                         if(is_yuv) \
429                                         { \
430                                                 y = input_row[j * components]; \
431                                         } \
432                                         else \
433                                         { \
434                                                 r = input_row[j * components]; \
435                                                 g = input_row[j * components + 1]; \
436                                                 b = input_row[j * components + 2]; \
437                                                 if(max == 0xff) \
438                                                 { \
439                                                         YUV::yuv.rgb_to_yuv_8( \
440                                                                 r,  \
441                                                                 g,  \
442                                                                 b,  \
443                                                                 y,  \
444                                                                 u,  \
445                                                                 v); \
446                                                 } \
447                                                 else \
448                                                 { \
449                                                         YUV::yuv.rgb_to_yuv_16( \
450                                                                 r,  \
451                                                                 g,  \
452                                                                 b,  \
453                                                                 y,  \
454                                                                 u,  \
455                                                                 v); \
456                                                 } \
457          \
458                                         } \
459          \
460                                         y = (y * scalar + offset) >> 8; \
461                                         CLAMP(y, 0, max); \
462          \
463          \
464                                         if(is_yuv) \
465                                         { \
466                                                 output_row[j * components] = y; \
467                                                 output_row[j * components + 1] = input_row[j * components + 1]; \
468                                                 output_row[j * components + 2] = input_row[j * components + 2]; \
469                                         } \
470                                         else \
471                                         { \
472                                                 if(max == 0xff) \
473                                                 { \
474                                                         YUV::yuv.yuv_to_rgb_8( \
475                                                                 r,  \
476                                                                 g,  \
477                                                                 b,  \
478                                                                 y,  \
479                                                                 u,  \
480                                                                 v); \
481                                                 } \
482                                                 else \
483                                                 { \
484                                                         YUV::yuv.yuv_to_rgb_16( \
485                                                                 r,  \
486                                                                 g,  \
487                                                                 b,  \
488                                                                 y,  \
489                                                                 u,  \
490                                                                 v); \
491                                                 } \
492                                                 input_row[j * components] = r; \
493                                                 input_row[j * components + 1] = g; \
494                                                 input_row[j * components + 2] = b; \
495                                         } \
496          \
497                                         if(components == 4)  \
498                                                 output_row[j * components + 3] = input_row[j * components + 3]; \
499                                 } \
500                         } \
501                         else \
502                         { \
503                                 for(int j = 0; j < width; j++) \
504                                 { \
505                                         r = input_row[j * components]; \
506                                         g = input_row[j * components + 1]; \
507                                         b = input_row[j * components + 2]; \
508  \
509                                         r = (r * scalar + offset) >> 8; \
510                                         g = (g * scalar + offset) >> 8; \
511                                         b = (b * scalar + offset) >> 8; \
512  \
513                                         CLAMP(r, 0, max); \
514                                         CLAMP(g, 0, max); \
515                                         CLAMP(b, 0, max); \
516  \
517                                         output_row[j * components] = r; \
518                                         output_row[j * components + 1] = g; \
519                                         output_row[j * components + 2] = b; \
520  \
521                                         if(components == 4)  \
522                                                 output_row[j * components + 3] = input_row[j * components + 3]; \
523                                 } \
524                         } \
525                 } \
526         } \
527 }
528
529
530
531 #define DO_BRIGHTNESS_F(components) \
532 { \
533         float **input_rows = (float**)input->get_rows(); \
534         float **output_rows = (float**)output->get_rows(); \
535         int row1 = pkg->row1; \
536         int row2 = pkg->row2; \
537         int width = output->get_w(); \
538         float r, g, b; \
539  \
540         if(!EQUIV(plugin->config.brightness, 0)) \
541         { \
542                 float offset = plugin->config.brightness / 100; \
543  \
544                 for(int i = row1; i < row2; i++) \
545                 { \
546                         float *input_row = input_rows[i]; \
547                         float *output_row = output_rows[i]; \
548  \
549                         for(int j = 0; j < width; j++) \
550                         { \
551                                 r = input_row[j * components] + offset; \
552                                 g = input_row[j * components + 1] + offset; \
553                                 b = input_row[j * components + 2] + offset; \
554  \
555                                 output_row[j * components] = r; \
556                                 output_row[j * components + 1] = g; \
557                                 output_row[j * components + 2] = b; \
558                                 if(components == 4)  \
559                                         output_row[j * components + 3] = input_row[j * components + 3]; \
560                         } \
561                 } \
562  \
563 /* Data to be processed is now in the output buffer */ \
564                 input_rows = output_rows; \
565         } \
566  \
567         if(!EQUIV(plugin->config.contrast, 0)) \
568         { \
569                 float contrast = (plugin->config.contrast < 0) ?  \
570                         (plugin->config.contrast + 100) / 100 :  \
571                         (plugin->config.contrast + 25) / 25; \
572  \
573 /* Shift black level down so shadows get darker instead of lighter */ \
574                 float offset = 0.5 - contrast / 2; \
575                 float y, u, v; \
576  \
577                 for(int i = row1; i < row2; i++) \
578                 { \
579                         float *input_row = input_rows[i]; \
580                         float *output_row = output_rows[i]; \
581  \
582                         if(plugin->config.luma) \
583                         { \
584                                 for(int j = 0; j < width; j++) \
585                                 { \
586                                         r = input_row[j * components]; \
587                                         g = input_row[j * components + 1]; \
588                                         b = input_row[j * components + 2]; \
589                                         YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v); \
590                                         y = y * contrast + offset; \
591                                         YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v); \
592                                         input_row[j * components] = r; \
593                                         input_row[j * components + 1] = g; \
594                                         input_row[j * components + 2] = b; \
595  \
596                                         if(components == 4)  \
597                                                 output_row[j * components + 3] = input_row[j * components + 3]; \
598                                 } \
599                         } \
600                         else \
601                         { \
602                                 for(int j = 0; j < width; j++) \
603                                 { \
604                                         r = input_row[j * components]; \
605                                         g = input_row[j * components + 1]; \
606                                         b = input_row[j * components + 2]; \
607  \
608                                         r = r * contrast + offset; \
609                                         g = g * contrast + offset; \
610                                         b = b * contrast + offset; \
611  \
612                                         output_row[j * components] = r; \
613                                         output_row[j * components + 1] = g; \
614                                         output_row[j * components + 2] = b; \
615  \
616                                         if(components == 4)  \
617                                                 output_row[j * components + 3] = input_row[j * components + 3]; \
618                                 } \
619                         } \
620                 } \
621         } \
622 }
623
624
625         switch(input->get_color_model())
626         {
627                 case BC_RGB888:
628                         DO_BRIGHTNESS(0xff, unsigned char, 3, 0)
629                         break;
630
631                 case BC_RGB_FLOAT:
632                         DO_BRIGHTNESS_F(3)
633                         break;
634
635                 case BC_YUV888:
636                         DO_BRIGHTNESS(0xff, unsigned char, 3, 1)
637                         break;
638
639                 case BC_RGBA8888:
640                         DO_BRIGHTNESS(0xff, unsigned char, 4, 0)
641                         break;
642
643                 case BC_RGBA_FLOAT:
644                         DO_BRIGHTNESS_F(4)
645                         break;
646
647                 case BC_YUVA8888:
648                         DO_BRIGHTNESS(0xff, unsigned char, 4, 1)
649                         break;
650
651                 case BC_RGB161616:
652                         DO_BRIGHTNESS(0xffff, uint16_t, 3, 0)
653                         break;
654
655                 case BC_YUV161616:
656                         DO_BRIGHTNESS(0xffff, uint16_t, 3, 1)
657                         break;
658
659                 case BC_RGBA16161616:
660                         DO_BRIGHTNESS(0xffff, uint16_t, 4, 0)
661                         break;
662
663                 case BC_YUVA16161616:
664                         DO_BRIGHTNESS(0xffff, uint16_t, 4, 1)
665                         break;
666         }
667
668
669
670
671
672
673
674
675
676 }
677
678
679
680
681
682
683 BrightnessEngine::BrightnessEngine(BrightnessMain *plugin, int cpus)
684  : LoadServer(cpus, cpus)
685 {
686         this->plugin = plugin;
687 }
688
689 BrightnessEngine::~BrightnessEngine()
690 {
691 }
692
693
694 void BrightnessEngine::init_packages()
695 {
696         for(int i = 0; i < LoadServer::get_total_packages(); i++)
697         {
698                 BrightnessPackage *package = (BrightnessPackage*)LoadServer::get_package(i);
699                 package->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
700                 package->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
701         }
702 }
703
704 LoadClient* BrightnessEngine::new_client()
705 {
706         return new BrightnessUnit(this, plugin);
707 }
708
709 LoadPackage* BrightnessEngine::new_package()
710 {
711         return new BrightnessPackage;
712 }
713
714
715
716
717