/* * CINELERRA * Copyright (C) 2008 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 "bcdisplayinfo.h" #include "clip.h" #include "bchash.h" #include "filexml.h" #include "guicast.h" #include "keyframe.h" #include "language.h" #include "loadbalance.h" #include "pluginvclient.h" #include "vframe.h" #include #include #include // Algorithm by Torsten Martinsen // Ported to Cinelerra by Heroine Virtual Ltd. class OilEffect; class OilWindow; class OilReset; class OilConfig { public: OilConfig(); void reset(); void copy_from(OilConfig &src); int equivalent(OilConfig &src); void interpolate(OilConfig &prev, OilConfig &next, long prev_frame, long next_frame, long current_frame); float radius; int use_intensity; }; class OilRadius : public BC_FSlider { public: OilRadius(OilEffect *plugin, int x, int y); int handle_event(); OilEffect *plugin; }; class OilIntensity : public BC_CheckBox { public: OilIntensity(OilEffect *plugin, int x, int y); int handle_event(); OilEffect *plugin; }; class OilReset : public BC_GenericButton { public: OilReset(OilEffect *plugin, OilWindow *window, int x, int y); ~OilReset(); int handle_event(); OilEffect *plugin; OilWindow *window; }; class OilWindow : public PluginClientWindow { public: OilWindow(OilEffect *plugin); ~OilWindow(); void create_objects(); void update(); OilEffect *plugin; OilRadius *radius; OilIntensity *intensity; OilReset *reset; }; class OilServer : public LoadServer { public: OilServer(OilEffect *plugin, int cpus); void init_packages(); LoadClient* new_client(); LoadPackage* new_package(); OilEffect *plugin; }; class OilPackage : public LoadPackage { public: OilPackage(); int row1, row2; }; class OilUnit : public LoadClient { public: OilUnit(OilEffect *plugin, OilServer *server); void process_package(LoadPackage *package); OilEffect *plugin; }; class OilEffect : public PluginVClient { public: OilEffect(PluginServer *server); ~OilEffect(); PLUGIN_CLASS_MEMBERS(OilConfig); int process_realtime(VFrame *input, VFrame *output); int is_realtime(); void save_data(KeyFrame *keyframe); void read_data(KeyFrame *keyframe); void update_gui(); VFrame *temp_frame; VFrame *input, *output; OilServer *engine; int need_reconfigure; }; OilConfig::OilConfig() { reset(); } void OilConfig::reset() { radius = 5; use_intensity = 0; } void OilConfig::copy_from(OilConfig &src) { this->radius = src.radius; this->use_intensity = src.use_intensity; } int OilConfig::equivalent(OilConfig &src) { return (EQUIV(this->radius, src.radius) && this->use_intensity == src.use_intensity); } void OilConfig::interpolate(OilConfig &prev, OilConfig &next, long prev_frame, long next_frame, long current_frame) { double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame); double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame); this->radius = prev.radius * prev_scale + next.radius * next_scale; this->use_intensity = prev.use_intensity; // printf("OilConfig::interpolate prev_frame=%ld current_frame=%ld next_frame=%ld prev.radius=%f this->radius=%f next.radius=%f\n", // prev_frame, current_frame, next_frame, prev.radius, this->radius, next.radius); } OilRadius::OilRadius(OilEffect *plugin, int x, int y) : BC_FSlider(x, y, 0, 200, 200, (float)0, (float)30, plugin->config.radius) { this->plugin = plugin; } int OilRadius::handle_event() { plugin->config.radius = get_value(); plugin->send_configure_change(); return 1; } OilIntensity::OilIntensity(OilEffect *plugin, int x, int y) : BC_CheckBox(x, y, plugin->config.use_intensity, _("Use intensity")) { this->plugin = plugin; } int OilIntensity::handle_event() { plugin->config.use_intensity = get_value(); plugin->send_configure_change(); return 1; } OilReset::OilReset(OilEffect *plugin, OilWindow *window, int x, int y) : BC_GenericButton(x, y, _("Reset")) { this->plugin = plugin; this->window = window; } OilReset::~OilReset() { } int OilReset::handle_event() { plugin->config.reset(); window->update(); plugin->send_configure_change(); return 1; } OilWindow::OilWindow(OilEffect *plugin) : PluginClientWindow(plugin, 300, 120, 300, 120, 0) { this->plugin = plugin; } OilWindow::~OilWindow() { } void OilWindow::create_objects() { int x = 10, y = 10; add_subwindow(new BC_Title(x, y, _("Radius:"))); add_subwindow(radius = new OilRadius(plugin, x + 70, y)); y += 40; add_subwindow(intensity = new OilIntensity(plugin, x, y)); y += 40; add_subwindow(reset = new OilReset(plugin, this, x, y)); show_window(); flush(); } // for Reset button void OilWindow::update() { radius->update(plugin->config.radius); intensity->update(plugin->config.use_intensity); } REGISTER_PLUGIN(OilEffect) OilEffect::OilEffect(PluginServer *server) : PluginVClient(server) { temp_frame = 0; need_reconfigure = 1; engine = 0; } OilEffect::~OilEffect() { if(temp_frame) delete temp_frame; if(engine) delete engine; } const char* OilEffect::plugin_title() { return N_("Oil painting"); } int OilEffect::is_realtime() { return 1; } NEW_WINDOW_MACRO(OilEffect, OilWindow) void OilEffect::update_gui() { if(thread) { thread->window->lock_window(); load_configuration(); //printf("OilEffect::update_gui 1 %ld %f\n", get_source_position(), config.radius); ((OilWindow*)thread->window)->radius->update(config.radius); ((OilWindow*)thread->window)->intensity->update(config.use_intensity); thread->window->unlock_window(); } } LOAD_CONFIGURATION_MACRO(OilEffect, OilConfig) void OilEffect::save_data(KeyFrame *keyframe) { FileXML output; // cause data to be stored directly in text output.set_shared_output(keyframe->xbuf); output.tag.set_title("OIL_PAINTING"); output.tag.set_property("RADIUS", config.radius); output.tag.set_property("USE_INTENSITY", config.use_intensity); output.append_tag(); output.tag.set_title("/OIL_PAINTING"); output.append_tag(); output.append_newline(); output.terminate_string(); } void OilEffect::read_data(KeyFrame *keyframe) { FileXML input; input.set_shared_input(keyframe->xbuf); while(!input.read_tag()) { if(input.tag.title_is("OIL_PAINTING")) { config.radius = input.tag.get_property("RADIUS", config.radius); config.use_intensity = input.tag.get_property("USE_INTENSITY", config.use_intensity); } } } int OilEffect::process_realtime(VFrame *input, VFrame *output) { need_reconfigure |= load_configuration(); //printf("OilEffect::process_realtime %f %d\n", config.radius, config.use_intensity); this->input = input; this->output = output; if(EQUIV(config.radius, 0)) { if(input->get_rows()[0] != output->get_rows()[0]) output->copy_from(input); } else { if(input->get_rows()[0] == output->get_rows()[0]) { if(!temp_frame) temp_frame = new VFrame(input->get_w(), input->get_h(), input->get_color_model(), 0); temp_frame->copy_from(input); this->input = temp_frame; } if(!engine) { engine = new OilServer(this, (PluginClient::smp + 1)); } engine->process_packages(); } return 0; } OilPackage::OilPackage() : LoadPackage() { } OilUnit::OilUnit(OilEffect *plugin, OilServer *server) : LoadClient(server) { this->plugin = plugin; } #define INTENSITY(p) ((unsigned int)(((p)[0]) * 77+ \ ((p)[1] * 150) + \ ((p)[2] * 29)) >> 8) #define OIL_MACRO(type, hist_size, components) \ { \ type *src, *dest; \ type val[components]; \ int count[components], count2; \ int *hist[components]; \ int *hist2; \ type **src_rows = (type**)plugin->input->get_rows(); \ \ for(int i = 0; i < components; i++) \ hist[i] = new int[hist_size + 1]; \ hist2 = new int[hist_size + 1]; \ \ for(int y1 = pkg->row1; y1 < pkg->row2; y1++) \ { \ dest = (type*)plugin->output->get_rows()[y1]; \ \ if(!plugin->config.use_intensity) \ { \ for(int x1 = 0; x1 < w; x1++) \ { \ bzero(count, sizeof(count)); \ bzero(val, sizeof(val)); \ bzero(hist[0], sizeof(int) * (hist_size + 1)); \ bzero(hist[1], sizeof(int) * (hist_size + 1)); \ bzero(hist[2], sizeof(int) * (hist_size + 1)); \ if (components == 4) bzero(hist[3], sizeof(int) * (hist_size + 1)); \ \ int x3 = CLIP((x1 - n), 0, w - 1); \ int y3 = CLIP((y1 - n), 0, h - 1); \ int x4 = CLIP((x1 + n + 1), 0, w - 1); \ int y4 = CLIP((y1 + n + 1), 0, h - 1); \ \ for(int y2 = y3; y2 < y4; y2++) \ { \ src = src_rows[y2]; \ for(int x2 = x3; x2 < x4; x2++) \ { \ int c; \ int subscript; \ type value; \ \ value = src[x2 * components + 0]; \ if(sizeof(type) == 4) \ { \ subscript = (int)(value * hist_size); \ CLAMP(subscript, 0, hist_size); \ } \ else \ subscript = (int)value; \ \ if((c = ++hist[0][subscript]) > count[0]) \ { \ val[0] = value; \ count[0] = c; \ } \ \ value = src[x2 * components + 1]; \ if(sizeof(type) == 4) \ { \ subscript = (int)(value * hist_size); \ CLAMP(subscript, 0, hist_size); \ } \ else \ subscript = (int)value; \ \ if((c = ++hist[1][subscript]) > count[1]) \ { \ val[1] = value; \ count[1] = c; \ } \ \ value = src[x2 * components + 2]; \ if(sizeof(type) == 4) \ { \ subscript = (int)(value * hist_size); \ CLAMP(subscript, 0, hist_size); \ } \ else \ subscript = (int)value; \ \ if((c = ++hist[2][subscript]) > count[2]) \ { \ val[2] = value; \ count[2] = c; \ } \ \ if(components == 4) \ { \ value = src[x2 * components + 3]; \ if(sizeof(type) == 4) \ { \ subscript = (int)(value * hist_size); \ CLAMP(subscript, 0, hist_size); \ } \ else \ subscript = (int)value; \ \ if((c = ++hist[3][subscript]) > count[3]) \ { \ val[3] = value; \ count[3] = c; \ } \ } \ } \ } \ \ dest[x1 * components + 0] = val[0]; \ dest[x1 * components + 1] = val[1]; \ dest[x1 * components + 2] = val[2]; \ if(components == 4) dest[x1 * components + 3] = val[3]; \ } \ } \ else \ { \ for(int x1 = 0; x1 < w; x1++) \ { \ count2 = 0; \ bzero(val, sizeof(val)); \ bzero(hist2, sizeof(int) * (hist_size + 1)); \ \ int x3 = CLIP((x1 - n), 0, w - 1); \ int y3 = CLIP((y1 - n), 0, h - 1); \ int x4 = CLIP((x1 + n + 1), 0, w - 1); \ int y4 = CLIP((y1 + n + 1), 0, h - 1); \ \ for(int y2 = y3; y2 < y4; y2++) \ { \ src = src_rows[y2]; \ for(int x2 = x3; x2 < x4; x2++) \ { \ int c; \ if((c = ++hist2[INTENSITY(src + x2 * components)]) > count2) \ { \ val[0] = src[x2 * components + 0]; \ val[1] = src[x2 * components + 1]; \ val[2] = src[x2 * components + 2]; \ if(components == 4) val[3] = src[x2 * components + 3]; \ count2 = c; \ } \ } \ } \ \ dest[x1 * components + 0] = val[0]; \ dest[x1 * components + 1] = val[1]; \ dest[x1 * components + 2] = val[2]; \ if(components == 4) dest[x1 * components + 3] = val[3]; \ } \ } \ } \ \ for(int i = 0; i < components; i++) \ delete [] hist[i]; \ delete [] hist2; \ } void OilUnit::process_package(LoadPackage *package) { OilPackage *pkg = (OilPackage*)package; int w = plugin->input->get_w(); int h = plugin->input->get_h(); int n = (int)(plugin->config.radius / 2); switch(plugin->input->get_color_model()) { case BC_RGB_FLOAT: OIL_MACRO(float, 0xffff, 3) break; case BC_RGB888: case BC_YUV888: OIL_MACRO(unsigned char, 0xff, 3) break; case BC_RGB161616: case BC_YUV161616: OIL_MACRO(uint16_t, 0xffff, 3) break; case BC_RGBA_FLOAT: OIL_MACRO(float, 0xffff, 4) break; case BC_RGBA8888: case BC_YUVA8888: OIL_MACRO(unsigned char, 0xff, 4) break; case BC_RGBA16161616: case BC_YUVA16161616: OIL_MACRO(uint16_t, 0xffff, 4) break; } } OilServer::OilServer(OilEffect *plugin, int cpus) : LoadServer(cpus, cpus) { this->plugin = plugin; } void OilServer::init_packages() { for(int i = 0; i < LoadServer::get_total_packages(); i++) { OilPackage *pkg = (OilPackage*)get_package(i); pkg->row1 = plugin->input->get_h() * i / LoadServer::get_total_packages(); pkg->row2 = plugin->input->get_h() * (i + 1) / LoadServer::get_total_packages(); } } LoadClient* OilServer::new_client() { return new OilUnit(plugin, this); } LoadPackage* OilServer::new_package() { return new OilPackage; }