4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
24 #include "brightness.h"
33 REGISTER_PLUGIN(BrightnessMain)
37 BrightnessConfig::BrightnessConfig()
44 int BrightnessConfig::equivalent(BrightnessConfig &that)
46 return (brightness == that.brightness &&
47 contrast == that.contrast &&
51 void BrightnessConfig::copy_from(BrightnessConfig &that)
53 brightness = that.brightness;
54 contrast = that.contrast;
58 void BrightnessConfig::interpolate(BrightnessConfig &prev,
59 BrightnessConfig &next,
62 int64_t current_frame)
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);
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);
80 BrightnessMain::BrightnessMain(PluginServer *server)
81 : PluginVClient(server)
88 BrightnessMain::~BrightnessMain()
91 if(engine) delete engine;
94 const char* BrightnessMain::plugin_title() { return _("Brightness/Contrast"); }
95 int BrightnessMain::is_realtime() { return 1; }
97 NEW_WINDOW_MACRO(BrightnessMain, BrightnessWindow)
98 LOAD_CONFIGURATION_MACRO(BrightnessMain, BrightnessConfig)
100 int BrightnessMain::process_buffer(VFrame *frame,
101 int64_t start_position,
104 load_configuration();
123 if(!engine) engine = new BrightnessEngine(this, PluginClient::smp + 1);
126 this->output = frame;
128 if(!EQUIV(config.brightness, 0) || !EQUIV(config.contrast, 0))
130 engine->process_packages();
136 int BrightnessMain::handle_opengl()
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"
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"
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"
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"
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"
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"
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"
187 " const mat3 yuv_to_rgb_matrix = mat3(\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"
203 get_output()->to_texture();
204 get_output()->enable_opengl();
206 unsigned int shader_id = 0;
207 switch(get_output()->get_color_model())
212 shader_id = VFrame::make_shader(0,
213 brightness_yuvluma_frag,
216 shader_id = VFrame::make_shader(0,
222 shader_id = VFrame::make_shader(0,
223 brightness_rgbluma_frag,
226 shader_id = VFrame::make_shader(0,
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);
246 get_output()->init_screen();
247 get_output()->bind_texture(0);
251 get_output()->draw_texture();
253 get_output()->set_opengl_state(VFrame::SCREEN);
254 //printf("BrightnessMain::handle_opengl 100 %x\n", glGetError());
260 void BrightnessMain::update_gui()
264 if(load_configuration())
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();
276 void BrightnessMain::save_data(KeyFrame *keyframe)
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);
287 output.tag.set_title("/BRIGHTNESS");
289 output.append_newline();
290 output.terminate_string();
293 void BrightnessMain::read_data(KeyFrame *keyframe)
297 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
303 result = input.read_tag();
307 if(input.tag.title_is("BRIGHTNESS"))
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);
329 BrightnessPackage::BrightnessPackage()
337 BrightnessUnit::BrightnessUnit(BrightnessEngine *server, BrightnessMain *plugin)
340 this->plugin = plugin;
343 BrightnessUnit::~BrightnessUnit()
347 void BrightnessUnit::process_package(LoadPackage *package)
349 BrightnessPackage *pkg = (BrightnessPackage*)package;
352 VFrame *output = plugin->output;
353 VFrame *input = plugin->input;
359 #define DO_BRIGHTNESS(max, type, components, is_yuv) \
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(); \
368 if(!EQUIV(plugin->config.brightness, 0)) \
370 int offset = (int)(plugin->config.brightness / 100 * max); \
371 /*printf("DO_BRIGHTNESS offset=%d\n", offset);*/ \
373 for(int i = row1; i < row2; i++) \
375 type *input_row = input_rows[i]; \
376 type *output_row = output_rows[i]; \
378 for(int j = 0; j < width; j++) \
380 r = input_row[j * components] + offset; \
384 g = input_row[j * components + 1] + offset; \
385 b = input_row[j * components + 2] + offset; \
395 output_row[j * components] = r; \
399 output_row[j * components + 1] = g; \
400 output_row[j * components + 2] = b; \
404 output_row[j * components + 1] = input_row[j * components + 1]; \
405 output_row[j * components + 2] = input_row[j * components + 2]; \
408 if(components == 4) \
409 output_row[j * components + 3] = input_row[j * components + 3]; \
413 /* Data to be processed is now in the output buffer */ \
414 input_rows = output_rows; \
417 if(!EQUIV(plugin->config.contrast, 0)) \
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);*/ \
424 int scalar = (int)(contrast * 0x100); \
425 int offset = (max << 8) / 2 - max * scalar / 2; \
428 for(int i = row1; i < row2; i++) \
430 type *input_row = input_rows[i]; \
431 type *output_row = output_rows[i]; \
433 if(plugin->config.luma) \
435 for(int j = 0; j < width; j++) \
439 y = input_row[j * components]; \
443 r = input_row[j * components]; \
444 g = input_row[j * components + 1]; \
445 b = input_row[j * components + 2]; \
448 YUV::yuv.rgb_to_yuv_8( \
458 YUV::yuv.rgb_to_yuv_16( \
469 y = (y * scalar + offset) >> 8; \
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]; \
483 YUV::yuv.yuv_to_rgb_8( \
493 YUV::yuv.yuv_to_rgb_16( \
501 input_row[j * components] = r; \
502 input_row[j * components + 1] = g; \
503 input_row[j * components + 2] = b; \
506 if(components == 4) \
507 output_row[j * components + 3] = input_row[j * components + 3]; \
512 for(int j = 0; j < width; j++) \
514 r = input_row[j * components]; \
515 g = input_row[j * components + 1]; \
516 b = input_row[j * components + 2]; \
518 r = (r * scalar + offset) >> 8; \
519 g = (g * scalar + offset) >> 8; \
520 b = (b * scalar + offset) >> 8; \
526 output_row[j * components] = r; \
527 output_row[j * components + 1] = g; \
528 output_row[j * components + 2] = b; \
530 if(components == 4) \
531 output_row[j * components + 3] = input_row[j * components + 3]; \
540 #define DO_BRIGHTNESS_F(components) \
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(); \
549 if(!EQUIV(plugin->config.brightness, 0)) \
551 float offset = plugin->config.brightness / 100; \
553 for(int i = row1; i < row2; i++) \
555 float *input_row = input_rows[i]; \
556 float *output_row = output_rows[i]; \
558 for(int j = 0; j < width; j++) \
560 r = input_row[j * components] + offset; \
561 g = input_row[j * components + 1] + offset; \
562 b = input_row[j * components + 2] + offset; \
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]; \
572 /* Data to be processed is now in the output buffer */ \
573 input_rows = output_rows; \
576 if(!EQUIV(plugin->config.contrast, 0)) \
578 float contrast = (plugin->config.contrast < 0) ? \
579 (plugin->config.contrast + 100) / 100 : \
580 (plugin->config.contrast + 25) / 25; \
582 /* Shift black level down so shadows get darker instead of lighter */ \
583 float offset = 0.5 - contrast / 2; \
586 for(int i = row1; i < row2; i++) \
588 float *input_row = input_rows[i]; \
589 float *output_row = output_rows[i]; \
591 if(plugin->config.luma) \
593 for(int j = 0; j < width; j++) \
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; \
605 if(components == 4) \
606 output_row[j * components + 3] = input_row[j * components + 3]; \
611 for(int j = 0; j < width; j++) \
613 r = input_row[j * components]; \
614 g = input_row[j * components + 1]; \
615 b = input_row[j * components + 2]; \
617 r = r * contrast + offset; \
618 g = g * contrast + offset; \
619 b = b * contrast + offset; \
621 output_row[j * components] = r; \
622 output_row[j * components + 1] = g; \
623 output_row[j * components + 2] = b; \
625 if(components == 4) \
626 output_row[j * components + 3] = input_row[j * components + 3]; \
634 switch(input->get_color_model())
637 DO_BRIGHTNESS(0xff, unsigned char, 3, 0)
645 DO_BRIGHTNESS(0xff, unsigned char, 3, 1)
649 DO_BRIGHTNESS(0xff, unsigned char, 4, 0)
657 DO_BRIGHTNESS(0xff, unsigned char, 4, 1)
661 DO_BRIGHTNESS(0xffff, uint16_t, 3, 0)
665 DO_BRIGHTNESS(0xffff, uint16_t, 3, 1)
668 case BC_RGBA16161616:
669 DO_BRIGHTNESS(0xffff, uint16_t, 4, 0)
672 case BC_YUVA16161616:
673 DO_BRIGHTNESS(0xffff, uint16_t, 4, 1)
692 BrightnessEngine::BrightnessEngine(BrightnessMain *plugin, int cpus)
693 : LoadServer(cpus, cpus)
695 this->plugin = plugin;
698 BrightnessEngine::~BrightnessEngine()
703 void BrightnessEngine::init_packages()
705 for(int i = 0; i < LoadServer::get_total_packages(); i++)
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();
713 LoadClient* BrightnessEngine::new_client()
715 return new BrightnessUnit(this, plugin);
718 LoadPackage* BrightnessEngine::new_package()
720 return new BrightnessPackage;