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 "bcsignals.h"
29 #include "../interpolate/aggregated.h"
30 #include "playback3d.h"
31 #include "workarounds.h"
37 #include "aggregated.h"
40 REGISTER_PLUGIN(GammaMain)
44 GammaConfig::GammaConfig()
49 void GammaConfig::reset()
57 int GammaConfig::equivalent(GammaConfig &that)
59 return (EQUIV(max, that.max) &&
60 EQUIV(gamma, that.gamma) &&
61 automatic == that.automatic) &&
65 void GammaConfig::copy_from(GammaConfig &that)
69 automatic = that.automatic;
73 void GammaConfig::interpolate(GammaConfig &prev,
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->max = prev.max * prev_scale + next.max * next_scale;
83 this->gamma = prev.gamma * prev_scale + next.gamma * next_scale;
84 this->automatic = prev.automatic;
85 this->plot = prev.plot;
95 GammaPackage::GammaPackage()
110 GammaUnit::GammaUnit(GammaMain *plugin)
112 this->plugin = plugin;
116 void GammaUnit::process_package(LoadPackage *package)
118 GammaPackage *pkg = (GammaPackage*)package;
119 GammaEngine *engine = (GammaEngine*)get_server();
120 VFrame *data = engine->data;
121 int w = data->get_w();
122 float r, g, b, y, u, v;
124 // The same algorithm used by dcraw
125 if(engine->operation == GammaEngine::HISTOGRAM)
127 #define HISTOGRAM_HEAD(type) \
128 for(int i = pkg->start; i < pkg->end; i++) \
130 type *row = (type*)data->get_rows()[i]; \
131 for(int j = 0; j < w; j++) \
134 #define HISTOGRAM_TAIL(components) \
136 slot = (int)(r * HISTOGRAM_SIZE); \
137 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
138 slot = (int)(g * HISTOGRAM_SIZE); \
139 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
140 slot = (int)(b * HISTOGRAM_SIZE); \
141 accum[CLIP(slot, 0, HISTOGRAM_SIZE - 1)]++; \
147 switch(data->get_color_model())
150 HISTOGRAM_HEAD(unsigned char)
151 r = (float)row[0] / 0xff;
152 g = (float)row[1] / 0xff;
153 b = (float)row[2] / 0xff;
157 HISTOGRAM_HEAD(unsigned char)
158 r = (float)row[0] / 0xff;
159 g = (float)row[1] / 0xff;
160 b = (float)row[2] / 0xff;
164 HISTOGRAM_HEAD(float)
171 HISTOGRAM_HEAD(float)
178 HISTOGRAM_HEAD(unsigned char)
183 u = (float)((u - 0x80) / 0xff);
184 v = (float)((v - 0x80) / 0xff);
185 YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v);
189 HISTOGRAM_HEAD(unsigned char)
194 u = (float)((u - 0x80) / 0xff);
195 v = (float)((v - 0x80) / 0xff);
196 YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v);
203 float max = plugin->config.max * plugin->config.gamma;
204 float scale = 1.0 / max;
205 float gamma = plugin->config.gamma - 1.0;
207 #define GAMMA_HEAD(type) \
208 for(int i = pkg->start; i < pkg->end; i++) \
210 type *row = (type*)data->get_rows()[i]; \
211 for(int j = 0; j < w; j++) \
214 // powf errors don't show up until later in the pipeline, which makes
215 // this very hard to isolate.
216 #define MY_POW(x, y) ((x > 0.0) ? powf(x * 2 / max, y) : 0.0)
219 r = r * scale * MY_POW(r, gamma); \
220 g = g * scale * MY_POW(g, gamma); \
221 b = b * scale * MY_POW(b, gamma); \
223 #define GAMMA_TAIL(components) \
229 switch(data->get_color_model())
232 GAMMA_HEAD(unsigned char)
233 r = (float)row[0] / 0xff;
234 g = (float)row[1] / 0xff;
235 b = (float)row[2] / 0xff;
237 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
238 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
239 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
243 GAMMA_HEAD(unsigned char)
244 r = (float)row[0] / 0xff;
245 g = (float)row[1] / 0xff;
246 b = (float)row[2] / 0xff;
248 row[0] = (int)CLIP(r * 0xff, 0, 0xff);
249 row[1] = (int)CLIP(g * 0xff, 0, 0xff);
250 row[2] = (int)CLIP(b * 0xff, 0, 0xff);
276 GAMMA_HEAD(unsigned char)
281 u = (float)((u - 0x80) / 0xff);
282 v = (float)((v - 0x80) / 0xff);
283 YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v);
285 YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v);
289 row[0] = (int)CLIP(y, 0, 0xff);
290 row[1] = (int)CLIP(u, 0, 0xff);
291 row[2] = (int)CLIP(v, 0, 0xff);
295 GAMMA_HEAD(unsigned char)
300 u = (float)((u - 0x80) / 0xff);
301 v = (float)((v - 0x80) / 0xff);
302 YUV::yuv.yuv_to_rgb_f(r, g, b, y, u, v);
304 YUV::yuv.rgb_to_yuv_f(r, g, b, y, u, v);
308 row[0] = (int)CLIP(y, 0, 0xff);
309 row[1] = (int)CLIP(u, 0, 0xff);
310 row[2] = (int)CLIP(v, 0, 0xff);
327 GammaEngine::GammaEngine(GammaMain *plugin)
328 : LoadServer(plugin->get_project_smp() + 1,
329 plugin->get_project_smp() + 1)
331 this->plugin = plugin;
334 void GammaEngine::init_packages()
336 for(int i = 0; i < get_total_packages(); i++)
338 GammaPackage *package = (GammaPackage*)get_package(i);
339 package->start = data->get_h() * i / get_total_packages();
340 package->end = data->get_h() * (i + 1) / get_total_packages();
343 // Initialize clients here in case some don't get run.
344 for(int i = 0; i < get_total_clients(); i++)
346 GammaUnit *unit = (GammaUnit*)get_client(i);
347 bzero(unit->accum, sizeof(int) * HISTOGRAM_SIZE);
349 bzero(accum, sizeof(int) * HISTOGRAM_SIZE);
352 LoadClient* GammaEngine::new_client()
354 return new GammaUnit(plugin);
357 LoadPackage* GammaEngine::new_package()
359 return new GammaPackage;
362 void GammaEngine::process_packages(int operation, VFrame *data)
365 this->operation = operation;
366 LoadServer::process_packages();
367 for(int i = 0; i < get_total_clients(); i++)
369 GammaUnit *unit = (GammaUnit*)get_client(i);
370 for(int j = 0; j < HISTOGRAM_SIZE; j++)
372 accum[j] += unit->accum[j];
392 GammaMain::GammaMain(PluginServer *server)
393 : PluginVClient(server)
399 GammaMain::~GammaMain()
406 const char* GammaMain::plugin_title() { return N_("Gamma"); }
407 int GammaMain::is_realtime() { return 1; }
413 NEW_WINDOW_MACRO(GammaMain, GammaWindow)
414 LOAD_CONFIGURATION_MACRO(GammaMain, GammaConfig)
420 int GammaMain::process_buffer(VFrame *frame,
421 int64_t start_position,
425 load_configuration();
427 frame->get_params()->update("GAMMA_GAMMA", config.gamma);
428 frame->get_params()->update("GAMMA_MAX", config.max);
430 int use_opengl = get_use_opengl() &&
432 (!config.plot || !gui_open());
440 int plot = config.plot;
441 if( config.automatic ) {
442 calculate_max(frame);
443 plot = 1; // Always plot to set the slider
446 send_render_gui(this);
451 if(next_effect_is(_("Histogram")))
453 if(next_effect_is(_("Color Balance")))
458 if(!engine) engine = new GammaEngine(this);
459 engine->process_packages(GammaEngine::APPLY, frame);
463 void GammaMain::calculate_max(VFrame *frame)
465 if(!engine) engine = new GammaEngine(this);
466 engine->process_packages(GammaEngine::HISTOGRAM, frame);
467 int total_pixels = frame->get_w() * frame->get_h() * 3;
468 int max_fraction = (int)((int64_t)total_pixels * 99 / 100);
471 for(int i = 0; i < HISTOGRAM_SIZE; i++)
473 current += engine->accum[i];
474 if(current > max_fraction)
476 config.max = (float)i / HISTOGRAM_SIZE;
483 void GammaMain::update_gui()
487 if(load_configuration())
489 thread->window->lock_window("GammaMain::update_gui");
490 ((GammaWindow*)thread->window)->update();
491 thread->window->unlock_window();
496 void GammaMain::render_gui(void *data)
498 GammaMain *ptr = (GammaMain*)data;
499 config.max = ptr->config.max;
501 if(!engine) engine = new GammaEngine(this);
502 if(ptr->engine && ptr->config.automatic)
504 memcpy(engine->accum,
506 sizeof(int) * HISTOGRAM_SIZE);
507 thread->window->lock_window("GammaMain::render_gui");
508 ((GammaWindow*)thread->window)->update();
509 thread->window->unlock_window();
513 engine->process_packages(GammaEngine::HISTOGRAM,
515 thread->window->lock_window("GammaMain::render_gui");
516 ((GammaWindow*)thread->window)->update_histogram();
517 thread->window->unlock_window();
523 void GammaMain::save_data(KeyFrame *keyframe)
527 // cause data to be stored directly in text
528 output.set_shared_output(keyframe->xbuf);
529 output.tag.set_title("GAMMA");
530 output.tag.set_property("MAX", config.max);
531 output.tag.set_property("GAMMA", config.gamma);
532 output.tag.set_property("AUTOMATIC", config.automatic);
533 output.tag.set_property("PLOT", config.plot);
535 output.tag.set_title("/GAMMA");
537 output.append_newline();
538 output.terminate_string();
541 void GammaMain::read_data(KeyFrame *keyframe)
545 input.set_shared_input(keyframe->xbuf);
551 result = input.read_tag();
555 if(input.tag.title_is("GAMMA"))
557 config.max = input.tag.get_property("MAX", config.max);
558 config.gamma = input.tag.get_property("GAMMA", config.gamma);
559 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
560 config.plot = input.tag.get_property("PLOT", config.plot);
561 //printf("GammaMain::read_data %f\n", config.max);
567 int GammaMain::handle_opengl()
570 //printf("GammaMain::handle_opengl 1\n");
572 get_output()->to_texture();
573 get_output()->enable_opengl();
575 const char *shader_stack[16];
576 memset(shader_stack,0, sizeof(shader_stack));
577 int current_shader = 0;
579 int need_color_matrix = BC_CModels::is_yuv(get_output()->get_color_model()) ? 1 : 0;
580 if( need_color_matrix )
581 shader_stack[current_shader++] = bc_gl_colors;
583 // Aggregate with interpolate
584 int aggregate = prev_effect_is(_("Interpolate Pixels")) ? 1 : 0;
586 INTERPOLATE_COMPILE(shader_stack, current_shader);
588 GAMMA_COMPILE(shader_stack, current_shader, aggregate);
590 shader_stack[current_shader] = 0;
591 unsigned int shader = VFrame::make_shader(shader_stack);
593 glUseProgram(shader);
594 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
596 INTERPOLATE_UNIFORMS(shader);
597 GAMMA_UNIFORMS(shader);
598 if( need_color_matrix ) BC_GL_COLORS(shader);
601 get_output()->init_screen();
602 get_output()->bind_texture(0);
603 get_output()->draw_texture();
605 get_output()->set_opengl_state(VFrame::SCREEN);