Merge CV, ver=5.1; ops/methods from HV, and interface from CV where possible
[goodguy/history.git] / cinelerra-5.1 / plugins / histogram / histogram.C
diff --git a/cinelerra-5.1/plugins/histogram/histogram.C b/cinelerra-5.1/plugins/histogram/histogram.C
new file mode 100644 (file)
index 0000000..b248785
--- /dev/null
@@ -0,0 +1,1225 @@
+
+/*
+ * CINELERRA
+ * Copyright (C) 2008-2012 Adam Williams <broadcast at earthling dot net>
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ */
+
+#include <math.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "bcdisplayinfo.h"
+#include "bcsignals.h"
+#include "clip.h"
+#include "bchash.h"
+#include "filexml.h"
+#include "histogram.h"
+#include "histogramconfig.h"
+#include "histogramwindow.h"
+#include "keyframe.h"
+#include "language.h"
+#include "loadbalance.h"
+#include "playback3d.h"
+#include "cicolors.h"
+#include "vframe.h"
+#include "workarounds.h"
+
+#include "aggregated.h"
+#include "../colorbalance/aggregated.h"
+#include "../interpolate/aggregated.h"
+#include "../gamma/aggregated.h"
+
+class HistogramMain;
+class HistogramEngine;
+class HistogramWindow;
+
+
+
+
+
+REGISTER_PLUGIN(HistogramMain)
+
+
+
+
+
+
+
+
+
+HistogramMain::HistogramMain(PluginServer *server)
+ : PluginVClient(server)
+{
+       
+       engine = 0;
+       for(int i = 0; i < HISTOGRAM_MODES; i++)
+       {
+               lookup[i] = 0;
+               accum[i] = 0;
+               preview_lookup[i] = 0;
+       }
+       current_point = -1;
+       mode = HISTOGRAM_VALUE;
+       dragging_point = 0;
+       input = 0;
+       output = 0;
+       w = 440;
+       h = 500;
+       parade = 0;
+}
+
+HistogramMain::~HistogramMain()
+{
+       
+       for(int i = 0; i < HISTOGRAM_MODES;i++)
+       {
+               delete [] lookup[i];
+               delete [] accum[i];
+               delete [] preview_lookup[i];
+       }
+       delete engine;
+}
+
+const char* HistogramMain::plugin_title() { return _("Histogram"); }
+int HistogramMain::is_realtime() { return 1; }
+
+
+
+NEW_WINDOW_MACRO(HistogramMain, HistogramWindow)
+
+LOAD_CONFIGURATION_MACRO(HistogramMain, HistogramConfig)
+
+void HistogramMain::render_gui(void *data)
+{
+       if(thread)
+       {
+// Process just the RGB values to determine the automatic points or
+// all the points if manual
+               if(!config.automatic)
+               {
+// Generate curves for value histogram
+// Lock out changes to curves
+                       ((HistogramWindow*)thread->window)->lock_window("HistogramMain::render_gui 1");
+                       tabulate_curve(HISTOGRAM_RED, 0);
+                       tabulate_curve(HISTOGRAM_GREEN, 0);
+                       tabulate_curve(HISTOGRAM_BLUE, 0);
+                       ((HistogramWindow*)thread->window)->unlock_window();
+               }
+
+               calculate_histogram((VFrame*)data, !config.automatic);
+
+
+               if(config.automatic)
+               {
+                       calculate_automatic((VFrame*)data);
+
+// Generate curves for value histogram
+// Lock out changes to curves
+                       ((HistogramWindow*)thread->window)->lock_window("HistogramMain::render_gui 1");
+                       tabulate_curve(HISTOGRAM_RED, 0);
+                       tabulate_curve(HISTOGRAM_GREEN, 0);
+                       tabulate_curve(HISTOGRAM_BLUE, 0);
+                       ((HistogramWindow*)thread->window)->unlock_window();
+
+
+// Need a second pass to get the luminance values.
+                       calculate_histogram((VFrame*)data, 1);
+               }
+
+               ((HistogramWindow*)thread->window)->lock_window("HistogramMain::render_gui 2");
+// Always draw the histogram but don't update widgets if automatic
+               ((HistogramWindow*)thread->window)->update(1,
+                       config.automatic && mode != HISTOGRAM_VALUE,
+                       config.automatic && mode != HISTOGRAM_VALUE,
+                       0);
+
+               ((HistogramWindow*)thread->window)->unlock_window();
+       }
+}
+
+void HistogramMain::update_gui()
+{
+       if(thread)
+       {
+               ((HistogramWindow*)thread->window)->lock_window("HistogramMain::update_gui");
+               int reconfigure = load_configuration();
+               if(reconfigure) 
+               {
+                       ((HistogramWindow*)thread->window)->update(1,
+                               1,
+                               1,
+                               1);
+               }
+               ((HistogramWindow*)thread->window)->unlock_window();
+       }
+}
+
+
+
+
+void HistogramMain::save_data(KeyFrame *keyframe)
+{
+       FileXML output;
+
+// cause data to be stored directly in text
+       output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
+       output.tag.set_title("HISTOGRAM");
+
+       char string[BCTEXTLEN];
+
+       output.tag.set_property("AUTOMATIC", config.automatic);
+       output.tag.set_property("THRESHOLD", config.threshold);
+       output.tag.set_property("PLOT", config.plot);
+       output.tag.set_property("SPLIT", config.split);
+       output.tag.set_property("W", w);
+       output.tag.set_property("H", h);
+       output.tag.set_property("PARADE", parade);
+       output.tag.set_property("MODE", mode);
+
+       for(int i = 0; i < HISTOGRAM_MODES; i++)
+       {
+               sprintf(string, "LOW_OUTPUT_%d", i);
+               output.tag.set_property(string, config.low_output[i]);
+               sprintf(string, "HIGH_OUTPUT_%d", i);
+               output.tag.set_property(string, config.high_output[i]);
+               sprintf(string, "LOW_INPUT_%d", i);
+               output.tag.set_property(string, config.low_input[i]);
+               sprintf(string, "HIGH_INPUT_%d", i);
+               output.tag.set_property(string, config.high_input[i]);
+               sprintf(string, "GAMMA_%d", i);
+               output.tag.set_property(string, config.gamma[i]);
+//printf("HistogramMain::save_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
+       }
+
+       output.append_tag();
+       output.tag.set_title("/HISTOGRAM");
+       output.append_tag();
+       output.append_newline();
+       output.terminate_string();
+}
+
+void HistogramMain::read_data(KeyFrame *keyframe)
+{
+       FileXML input;
+
+       input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
+
+       int result = 0;
+
+
+       while(!result)
+       {
+               result = input.read_tag();
+
+               if(!result)
+               {
+                       if(input.tag.title_is("HISTOGRAM"))
+                       {
+                               config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
+                               config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
+                               config.plot = input.tag.get_property("PLOT", config.plot);
+                               config.split = input.tag.get_property("SPLIT", config.split);
+
+                               if(is_defaults())
+                               {
+                                       w = input.tag.get_property("W", w);
+                                       h = input.tag.get_property("H", h);
+                                       parade = input.tag.get_property("PARADE", parade);
+                                       mode = input.tag.get_property("MODE", mode);
+                               }
+
+                               char string[BCTEXTLEN];
+                               for(int i = 0; i < HISTOGRAM_MODES; i++)
+                               {
+                                       sprintf(string, "LOW_OUTPUT_%d", i);
+                                       config.low_output[i] = input.tag.get_property(string, config.low_output[i]);
+                                       sprintf(string, "HIGH_OUTPUT_%d", i);
+                                       config.high_output[i] = input.tag.get_property(string, config.high_output[i]);
+                                       sprintf(string, "GAMMA_%d", i);
+                                       config.gamma[i] = input.tag.get_property(string, config.gamma[i]);
+
+                                       if(i == HISTOGRAM_VALUE || !config.automatic)
+                                       {
+                                               sprintf(string, "LOW_INPUT_%d", i);
+                                               config.low_input[i] = input.tag.get_property(string, config.low_input[i]);
+                                               sprintf(string, "HIGH_INPUT_%d", i);
+                                               config.high_input[i] = input.tag.get_property(string, config.high_input[i]);
+                                       }
+//printf("HistogramMain::read_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
+                               }
+                       }
+               }
+       }
+
+       config.boundaries();
+
+}
+
+float HistogramMain::calculate_level(float input, 
+       int mode,
+       int use_value)
+{
+       float output = 0.0;
+
+// Scale to input range
+       if(!EQUIV(config.high_input[mode], config.low_input[mode]))
+       {
+               output = (input - config.low_input[mode]) /
+                       (config.high_input[mode] - config.low_input[mode]);
+       }
+       else
+       {
+               output = input;
+       }
+       
+
+
+       if(!EQUIV(config.gamma[mode], 0))
+       {
+               output = pow(output, 1.0 / config.gamma[mode]);
+               CLAMP(output, 0, 1.0);
+       }
+
+// Apply value curve
+       if(use_value && mode != HISTOGRAM_VALUE)
+       {
+               output = calculate_level(output, HISTOGRAM_VALUE, 0);
+       }
+
+
+
+
+// scale to output range
+       if(!EQUIV(config.low_output[mode], config.high_output[mode]))
+       {
+               output = output * (config.high_output[mode] - config.low_output[mode]) +
+                       config.low_output[mode];
+       }
+
+       CLAMP(output, 0, 1.0);
+
+       return output;
+}
+
+
+
+void HistogramMain::calculate_histogram(VFrame *data, int do_value)
+{
+
+       if(!engine) engine = new HistogramEngine(this,
+               get_project_smp() + 1,
+               get_project_smp() + 1);
+
+       if(!accum[0])
+       {
+               for(int i = 0; i < HISTOGRAM_MODES; i++)
+                       accum[i] = new int[HISTOGRAM_SLOTS];
+       }
+
+       engine->process_packages(HistogramEngine::HISTOGRAM, data, do_value);
+
+       for(int i = 0; i < engine->get_total_clients(); i++)
+       {
+               HistogramUnit *unit = (HistogramUnit*)engine->get_client(i);
+
+               if(i == 0)
+               {
+                       for(int j = 0; j < HISTOGRAM_MODES; j++)
+                       {
+                               memcpy(accum[j], unit->accum[j], sizeof(int) * HISTOGRAM_SLOTS);
+                       }
+               }
+               else
+               {
+                       for(int j = 0; j < HISTOGRAM_MODES; j++)
+                       {
+                               int *out = accum[j];
+                               int *in = unit->accum[j];
+                               for(int k = 0; k < HISTOGRAM_SLOTS; k++)
+                                       out[k] += in[k];
+                       }
+               }
+       }
+
+// Remove top and bottom from calculations.  Doesn't work in high
+// precision colormodels.
+       for(int i = 0; i < HISTOGRAM_MODES; i++)
+       {
+               accum[i][0] = 0;
+               accum[i][HISTOGRAM_SLOTS - 1] = 0;
+       }
+}
+
+
+void HistogramMain::calculate_automatic(VFrame *data)
+{
+       calculate_histogram(data, 0);
+       config.reset_points(1);
+
+// Do each channel
+       for(int i = 0; i < 3; i++)
+       {
+               int *accum = this->accum[i];
+               int pixels = data->get_w() * data->get_h();
+               float white_fraction = 1.0 - (1.0 - config.threshold) / 2;
+               int threshold = (int)(white_fraction * pixels);
+               int total = 0;
+               float max_level = 1.0;
+               float min_level = 0.0;
+
+// Get histogram slot above threshold of pixels
+               for(int j = 0; j < HISTOGRAM_SLOTS; j++)
+               {
+                       total += accum[j];
+                       if(total >= threshold)
+                       {
+                               max_level = (float)j / HISTOGRAM_SLOTS * FLOAT_RANGE + HIST_MIN_INPUT;
+                               break;
+                       }
+               }
+
+// Get slot below 99% of pixels
+               total = 0;
+               for(int j = HISTOGRAM_SLOTS - 1; j >= 0; j--)
+               {
+                       total += accum[j];
+                       if(total >= threshold)
+                       {
+                               min_level = (float)j / HISTOGRAM_SLOTS * FLOAT_RANGE + HIST_MIN_INPUT;
+                               break;
+                       }
+               }
+
+
+               config.low_input[i] = min_level;
+               config.high_input[i] = max_level;
+       }
+}
+
+
+
+
+
+int HistogramMain::calculate_use_opengl()
+{
+// glHistogram doesn't work.
+       int result = get_use_opengl() &&
+               !config.automatic && 
+               (!config.plot || !gui_open());
+       return result;
+}
+
+
+int HistogramMain::process_buffer(VFrame *frame,
+       int64_t start_position,
+       double frame_rate)
+{
+       int need_reconfigure = load_configuration();
+
+
+
+       int use_opengl = calculate_use_opengl();
+
+//printf("%d\n", use_opengl);
+       read_frame(frame, 
+               0, 
+               start_position, 
+               frame_rate,
+               use_opengl);
+
+// Apply histogram in hardware
+       if(use_opengl) return run_opengl();
+
+       if(!engine) engine = new HistogramEngine(this,
+               get_project_smp() + 1,
+               get_project_smp() + 1);
+       this->input = frame;
+       this->output = frame;
+// Always plot to set the curves if automatic
+       if(config.plot || config.automatic) send_render_gui(frame);
+
+// Generate tables here.  The same table is used by many packages to render
+// each horizontal stripe.  Need to cover the entire output range in  each
+// table to avoid green borders
+
+
+       if(need_reconfigure || 
+               !lookup[0] || 
+               config.automatic)
+       {
+// Calculate new curves
+               if(config.automatic)
+               {
+                       calculate_automatic(input);
+               }
+
+
+// Generate transfer tables with value function for integer colormodels.
+               for(int i = 0; i < 3; i++)
+                       tabulate_curve(i, 1);
+       }
+
+// printf("HistogramMain::process_buffer %d %f %f %f  %f %f %f  %f %f %f\n", 
+// __LINE__, 
+// config.low_input[HISTOGRAM_RED],
+// config.gamma[HISTOGRAM_RED],
+// config.high_input[HISTOGRAM_RED],
+// config.low_input[HISTOGRAM_GREEN],
+// config.gamma[HISTOGRAM_GREEN],
+// config.high_input[HISTOGRAM_GREEN],
+// config.low_input[HISTOGRAM_BLUE],
+// config.gamma[HISTOGRAM_BLUE],
+// config.high_input[HISTOGRAM_BLUE]);
+
+
+
+// Apply histogram
+       engine->process_packages(HistogramEngine::APPLY, input, 0);
+
+
+       return 0;
+}
+
+void HistogramMain::tabulate_curve(int subscript, int use_value)
+{
+       int i;
+       if(!lookup[subscript])
+               lookup[subscript] = new int[HISTOGRAM_SLOTS];
+       if(!preview_lookup[subscript])
+               preview_lookup[subscript] = new int[HISTOGRAM_SLOTS];
+
+//printf("HistogramMain::tabulate_curve %d input=%p\n", __LINE__, input);
+
+
+// Generate lookup tables for integer colormodels
+       if(input)
+       {
+               switch(input->get_color_model())
+               {
+                       case BC_RGB888:
+                       case BC_RGBA8888:
+                               for(i = 0; i < 0x100; i++)
+                               {
+                                       lookup[subscript][i] = 
+                                               (int)(calculate_level((float)i / 0xff, subscript, use_value) * 
+                                               0xff);
+                                       CLAMP(lookup[subscript][i], 0, 0xff);
+                               }
+                               break;
+// All other integer colormodels are converted to 16 bit RGB
+                       default:
+                               for(i = 0; i < 0x10000; i++)
+                               {
+                                       lookup[subscript][i] = 
+                                               (int)(calculate_level((float)i / 0xffff, subscript, use_value) * 
+                                               0xffff);
+                                       CLAMP(lookup[subscript][i], 0, 0xffff);
+                               }
+// for(i = 0; i < 0x100; i++)
+// {
+// if(subscript == HISTOGRAM_BLUE) printf("%d ", lookup[subscript][i * 0x100]);
+// }
+// if(subscript == HISTOGRAM_BLUE) printf("\n");
+
+                               break;
+               }
+       }
+
+// Lookup table for preview only used for GUI
+       if(!use_value)
+       {
+               for(i = 0; i < 0x10000; i++)
+               {
+                       preview_lookup[subscript][i] = 
+                               (int)(calculate_level((float)i / 0xffff, subscript, use_value) * 
+                               0xffff);
+                       CLAMP(preview_lookup[subscript][i], 0, 0xffff);
+               }
+       }
+}
+
+int HistogramMain::handle_opengl()
+{
+#ifdef HAVE_GL
+// Functions to get pixel from either previous effect or texture
+       static const char *histogram_get_pixel1 =
+               "vec4 histogram_get_pixel()\n"
+               "{\n"
+               "       return gl_FragColor;\n"
+               "}\n";
+
+       static const char *histogram_get_pixel2 =
+               "uniform sampler2D tex;\n"
+               "vec4 histogram_get_pixel()\n"
+               "{\n"
+               "       return texture2D(tex, gl_TexCoord[0].st);\n"
+               "}\n";
+
+       static const char *head_frag = 
+               "uniform vec4 low_input;\n"
+               "uniform vec4 high_input;\n"
+               "uniform vec4 gamma;\n"
+               "uniform vec4 low_output;\n"
+               "uniform vec4 output_scale;\n"
+               "void main()\n"
+               "{\n"
+               "       float temp = 0.0;\n";
+
+       static const char *get_rgb_frag =
+               "       vec4 pixel = histogram_get_pixel();\n";
+
+       static const char *get_yuv_frag =
+               "       vec4 pixel = histogram_get_pixel();\n"
+                       YUV_TO_RGB_FRAG("pixel");
+
+#define APPLY_INPUT_CURVE(PIXEL, LOW_INPUT, HIGH_INPUT, GAMMA) \
+               "// apply input curve\n" \
+               "       temp = (" PIXEL " - " LOW_INPUT ") / \n" \
+               "               (" HIGH_INPUT " - " LOW_INPUT ");\n" \
+               "       temp = max(temp, 0.0);\n" \
+               "       " PIXEL " = pow(temp, 1.0 / " GAMMA ");\n"
+
+
+
+       static const char *apply_histogram_frag = 
+               APPLY_INPUT_CURVE("pixel.r", "low_input.r", "high_input.r", "gamma.r")
+               APPLY_INPUT_CURVE("pixel.g", "low_input.g", "high_input.g", "gamma.g")
+               APPLY_INPUT_CURVE("pixel.b", "low_input.b", "high_input.b", "gamma.b")
+               "// apply output curve\n"
+               "       pixel.rgb *= output_scale.rgb;\n"
+               "       pixel.rgb += low_output.rgb;\n"
+               APPLY_INPUT_CURVE("pixel.r", "low_input.a", "high_input.a", "gamma.a")
+               APPLY_INPUT_CURVE("pixel.g", "low_input.a", "high_input.a", "gamma.a")
+               APPLY_INPUT_CURVE("pixel.b", "low_input.a", "high_input.a", "gamma.a")
+               "// apply output curve\n"
+               "       pixel.rgb *= vec3(output_scale.a, output_scale.a, output_scale.a);\n"
+               "       pixel.rgb += vec3(low_output.a, low_output.a, low_output.a);\n";
+
+       static const char *put_rgb_frag =
+               "       gl_FragColor = pixel;\n"
+               "}\n";
+
+       static const char *put_yuv_frag =
+                       RGB_TO_YUV_FRAG("pixel")
+               "       gl_FragColor = pixel;\n"
+               "}\n";
+
+
+
+       get_output()->to_texture();
+       get_output()->enable_opengl();
+
+       const char *shader_stack[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+       int current_shader = 0;
+       int aggregate_interpolation = 0;
+       int aggregate_gamma = 0;
+       int aggregate_colorbalance = 0;
+// All aggregation possibilities must be accounted for because unsupported
+// effects can get in between the aggregation members.
+       if(!strcmp(get_output()->get_prev_effect(2), _("Interpolate Pixels")) &&
+               !strcmp(get_output()->get_prev_effect(1), _("Gamma")) &&
+               !strcmp(get_output()->get_prev_effect(0), _("Color Balance")))
+       {
+               aggregate_interpolation = 1;
+               aggregate_gamma = 1;
+               aggregate_colorbalance = 1;
+       }
+       else
+       if(!strcmp(get_output()->get_prev_effect(1), _("Gamma")) &&
+               !strcmp(get_output()->get_prev_effect(0), _("Color Balance")))
+       {
+               aggregate_gamma = 1;
+               aggregate_colorbalance = 1;
+       }
+       else
+       if(!strcmp(get_output()->get_prev_effect(1), _("Interpolate Pixels")) &&
+               !strcmp(get_output()->get_prev_effect(0), _("Gamma")))
+       {
+               aggregate_interpolation = 1;
+               aggregate_gamma = 1;
+       }
+       else
+       if(!strcmp(get_output()->get_prev_effect(1), _("Interpolate Pixels")) &&
+               !strcmp(get_output()->get_prev_effect(0), _("Color Balance")))
+       {
+               aggregate_interpolation = 1;
+               aggregate_colorbalance = 1;
+       }
+       else
+       if(!strcmp(get_output()->get_prev_effect(0), _("Interpolate Pixels")))
+               aggregate_interpolation = 1;
+       else
+       if(!strcmp(get_output()->get_prev_effect(0), _("Gamma")))
+               aggregate_gamma = 1;
+       else
+       if(!strcmp(get_output()->get_prev_effect(0), _("Color Balance")))
+               aggregate_colorbalance = 1;
+
+
+// The order of processing is fixed by this sequence
+       if(aggregate_interpolation)
+               INTERPOLATE_COMPILE(shader_stack, 
+                       current_shader)
+
+       if(aggregate_gamma)
+               GAMMA_COMPILE(shader_stack, 
+                       current_shader, 
+                       aggregate_interpolation)
+
+       if(aggregate_colorbalance)
+               COLORBALANCE_COMPILE(shader_stack, 
+                       current_shader, 
+                       aggregate_interpolation || aggregate_gamma)
+
+
+       if(aggregate_interpolation || aggregate_gamma || aggregate_colorbalance)
+               shader_stack[current_shader++] = histogram_get_pixel1;
+       else
+               shader_stack[current_shader++] = histogram_get_pixel2;
+
+       unsigned int shader = 0;
+       switch(get_output()->get_color_model())
+       {
+               case BC_YUV888:
+               case BC_YUVA8888:
+                       shader_stack[current_shader++] = head_frag;
+                       shader_stack[current_shader++] = get_yuv_frag;
+                       shader_stack[current_shader++] = apply_histogram_frag;
+                       shader_stack[current_shader++] = put_yuv_frag;
+                       break;
+               default:
+                       shader_stack[current_shader++] = head_frag;
+                       shader_stack[current_shader++] = get_rgb_frag;
+                       shader_stack[current_shader++] = apply_histogram_frag;
+                       shader_stack[current_shader++] = put_rgb_frag;
+                       break;
+       }
+
+       shader = VFrame::make_shader(0,
+               shader_stack[0],
+               shader_stack[1],
+               shader_stack[2],
+               shader_stack[3],
+               shader_stack[4],
+               shader_stack[5],
+               shader_stack[6],
+               shader_stack[7],
+               shader_stack[8],
+               shader_stack[9],
+               shader_stack[10],
+               shader_stack[11],
+               shader_stack[12],
+               shader_stack[13],
+               shader_stack[14],
+               shader_stack[15],
+               0);
+
+// printf("HistogramMain::handle_opengl %d %d %d %d shader=%d\n", 
+// aggregate_interpolation, 
+// aggregate_gamma,
+// aggregate_colorbalance,
+// current_shader,
+// shader);
+
+       float low_input[4];
+       float high_input[4];
+       float gamma[4];
+       float low_output[4];
+       float output_scale[4];
+
+
+// printf("min x    min y    max x    max y\n");
+// printf("%f %f %f %f\n", input_min_r[0], input_min_r[1], input_max_r[0], input_max_r[1]);
+// printf("%f %f %f %f\n", input_min_g[0], input_min_g[1], input_max_g[0], input_max_g[1]);
+// printf("%f %f %f %f\n", input_min_b[0], input_min_b[1], input_max_b[0], input_max_b[1]);
+// printf("%f %f %f %f\n", input_min_v[0], input_min_v[1], input_max_v[0], input_max_v[1]);
+
+       for(int i = 0; i < HISTOGRAM_MODES; i++)
+       {
+               low_input[i] = config.low_input[i];
+               high_input[i] = config.high_input[i];
+               gamma[i] = config.gamma[i];
+               low_output[i] = config.low_output[i];
+               output_scale[i] = config.high_output[i] - config.low_output[i];
+       }
+
+       if(shader > 0)
+       {
+               glUseProgram(shader);
+               glUniform1i(glGetUniformLocation(shader, "tex"), 0);
+               if(aggregate_gamma) GAMMA_UNIFORMS(shader)
+               if(aggregate_interpolation) INTERPOLATE_UNIFORMS(shader)
+               if(aggregate_colorbalance) COLORBALANCE_UNIFORMS(shader)
+               glUniform4fv(glGetUniformLocation(shader, "low_input"), 1, low_input);
+               glUniform4fv(glGetUniformLocation(shader, "high_input"), 1, high_input);
+               glUniform4fv(glGetUniformLocation(shader, "gamma"), 1, gamma);
+               glUniform4fv(glGetUniformLocation(shader, "low_output"), 1, low_output);
+               glUniform4fv(glGetUniformLocation(shader, "output_scale"), 1, output_scale);
+       }
+
+       get_output()->init_screen();
+       get_output()->bind_texture(0);
+
+       glDisable(GL_BLEND);
+
+// Draw the affected half
+       if(config.split)
+       {
+               glBegin(GL_TRIANGLES);
+               glNormal3f(0, 0, 1.0);
+
+               glTexCoord2f(0.0 / get_output()->get_texture_w(), 
+                       0.0 / get_output()->get_texture_h());
+               glVertex3f(0.0, -(float)get_output()->get_h(), 0);
+
+
+               glTexCoord2f((float)get_output()->get_w() / get_output()->get_texture_w(), 
+                       (float)get_output()->get_h() / get_output()->get_texture_h());
+               glVertex3f((float)get_output()->get_w(), -0.0, 0);
+
+               glTexCoord2f(0.0 / get_output()->get_texture_w(), 
+                       (float)get_output()->get_h() / get_output()->get_texture_h());
+               glVertex3f(0.0, -0.0, 0);
+
+
+               glEnd();
+       }
+       else
+       {
+               get_output()->draw_texture();
+       }
+
+       glUseProgram(0);
+
+// Draw the unaffected half
+       if(config.split)
+       {
+               glBegin(GL_TRIANGLES);
+               glNormal3f(0, 0, 1.0);
+
+
+               glTexCoord2f(0.0 / get_output()->get_texture_w(), 
+                       0.0 / get_output()->get_texture_h());
+               glVertex3f(0.0, -(float)get_output()->get_h(), 0);
+
+               glTexCoord2f((float)get_output()->get_w() / get_output()->get_texture_w(), 
+                       0.0 / get_output()->get_texture_h());
+               glVertex3f((float)get_output()->get_w(), 
+                       -(float)get_output()->get_h(), 0);
+
+               glTexCoord2f((float)get_output()->get_w() / get_output()->get_texture_w(), 
+                       (float)get_output()->get_h() / get_output()->get_texture_h());
+               glVertex3f((float)get_output()->get_w(), -0.0, 0);
+
+
+               glEnd();
+       }
+
+       get_output()->set_opengl_state(VFrame::SCREEN);
+#endif
+       return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+HistogramPackage::HistogramPackage()
+ : LoadPackage()
+{
+}
+
+
+
+
+HistogramUnit::HistogramUnit(HistogramEngine *server, 
+       HistogramMain *plugin)
+ : LoadClient(server)
+{
+       this->plugin = plugin;
+       this->server = server;
+       for(int i = 0; i < HISTOGRAM_MODES; i++)
+               accum[i] = new int[HISTOGRAM_SLOTS];
+}
+
+HistogramUnit::~HistogramUnit()
+{
+       for(int i = 0; i < HISTOGRAM_MODES; i++)
+               delete [] accum[i];
+}
+
+void HistogramUnit::process_package(LoadPackage *package)
+{
+       HistogramPackage *pkg = (HistogramPackage*)package;
+
+       if(server->operation == HistogramEngine::HISTOGRAM)
+       {
+               int do_value = server->do_value;
+
+
+#define HISTOGRAM_HEAD(type) \
+{ \
+       for(int i = pkg->start; i < pkg->end; i++) \
+       { \
+               type *row = (type*)data->get_rows()[i]; \
+               for(int j = 0; j < w; j++) \
+               {
+
+#define HISTOGRAM_TAIL(components) \
+/* Value takes the maximum of the output RGB values */ \
+                       if(do_value) \
+                       { \
+                               CLAMP(r, 0, HISTOGRAM_SLOTS - 1); \
+                               CLAMP(g, 0, HISTOGRAM_SLOTS - 1); \
+                               CLAMP(b, 0, HISTOGRAM_SLOTS - 1); \
+                               r_out = lookup_r[r]; \
+                               g_out = lookup_g[g]; \
+                               b_out = lookup_b[b]; \
+/*                             v = (r * 76 + g * 150 + b * 29) >> 8; */ \
+                               v = MAX(r_out, g_out); \
+                               v = MAX(v, b_out); \
+                               v += -HISTOGRAM_MIN * 0xffff / 100; \
+                               CLAMP(v, 0, HISTOGRAM_SLOTS - 1); \
+                               accum_v[v]++; \
+                       } \
+ \
+                       r += -HISTOGRAM_MIN * 0xffff / 100; \
+                       g += -HISTOGRAM_MIN * 0xffff / 100; \
+                       b += -HISTOGRAM_MIN * 0xffff / 100; \
+                       CLAMP(r, 0, HISTOGRAM_SLOTS - 1); \
+                       CLAMP(g, 0, HISTOGRAM_SLOTS - 1); \
+                       CLAMP(b, 0, HISTOGRAM_SLOTS - 1); \
+                       accum_r[r]++; \
+                       accum_g[g]++; \
+                       accum_b[b]++; \
+                       row += components; \
+               } \
+       } \
+}
+
+
+
+
+               VFrame *data = server->data;
+               int w = data->get_w();
+               //int h = data->get_h();
+               int *accum_r = accum[HISTOGRAM_RED];
+               int *accum_g = accum[HISTOGRAM_GREEN];
+               int *accum_b = accum[HISTOGRAM_BLUE];
+               int *accum_v = accum[HISTOGRAM_VALUE];
+               int32_t r, g, b, y, u, v;
+               int r_out, g_out, b_out;
+               int *lookup_r = plugin->preview_lookup[HISTOGRAM_RED];
+               int *lookup_g = plugin->preview_lookup[HISTOGRAM_GREEN];
+               int *lookup_b = plugin->preview_lookup[HISTOGRAM_BLUE];
+
+               switch(data->get_color_model())
+               {
+                       case BC_RGB888:
+                               HISTOGRAM_HEAD(unsigned char)
+                               r = (row[0] << 8) | row[0];
+                               g = (row[1] << 8) | row[1];
+                               b = (row[2] << 8) | row[2];
+                               HISTOGRAM_TAIL(3)
+                               break;
+                       case BC_RGB_FLOAT:
+                               HISTOGRAM_HEAD(float)
+                               r = (int)(row[0] * 0xffff);
+                               g = (int)(row[1] * 0xffff);
+                               b = (int)(row[2] * 0xffff);
+                               HISTOGRAM_TAIL(3)
+                               break;
+                       case BC_YUV888:
+                               HISTOGRAM_HEAD(unsigned char)
+                               y = (row[0] << 8) | row[0];
+                               u = (row[1] << 8) | row[1];
+                               v = (row[2] << 8) | row[2];
+                               plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
+                               HISTOGRAM_TAIL(3)
+                               break;
+                       case BC_RGBA8888:
+                               HISTOGRAM_HEAD(unsigned char)
+                               r = (row[0] << 8) | row[0];
+                               g = (row[1] << 8) | row[1];
+                               b = (row[2] << 8) | row[2];
+                               HISTOGRAM_TAIL(4)
+                               break;
+                       case BC_RGBA_FLOAT:
+                               HISTOGRAM_HEAD(float)
+                               r = (int)(row[0] * 0xffff);
+                               g = (int)(row[1] * 0xffff);
+                               b = (int)(row[2] * 0xffff);
+                               HISTOGRAM_TAIL(4)
+                               break;
+                       case BC_YUVA8888:
+                               HISTOGRAM_HEAD(unsigned char)
+                               y = (row[0] << 8) | row[0];
+                               u = (row[1] << 8) | row[1];
+                               v = (row[2] << 8) | row[2];
+                               plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
+                               HISTOGRAM_TAIL(4)
+                               break;
+                       case BC_RGB161616:
+                               HISTOGRAM_HEAD(uint16_t)
+                               r = row[0];
+                               g = row[1];
+                               b = row[2];
+                               HISTOGRAM_TAIL(3)
+                               break;
+                       case BC_YUV161616:
+                               HISTOGRAM_HEAD(uint16_t)
+                               y = row[0];
+                               u = row[1];
+                               v = row[2];
+                               plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
+                               HISTOGRAM_TAIL(3)
+                               break;
+                       case BC_RGBA16161616:
+                               HISTOGRAM_HEAD(uint16_t)
+                               r = row[0];
+                               g = row[1];
+                               b = row[2];
+                               HISTOGRAM_TAIL(3)
+                               break;
+                       case BC_YUVA16161616:
+                               HISTOGRAM_HEAD(uint16_t)
+                               y = row[0];
+                               u = row[1];
+                               v = row[2];
+                               plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
+                               HISTOGRAM_TAIL(4)
+                               break;
+               }
+       }
+       else
+       if(server->operation == HistogramEngine::APPLY)
+       {
+
+
+
+#define PROCESS(type, components) \
+{ \
+       for(int i = pkg->start; i < pkg->end; i++) \
+       { \
+               type *row = (type*)input->get_rows()[i]; \
+               for(int j = 0; j < w; j++) \
+               { \
+                       if ( plugin->config.split && ((j + i * w / h) < w) ) \
+                       continue; \
+                       row[0] = lookup_r[row[0]]; \
+                       row[1] = lookup_g[row[1]]; \
+                       row[2] = lookup_b[row[2]]; \
+                       row += components; \
+               } \
+       } \
+}
+
+#define PROCESS_YUV(type, components, max) \
+{ \
+       for(int i = pkg->start; i < pkg->end; i++) \
+       { \
+               type *row = (type*)input->get_rows()[i]; \
+               for(int j = 0; j < w; j++) \
+               { \
+                       if ( plugin->config.split && ((j + i * w / h) < w) ) \
+                       continue; \
+/* Convert to 16 bit RGB */ \
+                       if(max == 0xff) \
+                       { \
+                               y = (row[0] << 8) | row[0]; \
+                               u = (row[1] << 8) | row[1]; \
+                               v = (row[2] << 8) | row[2]; \
+                       } \
+                       else \
+                       { \
+                               y = row[0]; \
+                               u = row[1]; \
+                               v = row[2]; \
+                       } \
+ \
+                       plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v); \
+ \
+/* Look up in RGB domain */ \
+                       r = lookup_r[r]; \
+                       g = lookup_g[g]; \
+                       b = lookup_b[b]; \
+ \
+/* Convert to 16 bit YUV */ \
+                       plugin->yuv.rgb_to_yuv_16(r, g, b, y, u, v); \
+ \
+                       if(max == 0xff) \
+                       { \
+                               row[0] = y >> 8; \
+                               row[1] = u >> 8; \
+                               row[2] = v >> 8; \
+                       } \
+                       else \
+                       { \
+                               row[0] = y; \
+                               row[1] = u; \
+                               row[2] = v; \
+                       } \
+                       row += components; \
+               } \
+       } \
+}
+
+#define PROCESS_FLOAT(components) \
+{ \
+       for(int i = pkg->start; i < pkg->end; i++) \
+       { \
+               float *row = (float*)input->get_rows()[i]; \
+               for(int j = 0; j < w; j++) \
+               { \
+                       if ( plugin->config.split && ((j + i * w / h) < w) ) \
+                       continue; \
+                       float r = row[0]; \
+                       float g = row[1]; \
+                       float b = row[2]; \
+ \
+                       r = plugin->calculate_level(r, HISTOGRAM_RED, 1); \
+                       g = plugin->calculate_level(g, HISTOGRAM_GREEN, 1); \
+                       b = plugin->calculate_level(b, HISTOGRAM_BLUE, 1); \
+ \
+                       row[0] = r; \
+                       row[1] = g; \
+                       row[2] = b; \
+ \
+                       row += components; \
+               } \
+       } \
+}
+
+
+               VFrame *input = plugin->input;
+               //VFrame *output = plugin->output;
+               int w = input->get_w();
+               int h = input->get_h();
+               int *lookup_r = plugin->lookup[0];
+               int *lookup_g = plugin->lookup[1];
+               int *lookup_b = plugin->lookup[2];
+               int r, g, b, y, u, v;
+               switch(input->get_color_model())
+               {
+                       case BC_RGB888:
+                               PROCESS(unsigned char, 3)
+                               break;
+                       case BC_RGB_FLOAT:
+                               PROCESS_FLOAT(3);
+                               break;
+                       case BC_RGBA8888:
+                               PROCESS(unsigned char, 4)
+                               break;
+                       case BC_RGBA_FLOAT:
+                               PROCESS_FLOAT(4);
+                               break;
+                       case BC_RGB161616:
+                               PROCESS(uint16_t, 3)
+                               break;
+                       case BC_RGBA16161616:
+                               PROCESS(uint16_t, 4)
+                               break;
+                       case BC_YUV888:
+                               PROCESS_YUV(unsigned char, 3, 0xff)
+                               break;
+                       case BC_YUVA8888:
+                               PROCESS_YUV(unsigned char, 4, 0xff)
+                               break;
+                       case BC_YUV161616:
+                               PROCESS_YUV(uint16_t, 3, 0xffff)
+                               break;
+                       case BC_YUVA16161616:
+                               PROCESS_YUV(uint16_t, 4, 0xffff)
+                               break;
+               }
+       }
+}
+
+
+
+
+
+
+HistogramEngine::HistogramEngine(HistogramMain *plugin, 
+       int total_clients, 
+       int total_packages)
+ : LoadServer(total_clients, total_packages)
+{
+       this->plugin = plugin;
+}
+
+void HistogramEngine::init_packages()
+{
+       switch(operation)
+       {
+               case HISTOGRAM:
+                       total_size = data->get_h();
+                       break;
+               case APPLY:
+                       total_size = data->get_h();
+                       break;
+       }
+
+
+       //int package_size = (int)((float)total_size / get_total_packages() + 1);
+       //int start = 0;
+
+       for(int i = 0; i < get_total_packages(); i++)
+       {
+               HistogramPackage *package = (HistogramPackage*)get_package(i);
+               package->start = total_size * i / get_total_packages();
+               package->end = total_size * (i + 1) / get_total_packages();
+       }
+
+// Initialize clients here in case some don't get run.
+       for(int i = 0; i < get_total_clients(); i++)
+       {
+               HistogramUnit *unit = (HistogramUnit*)get_client(i);
+               for(int i = 0; i < HISTOGRAM_MODES; i++)
+                       bzero(unit->accum[i], sizeof(int) * HISTOGRAM_SLOTS);
+       }
+
+}
+
+LoadClient* HistogramEngine::new_client()
+{
+       return new HistogramUnit(this, plugin);
+}
+
+LoadPackage* HistogramEngine::new_package()
+{
+       return new HistogramPackage;
+}
+
+void HistogramEngine::process_packages(int operation, VFrame *data, int do_value)
+{
+       this->data = data;
+       this->operation = operation;
+       this->do_value = do_value;
+       LoadServer::process_packages();
+}
+
+