6 #include "bcdisplayinfo.h"
13 #include "loadbalance.h"
14 #include "max_picon_png.h"
15 #include "mid_picon_png.h"
16 #include "min_picon_png.h"
17 #include "picon_png.h"
18 #include "plugincolors.h"
19 #include "pluginvclient.h"
25 class HistogramEngine;
26 class HistogramWindow;
29 #define HISTOGRAM_RED 0
30 #define HISTOGRAM_GREEN 1
31 #define HISTOGRAM_BLUE 2
32 #define HISTOGRAM_ALPHA 3
33 #define HISTOGRAM_VALUE 4
36 #define HISTOGRAM_RANGE 0x10000
38 #define THRESHOLD_SCALE 1000
45 int equivalent(HistogramConfig &that);
46 void copy_from(HistogramConfig &that);
47 void interpolate(HistogramConfig &prev,
48 HistogramConfig &next,
51 int64_t current_frame);
52 void reset(int do_mode);
66 class HistogramSlider : public BC_SubWindow
69 HistogramSlider(HistogramMain *plugin,
78 int button_press_event();
79 int button_release_event();
80 int cursor_motion_event();
93 HistogramMain *plugin;
97 class HistogramAuto : public BC_CheckBox
100 HistogramAuto(HistogramMain *plugin,
104 HistogramMain *plugin;
107 class HistogramMode : public BC_Radial
110 HistogramMode(HistogramMain *plugin,
116 HistogramMain *plugin;
120 class HistogramReset : public BC_GenericButton
123 HistogramReset(HistogramMain *plugin,
127 HistogramMain *plugin;
130 class HistogramText : public BC_TumbleTextBox
133 HistogramText(HistogramMain *plugin,
134 HistogramWindow *gui,
139 HistogramMain *plugin;
143 class HistogramWindow : public BC_Window
146 HistogramWindow(HistogramMain *plugin, int x, int y);
149 int create_objects();
151 void update(int do_input);
153 void update_canvas();
155 void update_output();
157 HistogramSlider *input, *output;
158 HistogramAuto *automatic;
159 HistogramMode *mode_v, *mode_r, *mode_g, *mode_b, *mode_a;
160 HistogramText *input_min;
161 HistogramText *input_mid;
162 HistogramText *input_max;
163 HistogramText *output_min;
164 HistogramText *output_max;
165 HistogramText *threshold;
166 BC_SubWindow *canvas;
167 HistogramMain *plugin;
168 BC_Pixmap *max_picon, *mid_picon, *min_picon;
173 PLUGIN_THREAD_HEADER(HistogramMain, HistogramThread, HistogramWindow)
176 class HistogramMain : public PluginVClient
179 HistogramMain(PluginServer *server);
182 int process_realtime(VFrame *input_ptr, VFrame *output_ptr);
186 void save_data(KeyFrame *keyframe);
187 void read_data(KeyFrame *keyframe);
189 void render_gui(void *data);
190 PLUGIN_CLASS_MEMBERS(HistogramConfig, HistogramThread)
192 // Convert input to input curve
193 float calculate_curve(float input, int mode);
194 // Calculate automatic settings
195 void calculate_automatic(VFrame *data);
196 // Calculate histogram
197 void calculate_histogram(VFrame *data);
202 VFrame *input, *output;
203 HistogramEngine *engine;
208 class HistogramPackage : public LoadPackage
215 class HistogramUnit : public LoadClient
218 HistogramUnit(HistogramEngine *server, HistogramMain *plugin);
220 void process_package(LoadPackage *package);
221 HistogramEngine *server;
222 HistogramMain *plugin;
226 class HistogramEngine : public LoadServer
229 HistogramEngine(HistogramMain *plugin,
232 void process_packages(int operation, VFrame *data);
233 void init_packages();
234 LoadClient* new_client();
235 LoadPackage* new_package();
236 HistogramMain *plugin;
266 REGISTER_PLUGIN(HistogramMain)
270 HistogramConfig::HistogramConfig()
275 void HistogramConfig::reset(int do_mode)
277 for(int i = 0; i < 5; i++)
280 input_mid[i] = 0x8000;
281 input_max[i] = 0xffff;
283 output_max[i] = 0xffff;
287 mode = HISTOGRAM_VALUE;
293 int HistogramConfig::equivalent(HistogramConfig &that)
295 for(int i = 0; i < 5; i++)
297 if(input_min[i] != that.input_min[i] ||
298 input_mid[i] != that.input_mid[i] ||
299 input_max[i] != that.input_max[i] ||
300 output_min[i] != that.output_min[i] ||
301 output_max[i] != that.output_max[i]) return 0;
304 if(automatic != that.automatic ||
306 threshold != that.threshold) return 0;
311 void HistogramConfig::copy_from(HistogramConfig &that)
313 for(int i = 0; i < 5; i++)
315 input_min[i] = that.input_min[i];
316 input_mid[i] = that.input_mid[i];
317 input_max[i] = that.input_max[i];
318 output_min[i] = that.output_min[i];
319 output_max[i] = that.output_max[i];
322 automatic = that.automatic;
324 threshold = that.threshold;
327 void HistogramConfig::interpolate(HistogramConfig &prev,
328 HistogramConfig &next,
331 int64_t current_frame)
333 double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
334 double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
336 for(int i = 0; i < 5; i++)
338 input_min[i] = (int)(prev.input_min[i] * prev_scale + next.input_min[i] * next_scale);
339 input_mid[i] = (int)(prev.input_mid[i] * prev_scale + next.input_mid[i] * next_scale);
340 input_max[i] = (int)(prev.input_max[i] * prev_scale + next.input_max[i] * next_scale);
341 output_min[i] = (int)(prev.output_min[i] * prev_scale + next.output_min[i] * next_scale);
342 output_max[i] = (int)(prev.output_max[i] * prev_scale + next.output_max[i] * next_scale);
344 threshold = (int)(prev.threshold * prev_scale + next.threshold * next_scale);
345 automatic = prev.automatic;
355 PLUGIN_THREAD_OBJECT(HistogramMain, HistogramThread, HistogramWindow)
359 HistogramWindow::HistogramWindow(HistogramMain *plugin, int x, int y)
360 : BC_Window(plugin->gui_string,
370 this->plugin = plugin;
373 HistogramWindow::~HistogramWindow()
377 static VFrame max_picon_image(max_picon_png);
378 static VFrame mid_picon_image(mid_picon_png);
379 static VFrame min_picon_image(min_picon_png);
381 int HistogramWindow::create_objects()
383 int x = 10, y = 10, x1 = 10;
384 int subscript = plugin->config.mode;
386 max_picon = new BC_Pixmap(this, &max_picon_image);
387 mid_picon = new BC_Pixmap(this, &mid_picon_image);
388 min_picon = new BC_Pixmap(this, &min_picon_image);
389 add_subwindow(mode_v = new HistogramMode(plugin,
395 add_subwindow(mode_r = new HistogramMode(plugin,
401 add_subwindow(mode_g = new HistogramMode(plugin,
407 add_subwindow(mode_b = new HistogramMode(plugin,
413 add_subwindow(mode_a = new HistogramMode(plugin,
421 add_subwindow(new BC_Title(x, y, _("Input min:")));
423 input_min = new HistogramText(plugin,
427 &plugin->config.input_min[subscript]);
428 input_min->create_objects();
430 add_subwindow(new BC_Title(x, y, _("Mid:")));
432 input_mid = new HistogramText(plugin,
436 &plugin->config.input_mid[subscript]);
437 input_mid->create_objects();
438 input_mid->update((int64_t)plugin->config.input_mid[subscript]);
440 add_subwindow(new BC_Title(x, y, _("Max:")));
442 input_max = new HistogramText(plugin,
446 &plugin->config.input_max[subscript]);
447 input_max->create_objects();
451 add_subwindow(canvas = new BC_SubWindow(x,
457 y += canvas->get_h() + 10;
458 add_subwindow(input = new HistogramSlider(plugin,
467 y += input->get_h() + 10;
468 add_subwindow(new BC_Title(x, y, _("Output min:")));
470 output_min = new HistogramText(plugin,
474 &plugin->config.output_min[subscript]);
475 output_min->create_objects();
477 add_subwindow(new BC_Title(x, y, _("Max:")));
479 output_max = new HistogramText(plugin,
483 &plugin->config.output_max[subscript]);
484 output_max->create_objects();
491 add_subwindow(output = new HistogramSlider(plugin,
502 add_subwindow(automatic = new HistogramAuto(plugin,
507 add_subwindow(new HistogramReset(plugin,
511 add_subwindow(new BC_Title(x, y, _("Threshold:")));
513 threshold = new HistogramText(plugin,
517 &plugin->config.threshold);
518 threshold->create_objects();
525 WINDOW_CLOSE_EVENT(HistogramWindow)
527 void HistogramWindow::update(int do_input)
529 automatic->update(plugin->config.automatic);
530 threshold->update((int64_t)plugin->config.threshold);
533 if(do_input) update_input();
537 void HistogramWindow::update_input()
539 int subscript = plugin->config.mode;
541 input_min->update((int64_t)plugin->config.input_min[subscript]);
542 input_mid->update((int64_t)plugin->config.input_mid[subscript]);
543 input_max->update((int64_t)plugin->config.input_max[subscript]);
546 void HistogramWindow::update_output()
548 int subscript = plugin->config.mode;
550 output_min->update((int64_t)plugin->config.output_min[subscript]);
551 output_max->update((int64_t)plugin->config.output_max[subscript]);
554 void HistogramWindow::update_mode()
556 mode_v->update(plugin->config.mode == HISTOGRAM_VALUE ? 1 : 0);
557 mode_r->update(plugin->config.mode == HISTOGRAM_RED ? 1 : 0);
558 mode_g->update(plugin->config.mode == HISTOGRAM_GREEN ? 1 : 0);
559 mode_b->update(plugin->config.mode == HISTOGRAM_BLUE ? 1 : 0);
560 mode_a->update(plugin->config.mode == HISTOGRAM_ALPHA ? 1 : 0);
561 input_min->output = &plugin->config.input_min[plugin->config.mode];
562 input_mid->output = &plugin->config.input_mid[plugin->config.mode];
563 input_max->output = &plugin->config.input_max[plugin->config.mode];
564 output_min->output = &plugin->config.output_min[plugin->config.mode];
565 output_max->output = &plugin->config.output_max[plugin->config.mode];
568 void HistogramWindow::update_canvas()
570 int64_t *accum = plugin->accum[plugin->config.mode];
571 int canvas_w = canvas->get_w();
572 int canvas_h = canvas->get_h();
573 int accum_per_canvas_i = HISTOGRAM_RANGE / canvas_w + 1;
574 float accum_per_canvas_f = (float)HISTOGRAM_RANGE / canvas_w;
577 for(int i = 0; i < HISTOGRAM_RANGE; i++)
579 if(accum[i] > normalize) normalize = accum[i];
585 for(int i = 0; i < canvas_w; i++)
587 int accum_start = (int)(accum_per_canvas_f * i);
588 int accum_end = accum_start + accum_per_canvas_i;
590 for(int j = accum_start; j < accum_end; j++)
592 max = MAX(accum[j], max);
594 //printf("HistogramWindow::update_canvas 1 %d %d\n", i, max);
596 // max = max * canvas_h / normalize;
597 max = (int)(log(max) / log(normalize) * canvas_h);
600 canvas->set_color(0xffffff);
601 canvas->draw_line(i, 0, i, canvas_h - max);
602 canvas->set_color(0x000000);
603 canvas->draw_line(i, canvas_h - max, i, canvas_h);
608 canvas->set_color(0xffffff);
609 canvas->draw_box(0, 0, canvas_w, canvas_h);
612 canvas->set_color(0x00ff00);
614 for(int i = 0; i < canvas_w; i++)
616 int y2 = canvas_h - (int)(plugin->calculate_curve((float)i / canvas_w * (HISTOGRAM_RANGE - 1),
617 plugin->config.mode) * canvas_h / (HISTOGRAM_RANGE - 1));
620 canvas->draw_line(i - 1, y1, i, y2);
635 HistogramReset::HistogramReset(HistogramMain *plugin,
638 : BC_GenericButton(x, y, _("Reset"))
640 this->plugin = plugin;
642 int HistogramReset::handle_event()
644 plugin->config.reset(0);
645 plugin->thread->window->update(1);
646 plugin->send_configure_change();
658 HistogramSlider::HistogramSlider(HistogramMain *plugin,
659 HistogramWindow *gui,
665 : BC_SubWindow(x, y, w, h)
667 this->plugin = plugin;
669 this->is_input = is_input;
673 int HistogramSlider::button_press_event()
675 if(is_event_win() && cursor_inside())
677 int subscript = plugin->config.mode;
682 int half_h = get_h() / 2;
686 int x1 = (int)(plugin->config.input_mid[subscript] * w / 0xffff) -
687 gui->mid_picon->get_w() / 2;
688 int x2 = x1 + gui->mid_picon->get_w();
689 if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
690 get_cursor_y() >= half_h && get_cursor_y() < h)
692 operation = DRAG_MID_INPUT;
696 if(operation == NONE)
700 int x1 = (int)(plugin->config.input_min[subscript] * w / 0xffff) -
701 gui->mid_picon->get_w() / 2;
702 int x2 = x1 + gui->mid_picon->get_w();
703 if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
704 get_cursor_y() >= half_h && get_cursor_y() < h)
706 operation = DRAG_MIN_INPUT;
711 int x1 = (int)(plugin->config.output_min[subscript] * w / 0xffff) -
712 gui->mid_picon->get_w() / 2;
713 int x2 = x1 + gui->mid_picon->get_w();
714 if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
715 get_cursor_y() >= half_h && get_cursor_y() < h)
717 operation = DRAG_MIN_OUTPUT;
722 if(operation == NONE)
726 int x1 = (int)(plugin->config.input_max[subscript] * w / 0xffff) -
727 gui->mid_picon->get_w() / 2;
728 int x2 = x1 + gui->mid_picon->get_w();
729 if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
730 get_cursor_y() >= half_h && get_cursor_y() < h)
732 operation = DRAG_MAX_INPUT;
737 int x1 = (int)(plugin->config.output_max[subscript] * w / 0xffff) -
738 gui->mid_picon->get_w() / 2;
739 int x2 = x1 + gui->mid_picon->get_w();
740 if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
741 get_cursor_y() >= half_h && get_cursor_y() < h)
743 operation = DRAG_MAX_OUTPUT;
752 int HistogramSlider::button_release_event()
754 if(operation != NONE)
762 int HistogramSlider::cursor_motion_event()
764 //printf("HistogramSlider::cursor_motion_event 1\n");
765 if(operation != NONE)
767 float value = (float)get_cursor_x() * 0xffff / get_w();
768 CLAMP(value, 0, 0xffff);
769 int subscript = plugin->config.mode;
770 float input_min = plugin->config.input_min[subscript];
771 float input_max = plugin->config.input_max[subscript];
772 float input_mid = plugin->config.input_mid[subscript];
773 float input_mid_fraction = (input_mid - input_min) / (input_max - input_min);
778 input_min = MIN(input_max, value);
779 plugin->config.input_min[subscript] = (int)input_min;
780 input_mid = input_min + (input_max - input_min) * input_mid_fraction;
783 CLAMP(value, input_min, input_max);
784 input_mid = (int)value;
787 input_max = MAX(input_mid, value);
788 input_mid = input_min + (input_max - input_min) * input_mid_fraction;
790 case DRAG_MIN_OUTPUT:
791 value = MIN(plugin->config.output_max[subscript], value);
792 plugin->config.output_min[subscript] = (int)value;
794 case DRAG_MAX_OUTPUT:
795 value = MAX(plugin->config.output_min[subscript], value);
796 plugin->config.output_max[subscript] = (int)value;
800 if(operation == DRAG_MIN_INPUT ||
801 operation == DRAG_MID_INPUT ||
802 operation == DRAG_MAX_INPUT)
804 plugin->config.input_mid[subscript] = (int)input_mid;
805 plugin->config.input_min[subscript] = (int)input_min;
806 plugin->config.input_max[subscript] = (int)input_max;
811 gui->update_output();
814 gui->unlock_window();
815 plugin->send_configure_change();
816 //printf("HistogramSlider::cursor_motion_event 2\n");
817 gui->lock_window("HistogramSlider::cursor_motion_event");
818 //printf("HistogramSlider::cursor_motion_event 3\n");
824 void HistogramSlider::update()
828 int half_h = get_h() / 2;
829 int quarter_h = get_h() / 4;
830 int mode = plugin->config.mode;
834 int subscript = plugin->config.mode;
836 clear_box(0, 0, w, h);
843 case HISTOGRAM_GREEN:
851 for(int i = 0; i < w; i++)
853 int color = (int)(i * 0xff / w);
854 set_color(((r * color / 0xff) << 16) |
855 ((g * color / 0xff) << 8) |
860 draw_line(i, quarter_h, i, half_h);
861 color = (int)plugin->calculate_curve(i * 0xffff / w,
863 set_color(((r * color / 0xffff) << 16) |
864 ((g * color / 0xffff) << 8) |
865 (b * color / 0xffff));
866 draw_line(i, 0, i, quarter_h);
869 draw_line(i, 0, i, half_h);
878 draw_pixmap(gui->mid_picon,
879 (int)(plugin->config.input_mid[subscript] * w / 0xffff) -
880 gui->mid_picon->get_w() / 2,
882 min = plugin->config.input_min[subscript];
883 max = plugin->config.input_max[subscript];
887 min = plugin->config.output_min[subscript];
888 max = plugin->config.output_max[subscript];
891 draw_pixmap(gui->min_picon,
892 min * w / 0xffff - gui->min_picon->get_w() / 2,
894 draw_pixmap(gui->max_picon,
895 max * w / 0xffff - gui->max_picon->get_w() / 2,
898 // printf("HistogramSlider::update %d %d\n", min, max);
911 HistogramAuto::HistogramAuto(HistogramMain *plugin,
914 : BC_CheckBox(x, y, plugin->config.automatic, _("Automatic"))
916 this->plugin = plugin;
919 int HistogramAuto::handle_event()
921 plugin->config.automatic = get_value();
922 plugin->send_configure_change();
929 HistogramMode::HistogramMode(HistogramMain *plugin,
934 : BC_Radial(x, y, plugin->config.mode == value, text)
936 this->plugin = plugin;
939 int HistogramMode::handle_event()
941 plugin->config.mode = value;
942 plugin->thread->window->update_mode();
943 plugin->thread->window->input->update();
944 plugin->thread->window->output->update();
945 plugin->send_configure_change();
957 HistogramText::HistogramText(HistogramMain *plugin,
958 HistogramWindow *gui,
962 : BC_TumbleTextBox(gui,
970 this->plugin = plugin;
971 this->output = output;
975 int HistogramText::handle_event()
979 *output = atol(get_text());
981 plugin->thread->window->input->update();
982 plugin->thread->window->output->update();
983 plugin->send_configure_change();
1008 HistogramMain::HistogramMain(PluginServer *server)
1009 : PluginVClient(server)
1011 PLUGIN_CONSTRUCTOR_MACRO
1013 lookup[0] = lookup[1] = lookup[2] = lookup[3] = 0;
1014 accum[0] = accum[1] = accum[2] = accum[3] = accum[3] = 0;
1017 HistogramMain::~HistogramMain()
1019 PLUGIN_DESTRUCTOR_MACRO
1020 if(lookup[0]) delete [] lookup[0];
1021 if(lookup[1]) delete [] lookup[1];
1022 if(lookup[2]) delete [] lookup[2];
1023 if(lookup[3]) delete [] lookup[3];
1024 if(accum[0]) delete [] accum[0];
1025 if(accum[1]) delete [] accum[1];
1026 if(accum[2]) delete [] accum[2];
1027 if(accum[3]) delete [] accum[3];
1028 if(accum[4]) delete [] accum[4];
1029 if(engine) delete engine;
1032 char* HistogramMain::plugin_title() { return _("Histogram"); }
1033 int HistogramMain::is_realtime() { return 1; }
1036 NEW_PICON_MACRO(HistogramMain)
1038 SHOW_GUI_MACRO(HistogramMain, HistogramThread)
1040 SET_STRING_MACRO(HistogramMain)
1042 RAISE_WINDOW_MACRO(HistogramMain)
1044 LOAD_CONFIGURATION_MACRO(HistogramMain, HistogramConfig)
1046 void HistogramMain::render_gui(void *data)
1050 calculate_histogram((VFrame*)data);
1051 if(config.automatic)
1053 calculate_automatic((VFrame*)data);
1056 thread->window->lock_window("HistogramMain::render_gui");
1057 thread->window->update_canvas();
1058 if(config.automatic)
1060 thread->window->update_input();
1062 thread->window->unlock_window();
1066 void HistogramMain::update_gui()
1070 thread->window->lock_window("HistogramMain::update_gui");
1071 int reconfigure = load_configuration();
1074 thread->window->update(0);
1075 if(!config.automatic)
1077 thread->window->update_input();
1080 thread->window->unlock_window();
1085 int HistogramMain::load_defaults()
1087 char directory[BCTEXTLEN], string[BCTEXTLEN];
1088 // set the default directory
1089 sprintf(directory, "%shistogram.rc", BCASTDIR);
1091 // load the defaults
1092 defaults = new Defaults(directory);
1095 for(int i = 0; i < 5; i++)
1097 sprintf(string, "INPUT_MIN_%d", i);
1098 config.input_min[i] = defaults->get(string, config.input_min[i]);
1099 sprintf(string, "INPUT_MID_%d", i);
1100 config.input_mid[i] = defaults->get(string, config.input_mid[i]);
1101 sprintf(string, "INPUT_MAX_%d", i);
1102 config.input_max[i] = defaults->get(string, config.input_max[i]);
1103 sprintf(string, "OUTPUT_MIN_%d", i);
1104 config.output_min[i] = defaults->get(string, config.output_min[i]);
1105 sprintf(string, "OUTPUT_MAX_%d", i);
1106 config.output_max[i] = defaults->get(string, config.output_max[i]);
1107 //printf("HistogramMain::load_defaults %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
1109 config.automatic = defaults->get("AUTOMATIC", config.automatic);
1110 config.mode = defaults->get("MODE", config.mode);
1111 config.threshold = defaults->get("THRESHOLD", config.threshold);
1116 int HistogramMain::save_defaults()
1118 char string[BCTEXTLEN];
1119 //printf("HistogramMain::save_defaults 1 %p\n", defaults);
1120 for(int i = 0; i < 5; i++)
1122 //printf("HistogramMain::save_defaults 1 %d\n", i);
1123 sprintf(string, "INPUT_MIN_%d", i);
1124 defaults->update(string, config.input_min[i]);
1125 //printf("HistogramMain::save_defaults 1 %d\n", i);
1126 sprintf(string, "INPUT_MID_%d", i);
1127 defaults->update(string, config.input_mid[i]);
1128 //printf("HistogramMain::save_defaults 1 %d\n", i);
1129 sprintf(string, "INPUT_MAX_%d", i);
1130 //printf("HistogramMain::save_defaults 1 %d\n", config.input_max[i]);
1131 defaults->update(string, config.input_max[i]);
1132 //printf("HistogramMain::save_defaults 1 %d\n", i);
1133 sprintf(string, "OUTPUT_MIN_%d", i);
1134 defaults->update(string, config.output_min[i]);
1135 //printf("HistogramMain::save_defaults 1 %d\n", i);
1136 sprintf(string, "OUTPUT_MAX_%d", i);
1137 defaults->update(string, config.output_max[i]);
1138 //printf("HistogramMain::save_defaults %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
1140 //printf("HistogramMain::save_defaults 3\n");
1141 defaults->update("AUTOMATIC", config.automatic);
1142 //printf("HistogramMain::save_defaults 4\n");
1143 defaults->update("MODE", config.mode);
1144 defaults->update("THRESHOLD", config.threshold);
1145 //printf("HistogramMain::save_defaults 5\n");
1147 //printf("HistogramMain::save_defaults 6\n");
1153 void HistogramMain::save_data(KeyFrame *keyframe)
1157 // cause data to be stored directly in text
1158 output.set_shared_string(keyframe->data, MESSAGESIZE);
1159 output.tag.set_title("HISTOGRAM");
1161 char string[BCTEXTLEN];
1162 for(int i = 0; i < 5; i++)
1164 sprintf(string, "INPUT_MIN_%d", i);
1165 output.tag.set_property(string, config.input_min[i]);
1166 sprintf(string, "INPUT_MID_%d", i);
1167 output.tag.set_property(string, config.input_mid[i]);
1168 sprintf(string, "INPUT_MAX_%d", i);
1169 output.tag.set_property(string, config.input_max[i]);
1170 sprintf(string, "OUTPUT_MIN_%d", i);
1171 output.tag.set_property(string, config.output_min[i]);
1172 sprintf(string, "OUTPUT_MAX_%d", i);
1173 output.tag.set_property(string, config.output_max[i]);
1174 //printf("HistogramMain::save_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
1176 output.tag.set_property("AUTOMATIC", config.automatic);
1177 output.tag.set_property("MODE", config.mode);
1178 output.tag.set_property("THRESHOLD", config.threshold);
1179 output.append_tag();
1180 output.terminate_string();
1183 void HistogramMain::read_data(KeyFrame *keyframe)
1187 input.set_shared_string(keyframe->data, strlen(keyframe->data));
1193 result = input.read_tag();
1197 if(input.tag.title_is("HISTOGRAM"))
1199 char string[BCTEXTLEN];
1200 for(int i = 0; i < 5; i++)
1202 sprintf(string, "INPUT_MIN_%d", i);
1203 config.input_min[i] = input.tag.get_property(string, config.input_min[i]);
1204 sprintf(string, "INPUT_MID_%d", i);
1205 config.input_mid[i] = input.tag.get_property(string, config.input_mid[i]);
1206 sprintf(string, "INPUT_MAX_%d", i);
1207 config.input_max[i] = input.tag.get_property(string, config.input_max[i]);
1208 sprintf(string, "OUTPUT_MIN_%d", i);
1209 config.output_min[i] = input.tag.get_property(string, config.output_min[i]);
1210 sprintf(string, "OUTPUT_MAX_%d", i);
1211 config.output_max[i] = input.tag.get_property(string, config.output_max[i]);
1212 //printf("HistogramMain::read_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
1214 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
1215 config.mode = input.tag.get_property("MODE", config.mode);
1216 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
1222 float HistogramMain::calculate_curve(float input, int subscript)
1224 float y1, y2, y3, y4;
1225 float min = (float)config.input_min[subscript];
1226 float max = (float)config.input_max[subscript];
1227 float mid = (float)config.input_mid[subscript];
1228 float half = (float)HISTOGRAM_RANGE / 2;
1229 float output, output_perfect;
1230 float control = 1.0 / M_PI;
1231 float output_linear;
1234 if(input < min) return 0;
1237 if(input >= max) return HISTOGRAM_RANGE - 1;
1239 float slope1 = half / (mid - min);
1240 float slope2 = half / (max - mid);
1241 float min_slope = MIN(slope1, slope2);
1243 // value of 45` diagonal with midpoint
1244 output_perfect = half + min_slope * (input - mid);
1248 // Fraction of perfect diagonal to use
1249 float mid_fraction = (input - min) / (mid - min);
1250 // value of line connecting min to mid
1251 output_linear = mid_fraction * half;
1252 // Blend perfect diagonal with linear
1253 output = output_linear * (1.0 - mid_fraction) + output_perfect * mid_fraction;
1257 // Fraction of perfect diagonal to use
1258 float mid_fraction = (max - input) / (max - mid);
1259 // value of line connecting max to mid
1260 output_linear = half + (1.0 - mid_fraction) * half;
1261 // Blend perfect diagonal with linear
1262 output = output_linear * (1.0 - mid_fraction) + output_perfect * mid_fraction;
1271 // printf("HistogramMain::calculate_curve 1 %.0f %.0f %.0f %.0f %.0f\n",
1280 void HistogramMain::calculate_histogram(VFrame *data)
1283 if(!engine) engine = new HistogramEngine(this,
1284 get_project_smp() + 1,
1285 get_project_smp() + 1);
1289 for(int i = 0; i < 5; i++)
1290 accum[i] = new int64_t[HISTOGRAM_RANGE];
1292 engine->process_packages(HistogramEngine::HISTOGRAM, data);
1294 for(int i = 0; i < engine->get_total_clients(); i++)
1296 HistogramUnit *unit = (HistogramUnit*)engine->get_client(i);
1299 for(int j = 0; j < 5; j++)
1300 memcpy(accum[j], unit->accum[j], sizeof(int64_t) * HISTOGRAM_RANGE);
1304 for(int j = 0; j < 5; j++)
1306 int64_t *out = accum[j];
1307 int64_t *in = unit->accum[j];
1308 for(int k = 0; k < HISTOGRAM_RANGE; k++)
1314 // Remove top and bottom from calculations. Doesn't work in high
1315 // precision colormodels.
1316 for(int i = 0; i < 5; i++)
1319 accum[i][HISTOGRAM_RANGE - 1] = 0;
1324 void HistogramMain::calculate_automatic(VFrame *data)
1326 calculate_histogram(data);
1329 for(int i = 0; i < 3; i++)
1331 int64_t *accum = this->accum[i];
1334 for(int j = 0; j < HISTOGRAM_RANGE; j++)
1336 max = MAX(accum[j], max);
1339 int threshold = config.threshold * max / THRESHOLD_SCALE;
1343 config.input_min[i] = 0;
1344 for(int j = 0; j < HISTOGRAM_RANGE; j++)
1346 if(accum[j] > threshold)
1348 config.input_min[i] = j;
1355 config.input_max[i] = 0xffff;
1356 for(int j = HISTOGRAM_RANGE - 1; j >= 0; j--)
1358 if(accum[j] > threshold)
1360 config.input_max[i] = j;
1365 config.input_mid[i] = (config.input_min[i] + config.input_max[i]) / 2;
1374 int HistogramMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
1376 TRACE("HistogramMain::process_realtime");
1377 int need_reconfigure = load_configuration();
1380 if(!engine) engine = new HistogramEngine(this,
1381 get_project_smp() + 1,
1382 get_project_smp() + 1);
1383 this->input = input_ptr;
1384 this->output = output_ptr;
1386 send_render_gui(input_ptr);
1388 if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
1390 output_ptr->copy_from(input_ptr);
1392 //printf("HistogramMain::process_realtime 1\n");
1394 // Generate tables here. The same table is used by many packages to render
1395 // each horizontal stripe. Need to cover the entire output range in each
1396 // table to avoid green borders
1397 if(need_reconfigure || !lookup[0] || config.automatic)
1400 for(int i = 0; i < 4; i++)
1401 lookup[i] = new int[HISTOGRAM_RANGE];
1403 // Calculate new curves
1404 if(config.automatic)
1406 calculate_automatic(input);
1409 engine->process_packages(HistogramEngine::TABULATE, input);
1413 // Convert 16 bit lookup table to 8 bits
1414 switch(input->get_color_model())
1418 for(int i = 0; i < 0x100; i++)
1420 int subscript = (i << 8) | i;
1421 lookup[0][i] = lookup[0][subscript];
1422 lookup[1][i] = lookup[1][subscript];
1423 lookup[2][i] = lookup[2][subscript];
1424 lookup[3][i] = lookup[3][subscript];
1429 //printf("HistogramMain::process_realtime 1\n");
1435 engine->process_packages(HistogramEngine::APPLY, input);
1437 //printf("HistogramMain::process_realtime 100\n");
1450 HistogramPackage::HistogramPackage()
1458 HistogramUnit::HistogramUnit(HistogramEngine *server,
1459 HistogramMain *plugin)
1460 : LoadClient(server)
1462 this->plugin = plugin;
1463 this->server = server;
1464 for(int i = 0; i < 5; i++)
1465 accum[i] = new int64_t[HISTOGRAM_RANGE];
1468 HistogramUnit::~HistogramUnit()
1470 for(int i = 0; i < 5; i++)
1474 void HistogramUnit::process_package(LoadPackage *package)
1476 HistogramPackage *pkg = (HistogramPackage*)package;
1478 if(server->operation == HistogramEngine::HISTOGRAM)
1481 #define HISTOGRAM_HEAD(type) \
1483 for(int i = pkg->start; i < pkg->end; i++) \
1485 type *row = (type*)data->get_rows()[i]; \
1486 for(int j = 0; j < w; j++) \
1489 #define HISTOGRAM_TAIL(components) \
1495 if(components == 4) accum_a[row[3]]++; \
1497 row += components; \
1505 VFrame *data = server->data;
1506 int w = data->get_w();
1507 int h = data->get_h();
1508 int64_t *accum_r = accum[HISTOGRAM_RED];
1509 int64_t *accum_g = accum[HISTOGRAM_GREEN];
1510 int64_t *accum_b = accum[HISTOGRAM_BLUE];
1511 int64_t *accum_a = accum[HISTOGRAM_ALPHA];
1512 int64_t *accum_v = accum[HISTOGRAM_VALUE];
1513 int r, g, b, a, y, u, v;
1515 switch(data->get_color_model())
1518 HISTOGRAM_HEAD(unsigned char)
1519 r = (row[0] << 8) | row[0];
1520 g = (row[1] << 8) | row[1];
1521 b = (row[2] << 8) | row[2];
1525 HISTOGRAM_HEAD(unsigned char)
1526 y = (row[0] << 8) | row[0];
1527 u = (row[1] << 8) | row[1];
1528 v = (row[2] << 8) | row[2];
1529 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
1533 HISTOGRAM_HEAD(unsigned char)
1534 r = (row[0] << 8) | row[0];
1535 g = (row[1] << 8) | row[1];
1536 b = (row[2] << 8) | row[2];
1540 HISTOGRAM_HEAD(unsigned char)
1541 y = (row[0] << 8) | row[0];
1542 u = (row[1] << 8) | row[1];
1543 v = (row[2] << 8) | row[2];
1544 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
1548 HISTOGRAM_HEAD(uint16_t)
1555 HISTOGRAM_HEAD(uint16_t)
1559 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
1562 case BC_RGBA16161616:
1563 HISTOGRAM_HEAD(uint16_t)
1569 case BC_YUVA16161616:
1570 HISTOGRAM_HEAD(uint16_t)
1574 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
1580 if(server->operation == HistogramEngine::APPLY)
1585 #define PROCESS(type, components) \
1587 for(int i = pkg->start; i < pkg->end; i++) \
1589 type *row = (type*)input->get_rows()[i]; \
1590 for(int j = 0; j < w; j++) \
1592 row[0] = lookup_r[row[0]]; \
1593 row[1] = lookup_g[row[1]]; \
1594 row[2] = lookup_b[row[2]]; \
1595 if(components == 4) row[3] = lookup_a[row[3]]; \
1596 row += components; \
1601 #define PROCESS_YUV(type, components, max) \
1603 for(int i = pkg->start; i < pkg->end; i++) \
1605 type *row = (type*)input->get_rows()[i]; \
1606 for(int j = 0; j < w; j++) \
1608 /* Convert to 16 bit RGB */ \
1611 y = (row[0] << 8) | row[0]; \
1612 u = (row[1] << 8) | row[1]; \
1613 v = (row[2] << 8) | row[2]; \
1614 if(components == 4) a = (row[3] << 8) | row[3]; \
1621 if(components == 4) a = row[3]; \
1624 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v); \
1626 /* Look up in RGB domain */ \
1630 if(components == 4) a = lookup_a[a]; \
1632 /* Convert to 16 bit YUV */ \
1633 plugin->yuv.rgb_to_yuv_16(r, g, b, y, u, v); \
1640 if(components == 4) row[3] = a >> 8; \
1647 if(components == 4) row[3] = a; \
1649 row += components; \
1657 VFrame *input = plugin->input;
1658 VFrame *output = plugin->output;
1659 int w = input->get_w();
1660 int h = input->get_h();
1661 int *lookup_r = plugin->lookup[0];
1662 int *lookup_g = plugin->lookup[1];
1663 int *lookup_b = plugin->lookup[2];
1664 int *lookup_a = plugin->lookup[3];
1665 int r, g, b, y, u, v, a;
1666 switch(input->get_color_model())
1669 PROCESS(unsigned char, 3)
1672 PROCESS(unsigned char, 4)
1675 PROCESS(uint16_t, 3)
1677 case BC_RGBA16161616:
1678 PROCESS(uint16_t, 4)
1681 PROCESS_YUV(unsigned char, 3, 0xff)
1684 PROCESS_YUV(unsigned char, 4, 0xff)
1687 PROCESS_YUV(uint16_t, 3, 0xffff)
1689 case BC_YUVA16161616:
1690 PROCESS_YUV(uint16_t, 4, 0xffff)
1694 if(server->operation == HistogramEngine::TABULATE)
1696 // Do conversion in 16 bit YUVA
1697 int min_output_r = plugin->config.output_min[0];
1698 int min_output_g = plugin->config.output_min[1];
1699 int min_output_b = plugin->config.output_min[2];
1700 int min_output_a = plugin->config.output_min[3];
1701 int min_output_v = plugin->config.output_min[4];
1702 int max_output_r = plugin->config.output_max[0];
1703 int max_output_g = plugin->config.output_max[1];
1704 int max_output_b = plugin->config.output_max[2];
1705 int max_output_a = plugin->config.output_max[3];
1706 int max_output_v = plugin->config.output_max[4];
1707 int colormodel = plugin->input->get_color_model();
1709 for(int i = pkg->start; i < pkg->end; i++)
1712 float r = plugin->calculate_curve((float)i, HISTOGRAM_RED);
1713 float g = plugin->calculate_curve((float)i, HISTOGRAM_GREEN);
1714 float b = plugin->calculate_curve((float)i, HISTOGRAM_BLUE);
1715 float a = plugin->calculate_curve((float)i, HISTOGRAM_ALPHA);
1718 r = plugin->calculate_curve((float)r, HISTOGRAM_VALUE);
1719 g = plugin->calculate_curve((float)g, HISTOGRAM_VALUE);
1720 b = plugin->calculate_curve((float)b, HISTOGRAM_VALUE);
1726 r = (float)min_output_r +
1728 (max_output_r - min_output_r) /
1729 (HISTOGRAM_RANGE - 1);
1732 (max_output_g - min_output_g) /
1733 (HISTOGRAM_RANGE - 1);
1736 (max_output_b - min_output_b) /
1737 (HISTOGRAM_RANGE - 1);
1740 (max_output_a - min_output_a) /
1741 (HISTOGRAM_RANGE - 1);
1743 //printf(" 1 %d -> ", r);
1746 (max_output_v - min_output_v) /
1747 (HISTOGRAM_RANGE - 1);
1748 //printf("%d\n", r);
1751 (max_output_v - min_output_v) /
1752 (HISTOGRAM_RANGE - 1);
1755 (max_output_v - min_output_v) /
1756 (HISTOGRAM_RANGE - 1);
1759 (max_output_v - min_output_v) /
1760 (HISTOGRAM_RANGE - 1);
1764 // Convert to desired colormodel
1769 plugin->lookup[0][i] = ((int)r) >> 8;
1770 plugin->lookup[1][i] = ((int)g) >> 8;
1771 plugin->lookup[2][i] = ((int)b) >> 8;
1772 plugin->lookup[3][i] = ((int)a) >> 8;
1775 // Can't look up yuv.
1776 plugin->lookup[0][i] = (int)r;
1777 plugin->lookup[1][i] = (int)g;
1778 plugin->lookup[2][i] = (int)b;
1779 plugin->lookup[3][i] = (int)a;
1791 HistogramEngine::HistogramEngine(HistogramMain *plugin,
1794 : LoadServer(total_clients, total_packages)
1796 this->plugin = plugin;
1799 void HistogramEngine::init_packages()
1806 total_size = data->get_h();
1809 total_size = HISTOGRAM_RANGE;
1812 total_size = data->get_h();
1815 int package_size = (int)((float)total_size /
1816 get_total_packages() + 1);
1818 for(int i = 0; i < get_total_packages(); i++)
1820 HistogramPackage *package = (HistogramPackage*)get_package(i);
1821 package->start = start;
1822 package->end = start + package_size;
1823 if(package->end > total_size)
1824 package->end = total_size;
1825 start = package->end;
1828 // Initialize clients here in case some don't get run.
1829 for(int i = 0; i < get_total_clients(); i++)
1831 HistogramUnit *unit = (HistogramUnit*)get_client(i);
1832 for(int i = 0; i < 5; i++)
1833 bzero(unit->accum[i], sizeof(int64_t) * HISTOGRAM_RANGE);
1837 LoadClient* HistogramEngine::new_client()
1839 return new HistogramUnit(this, plugin);
1842 LoadPackage* HistogramEngine::new_package()
1844 return new HistogramPackage;
1847 void HistogramEngine::process_packages(int operation, VFrame *data)
1850 this->operation = operation;
1851 LoadServer::process_packages();