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()
51 int BrightnessConfig::equivalent(BrightnessConfig &that)
53 return (brightness == that.brightness &&
54 contrast == that.contrast &&
58 void BrightnessConfig::copy_from(BrightnessConfig &that)
60 brightness = that.brightness;
61 contrast = that.contrast;
65 void BrightnessConfig::interpolate(BrightnessConfig &prev,
66 BrightnessConfig &next,
69 int64_t current_frame)
71 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
72 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
74 this->brightness = prev.brightness * prev_scale + next.brightness * next_scale;
75 this->contrast = prev.contrast * prev_scale + next.contrast * next_scale;
76 this->luma = (int)(prev.luma * prev_scale + next.luma * next_scale);
87 BrightnessMain::BrightnessMain(PluginServer *server)
88 : PluginVClient(server)
95 BrightnessMain::~BrightnessMain()
98 if(engine) delete engine;
101 const char* BrightnessMain::plugin_title() { return N_("Brightness/Contrast"); }
102 int BrightnessMain::is_realtime() { return 1; }
104 NEW_WINDOW_MACRO(BrightnessMain, BrightnessWindow)
105 LOAD_CONFIGURATION_MACRO(BrightnessMain, BrightnessConfig)
107 int BrightnessMain::process_buffer(VFrame *frame,
108 int64_t start_position,
111 load_configuration();
130 if(!engine) engine = new BrightnessEngine(this, PluginClient::smp + 1);
133 this->output = frame;
135 if(!EQUIV(config.brightness, 0) || !EQUIV(config.contrast, 0))
137 engine->process_packages();
143 int BrightnessMain::handle_opengl()
146 static const char *brightness_yuvluma_frag =
147 "uniform sampler2D tex;\n"
148 "uniform float brightness;\n"
149 "uniform float contrast;\n"
150 "uniform float offset;\n"
153 " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n"
154 " yuva.r += brightness;\n"
155 " yuva.r = yuva.r * contrast + offset;\n"
156 " gl_FragColor = yuva;\n"
159 static const char *brightness_yuv_frag =
160 "uniform sampler2D tex;\n"
161 "uniform float brightness;\n"
162 "uniform float contrast;\n"
163 "uniform float offset;\n"
166 " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n"
167 " yuva.r += brightness;\n"
168 " yuva.rgb *= vec3(contrast, contrast, contrast);\n"
169 " yuva.rgb += vec3(offset, offset, offset);\n"
170 " gl_FragColor = yuva;\n"
173 static const char *brightness_rgb_frag =
174 "uniform sampler2D tex;\n"
175 "uniform float brightness;\n"
176 "uniform float contrast;\n"
177 "uniform float offset;\n"
180 " vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n"
181 " rgba.rgb += vec3(brightness, brightness, brightness);\n"
182 " rgba.rgb *= vec3(contrast, contrast, contrast);\n"
183 " rgba.rgb += vec3(offset, offset, offset);\n"
184 " gl_FragColor = rgba;\n"
187 static const char *brightness_rgbluma_frag =
188 "uniform sampler2D tex;\n"
189 "uniform float brightness;\n"
190 "uniform float contrast;\n"
191 "uniform float offset;\n"
192 "uniform mat3 yuv_to_rgb_matrix;\n"
193 "uniform mat3 rgb_to_yuv_matrix;\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"
205 get_output()->to_texture();
206 get_output()->enable_opengl();
208 const char *brightness_frag = BC_CModels::is_yuv(get_output()->get_color_model()) ?
209 (config.luma ? (need_matrix = 0, brightness_yuvluma_frag) : brightness_yuv_frag) :
210 (config.luma ? (need_matrix = 1, brightness_rgbluma_frag) : brightness_rgb_frag) ;
212 unsigned int shader_id = VFrame::make_shader(0, brightness_frag, 0);
213 if( shader_id > 0 ) {
214 glUseProgram(shader_id);
215 glUniform1i(glGetUniformLocation(shader_id, "tex"), 0);
216 glUniform1f(glGetUniformLocation(shader_id, "brightness"), config.brightness / 100);
217 float contrast = (config.contrast < 0) ?
218 (config.contrast + 100) / 100 :
219 (config.contrast + 25) / 25;
220 glUniform1f(glGetUniformLocation(shader_id, "contrast"), contrast);
221 float offset = 0.5 - contrast / 2;
222 glUniform1f(glGetUniformLocation(shader_id, "offset"), offset);
224 BC_GL_MATRIX(shader_id, yuv_to_rgb_matrix);
225 BC_GL_MATRIX(shader_id, rgb_to_yuv_matrix);
229 get_output()->init_screen();
230 get_output()->bind_texture(0);
234 get_output()->draw_texture();
236 get_output()->set_opengl_state(VFrame::SCREEN);
237 //printf("BrightnessMain::handle_opengl 100 %x\n", glGetError());
243 void BrightnessMain::update_gui()
247 if(load_configuration())
249 ((BrightnessWindow*)thread->window)->lock_window("BrightnessMain::update_gui");
250 ((BrightnessWindow*)thread->window)->brightness->update(config.brightness);
251 ((BrightnessWindow*)thread->window)->contrast->update(config.contrast);
252 ((BrightnessWindow*)thread->window)->luma->update(config.luma);
253 ((BrightnessWindow*)thread->window)->unlock_window();
259 void BrightnessMain::save_data(KeyFrame *keyframe)
263 // cause data to be stored directly in text
264 output.set_shared_output(keyframe->xbuf);
265 output.tag.set_title("BRIGHTNESS");
266 output.tag.set_property("BRIGHTNESS", config.brightness);
267 output.tag.set_property("CONTRAST", config.contrast);
268 output.tag.set_property("LUMA", config.luma);
270 output.tag.set_title("/BRIGHTNESS");
272 output.append_newline();
273 output.terminate_string();
276 void BrightnessMain::read_data(KeyFrame *keyframe)
280 input.set_shared_input(keyframe->xbuf);
286 result = input.read_tag();
290 if(input.tag.title_is("BRIGHTNESS"))
292 config.brightness = input.tag.get_property("BRIGHTNESS", config.brightness);
293 config.contrast = input.tag.get_property("CONTRAST", config.contrast);
294 config.luma = input.tag.get_property("LUMA", config.luma);
312 BrightnessPackage::BrightnessPackage()
320 BrightnessUnit::BrightnessUnit(BrightnessEngine *server, BrightnessMain *plugin)
323 this->plugin = plugin;
326 BrightnessUnit::~BrightnessUnit()
330 void BrightnessUnit::process_package(LoadPackage *package)
332 BrightnessPackage *pkg = (BrightnessPackage*)package;
335 VFrame *output = plugin->output;
336 VFrame *input = plugin->input;
342 #define DO_BRIGHTNESS(max, type, components, is_yuv) \
344 type **input_rows = (type**)input->get_rows(); \
345 type **output_rows = (type**)output->get_rows(); \
346 int row1 = pkg->row1; \
347 int row2 = pkg->row2; \
348 int width = output->get_w(); \
351 if(!EQUIV(plugin->config.brightness, 0)) \
353 int offset = (int)(plugin->config.brightness / 100 * max); \
354 /*printf("DO_BRIGHTNESS offset=%d\n", offset);*/ \
356 for(int i = row1; i < row2; i++) \
358 type *input_row = input_rows[i]; \
359 type *output_row = output_rows[i]; \
361 for(int j = 0; j < width; j++) \
363 r = input_row[j * components] + offset; \
367 g = input_row[j * components + 1] + offset; \
368 b = input_row[j * components + 2] + offset; \
378 output_row[j * components] = r; \
382 output_row[j * components + 1] = g; \
383 output_row[j * components + 2] = b; \
387 output_row[j * components + 1] = input_row[j * components + 1]; \
388 output_row[j * components + 2] = input_row[j * components + 2]; \
391 if(components == 4) \
392 output_row[j * components + 3] = input_row[j * components + 3]; \
396 /* Data to be processed is now in the output buffer */ \
397 input_rows = output_rows; \
400 if(!EQUIV(plugin->config.contrast, 0)) \
402 float contrast = (plugin->config.contrast < 0) ? \
403 (plugin->config.contrast + 100) / 100 : \
404 (plugin->config.contrast + 25) / 25; \
405 /*printf("DO_BRIGHTNESS contrast=%f\n", contrast);*/ \
407 int scalar = (int)(contrast * 0x100); \
408 int offset = (max << 8) / 2 - max * scalar / 2; \
411 for(int i = row1; i < row2; i++) \
413 type *input_row = input_rows[i]; \
414 type *output_row = output_rows[i]; \
416 if(plugin->config.luma) \
418 for(int j = 0; j < width; j++) \
422 y = input_row[j * components]; \
426 r = input_row[j * components]; \
427 g = input_row[j * components + 1]; \
428 b = input_row[j * components + 2]; \
431 YUV::yuv.rgb_to_yuv_8( \
441 YUV::yuv.rgb_to_yuv_16( \
452 y = (y * scalar + offset) >> 8; \
458 output_row[j * components] = y; \
459 output_row[j * components + 1] = input_row[j * components + 1]; \
460 output_row[j * components + 2] = input_row[j * components + 2]; \
466 YUV::yuv.yuv_to_rgb_8( \
476 YUV::yuv.yuv_to_rgb_16( \
484 input_row[j * components] = r; \
485 input_row[j * components + 1] = g; \
486 input_row[j * components + 2] = b; \
489 if(components == 4) \
490 output_row[j * components + 3] = input_row[j * components + 3]; \
495 for(int j = 0; j < width; j++) \
497 r = input_row[j * components]; \
498 g = input_row[j * components + 1]; \
499 b = input_row[j * components + 2]; \
501 r = (r * scalar + offset) >> 8; \
502 g = (g * scalar + offset) >> 8; \
503 b = (b * scalar + offset) >> 8; \
509 output_row[j * components] = r; \
510 output_row[j * components + 1] = g; \
511 output_row[j * components + 2] = b; \
513 if(components == 4) \
514 output_row[j * components + 3] = input_row[j * components + 3]; \
523 #define DO_BRIGHTNESS_F(components) \
525 float **input_rows = (float**)input->get_rows(); \
526 float **output_rows = (float**)output->get_rows(); \
527 int row1 = pkg->row1; \
528 int row2 = pkg->row2; \
529 int width = output->get_w(); \
532 if(!EQUIV(plugin->config.brightness, 0)) \
534 float offset = plugin->config.brightness / 100; \
536 for(int i = row1; i < row2; i++) \
538 float *input_row = input_rows[i]; \
539 float *output_row = output_rows[i]; \
541 for(int j = 0; j < width; j++) \
543 r = input_row[j * components] + offset; \
544 g = input_row[j * components + 1] + offset; \
545 b = input_row[j * components + 2] + offset; \
547 output_row[j * components] = r; \
548 output_row[j * components + 1] = g; \
549 output_row[j * components + 2] = b; \
550 if(components == 4) \
551 output_row[j * components + 3] = input_row[j * components + 3]; \
555 /* Data to be processed is now in the output buffer */ \
556 input_rows = output_rows; \
559 if(!EQUIV(plugin->config.contrast, 0)) \
561 float contrast = (plugin->config.contrast < 0) ? \
562 (plugin->config.contrast + 100) / 100 : \
563 (plugin->config.contrast + 25) / 25; \
565 /* Shift black level down so shadows get darker instead of lighter */ \
566 float offset = 0.5 - contrast / 2; \
569 for(int i = row1; i < row2; i++) \
571 float *input_row = input_rows[i]; \
572 float *output_row = output_rows[i]; \
574 if(plugin->config.luma) \
576 for(int j = 0; j < width; j++) \
578 r = input_row[j * components]; \
579 g = input_row[j * components + 1]; \
580 b = input_row[j * components + 2]; \
581 YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v); \
582 y = y * contrast + offset; \
583 YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v); \
584 input_row[j * components] = r; \
585 input_row[j * components + 1] = g; \
586 input_row[j * components + 2] = b; \
588 if(components == 4) \
589 output_row[j * components + 3] = input_row[j * components + 3]; \
594 for(int j = 0; j < width; j++) \
596 r = input_row[j * components]; \
597 g = input_row[j * components + 1]; \
598 b = input_row[j * components + 2]; \
600 r = r * contrast + offset; \
601 g = g * contrast + offset; \
602 b = b * contrast + offset; \
604 output_row[j * components] = r; \
605 output_row[j * components + 1] = g; \
606 output_row[j * components + 2] = b; \
608 if(components == 4) \
609 output_row[j * components + 3] = input_row[j * components + 3]; \
617 switch(input->get_color_model())
620 DO_BRIGHTNESS(0xff, unsigned char, 3, 0)
628 DO_BRIGHTNESS(0xff, unsigned char, 3, 1)
632 DO_BRIGHTNESS(0xff, unsigned char, 4, 0)
640 DO_BRIGHTNESS(0xff, unsigned char, 4, 1)
644 DO_BRIGHTNESS(0xffff, uint16_t, 3, 0)
648 DO_BRIGHTNESS(0xffff, uint16_t, 3, 1)
651 case BC_RGBA16161616:
652 DO_BRIGHTNESS(0xffff, uint16_t, 4, 0)
655 case BC_YUVA16161616:
656 DO_BRIGHTNESS(0xffff, uint16_t, 4, 1)
675 BrightnessEngine::BrightnessEngine(BrightnessMain *plugin, int cpus)
676 : LoadServer(cpus, cpus)
678 this->plugin = plugin;
681 BrightnessEngine::~BrightnessEngine()
686 void BrightnessEngine::init_packages()
688 for(int i = 0; i < LoadServer::get_total_packages(); i++)
690 BrightnessPackage *package = (BrightnessPackage*)LoadServer::get_package(i);
691 package->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages();
692 package->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages();
696 LoadClient* BrightnessEngine::new_client()
698 return new BrightnessUnit(this, plugin);
701 LoadPackage* BrightnessEngine::new_package()
703 return new BrightnessPackage;