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