6 #include "bcdisplayinfo.h"
11 #include "histogram.h"
12 #include "histogramconfig.h"
13 #include "histogramwindow.h"
16 #include "loadbalance.h"
17 #include "plugincolors.h"
23 class HistogramEngine;
24 class HistogramWindow;
30 REGISTER_PLUGIN(HistogramMain)
43 HistogramMain::HistogramMain(PluginServer *server)
44 : PluginVClient(server)
46 PLUGIN_CONSTRUCTOR_MACRO
48 for(int i = 0; i < HISTOGRAM_MODES; i++)
56 mode = HISTOGRAM_VALUE;
62 HistogramMain::~HistogramMain()
64 PLUGIN_DESTRUCTOR_MACRO
65 for(int i = 0; i < HISTOGRAM_MODES;i++)
68 delete [] smoothed[i];
75 char* HistogramMain::plugin_title() { return N_("Histogram"); }
76 int HistogramMain::is_realtime() { return 1; }
79 #include "picon_png.h"
80 NEW_PICON_MACRO(HistogramMain)
82 SHOW_GUI_MACRO(HistogramMain, HistogramThread)
84 SET_STRING_MACRO(HistogramMain)
86 RAISE_WINDOW_MACRO(HistogramMain)
88 LOAD_CONFIGURATION_MACRO(HistogramMain, HistogramConfig)
90 void HistogramMain::render_gui(void *data)
94 calculate_histogram((VFrame*)data);
99 calculate_automatic((VFrame*)data);
104 thread->window->lock_window("HistogramMain::render_gui");
105 thread->window->update_canvas();
108 thread->window->update_input();
110 thread->window->unlock_window();
115 void HistogramMain::update_gui()
119 thread->window->lock_window("HistogramMain::update_gui");
120 int reconfigure = load_configuration();
123 thread->window->update(0);
124 if(!config.automatic)
126 thread->window->update_input();
129 thread->window->unlock_window();
134 int HistogramMain::load_defaults()
136 char directory[BCTEXTLEN], string[BCTEXTLEN];
137 // set the default directory
138 sprintf(directory, "%shistogram.rc", BCASTDIR);
141 defaults = new BC_Hash(directory);
144 for(int j = 0; j < HISTOGRAM_MODES; j++)
146 while(config.points[j].last) delete config.points[j].last;
148 sprintf(string, "TOTAL_POINTS_%d", j);
149 int total_points = defaults->get(string, 0);
151 for(int i = 0; i < total_points; i++)
153 HistogramPoint *point = new HistogramPoint;
154 sprintf(string, "INPUT_X_%d_%d", j, i);
155 point->x = defaults->get(string, point->x);
156 sprintf(string, "INPUT_Y_%d_%d", j, i);
157 point->y = defaults->get(string, point->y);
158 config.points[j].append(point);
163 for(int i = 0; i < HISTOGRAM_MODES; i++)
165 sprintf(string, "OUTPUT_MIN_%d", i);
166 config.output_min[i] = defaults->get(string, config.output_min[i]);
167 sprintf(string, "OUTPUT_MAX_%d", i);
168 config.output_max[i] = defaults->get(string, config.output_max[i]);
171 config.automatic = defaults->get("AUTOMATIC", config.automatic);
172 mode = defaults->get("MODE", mode);
173 CLAMP(mode, 0, HISTOGRAM_MODES - 1);
174 config.threshold = defaults->get("THRESHOLD", config.threshold);
175 config.split = defaults->get("SPLIT", config.split);
181 int HistogramMain::save_defaults()
183 char string[BCTEXTLEN];
187 for(int j = 0; j < HISTOGRAM_MODES; j++)
189 int total_points = config.points[j].total();
190 sprintf(string, "TOTAL_POINTS_%d", j);
191 defaults->update(string, total_points);
192 HistogramPoint *current = config.points[j].first;
196 sprintf(string, "INPUT_X_%d_%d", j, number);
197 defaults->update(string, current->x);
198 sprintf(string, "INPUT_Y_%d_%d", j, number);
199 defaults->update(string, current->y);
206 for(int i = 0; i < HISTOGRAM_MODES; i++)
208 sprintf(string, "OUTPUT_MIN_%d", i);
209 defaults->update(string, config.output_min[i]);
210 sprintf(string, "OUTPUT_MAX_%d", i);
211 defaults->update(string, config.output_max[i]);
214 defaults->update("AUTOMATIC", config.automatic);
215 defaults->update("MODE", mode);
216 defaults->update("THRESHOLD", config.threshold);
217 defaults->update("SPLIT", config.split);
224 void HistogramMain::save_data(KeyFrame *keyframe)
228 // cause data to be stored directly in text
229 output.set_shared_string(keyframe->data, MESSAGESIZE);
230 output.tag.set_title("HISTOGRAM");
232 char string[BCTEXTLEN];
235 for(int i = 0; i < HISTOGRAM_MODES; i++)
237 sprintf(string, "OUTPUT_MIN_%d", i);
238 output.tag.set_property(string, config.output_min[i]);
239 sprintf(string, "OUTPUT_MAX_%d", i);
240 output.tag.set_property(string, config.output_max[i]);
241 //printf("HistogramMain::save_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
244 output.tag.set_property("AUTOMATIC", config.automatic);
245 output.tag.set_property("THRESHOLD", config.threshold);
246 output.tag.set_property("SPLIT", config.split);
248 output.append_newline();
254 for(int j = 0; j < HISTOGRAM_MODES; j++)
256 output.tag.set_title("POINTS");
258 output.append_newline();
261 HistogramPoint *current = config.points[j].first;
264 output.tag.set_title("POINT");
265 output.tag.set_property("X", current->x);
266 output.tag.set_property("Y", current->y);
268 output.append_newline();
273 output.tag.set_title("/POINTS");
275 output.append_newline();
283 output.terminate_string();
286 void HistogramMain::read_data(KeyFrame *keyframe)
290 input.set_shared_string(keyframe->data, strlen(keyframe->data));
293 int current_input_mode = 0;
298 result = input.read_tag();
302 if(input.tag.title_is("HISTOGRAM"))
304 char string[BCTEXTLEN];
305 for(int i = 0; i < HISTOGRAM_MODES; i++)
307 sprintf(string, "OUTPUT_MIN_%d", i);
308 config.output_min[i] = input.tag.get_property(string, config.output_min[i]);
309 sprintf(string, "OUTPUT_MAX_%d", i);
310 config.output_max[i] = input.tag.get_property(string, config.output_max[i]);
311 //printf("HistogramMain::read_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
313 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
314 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
315 config.split = input.tag.get_property("SPLIT", config.split);
318 if(input.tag.title_is("POINTS"))
320 if(current_input_mode < HISTOGRAM_MODES)
322 HistogramPoints *points = &config.points[current_input_mode];
327 result = input.read_tag();
330 if(input.tag.title_is("/POINTS"))
335 if(input.tag.title_is("POINT"))
338 input.tag.get_property("X", 0.0),
339 input.tag.get_property("Y", 0.0));
345 current_input_mode++;
354 float HistogramMain::calculate_linear(float input,
383 // Get 2 points surrounding current position
384 HistogramPoints *points = &config.points[subscript];
385 HistogramPoint *current = points->first;
387 while(current && !done)
389 if(current->x > input)
399 current = points->last;
401 while(current && !done)
403 if(current->x <= input)
417 if(!EQUIV(x2 - x1, 0))
418 output = (input - x1) * (y2 - y1) / (x2 - x1) + y1;
431 output = calculate_linear(output, HISTOGRAM_VALUE, 0);
435 float output_min = config.output_min[subscript];
436 float output_max = config.output_max[subscript];
441 // Compress output for value followed by channel
442 output = output_min +
444 (output_max - output_min);
450 float HistogramMain::calculate_smooth(float input, int subscript)
452 float x_f = (input - MIN_INPUT) * HISTOGRAM_SLOTS / FLOAT_RANGE;
455 CLAMP(x_i1, 0, HISTOGRAM_SLOTS - 1);
456 CLAMP(x_i2, 0, HISTOGRAM_SLOTS - 1);
457 CLAMP(x_f, 0, HISTOGRAM_SLOTS - 1);
459 float smooth1 = smoothed[subscript][x_i1];
460 float smooth2 = smoothed[subscript][x_i2];
461 float result = smooth1 + (smooth2 - smooth1) * (x_f - x_i1);
462 CLAMP(result, 0, 1.0);
467 void HistogramMain::calculate_histogram(VFrame *data)
470 if(!engine) engine = new HistogramEngine(this,
471 get_project_smp() + 1,
472 get_project_smp() + 1);
476 for(int i = 0; i < HISTOGRAM_MODES; i++)
477 accum[i] = new int[HISTOGRAM_SLOTS];
480 engine->process_packages(HistogramEngine::HISTOGRAM, data);
482 for(int i = 0; i < engine->get_total_clients(); i++)
484 HistogramUnit *unit = (HistogramUnit*)engine->get_client(i);
488 for(int j = 0; j < HISTOGRAM_MODES; j++)
490 memcpy(accum[j], unit->accum[j], sizeof(int) * HISTOGRAM_SLOTS);
495 for(int j = 0; j < HISTOGRAM_MODES; j++)
498 int *in = unit->accum[j];
499 for(int k = 0; k < HISTOGRAM_SLOTS; k++)
505 // Remove top and bottom from calculations. Doesn't work in high
506 // precision colormodels.
507 for(int i = 0; i < HISTOGRAM_MODES; i++)
510 accum[i][HISTOGRAM_SLOTS - 1] = 0;
515 void HistogramMain::calculate_automatic(VFrame *data)
517 calculate_histogram(data);
518 config.reset_points();
521 for(int i = 0; i < 3; i++)
523 int *accum = this->accum[i];
524 int pixels = data->get_w() * data->get_h();
525 float white_fraction = 1.0 - (1.0 - config.threshold) / 2;
526 int threshold = (int)(white_fraction * pixels);
528 float max_level = 1.0;
529 float min_level = 0.0;
531 // Get histogram slot above threshold of pixels
532 for(int j = 0; j < HISTOGRAM_SLOTS; j++)
535 if(total >= threshold)
537 max_level = (float)j / HISTOGRAM_SLOTS * FLOAT_RANGE + MIN_INPUT;
542 // Get slot below 99% of pixels
544 for(int j = HISTOGRAM_SLOTS - 1; j >= 0; j--)
547 if(total >= threshold)
549 min_level = (float)j / HISTOGRAM_SLOTS * FLOAT_RANGE + MIN_INPUT;
555 config.points[i].insert(max_level, 1.0);
556 config.points[i].insert(min_level, 0.0);
565 int HistogramMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
568 int need_reconfigure = load_configuration();
573 if(!engine) engine = new HistogramEngine(this,
574 get_project_smp() + 1,
575 get_project_smp() + 1);
576 this->input = input_ptr;
577 this->output = output_ptr;
579 send_render_gui(input_ptr);
581 if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
583 output_ptr->copy_from(input_ptr);
587 // Generate tables here. The same table is used by many packages to render
588 // each horizontal stripe. Need to cover the entire output range in each
589 // table to avoid green borders
590 if(need_reconfigure ||
597 // Calculate new curves
600 calculate_automatic(input);
604 // Generate transfer tables for integer colormodels.
605 for(int i = 0; i < 3; i++)
606 tabulate_curve(i, 1);
613 engine->process_packages(HistogramEngine::APPLY, input);
620 void HistogramMain::tabulate_curve(int subscript, int use_value)
623 if(!lookup[subscript])
624 lookup[subscript] = new int[HISTOGRAM_SLOTS];
625 if(!smoothed[subscript])
626 smoothed[subscript] = new float[HISTOGRAM_SLOTS];
627 if(!linear[subscript])
628 linear[subscript] = new float[HISTOGRAM_SLOTS];
630 float *current_smooth = smoothed[subscript];
631 float *current_linear = linear[subscript];
634 for(i = 0; i < HISTOGRAM_SLOTS; i++)
636 float input = (float)i / HISTOGRAM_SLOTS * FLOAT_RANGE + MIN_INPUT;
637 current_linear[i] = calculate_linear(input, subscript, use_value);
645 // Make smooth curve (currently a copy of the linear curve)
647 for(i = 0; i < HISTOGRAM_SLOTS; i++)
649 // current_smooth[i] = current_linear[i] * 0.001 +
651 current_smooth[i] = current_linear[i];
652 prev = current_smooth[i];
655 // Generate lookup tables for integer colormodels
658 switch(input->get_color_model())
662 for(i = 0; i < 0x100; i++)
663 lookup[subscript][i] =
664 (int)(calculate_smooth((float)i / 0xff, subscript) * 0xff);
666 // All other integer colormodels are converted to 16 bit RGB
668 for(i = 0; i < 0x10000; i++)
669 lookup[subscript][i] =
670 (int)(calculate_smooth((float)i / 0xffff, subscript) * 0xffff);
687 HistogramPackage::HistogramPackage()
695 HistogramUnit::HistogramUnit(HistogramEngine *server,
696 HistogramMain *plugin)
699 this->plugin = plugin;
700 this->server = server;
701 for(int i = 0; i < HISTOGRAM_MODES; i++)
702 accum[i] = new int[HISTOGRAM_SLOTS];
705 HistogramUnit::~HistogramUnit()
707 for(int i = 0; i < HISTOGRAM_MODES; i++)
711 void HistogramUnit::process_package(LoadPackage *package)
713 HistogramPackage *pkg = (HistogramPackage*)package;
715 if(server->operation == HistogramEngine::HISTOGRAM)
719 #define HISTOGRAM_HEAD(type) \
721 for(int i = pkg->start; i < pkg->end; i++) \
723 type *row = (type*)data->get_rows()[i]; \
724 for(int j = 0; j < w; j++) \
727 #define HISTOGRAM_TAIL(components) \
728 /* v = (r * 76 + g * 150 + b * 29) >> 8; */ \
731 r += -HISTOGRAM_MIN * 0xffff / 100; \
732 g += -HISTOGRAM_MIN * 0xffff / 100; \
733 b += -HISTOGRAM_MIN * 0xffff / 100; \
734 v += -HISTOGRAM_MIN * 0xffff / 100; \
735 CLAMP(r, 0, HISTOGRAM_SLOTS); \
736 CLAMP(g, 0, HISTOGRAM_SLOTS); \
737 CLAMP(b, 0, HISTOGRAM_SLOTS); \
738 CLAMP(v, 0, HISTOGRAM_SLOTS); \
751 VFrame *data = server->data;
752 int w = data->get_w();
753 int h = data->get_h();
754 int *accum_r = accum[HISTOGRAM_RED];
755 int *accum_g = accum[HISTOGRAM_GREEN];
756 int *accum_b = accum[HISTOGRAM_BLUE];
757 int *accum_v = accum[HISTOGRAM_VALUE];
758 int r, g, b, a, y, u, v;
760 switch(data->get_color_model())
763 HISTOGRAM_HEAD(unsigned char)
764 r = (row[0] << 8) | row[0];
765 g = (row[1] << 8) | row[1];
766 b = (row[2] << 8) | row[2];
770 HISTOGRAM_HEAD(float)
771 r = (int)(row[0] * 0xffff);
772 g = (int)(row[1] * 0xffff);
773 b = (int)(row[2] * 0xffff);
777 HISTOGRAM_HEAD(unsigned char)
778 y = (row[0] << 8) | row[0];
779 u = (row[1] << 8) | row[1];
780 v = (row[2] << 8) | row[2];
781 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
785 HISTOGRAM_HEAD(unsigned char)
786 r = (row[0] << 8) | row[0];
787 g = (row[1] << 8) | row[1];
788 b = (row[2] << 8) | row[2];
792 HISTOGRAM_HEAD(float)
793 r = (int)(row[0] * 0xffff);
794 g = (int)(row[1] * 0xffff);
795 b = (int)(row[2] * 0xffff);
799 HISTOGRAM_HEAD(unsigned char)
800 y = (row[0] << 8) | row[0];
801 u = (row[1] << 8) | row[1];
802 v = (row[2] << 8) | row[2];
803 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
807 HISTOGRAM_HEAD(uint16_t)
814 HISTOGRAM_HEAD(uint16_t)
818 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
821 case BC_RGBA16161616:
822 HISTOGRAM_HEAD(uint16_t)
828 case BC_YUVA16161616:
829 HISTOGRAM_HEAD(uint16_t)
833 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
839 if(server->operation == HistogramEngine::APPLY)
844 #define PROCESS(type, components) \
846 for(int i = pkg->start; i < pkg->end; i++) \
848 type *row = (type*)input->get_rows()[i]; \
849 for(int j = 0; j < w; j++) \
851 if ( plugin->config.split && ((j + i * w / h) < w) ) \
853 row[0] = lookup_r[row[0]]; \
854 row[1] = lookup_g[row[1]]; \
855 row[2] = lookup_b[row[2]]; \
861 #define PROCESS_YUV(type, components, max) \
863 for(int i = pkg->start; i < pkg->end; i++) \
865 type *row = (type*)input->get_rows()[i]; \
866 for(int j = 0; j < w; j++) \
868 if ( plugin->config.split && ((j + i * w / h) < w) ) \
870 /* Convert to 16 bit RGB */ \
873 y = (row[0] << 8) | row[0]; \
874 u = (row[1] << 8) | row[1]; \
875 v = (row[2] << 8) | row[2]; \
884 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v); \
886 /* Look up in RGB domain */ \
891 /* Convert to 16 bit YUV */ \
892 plugin->yuv.rgb_to_yuv_16(r, g, b, y, u, v); \
911 #define PROCESS_FLOAT(components) \
913 for(int i = pkg->start; i < pkg->end; i++) \
915 float *row = (float*)input->get_rows()[i]; \
916 for(int j = 0; j < w; j++) \
918 if ( plugin->config.split && ((j + i * w / h) < w) ) \
924 r = plugin->calculate_smooth(r, HISTOGRAM_RED); \
925 g = plugin->calculate_smooth(g, HISTOGRAM_GREEN); \
926 b = plugin->calculate_smooth(b, HISTOGRAM_BLUE); \
938 VFrame *input = plugin->input;
939 VFrame *output = plugin->output;
940 int w = input->get_w();
941 int h = input->get_h();
942 int *lookup_r = plugin->lookup[0];
943 int *lookup_g = plugin->lookup[1];
944 int *lookup_b = plugin->lookup[2];
945 int r, g, b, y, u, v, a;
946 switch(input->get_color_model())
949 PROCESS(unsigned char, 3)
955 PROCESS(unsigned char, 4)
963 case BC_RGBA16161616:
967 PROCESS_YUV(unsigned char, 3, 0xff)
970 PROCESS_YUV(unsigned char, 4, 0xff)
973 PROCESS_YUV(uint16_t, 3, 0xffff)
975 case BC_YUVA16161616:
976 PROCESS_YUV(uint16_t, 4, 0xffff)
987 HistogramEngine::HistogramEngine(HistogramMain *plugin,
990 : LoadServer(total_clients, total_packages)
992 this->plugin = plugin;
995 void HistogramEngine::init_packages()
1000 total_size = data->get_h();
1003 total_size = data->get_h();
1008 int package_size = (int)((float)total_size /
1009 get_total_packages() + 1);
1012 for(int i = 0; i < get_total_packages(); i++)
1014 HistogramPackage *package = (HistogramPackage*)get_package(i);
1015 package->start = total_size * i / get_total_packages();
1016 package->end = total_size * (i + 1) / get_total_packages();
1019 // Initialize clients here in case some don't get run.
1020 for(int i = 0; i < get_total_clients(); i++)
1022 HistogramUnit *unit = (HistogramUnit*)get_client(i);
1023 for(int i = 0; i < HISTOGRAM_MODES; i++)
1024 bzero(unit->accum[i], sizeof(int) * HISTOGRAM_SLOTS);
1029 LoadClient* HistogramEngine::new_client()
1031 return new HistogramUnit(this, plugin);
1034 LoadPackage* HistogramEngine::new_package()
1036 return new HistogramPackage;
1039 void HistogramEngine::process_packages(int operation, VFrame *data)
1042 this->operation = operation;
1043 LoadServer::process_packages();