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
27 #include "bcdisplayinfo.h"
28 #include "bcsignals.h"
32 #include "bistogram.h"
33 #include "bistogramconfig.h"
34 #include "bistogramwindow.h"
37 #include "loadbalance.h"
44 class HistogramEngine;
45 class HistogramWindow;
47 REGISTER_PLUGIN(HistogramMain)
49 HistogramMain::HistogramMain(PluginServer *server)
50 : PluginVClient(server)
54 for(int i = 0; i < HISTOGRAM_MODES; i++)
62 mode = HISTOGRAM_VALUE;
68 HistogramMain::~HistogramMain()
71 for(int i = 0; i < HISTOGRAM_MODES;i++)
74 delete [] smoothed[i];
81 const char* HistogramMain::plugin_title() { return N_("Histogram Bezier"); }
82 int HistogramMain::is_realtime() { return 1; }
85 NEW_WINDOW_MACRO(HistogramMain, HistogramWindow)
86 LOAD_CONFIGURATION_MACRO(HistogramMain, HistogramConfig)
88 void HistogramMain::render_gui(void *data)
92 calculate_histogram((VFrame*)data);
96 calculate_automatic((VFrame*)data);
99 HistogramWindow *window = (HistogramWindow *)thread->window;
100 window->lock_window("HistogramMain::render_gui");
101 window->update_canvas();
104 window->update_input();
106 window->unlock_window();
110 void HistogramMain::update_gui()
112 if( !thread ) return;
113 HistogramWindow *window = (HistogramWindow *)thread->window;
114 // points delete in load_configuration,read_data
115 window->lock_window("HistogramMain::update_gui");
116 if( load_configuration() ) {
118 if(!config.automatic)
119 window->update_input();
121 window->unlock_window();
125 void HistogramMain::save_data(KeyFrame *keyframe)
129 // cause data to be stored directly in text
130 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
131 output.tag.set_title("HISTOGRAM");
133 char string[BCTEXTLEN];
136 for(int i = 0; i < HISTOGRAM_MODES; i++)
138 sprintf(string, "OUTPUT_MIN_%d", i);
139 output.tag.set_property(string, config.output_min[i]);
140 sprintf(string, "OUTPUT_MAX_%d", i);
141 output.tag.set_property(string, config.output_max[i]);
142 //printf("HistogramMain::save_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
145 output.tag.set_property("AUTOMATIC", config.automatic);
146 output.tag.set_property("THRESHOLD", config.threshold);
147 output.tag.set_property("SPLIT", config.split);
148 output.tag.set_property("INTERPOLATION", config.smoothMode);
150 output.tag.set_title("/HISTOGRAM");
152 output.append_newline();
158 for(int j = 0; j < HISTOGRAM_MODES; j++)
160 output.tag.set_title("POINTS");
162 output.append_newline();
165 HistogramPoint *current = config.points[j].first;
168 output.tag.set_title("POINT");
169 output.tag.set_property("X", current->x);
170 output.tag.set_property("Y", current->y);
171 output.tag.set_property("GRADIENT", current->gradient);
172 output.tag.set_property("XOFFSET_LEFT", current->xoffset_left);
173 output.tag.set_property("XOFFSET_RIGHT", current->xoffset_right);
175 output.tag.set_title("/POINT");
177 output.append_newline();
182 output.tag.set_title("/POINTS");
184 output.append_newline();
192 output.terminate_string();
195 void HistogramMain::read_data(KeyFrame *keyframe)
199 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
202 int current_input_mode = 0;
207 result = input.read_tag();
211 if(input.tag.title_is("HISTOGRAM"))
213 char string[BCTEXTLEN];
214 for(int i = 0; i < HISTOGRAM_MODES; i++)
216 sprintf(string, "OUTPUT_MIN_%d", i);
217 config.output_min[i] = input.tag.get_property(string, config.output_min[i]);
218 sprintf(string, "OUTPUT_MAX_%d", i);
219 config.output_max[i] = input.tag.get_property(string, config.output_max[i]);
220 //printf("HistogramMain::read_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
222 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
223 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
224 config.split = input.tag.get_property("SPLIT", config.split);
225 config.smoothMode = input.tag.get_property("INTERPOLATION", config.smoothMode);
228 if(input.tag.title_is("POINTS"))
230 if(current_input_mode < HISTOGRAM_MODES)
232 HistogramPoints *points = &config.points[current_input_mode];
237 result = input.read_tag();
240 if(input.tag.title_is("/POINTS"))
245 if(input.tag.title_is("POINT"))
248 input.tag.get_property("X", 0.0),
249 input.tag.get_property("Y", 0.0));
250 points->last->gradient =
251 input.tag.get_property("GRADIENT", 1.0);
252 points->last->xoffset_left =
253 input.tag.get_property("XOFFSET_LEFT", -0.02);
254 points->last->xoffset_right =
255 input.tag.get_property("XOFFSET_RIGHT", 0.02);
261 current_input_mode++;
270 float HistogramMain::calculate_linear(float input,
289 float x1 = 0, y1 = 0;
292 float x2 = 1, y2 = 1;
296 // Get 2 points surrounding current position
297 HistogramPoints *points = &config.points[subscript];
298 HistogramPoint *current = points->first;
300 while(current && !done) {
301 if(current->x > input) {
304 grad2 = current->gradient;
305 x2left = current->xoffset_left;
312 current = points->last;
314 while(current && !done) {
315 if(current->x <= input) {
318 grad1 = current->gradient;
320 x1right = current->xoffset_right;
329 if(!EQUIV(x2 - x1, 0))
331 if (config.smoothMode == HISTOGRAM_LINEAR)
332 output = (input - x1) * (y2 - y1) / (x2 - x1) + y1;
333 else if (config.smoothMode == HISTOGRAM_POLYNOMINAL)
335 /* Construct third grade polynom between every two points */
338 float delx = input - x1;
339 output = (grad2 * dx + grad1 * dx - 2*dy) / (dx * dx * dx) * delx * delx * delx +
340 (3*dy - 2* grad1*dx - grad2*dx) / (dx * dx) * delx * delx + grad1*delx + y1;
342 else if (config.smoothMode == HISTOGRAM_BEZIER)
344 /* Using standart DeCasteljau algorithm */
345 float y1right = y1 + grad1 * x1right;
346 float y2left = y2 + grad2 * x2left;
348 float t = (input - x1) / (x2 - x1);
350 float pointAy = y1 + (y1right - y1) * t;
351 float pointBy = y1right + (y2left - y1right) * t;
352 float pointCy = y2left + (y2 - y2left) * t;
353 float pointABy = pointAy + (pointBy - pointAy) * t;
354 float pointBCy = pointBy + (pointCy - pointBy) * t;
355 output = pointABy + (pointBCy - pointABy) * t;
365 output = calculate_linear(output, HISTOGRAM_VALUE, 0);
369 float output_min = config.output_min[subscript];
370 float output_max = config.output_max[subscript];
372 // Compress output for value followed by channel
373 output = output_min + output * (output_max - output_min);
377 float HistogramMain::calculate_smooth(float input, int subscript)
379 float x_f = (input - HIST_MIN_INPUT) * HISTOGRAM_SLOTS / FLOAT_RANGE;
382 CLAMP(x_i1, 0, HISTOGRAM_SLOTS-1);
383 CLAMP(x_i2, 0, HISTOGRAM_SLOTS-1);
384 CLAMP(x_f, 0, HISTOGRAM_SLOTS-1);
386 float smooth1 = smoothed[subscript][x_i1];
387 float smooth2 = smoothed[subscript][x_i2];
388 float result = smooth1 + (smooth2 - smooth1) * (x_f - x_i1);
389 CLAMP(result, 0, 1.0);
394 void HistogramMain::calculate_histogram(VFrame *data)
397 if(!engine) engine = new HistogramEngine(this,
398 get_project_smp() + 1,
399 get_project_smp() + 1);
403 for(int i = 0; i < HISTOGRAM_MODES; i++)
404 accum[i] = new int[HISTOGRAM_SLOTS];
406 engine->process_packages(HistogramEngine::HISTOGRAM, data);
408 for(int i = 0; i < engine->get_total_clients(); i++) {
409 HistogramUnit *unit = (HistogramUnit*)engine->get_client(i);
411 for(int j = 0; j < HISTOGRAM_MODES; j++)
412 memcpy(accum[j], unit->accum[j], sizeof(int) * HISTOGRAM_SLOTS);
415 for(int j = 0; j < HISTOGRAM_MODES; j++) {
417 int *in = unit->accum[j];
418 for(int k = 0; k < HISTOGRAM_SLOTS; k++)
424 // Remove top and bottom from calculations. Doesn't work in high
425 // precision colormodels.
426 for(int i = 0; i < HISTOGRAM_MODES; i++) {
428 accum[i][HISTOGRAM_SLOTS - 1] = 0;
433 void HistogramMain::calculate_automatic(VFrame *data)
435 calculate_histogram(data);
436 config.reset_points();
439 for(int i = 0; i < 3; i++) {
440 int *accum = this->accum[i];
441 int pixels = data->get_w() * data->get_h();
442 float white_fraction = 1.0 - (1.0 - config.threshold) / 2;
443 int threshold = (int)(white_fraction * pixels);
445 float max_level = 1.0;
446 float min_level = 0.0;
448 // Get histogram slot above threshold of pixels
449 for(int j = 0; j < HISTOGRAM_SLOTS; j++) {
451 if(total >= threshold) {
452 max_level = (float)j / HISTOGRAM_SLOTS * FLOAT_RANGE + HIST_MIN_INPUT;
457 // Get slot below 99% of pixels
459 for(int j = HISTOGRAM_SLOTS - 1; j >= 0; j--) {
461 if(total >= threshold) {
462 min_level = (float)j / HISTOGRAM_SLOTS * FLOAT_RANGE + HIST_MIN_INPUT;
466 config.points[i].insert(max_level, 1.0);
467 config.points[i].insert(min_level, 0.0);
471 int HistogramMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
474 int need_reconfigure = load_configuration();
477 if(!engine) engine = new HistogramEngine(this,
478 get_project_smp() + 1,
479 get_project_smp() + 1);
480 this->input = input_ptr;
481 this->output = output_ptr;
483 send_render_gui(input_ptr);
485 if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
487 output_ptr->copy_from(input_ptr);
491 // Generate tables here. The same table is used by many packages to render
492 // each horizontal stripe. Need to cover the entire output range in each
493 // table to avoid green borders
494 if( !lookup[0] || !smoothed[0] || !linear[0] || config.automatic)
495 need_reconfigure = 1;
496 if( need_reconfigure ) {
498 // Calculate new curves
499 if(config.automatic) {
500 calculate_automatic(input);
504 // Generate transfer tables for integer colormodels.
505 for(int i = 0; i < 3; i++)
506 tabulate_curve(i, 1);
511 engine->process_packages(HistogramEngine::APPLY, input);
517 void HistogramMain::tabulate_curve(int subscript, int use_value)
520 if(!lookup[subscript])
521 lookup[subscript] = new int[HISTOGRAM_SLOTS];
522 if(!smoothed[subscript])
523 smoothed[subscript] = new float[HISTOGRAM_SLOTS];
524 if(!linear[subscript])
525 linear[subscript] = new float[HISTOGRAM_SLOTS];
527 float *current_smooth = smoothed[subscript];
528 float *current_linear = linear[subscript];
531 for(i = 0; i < HISTOGRAM_SLOTS; i++) {
532 float input = (float)i / HISTOGRAM_SLOTS * FLOAT_RANGE + HIST_MIN_INPUT;
533 current_linear[i] = calculate_linear(input, subscript, use_value);
537 for(i = 0; i < HISTOGRAM_SLOTS; i++)
539 // current_smooth[i] = current_linear[i] * 0.001 +
541 current_smooth[i] = current_linear[i];
542 // prev = current_smooth[i];
544 // Generate lookup tables for integer colormodels
547 switch(input->get_color_model())
551 for(i = 0; i < 0x100; i++)
552 lookup[subscript][i] =
553 (int)(calculate_smooth((float)i / 0xff, subscript) * 0xff);
555 // All other integer colormodels are converted to 16 bit RGB
557 for(i = 0; i < 0x10000; i++)
558 lookup[subscript][i] =
559 (int)(calculate_smooth((float)i / 0xffff, subscript) * 0xffff);
565 HistogramPackage::HistogramPackage()
570 HistogramUnit::HistogramUnit(HistogramEngine *server,
571 HistogramMain *plugin)
574 this->plugin = plugin;
575 this->server = server;
576 for(int i = 0; i < HISTOGRAM_MODES; i++)
577 accum[i] = new int[HISTOGRAM_SLOTS];
580 HistogramUnit::~HistogramUnit()
582 for(int i = 0; i < HISTOGRAM_MODES; i++)
586 void HistogramUnit::process_package(LoadPackage *package)
588 HistogramPackage *pkg = (HistogramPackage*)package;
590 if(server->operation == HistogramEngine::HISTOGRAM)
593 #define HISTOGRAM_HEAD(type) \
595 for(int i = pkg->start; i < pkg->end; i++) \
597 type *row = (type*)data->get_rows()[i]; \
598 for(int j = 0; j < w; j++) \
601 #define HISTOGRAM_TAIL(components) \
602 /* v = (r * 76 + g * 150 + b * 29) >> 8; */ \
605 r += -HISTOGRAM_MIN * 0xffff / 100; \
606 g += -HISTOGRAM_MIN * 0xffff / 100; \
607 b += -HISTOGRAM_MIN * 0xffff / 100; \
608 v += -HISTOGRAM_MIN * 0xffff / 100; \
609 CLAMP(r, 0, HISTOGRAM_SLOTS-1); \
610 CLAMP(g, 0, HISTOGRAM_SLOTS-1); \
611 CLAMP(b, 0, HISTOGRAM_SLOTS-1); \
612 CLAMP(v, 0, HISTOGRAM_SLOTS-1); \
622 VFrame *data = server->data;
623 int w = data->get_w();
624 // int h = data->get_h();
625 int *accum_r = accum[HISTOGRAM_RED];
626 int *accum_g = accum[HISTOGRAM_GREEN];
627 int *accum_b = accum[HISTOGRAM_BLUE];
628 int *accum_v = accum[HISTOGRAM_VALUE];
629 int r, g, b, y, u, v;
631 switch(data->get_color_model())
634 HISTOGRAM_HEAD(unsigned char)
635 r = (row[0] << 8) | row[0];
636 g = (row[1] << 8) | row[1];
637 b = (row[2] << 8) | row[2];
641 HISTOGRAM_HEAD(float)
642 r = (int)(row[0] * 0xffff);
643 g = (int)(row[1] * 0xffff);
644 b = (int)(row[2] * 0xffff);
648 HISTOGRAM_HEAD(unsigned char)
649 y = (row[0] << 8) | row[0];
650 u = (row[1] << 8) | row[1];
651 v = (row[2] << 8) | row[2];
652 YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v);
656 HISTOGRAM_HEAD(unsigned char)
657 r = (row[0] << 8) | row[0];
658 g = (row[1] << 8) | row[1];
659 b = (row[2] << 8) | row[2];
663 HISTOGRAM_HEAD(float)
664 r = (int)(row[0] * 0xffff);
665 g = (int)(row[1] * 0xffff);
666 b = (int)(row[2] * 0xffff);
670 HISTOGRAM_HEAD(unsigned char)
671 y = (row[0] << 8) | row[0];
672 u = (row[1] << 8) | row[1];
673 v = (row[2] << 8) | row[2];
674 YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v);
678 HISTOGRAM_HEAD(uint16_t)
685 HISTOGRAM_HEAD(uint16_t)
689 YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v);
692 case BC_RGBA16161616:
693 HISTOGRAM_HEAD(uint16_t)
699 case BC_YUVA16161616:
700 HISTOGRAM_HEAD(uint16_t)
704 YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v);
710 if(server->operation == HistogramEngine::APPLY)
715 #define PROCESS(type, components) \
717 for(int i = pkg->start; i < pkg->end; i++) \
719 type *row = (type*)input->get_rows()[i]; \
720 for(int j = 0; j < w; j++) \
722 if ( plugin->config.split && ((j + i * w / h) < w) ) \
724 row[0] = lookup_r[row[0]]; \
725 row[1] = lookup_g[row[1]]; \
726 row[2] = lookup_b[row[2]]; \
732 #define PROCESS_YUV(type, components, max) \
734 for(int i = pkg->start; i < pkg->end; i++) \
736 type *row = (type*)input->get_rows()[i]; \
737 for(int j = 0; j < w; j++) \
739 if ( plugin->config.split && ((j + i * w / h) < w) ) \
741 /* Convert to 16 bit RGB */ \
744 y = (row[0] << 8) | row[0]; \
745 u = (row[1] << 8) | row[1]; \
746 v = (row[2] << 8) | row[2]; \
755 YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v); \
757 /* Look up in RGB domain */ \
762 /* Convert to 16 bit YUV */ \
763 YUV::yuv.rgb_to_yuv_16(r, g, b, y, u, v); \
782 #define PROCESS_FLOAT(components) \
784 for(int i = pkg->start; i < pkg->end; i++) \
786 float *row = (float*)input->get_rows()[i]; \
787 for(int j = 0; j < w; j++) \
789 if ( plugin->config.split && ((j + i * w / h) < w) ) \
795 r = plugin->calculate_smooth(r, HISTOGRAM_RED); \
796 g = plugin->calculate_smooth(g, HISTOGRAM_GREEN); \
797 b = plugin->calculate_smooth(b, HISTOGRAM_BLUE); \
809 VFrame *input = plugin->input;
810 // VFrame *output = plugin->output;
811 int w = input->get_w(), h = input->get_h();
812 int *lookup_r = plugin->lookup[0];
813 int *lookup_g = plugin->lookup[1];
814 int *lookup_b = plugin->lookup[2];
815 int r, g, b, y, u, v;
816 switch(input->get_color_model())
819 PROCESS(unsigned char, 3)
825 PROCESS(unsigned char, 4)
833 case BC_RGBA16161616:
837 PROCESS_YUV(unsigned char, 3, 0xff)
840 PROCESS_YUV(unsigned char, 4, 0xff)
843 PROCESS_YUV(uint16_t, 3, 0xffff)
845 case BC_YUVA16161616:
846 PROCESS_YUV(uint16_t, 4, 0xffff)
857 HistogramEngine::HistogramEngine(HistogramMain *plugin,
860 : LoadServer(total_clients, total_packages)
862 this->plugin = plugin;
865 void HistogramEngine::init_packages()
869 total_size = data->get_h();
872 total_size = data->get_h();
876 for(int i = 0; i < get_total_packages(); i++) {
877 HistogramPackage *package = (HistogramPackage*)get_package(i);
878 package->start = total_size * i / get_total_packages();
879 package->end = total_size * (i + 1) / get_total_packages();
882 // Initialize clients here in case some don't get run.
883 for(int i = 0; i < get_total_clients(); i++) {
884 HistogramUnit *unit = (HistogramUnit*)get_client(i);
885 for(int i = 0; i < HISTOGRAM_MODES; i++)
886 bzero(unit->accum[i], sizeof(int) * HISTOGRAM_SLOTS);
891 LoadClient* HistogramEngine::new_client()
893 return new HistogramUnit(this, plugin);
896 LoadPackage* HistogramEngine::new_package()
898 return new HistogramPackage;
901 void HistogramEngine::process_packages(int operation, VFrame *data)
904 this->operation = operation;
905 LoadServer::process_packages();