X-Git-Url: http://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fplugins%2Fhistogram%2Fhistogram.C;fp=cinelerra-5.1%2Fplugins%2Fhistogram%2Fhistogram.C;h=b248785724062d84120cda952d7833fe1d0d2b24;hb=30bdb85eb33a8ee7ba675038a86c6be59c43d7bd;hp=0000000000000000000000000000000000000000;hpb=52fcc46226f9df46f9ce9d0566dc568455a7db0b;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/plugins/histogram/histogram.C b/cinelerra-5.1/plugins/histogram/histogram.C new file mode 100644 index 00000000..b2487857 --- /dev/null +++ b/cinelerra-5.1/plugins/histogram/histogram.C @@ -0,0 +1,1225 @@ + +/* + * CINELERRA + * Copyright (C) 2008-2012 Adam Williams + * + * 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 +#include +#include +#include + +#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(); +} + +