/* * 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 #include #include #include "bcdisplayinfo.h" #include "clip.h" #include "bchash.h" #include "filexml.h" #include "timefront.h" #include "keyframe.h" #include "language.h" #include "overlayframe.h" #include "vframe.h" REGISTER_PLUGIN(TimeFrontMain) TimeFrontConfig::TimeFrontConfig() { angle = 0; in_radius = 0; out_radius = 100; frame_range = 16; track_usage = TimeFrontConfig::OTHERTRACK_INTENSITY; shape = TimeFrontConfig::LINEAR; rate = TimeFrontConfig::LINEAR; center_x = 50; center_y = 50; invert = 0; show_grayscale = 0; } int TimeFrontConfig::equivalent(TimeFrontConfig &that) { return (EQUIV(angle, that.angle) && EQUIV(in_radius, that.in_radius) && EQUIV(out_radius, that.out_radius) && frame_range == that.frame_range && track_usage == that.track_usage && shape == that.shape && rate == that.rate && EQUIV(center_x, that.center_x) && EQUIV(center_y, that.center_y) && invert == that.invert && show_grayscale == that.show_grayscale); } void TimeFrontConfig::copy_from(TimeFrontConfig &that) { angle = that.angle; in_radius = that.in_radius; out_radius = that.out_radius; frame_range = that.frame_range; track_usage = that.track_usage; shape = that.shape; rate = that.rate; center_x = that.center_x; center_y = that.center_y; invert = that.invert; show_grayscale = that.show_grayscale; } void TimeFrontConfig::interpolate(TimeFrontConfig &prev, TimeFrontConfig &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->angle = (int)(prev.angle * prev_scale + next.angle * next_scale); this->in_radius = (int)(prev.in_radius * prev_scale + next.in_radius * next_scale); this->out_radius = (int)(prev.out_radius * prev_scale + next.out_radius * next_scale); frame_range = (int)(prev.frame_range * prev_scale + next.frame_range * next_scale); track_usage = prev.track_usage; shape = prev.shape; rate = prev.rate; center_x = prev.center_x * prev_scale + next.center_x * next_scale; center_y = prev.center_y * prev_scale + next.center_y * next_scale; invert = prev.invert; show_grayscale = prev.show_grayscale; } TimeFrontWindow::TimeFrontWindow(TimeFrontMain *plugin) : PluginClientWindow(plugin, 350, 290, 350, 290, 0) { this->plugin = plugin; angle = 0; angle_title = 0; center_x = 0; center_y = 0; center_x_title = 0; center_y_title = 0; rate_title = 0; rate = 0; in_radius_title = 0; in_radius = 0; out_radius_title = 0; out_radius = 0; track_usage_title = 0; track_usage = 0; } TimeFrontWindow::~TimeFrontWindow() { } void TimeFrontWindow::create_objects() { int x = 10, y = 10; BC_Title *title; add_subwindow(title = new BC_Title(x, y, _("Type:"))); add_subwindow(shape = new TimeFrontShape(plugin, this, x + title->get_w() + 10, y)); shape->create_objects(); y += 40; shape_x = x; shape_y = y; y += 140; add_subwindow(title = new BC_Title(x, y, _("Time range:"))); add_subwindow(frame_range = new TimeFrontFrameRange(plugin, x + title->get_w() + 10, y)); frame_range_x = x + frame_range->get_w() + 10; frame_range_y = y; y += 35; update_shape(); add_subwindow(invert = new TimeFrontInvert(plugin, x, y)); add_subwindow(show_grayscale = new TimeFrontShowGrayscale(plugin, x+ 100, y)); show_window(); flush(); } void TimeFrontWindow::update_shape() { int x = shape_x, y = shape_y; if(plugin->config.shape == TimeFrontConfig::LINEAR) { delete center_x_title; delete center_y_title; delete center_x; delete center_y; delete track_usage_title; delete track_usage; center_x_title = 0; center_y_title = 0; center_x = 0; center_y = 0; track_usage_title = 0; track_usage = 0; if(!angle) { add_subwindow(angle_title = new BC_Title(x, y, _("Angle:"))); add_subwindow(angle = new TimeFrontAngle(plugin, x + angle_title->get_w() + 10, y)); } if(!rate){ y = shape_y + 40; add_subwindow(rate_title = new BC_Title(x, y, _("Rate:"))); add_subwindow(rate = new TimeFrontRate(plugin, x + rate_title->get_w() + 10, y)); rate->create_objects(); y += 40; add_subwindow(in_radius_title = new BC_Title(x, y, _("Inner radius:"))); add_subwindow(in_radius = new TimeFrontInRadius(plugin, x + in_radius_title->get_w() + 10, y)); y += 30; add_subwindow(out_radius_title = new BC_Title(x, y, _("Outer radius:"))); add_subwindow(out_radius = new TimeFrontOutRadius(plugin, x + out_radius_title->get_w() + 10, y)); y += 35; } } else if(plugin->config.shape == TimeFrontConfig::RADIAL) { delete angle_title; delete angle; delete track_usage_title; delete track_usage; angle_title = 0; angle = 0; track_usage_title = 0; track_usage = 0; if(!center_x) { add_subwindow(center_x_title = new BC_Title(x, y, _("Center X:"))); add_subwindow(center_x = new TimeFrontCenterX(plugin, x + center_x_title->get_w() + 10, y)); x += center_x_title->get_w() + 10 + center_x->get_w() + 10; add_subwindow(center_y_title = new BC_Title(x, y, _("Center Y:"))); add_subwindow(center_y = new TimeFrontCenterY(plugin, x + center_y_title->get_w() + 10, y)); } if(!rate) { y = shape_y + 40; x = shape_x; add_subwindow(rate_title = new BC_Title(x, y, _("Rate:"))); add_subwindow(rate = new TimeFrontRate(plugin, x + rate_title->get_w() + 10, y)); rate->create_objects(); y += 40; add_subwindow(in_radius_title = new BC_Title(x, y, _("Inner radius:"))); add_subwindow(in_radius = new TimeFrontInRadius(plugin, x + in_radius_title->get_w() + 10, y)); y += 30; add_subwindow(out_radius_title = new BC_Title(x, y, _("Outer radius:"))); add_subwindow(out_radius = new TimeFrontOutRadius(plugin, x + out_radius_title->get_w() + 10, y)); y += 35; } } else if(plugin->config.shape == TimeFrontConfig::OTHERTRACK) { delete center_x_title; delete center_y_title; delete center_x; delete center_y; delete angle_title; delete angle; delete rate_title; delete rate; delete in_radius_title; delete in_radius; delete out_radius_title; delete out_radius; center_x_title = 0; center_y_title = 0; center_x = 0; center_y = 0; angle_title = 0; angle = 0; rate_title = 0; rate = 0; in_radius_title = 0; in_radius = 0; out_radius_title = 0; out_radius = 0; if(!track_usage) { add_subwindow(track_usage_title = new BC_Title(x, y, _("As timefront use:"))); add_subwindow(track_usage = new TimeFrontTrackUsage(plugin, this, x + track_usage_title->get_w() + 10, y)); track_usage->create_objects(); } } else if(plugin->config.shape == TimeFrontConfig::ALPHA) { delete center_x_title; delete center_y_title; delete center_x; delete center_y; delete angle_title; delete angle; delete rate_title; delete rate; delete in_radius_title; delete in_radius; delete out_radius_title; delete out_radius; delete track_usage_title; delete track_usage; center_x_title = 0; center_y_title = 0; center_x = 0; center_y = 0; angle_title = 0; angle = 0; rate_title = 0; rate = 0; in_radius_title = 0; in_radius = 0; out_radius_title = 0; out_radius = 0; track_usage_title = 0; track_usage = 0; } } TimeFrontShape::TimeFrontShape(TimeFrontMain *plugin, TimeFrontWindow *gui, int x, int y) : BC_PopupMenu(x, y, 230, to_text(plugin->config.shape), 1) { this->plugin = plugin; this->gui = gui; } void TimeFrontShape::create_objects() { add_item(new BC_MenuItem(to_text(TimeFrontConfig::LINEAR))); add_item(new BC_MenuItem(to_text(TimeFrontConfig::RADIAL))); add_item(new BC_MenuItem(to_text(TimeFrontConfig::ALPHA))); add_item(new BC_MenuItem(to_text(TimeFrontConfig::OTHERTRACK))); } char* TimeFrontShape::to_text(int shape) { switch(shape) { case TimeFrontConfig::LINEAR: return _("Linear"); case TimeFrontConfig::OTHERTRACK: return _("Other track as timefront"); case TimeFrontConfig::ALPHA: return _("Alpha as timefront"); default: return _("Radial"); } } int TimeFrontShape::from_text(char *text) { if(!strcmp(text, to_text(TimeFrontConfig::LINEAR))) return TimeFrontConfig::LINEAR; if(!strcmp(text, to_text(TimeFrontConfig::OTHERTRACK))) return TimeFrontConfig::OTHERTRACK; if(!strcmp(text, to_text(TimeFrontConfig::ALPHA))) return TimeFrontConfig::ALPHA; return TimeFrontConfig::RADIAL; } int TimeFrontShape::handle_event() { plugin->config.shape = from_text(get_text()); gui->update_shape(); gui->show_window(); plugin->send_configure_change(); return 1; } TimeFrontTrackUsage::TimeFrontTrackUsage(TimeFrontMain *plugin, TimeFrontWindow *gui, int x, int y) : BC_PopupMenu(x, y, 140, to_text(plugin->config.track_usage), 1) { this->plugin = plugin; this->gui = gui; } void TimeFrontTrackUsage::create_objects() { add_item(new BC_MenuItem(to_text(TimeFrontConfig::OTHERTRACK_INTENSITY))); add_item(new BC_MenuItem(to_text(TimeFrontConfig::OTHERTRACK_ALPHA))); } char* TimeFrontTrackUsage::to_text(int track_usage) { switch(track_usage) { case TimeFrontConfig::OTHERTRACK_INTENSITY: return _("Intensity"); case TimeFrontConfig::OTHERTRACK_ALPHA: return _("Alpha mask"); default: return _("Unknown"); } } int TimeFrontTrackUsage::from_text(char *text) { if(!strcmp(text, to_text(TimeFrontConfig::OTHERTRACK_INTENSITY))) return TimeFrontConfig::OTHERTRACK_INTENSITY; if(!strcmp(text, to_text(TimeFrontConfig::OTHERTRACK_ALPHA))) return TimeFrontConfig::OTHERTRACK_ALPHA; return TimeFrontConfig::OTHERTRACK_INTENSITY; } int TimeFrontTrackUsage::handle_event() { plugin->config.track_usage = from_text(get_text()); gui->update_shape(); gui->show_window(); plugin->send_configure_change(); return 1; } TimeFrontCenterX::TimeFrontCenterX(TimeFrontMain *plugin, int x, int y) : BC_FPot(x, y, plugin->config.center_x, 0, 100) { this->plugin = plugin; } int TimeFrontCenterX::handle_event() { plugin->config.center_x = get_value(); plugin->send_configure_change(); return 1; } TimeFrontCenterY::TimeFrontCenterY(TimeFrontMain *plugin, int x, int y) : BC_FPot(x, y, plugin->config.center_y, 0, 100) { this->plugin = plugin; } int TimeFrontCenterY::handle_event() { plugin->config.center_y = get_value(); plugin->send_configure_change(); return 1; } TimeFrontAngle::TimeFrontAngle(TimeFrontMain *plugin, int x, int y) : BC_FPot(x, y, plugin->config.angle, -180, 180) { this->plugin = plugin; } int TimeFrontAngle::handle_event() { plugin->config.angle = get_value(); plugin->send_configure_change(); return 1; } TimeFrontRate::TimeFrontRate(TimeFrontMain *plugin, int x, int y) : BC_PopupMenu(x, y, 155, to_text(plugin->config.rate), 1) { this->plugin = plugin; } void TimeFrontRate::create_objects() { add_item(new BC_MenuItem(to_text(TimeFrontConfig::LINEAR))); add_item(new BC_MenuItem(to_text(TimeFrontConfig::LOG))); add_item(new BC_MenuItem(to_text(TimeFrontConfig::SQUARE))); } char* TimeFrontRate::to_text(int shape) { switch(shape) { case TimeFrontConfig::LINEAR: return _("Linear"); case TimeFrontConfig::LOG: return _("Log"); default: return C_("Square"); } } int TimeFrontRate::from_text(char *text) { if(!strcmp(text, to_text(TimeFrontConfig::LINEAR))) return TimeFrontConfig::LINEAR; if(!strcmp(text, to_text(TimeFrontConfig::LOG))) return TimeFrontConfig::LOG; return TimeFrontConfig::SQUARE; } int TimeFrontRate::handle_event() { plugin->config.rate = from_text(get_text()); plugin->send_configure_change(); return 1; } TimeFrontInRadius::TimeFrontInRadius(TimeFrontMain *plugin, int x, int y) : BC_FSlider(x, y, 0, 200, 200, (float)0, (float)100, (float)plugin->config.in_radius) { this->plugin = plugin; } int TimeFrontInRadius::handle_event() { plugin->config.in_radius = get_value(); plugin->send_configure_change(); return 1; } TimeFrontOutRadius::TimeFrontOutRadius(TimeFrontMain *plugin, int x, int y) : BC_FSlider(x, y, 0, 200, 200, (float)0, (float)100, (float)plugin->config.out_radius) { this->plugin = plugin; } int TimeFrontOutRadius::handle_event() { plugin->config.out_radius = get_value(); plugin->send_configure_change(); return 1; } TimeFrontFrameRange::TimeFrontFrameRange(TimeFrontMain *plugin, int x, int y) : BC_ISlider(x, y, 0, 200, 200, (int)1, (int)255, (int)plugin->config.frame_range) { this->plugin = plugin; } int TimeFrontFrameRange::handle_event() { plugin->config.frame_range = get_value(); plugin->send_configure_change(); return 1; } TimeFrontInvert::TimeFrontInvert(TimeFrontMain *client, int x, int y) : BC_CheckBox(x, y, client->config.invert, _("Inversion")) { this->plugin = client; } int TimeFrontInvert::handle_event() { plugin->config.invert = get_value(); plugin->send_configure_change(); return 1; } TimeFrontShowGrayscale::TimeFrontShowGrayscale(TimeFrontMain *client, int x, int y) : BC_CheckBox(x, y, client->config.show_grayscale, _("Show grayscale (for tuning")) { this->plugin = client; } int TimeFrontShowGrayscale::handle_event() { plugin->config.show_grayscale = get_value(); plugin->send_configure_change(); return 1; } TimeFrontMain::TimeFrontMain(PluginServer *server) : PluginVClient(server) { need_reconfigure = 1; gradient = 0; engine = 0; overlayer = 0; } TimeFrontMain::~TimeFrontMain() { if(gradient) delete gradient; if(engine) delete engine; if(overlayer) delete overlayer; } const char* TimeFrontMain::plugin_title() { return N_("TimeFront"); } int TimeFrontMain::is_realtime() { return 1; } int TimeFrontMain::is_multichannel() { return 1; } NEW_WINDOW_MACRO(TimeFrontMain, TimeFrontWindow) LOAD_CONFIGURATION_MACRO(TimeFrontMain, TimeFrontConfig) int TimeFrontMain::is_synthesis() { return 1; } #define GRADIENTFROMAVG(type, inttype, components, maxval) \ for(int i = 0; i < tfframe->get_h(); i++) \ { \ type *in_row = (type *)tfframe->get_rows()[i]; \ unsigned char *grad_row = gradient->get_rows()[i]; \ for(int j = 0; j < tfframe->get_w(); j++) \ { \ inttype tmp = (inttype) in_row[j * components] + \ in_row[j * components + 1] + \ in_row[j * components + 2]; \ if (components == 3) \ grad_row[j] = (unsigned char) (CLIP((float)config.frame_range * tmp / maxval / 3, 0.0F, config.frame_range)); \ else if(components == 4) \ grad_row[j] = (unsigned char) (CLIP((float)config.frame_range * tmp * in_row[j * components + 3] / maxval / maxval / 3, 0.0F, config.frame_range)); \ } \ } #define GRADIENTFROMCHANNEL(type, components, max, channel) \ for(int i = 0; i < tfframe->get_h(); i++) \ { \ type *in_row = (type *)tfframe->get_rows()[i]; \ unsigned char *grad_row = gradient->get_rows()[i]; \ for(int j = 0; j < tfframe->get_w(); j++) \ { \ if (components == 3) \ grad_row[j] = (unsigned char) (CLIP((float)config.frame_range * in_row[j * components + channel] / max, 0.0F, config.frame_range)); \ else if(components == 4) \ grad_row[j] = (unsigned char) (CLIP((float)config.frame_range * in_row[j * components + channel] * in_row[j * components + 3]/ max /max, 0.0F, config.frame_range)); \ } \ } #define SETALPHA(type, max) \ for(int i = 0; i < outframes[0]->get_h(); i++) \ { \ type *out_row = (type *)outframes[0]->get_rows()[i]; \ for(int j = 0; j < outframes[0]->get_w(); j++) \ { \ out_row[j * 4 + 3] = max; \ } \ } #define GRADIENTTOPICTURE(type, inttype, components, max, invertion) \ for(int i = 0; i < height; i++) \ { \ type *out_row = (type *)outframes[0]->get_rows()[i]; \ unsigned char *grad_row = gradient->get_rows()[i]; \ for (int j = 0; j < width; j++) \ { \ out_row[0] = (inttype)max * (invertion grad_row[0]) / config.frame_range; \ out_row[1] = (inttype)max * (invertion grad_row[0]) / config.frame_range; \ out_row[2] = (inttype)max * (invertion grad_row[0]) / config.frame_range; \ if (components == 4) \ out_row[3] = max; \ out_row += components; \ grad_row ++; \ } \ } #define GRADIENTTOYUVPICTURE(type, inttype, components, max, invertion) \ for(int i = 0; i < height; i++) \ { \ type *out_row = (type *)outframes[0]->get_rows()[i]; \ unsigned char *grad_row = gradient->get_rows()[i]; \ for (int j = 0; j < width; j++) \ { \ out_row[0] = (inttype)max * (invertion grad_row[0]) / config.frame_range; \ out_row[1] = max/2; \ out_row[2] = max/2; \ if (components == 4) \ out_row[3] = max; \ out_row += components; \ grad_row ++; \ } \ } #define COMPOSITEIMAGE(type, components, invertion) \ for (int i = 0; i < height; i++) \ { \ type *out_row = (type *)outframes[0]->get_rows()[i]; \ unsigned char *gradient_row = gradient->get_rows()[i]; \ for (int j = 0; j < width; j++) \ { \ unsigned int choice = invertion gradient_row[j]; \ { \ type *in_row = (type *)framelist[choice]->get_rows()[i]; \ out_row[0] = in_row[j * components + 0]; \ out_row[1] = in_row[j * components + 1]; \ out_row[2] = in_row[j * components + 2]; \ if (components == 4) \ out_row[3] = in_row[j * components + 3]; \ } \ out_row += components; \ } \ } int TimeFrontMain::process_buffer(VFrame **frame, int64_t start_position, double frame_rate) //int TimeFrontMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr) { VFrame **outframes = frame; VFrame *(framelist[1024]); framelist[0] = new VFrame (outframes[0]->get_w(), outframes[0]->get_h(), outframes[0]->get_color_model(), 0); read_frame(framelist[0], 0, start_position, frame_rate, 0); this->input = framelist[0]; this->output = outframes[0]; need_reconfigure |= load_configuration(); if (config.shape == TimeFrontConfig::OTHERTRACK) { // this->output = frame[1]; if (get_total_buffers() != 2) { // FIXME, maybe this should go to some other notification area? printf(_("ERROR: TimeFront plugin - If you are using another track for timefront, you have to have it under shared effects\n")); return 0; } if (outframes[0]->get_w() != outframes[1]->get_w() || outframes[0]->get_h() != outframes[1]->get_h()) { printf(_("Sizes of master track and timefront track do not match\n")); return 0; } } // Generate new gradient if(need_reconfigure) { need_reconfigure = 0; if(!gradient) gradient = new VFrame( outframes[0]->get_w(), outframes[0]->get_h(), BC_A8, 0); if (config.shape != TimeFrontConfig::OTHERTRACK && config.shape != TimeFrontConfig::ALPHA) { if(!engine) engine = new TimeFrontServer(this, get_project_smp() + 1, get_project_smp() + 1); engine->process_packages(); } } if (config.shape == TimeFrontConfig::ALPHA) { if(!gradient) gradient = new VFrame(outframes[0]->get_w(), outframes[0]->get_h(), BC_A8, 0); VFrame *tfframe = framelist[0]; switch (tfframe->get_color_model()) { case BC_YUVA8888: case BC_RGBA8888: GRADIENTFROMCHANNEL(unsigned char, 4, 255, 3); break; case BC_RGBA_FLOAT: GRADIENTFROMCHANNEL(float, 4, 1.0f, 3); break; default: { printf(_("TimeFront plugin error: ALPHA used, but project color model does not have alpha\n")); return 1; break; } } } else if (config.shape == TimeFrontConfig::OTHERTRACK) { if(!gradient) gradient = new VFrame(outframes[0]->get_w(), outframes[0]->get_h(), BC_A8, 0); VFrame *tfframe = outframes[1]; read_frame(tfframe, 1, start_position, frame_rate, 0); if (config.track_usage == TimeFrontConfig::OTHERTRACK_INTENSITY) { switch (tfframe->get_color_model()) { case BC_RGBA8888: GRADIENTFROMAVG(unsigned char, unsigned short, 4, 255); // Has to be 2 ranges bigger, sice we need precision for alpha break; case BC_RGB888: GRADIENTFROMAVG(unsigned char, unsigned short, 3, 255); break; case BC_RGB_FLOAT: GRADIENTFROMAVG(float, float, 3, 1.0f); break; case BC_RGBA_FLOAT: GRADIENTFROMAVG(float, float, 4, 1.0f); break; case BC_YUV888: // We cheat and take Y component as intensity GRADIENTFROMCHANNEL(unsigned char, 3, 255, 0); break; case BC_YUVA8888: GRADIENTFROMCHANNEL(unsigned char, 4, 255, 0); break; default: break; } } else if (config.track_usage == TimeFrontConfig::OTHERTRACK_ALPHA) { switch (tfframe->get_color_model()) { case BC_YUVA8888: case BC_RGBA8888: GRADIENTFROMCHANNEL(unsigned char, 4, 255, 3); break; case BC_RGBA_FLOAT: GRADIENTFROMCHANNEL(float, 4, 1.0f, 3); break; default: { printf(_("TimeFront plugin error: ALPHA track used, but project color model does not have alpha\n")); return 1; break; } } } else { printf(_("TimeFront plugin error: unsupported track_usage parameter\n")); return 1; } } if (!config.show_grayscale) { for (int i = 1; i <= config.frame_range; i++) { framelist[i] = new VFrame (outframes[0]->get_w(), outframes[0]->get_h(), outframes[0]->get_color_model(), 0); read_frame(framelist[i], 0, start_position - i, frame_rate, 0); } } int width = outframes[0]->get_w(); int height = outframes[0]->get_h(); if (config.show_grayscale) { if (!config.invert) { switch (outframes[0]->get_color_model()) { case BC_RGB888: GRADIENTTOPICTURE(unsigned char, unsigned short, 3, 255, ); break; case BC_RGBA8888: GRADIENTTOPICTURE(unsigned char, unsigned short, 4, 255, ); break; case BC_YUV888: GRADIENTTOYUVPICTURE(unsigned char, unsigned short, 3, 255, ); break; case BC_YUVA8888: GRADIENTTOYUVPICTURE(unsigned char, unsigned short, 4, 255, ); break; case BC_RGB_FLOAT: GRADIENTTOPICTURE(float, float, 3, 1.0f, ); break; case BC_RGBA_FLOAT: GRADIENTTOPICTURE(float, float, 4, 1.0f, ); break; default: break; } } else { switch (outframes[0]->get_color_model()) { case BC_RGB888: GRADIENTTOPICTURE(unsigned char, unsigned short, 3, 255, config.frame_range -); break; case BC_RGBA8888: GRADIENTTOPICTURE(unsigned char, unsigned short, 4, 255, config.frame_range -); break; case BC_YUV888: GRADIENTTOYUVPICTURE(unsigned char, unsigned short, 3, 255, config.frame_range -); break; case BC_YUVA8888: GRADIENTTOYUVPICTURE(unsigned char, unsigned short, 4, 255, config.frame_range -); break; case BC_RGB_FLOAT: GRADIENTTOPICTURE(float, float, 3, 1.0f, config.frame_range -); break; case BC_RGBA_FLOAT: GRADIENTTOPICTURE(float, float, 4, 1.0f, config.frame_range -); break; default: break; } } } else if (!config.invert) { switch (outframes[0]->get_color_model()) { case BC_RGB888: COMPOSITEIMAGE(unsigned char, 3, ); break; case BC_RGBA8888: COMPOSITEIMAGE(unsigned char, 4, ); break; case BC_YUV888: COMPOSITEIMAGE(unsigned char, 3, ); break; case BC_YUVA8888: COMPOSITEIMAGE(unsigned char, 4, ); break; case BC_RGB_FLOAT: COMPOSITEIMAGE(float, 3, ); break; case BC_RGBA_FLOAT: COMPOSITEIMAGE(float, 4, ); break; default: break; } } else { switch (outframes[0]->get_color_model()) { case BC_RGB888: COMPOSITEIMAGE(unsigned char, 3, config.frame_range -); break; case BC_RGBA8888: COMPOSITEIMAGE(unsigned char, 4, config.frame_range -); break; case BC_YUV888: COMPOSITEIMAGE(unsigned char, 3, config.frame_range -); break; case BC_YUVA8888: COMPOSITEIMAGE(unsigned char, 4, config.frame_range -); break; case BC_RGB_FLOAT: COMPOSITEIMAGE(float, 3, config.frame_range -); break; case BC_RGBA_FLOAT: COMPOSITEIMAGE(float, 4, config.frame_range -); break; default: break; } } if (config.shape == TimeFrontConfig::ALPHA) { // Set alpha to max switch (outframes[0]->get_color_model()) { case BC_YUVA8888: case BC_RGBA8888: SETALPHA(unsigned char, 255); break; case BC_RGBA_FLOAT: SETALPHA(float, 1.0f); break; default: break; } } delete framelist[0]; if (!config.show_grayscale) { for (int i = 1; i <= config.frame_range; i++) delete framelist[i]; } return 0; } void TimeFrontMain::update_gui() { if(thread) { if(load_configuration()) { thread->window->lock_window("TimeFrontMain::update_gui"); ((TimeFrontWindow*)thread->window)->frame_range->update(config.frame_range); ((TimeFrontWindow*)thread->window)->shape->set_text(TimeFrontShape::to_text(config.shape)); ((TimeFrontWindow*)thread->window)->show_grayscale->update(config.show_grayscale); ((TimeFrontWindow*)thread->window)->invert->update(config.invert); ((TimeFrontWindow*)thread->window)->shape->set_text(TimeFrontShape::to_text(config.shape)); if (((TimeFrontWindow*)thread->window)->rate) ((TimeFrontWindow*)thread->window)->rate->set_text(TimeFrontRate::to_text(config.rate)); if (((TimeFrontWindow*)thread->window)->in_radius) ((TimeFrontWindow*)thread->window)->in_radius->update(config.in_radius); if (((TimeFrontWindow*)thread->window)->out_radius) ((TimeFrontWindow*)thread->window)->out_radius->update(config.out_radius); if (((TimeFrontWindow*)thread->window)->track_usage) ((TimeFrontWindow*)thread->window)->track_usage->set_text(TimeFrontTrackUsage::to_text(config.track_usage)); if(((TimeFrontWindow*)thread->window)->angle) ((TimeFrontWindow*)thread->window)->angle->update(config.angle); if(((TimeFrontWindow*)thread->window)->center_x) ((TimeFrontWindow*)thread->window)->center_x->update(config.center_x); if(((TimeFrontWindow*)thread->window)->center_y) ((TimeFrontWindow*)thread->window)->center_y->update(config.center_y); ((TimeFrontWindow*)thread->window)->update_shape(); thread->window->unlock_window(); } } } void TimeFrontMain::save_data(KeyFrame *keyframe) { FileXML output; // cause data to be stored directly in text output.set_shared_output(keyframe->xbuf); output.tag.set_title("TIMEFRONT"); output.tag.set_property("ANGLE", config.angle); output.tag.set_property("IN_RADIUS", config.in_radius); output.tag.set_property("OUT_RADIUS", config.out_radius); output.tag.set_property("FRAME_RANGE", config.frame_range); output.tag.set_property("SHAPE", config.shape); output.tag.set_property("TRACK_USAGE", config.track_usage); output.tag.set_property("RATE", config.rate); output.tag.set_property("CENTER_X", config.center_x); output.tag.set_property("CENTER_Y", config.center_y); output.tag.set_property("INVERT", config.invert); output.tag.set_property("SHOW_GRAYSCALE", config.show_grayscale); output.append_tag(); output.tag.set_title("/TIMEFRONT"); output.append_tag(); output.append_newline(); output.terminate_string(); } void TimeFrontMain::read_data(KeyFrame *keyframe) { FileXML input; input.set_shared_input(keyframe->xbuf); int result = 0; while(!result) { result = input.read_tag(); if(!result) { if(input.tag.title_is("TIMEFRONT")) { config.angle = input.tag.get_property("ANGLE", config.angle); config.rate = input.tag.get_property("RATE", config.rate); config.in_radius = input.tag.get_property("IN_RADIUS", config.in_radius); config.out_radius = input.tag.get_property("OUT_RADIUS", config.out_radius); config.frame_range = input.tag.get_property("FRAME_RANGE", config.frame_range); config.shape = input.tag.get_property("SHAPE", config.shape); config.track_usage = input.tag.get_property("TRACK_USAGE", config.track_usage); config.center_x = input.tag.get_property("CENTER_X", config.center_x); config.center_y = input.tag.get_property("CENTER_Y", config.center_y); config.invert = input.tag.get_property("INVERT", config.invert); config.show_grayscale = input.tag.get_property("SHOW_GRAYSCALE", config.show_grayscale); } } } } TimeFrontPackage::TimeFrontPackage() : LoadPackage() { } TimeFrontUnit::TimeFrontUnit(TimeFrontServer *server, TimeFrontMain *plugin) : LoadClient(server) { this->plugin = plugin; this->server = server; } #define LOG_RANGE 1 #define CREATE_GRADIENT \ { \ /* Synthesize linear gradient for lookups */ \ \ a_table = (unsigned char *)malloc(sizeof(unsigned char) * gradient_size); \ \ for(int i = 0; i < gradient_size; i++) \ { \ float opacity = 0.0; \ switch(plugin->config.rate) \ { \ case TimeFrontConfig::LINEAR: \ if(i < in_radius) \ opacity = 0.0; \ else \ if(i >= out_radius) \ opacity = 1.0; \ else \ opacity = (float)(i - in_radius) / (out_radius - in_radius); \ break; \ case TimeFrontConfig::LOG: \ opacity = 1 - exp(LOG_RANGE * -(float)(i - in_radius) / (out_radius - in_radius)); \ break; \ case TimeFrontConfig::SQUARE: \ opacity = SQR((float)(i - in_radius) / (out_radius - in_radius)); \ break; \ } \ \ CLAMP(opacity, 0, 1); \ float transparency = 1.0 - opacity; \ a_table[i] = (unsigned char)(out4 * opacity + in4 * transparency); \ } \ \ for(int i = pkg->y1; i < pkg->y2; i++) \ { \ unsigned char *out_row = plugin->gradient->get_rows()[i]; \ \ switch(plugin->config.shape) \ { \ case TimeFrontConfig::LINEAR: \ for(int j = 0; j < w; j++) \ { \ int x = j - half_w; \ int y = -(i - half_h); \ \ /* Rotate by effect angle */ \ int input_y = (int)(gradient_size / 2 - \ (x * sin_angle + y * cos_angle) + \ 0.5); \ \ /* Get gradient value from these coords */ \ \ if(input_y < 0) \ { \ out_row[0] = out4; \ } \ else \ if(input_y >= gradient_size) \ { \ out_row[0] = in4; \ } \ else \ { \ out_row[0] = a_table[input_y]; \ } \ \ out_row ++; \ } \ break; \ \ case TimeFrontConfig::RADIAL: \ for(int j = 0; j < w; j++) \ { \ double x = j - center_x; \ double y = i - center_y; \ double magnitude = hypot(x, y); \ int input_y = (int)magnitude; \ out_row[0] = a_table[input_y]; \ out_row ++; \ } \ break; \ } \ } \ } void TimeFrontUnit::process_package(LoadPackage *package) { TimeFrontPackage *pkg = (TimeFrontPackage*)package; int h = plugin->input->get_h(); int w = plugin->input->get_w(); int half_w = w / 2; int half_h = h / 2; int gradient_size = (int)(ceil(hypot(w, h))); int in_radius = (int)(plugin->config.in_radius / 100 * gradient_size); int out_radius = (int)(plugin->config.out_radius / 100 * gradient_size); double sin_angle = sin(plugin->config.angle * (M_PI / 180)); double cos_angle = cos(plugin->config.angle * (M_PI / 180)); double center_x = plugin->config.center_x * w / 100; double center_y = plugin->config.center_y * h / 100; unsigned char *a_table = 0; if(in_radius > out_radius) { in_radius ^= out_radius; out_radius ^= in_radius; in_radius ^= out_radius; } int in4 = plugin->config.frame_range; int out4 = 0; CREATE_GRADIENT if(a_table) free(a_table); } TimeFrontServer::TimeFrontServer(TimeFrontMain *plugin, int total_clients, int total_packages) : LoadServer(total_clients, total_packages) { this->plugin = plugin; } void TimeFrontServer::init_packages() { for(int i = 0; i < get_total_packages(); i++) { TimeFrontPackage *package = (TimeFrontPackage*)get_package(i); package->y1 = plugin->input->get_h() * i / get_total_packages(); package->y2 = plugin->input->get_h() * (i + 1) / get_total_packages(); } } LoadClient* TimeFrontServer::new_client() { return new TimeFrontUnit(this, plugin); } LoadPackage* TimeFrontServer::new_package() { return new TimeFrontPackage; }