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