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
22 #include "brightness.h"
28 #include "playback3d.h"
35 REGISTER_PLUGIN(BrightnessMain)
38 BrightnessConfig::BrightnessConfig()
43 void BrightnessConfig::reset(int clear)
46 case RESET_CONTRAST : contrast = 0;
48 case RESET_BRIGHTNESS : brightness = 0;
59 int BrightnessConfig::equivalent(BrightnessConfig &that)
61 return (brightness == that.brightness &&
62 contrast == that.contrast &&
66 void BrightnessConfig::copy_from(BrightnessConfig &that)
68 brightness = that.brightness;
69 contrast = that.contrast;
73 void BrightnessConfig::interpolate(BrightnessConfig &prev,
74 BrightnessConfig &next,
77 int64_t current_frame)
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);
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);
95 BrightnessMain::BrightnessMain(PluginServer *server)
96 : PluginVClient(server)
103 BrightnessMain::~BrightnessMain()
106 if(engine) delete engine;
109 const char* BrightnessMain::plugin_title() { return N_("Brightness/Contrast"); }
110 int BrightnessMain::is_realtime() { return 1; }
112 NEW_WINDOW_MACRO(BrightnessMain, BrightnessWindow)
113 LOAD_CONFIGURATION_MACRO(BrightnessMain, BrightnessConfig)
115 int BrightnessMain::process_buffer(VFrame *frame,
116 int64_t start_position,
119 load_configuration();
138 if(!engine) engine = new BrightnessEngine(this, PluginClient::smp + 1);
141 this->output = frame;
143 if(!EQUIV(config.brightness, 0) || !EQUIV(config.contrast, 0))
145 engine->process_packages();
151 int BrightnessMain::handle_opengl()
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"
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"
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"
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"
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"
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"
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"
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"
213 get_output()->to_texture();
214 get_output()->enable_opengl();
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) ;
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);
232 BC_GL_MATRIX(shader_id, yuv_to_rgb_matrix);
233 BC_GL_MATRIX(shader_id, rgb_to_yuv_matrix);
237 get_output()->init_screen();
238 get_output()->bind_texture(0);
242 get_output()->draw_texture();
244 get_output()->set_opengl_state(VFrame::SCREEN);
245 //printf("BrightnessMain::handle_opengl 100 %x\n", glGetError());
251 void BrightnessMain::update_gui()
255 if(load_configuration())
257 ((BrightnessWindow*)thread->window)->lock_window("BrightnessMain::update_gui");
258 ((BrightnessWindow*)thread->window)->brightness_text->update(config.brightness);
259 ((BrightnessWindow*)thread->window)->brightness_slider->update(config.brightness);
260 ((BrightnessWindow*)thread->window)->contrast_text->update(config.contrast);
261 ((BrightnessWindow*)thread->window)->contrast_slider->update(config.contrast);
262 ((BrightnessWindow*)thread->window)->luma->update(config.luma);
263 ((BrightnessWindow*)thread->window)->unlock_window();
269 void BrightnessMain::save_data(KeyFrame *keyframe)
273 // cause data to be stored directly in text
274 output.set_shared_output(keyframe->xbuf);
275 output.tag.set_title("BRIGHTNESS");
276 output.tag.set_property("BRIGHTNESS", config.brightness);
277 output.tag.set_property("CONTRAST", config.contrast);
278 output.tag.set_property("LUMA", config.luma);
280 output.tag.set_title("/BRIGHTNESS");
282 output.append_newline();
283 output.terminate_string();
286 void BrightnessMain::read_data(KeyFrame *keyframe)
290 input.set_shared_input(keyframe->xbuf);
296 result = input.read_tag();
300 if(input.tag.title_is("BRIGHTNESS"))
302 config.brightness = input.tag.get_property("BRIGHTNESS", config.brightness);
303 config.contrast = input.tag.get_property("CONTRAST", config.contrast);
304 config.luma = input.tag.get_property("LUMA", config.luma);
322 BrightnessPackage::BrightnessPackage()
330 BrightnessUnit::BrightnessUnit(BrightnessEngine *server, BrightnessMain *plugin)
333 this->plugin = plugin;
336 BrightnessUnit::~BrightnessUnit()
340 void BrightnessUnit::process_package(LoadPackage *package)
342 BrightnessPackage *pkg = (BrightnessPackage*)package;
345 VFrame *output = plugin->output;
346 VFrame *input = plugin->input;
352 #define DO_BRIGHTNESS(max, type, components, is_yuv) \
354 type **input_rows = (type**)input->get_rows(); \
355 type **output_rows = (type**)output->get_rows(); \
356 int row1 = pkg->row1; \
357 int row2 = pkg->row2; \
358 int width = output->get_w(); \
361 if(!EQUIV(plugin->config.brightness, 0)) \
363 int offset = (int)(plugin->config.brightness / 100 * max); \
364 /*printf("DO_BRIGHTNESS offset=%d\n", offset);*/ \
366 for(int i = row1; i < row2; i++) \
368 type *input_row = input_rows[i]; \
369 type *output_row = output_rows[i]; \
371 for(int j = 0; j < width; j++) \
373 r = input_row[j * components] + offset; \
377 g = input_row[j * components + 1] + offset; \
378 b = input_row[j * components + 2] + offset; \
388 output_row[j * components] = r; \
392 output_row[j * components + 1] = g; \
393 output_row[j * components + 2] = b; \
397 output_row[j * components + 1] = input_row[j * components + 1]; \
398 output_row[j * components + 2] = input_row[j * components + 2]; \
401 if(components == 4) \
402 output_row[j * components + 3] = input_row[j * components + 3]; \
406 /* Data to be processed is now in the output buffer */ \
407 input_rows = output_rows; \
410 if(!EQUIV(plugin->config.contrast, 0)) \
412 float contrast = (plugin->config.contrast < 0) ? \
413 (plugin->config.contrast + 100) / 100 : \
414 (plugin->config.contrast + 25) / 25; \
415 /*printf("DO_BRIGHTNESS contrast=%f\n", contrast);*/ \
417 int scalar = (int)(contrast * 0x100); \
418 int offset = (max << 8) / 2 - max * scalar / 2; \
421 for(int i = row1; i < row2; i++) \
423 type *input_row = input_rows[i]; \
424 type *output_row = output_rows[i]; \
426 if(plugin->config.luma) \
428 for(int j = 0; j < width; j++) \
432 y = input_row[j * components]; \
436 r = input_row[j * components]; \
437 g = input_row[j * components + 1]; \
438 b = input_row[j * components + 2]; \
441 YUV::yuv.rgb_to_yuv_8( \
451 YUV::yuv.rgb_to_yuv_16( \
462 y = (y * scalar + offset) >> 8; \
468 output_row[j * components] = y; \
469 output_row[j * components + 1] = input_row[j * components + 1]; \
470 output_row[j * components + 2] = input_row[j * components + 2]; \
476 YUV::yuv.yuv_to_rgb_8( \
486 YUV::yuv.yuv_to_rgb_16( \
494 input_row[j * components] = r; \
495 input_row[j * components + 1] = g; \
496 input_row[j * components + 2] = b; \
499 if(components == 4) \
500 output_row[j * components + 3] = input_row[j * components + 3]; \
505 for(int j = 0; j < width; j++) \
507 r = input_row[j * components]; \
508 g = input_row[j * components + 1]; \
509 b = input_row[j * components + 2]; \
511 r = (r * scalar + offset) >> 8; \
512 g = (g * scalar + offset) >> 8; \
513 b = (b * scalar + offset) >> 8; \
519 output_row[j * components] = r; \
520 output_row[j * components + 1] = g; \
521 output_row[j * components + 2] = b; \
523 if(components == 4) \
524 output_row[j * components + 3] = input_row[j * components + 3]; \
533 #define DO_BRIGHTNESS_F(components) \
535 float **input_rows = (float**)input->get_rows(); \
536 float **output_rows = (float**)output->get_rows(); \
537 int row1 = pkg->row1; \
538 int row2 = pkg->row2; \
539 int width = output->get_w(); \
542 if(!EQUIV(plugin->config.brightness, 0)) \
544 float offset = plugin->config.brightness / 100; \
546 for(int i = row1; i < row2; i++) \
548 float *input_row = input_rows[i]; \
549 float *output_row = output_rows[i]; \
551 for(int j = 0; j < width; j++) \
553 r = input_row[j * components] + offset; \
554 g = input_row[j * components + 1] + offset; \
555 b = input_row[j * components + 2] + offset; \
557 output_row[j * components] = r; \
558 output_row[j * components + 1] = g; \
559 output_row[j * components + 2] = b; \
560 if(components == 4) \
561 output_row[j * components + 3] = input_row[j * components + 3]; \
565 /* Data to be processed is now in the output buffer */ \
566 input_rows = output_rows; \
569 if(!EQUIV(plugin->config.contrast, 0)) \
571 float contrast = (plugin->config.contrast < 0) ? \
572 (plugin->config.contrast + 100) / 100 : \
573 (plugin->config.contrast + 25) / 25; \
575 /* Shift black level down so shadows get darker instead of lighter */ \
576 float offset = 0.5 - contrast / 2; \
579 for(int i = row1; i < row2; i++) \
581 float *input_row = input_rows[i]; \
582 float *output_row = output_rows[i]; \
584 if(plugin->config.luma) \
586 for(int j = 0; j < width; j++) \
588 r = input_row[j * components]; \
589 g = input_row[j * components + 1]; \
590 b = input_row[j * components + 2]; \
591 YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v); \
592 y = y * contrast + offset; \
593 YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v); \
594 input_row[j * components] = r; \
595 input_row[j * components + 1] = g; \
596 input_row[j * components + 2] = b; \
598 if(components == 4) \
599 output_row[j * components + 3] = input_row[j * components + 3]; \
604 for(int j = 0; j < width; j++) \
606 r = input_row[j * components]; \
607 g = input_row[j * components + 1]; \
608 b = input_row[j * components + 2]; \
610 r = r * contrast + offset; \
611 g = g * contrast + offset; \
612 b = b * contrast + offset; \
614 output_row[j * components] = r; \
615 output_row[j * components + 1] = g; \
616 output_row[j * components + 2] = b; \
618 if(components == 4) \
619 output_row[j * components + 3] = input_row[j * components + 3]; \
627 switch(input->get_color_model())
630 DO_BRIGHTNESS(0xff, unsigned char, 3, 0)
638 DO_BRIGHTNESS(0xff, unsigned char, 3, 1)
642 DO_BRIGHTNESS(0xff, unsigned char, 4, 0)
650 DO_BRIGHTNESS(0xff, unsigned char, 4, 1)
654 DO_BRIGHTNESS(0xffff, uint16_t, 3, 0)
658 DO_BRIGHTNESS(0xffff, uint16_t, 3, 1)
661 case BC_RGBA16161616:
662 DO_BRIGHTNESS(0xffff, uint16_t, 4, 0)
665 case BC_YUVA16161616:
666 DO_BRIGHTNESS(0xffff, uint16_t, 4, 1)
685 BrightnessEngine::BrightnessEngine(BrightnessMain *plugin, int cpus)
686 : LoadServer(cpus, cpus)
688 this->plugin = plugin;
691 BrightnessEngine::~BrightnessEngine()
696 void BrightnessEngine::init_packages()
698 for(int i = 0; i < LoadServer::get_total_packages(); i++)
700 BrightnessPackage *package = (BrightnessPackage*)LoadServer::get_package(i);
701 package->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
702 package->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
706 LoadClient* BrightnessEngine::new_client()
708 return new BrightnessUnit(this, plugin);
711 LoadPackage* BrightnessEngine::new_package()
713 return new BrightnessPackage;