#define SHAPE_SEARCHPATH "/shapes"
#define DEFAULT_SHAPE "circle"
+// feather slider range log2 = -10 .. -1 == 9.8e-4 .. 0.5
+#define SHAPE_FLOG_MIN -10.
+#define SHAPE_FLOG_MAX -1.
+#define SHAPE_FMIN expf(M_LN2*SHAPE_FLOG_MIN)
+#define SHAPE_FMAX expf(M_LN2*SHAPE_FLOG_MAX)
REGISTER_PLUGIN(ShapeWipeMain)
+ShapeWipeConfig::ShapeWipeConfig()
+{
+ direction = 0;
+ feather = SHAPE_FMIN;
+ preserve_aspect = 0;
+ strcpy(shape_name, DEFAULT_SHAPE);
+}
+ShapeWipeConfig::~ShapeWipeConfig()
+{
+}
+
+void ShapeWipeConfig::read_xml(KeyFrame *keyframe)
+{
+ FileXML input;
+ input.set_shared_input(keyframe->xbuf);
+
+ while( !input.read_tag() ) {
+ if( input.tag.title_is("SHAPEWIPE") ) {
+ direction = input.tag.get_property("DIRECTION", direction);
+ feather = input.tag.get_property("FEATHER", feather);
+ preserve_aspect = input.tag.get_property("PRESERVE_ASPECT", preserve_aspect);
+ input.tag.get_property("SHAPE_NAME", shape_name);
+ }
+ }
+}
+void ShapeWipeConfig::save_xml(KeyFrame *keyframe)
+{
+ FileXML output;
+ output.set_shared_output(keyframe->xbuf);
+ output.tag.set_title("SHAPEWIPE");
+ output.tag.set_property("DIRECTION", direction);
+ output.tag.set_property("FEATHER", feather);
+ output.tag.set_property("PRESERVE_ASPECT", preserve_aspect);
+ output.tag.set_property("SHAPE_NAME", shape_name);
+ output.append_tag();
+ output.tag.set_title("/SHAPEWIPE");
+ output.append_tag();
+ output.terminate_string();
+}
+
+
ShapeWipeW2B::ShapeWipeW2B(ShapeWipeMain *plugin,
- ShapeWipeWindow *window,
- int x,
- int y)
- : BC_Radial(x,
- y,
- plugin->direction == 0,
- _("White to Black"))
+ ShapeWipeWindow *window, int x, int y)
+ : BC_Radial(x, y, plugin->config.direction == 0, _("White to Black"))
{
this->plugin = plugin;
this->window = window;
int ShapeWipeW2B::handle_event()
{
update(1);
- plugin->direction = 0;
+ plugin->config.direction = 0;
window->right->update(0);
plugin->send_configure_change();
return 0;
}
ShapeWipeB2W::ShapeWipeB2W(ShapeWipeMain *plugin,
- ShapeWipeWindow *window,
- int x,
- int y)
- : BC_Radial(x,
- y,
- plugin->direction == 1,
- _("Black to White"))
+ ShapeWipeWindow *window, int x, int y)
+ : BC_Radial(x, y, plugin->config.direction == 1, _("Black to White"))
{
this->plugin = plugin;
this->window = window;
int ShapeWipeB2W::handle_event()
{
update(1);
- plugin->direction = 1;
+ plugin->config.direction = 1;
window->left->update(0);
plugin->send_configure_change();
return 0;
}
-ShapeWipeAntiAlias::ShapeWipeAntiAlias(ShapeWipeMain *plugin,
- ShapeWipeWindow *window,
- int x,
- int y)
- : BC_CheckBox (x,y,plugin->antialias, _("Anti-aliasing"))
-{
- this->plugin = plugin;
- this->window = window;
-}
-
-int ShapeWipeAntiAlias::handle_event()
-{
- plugin->antialias = get_value();
- plugin->send_configure_change();
- return 0;
-}
ShapeWipePreserveAspectRatio::ShapeWipePreserveAspectRatio(ShapeWipeMain *plugin,
- ShapeWipeWindow *window,
- int x,
- int y)
- : BC_CheckBox (x, y, plugin->preserve_aspect, _("Preserve shape aspect ratio"))
+ ShapeWipeWindow *window, int x, int y)
+ : BC_CheckBox (x, y, plugin->config.preserve_aspect, _("Preserve shape aspect ratio"))
{
this->plugin = plugin;
this->window = window;
int ShapeWipePreserveAspectRatio::handle_event()
{
- plugin->preserve_aspect = get_value();
+ plugin->config.preserve_aspect = get_value();
plugin->send_configure_change();
return 0;
}
-
-
-
-
ShapeWipeTumble::ShapeWipeTumble(ShapeWipeMain *client,
- ShapeWipeWindow *window,
- int x,
- int y)
+ ShapeWipeWindow *window, int x, int y)
: BC_Tumbler(x, y)
{
this->client = client;
this->window = window;
+ set_increment(0.01);
}
int ShapeWipeTumble::handle_up_event()
}
+ShapeWipeFeather::ShapeWipeFeather(ShapeWipeMain *client,
+ ShapeWipeWindow *window, int x, int y)
+ : BC_TumbleTextBox(window,
+ bclip(client->config.feather, SHAPE_FMIN, SHAPE_FMAX),
+ SHAPE_FMIN, SHAPE_FMAX, x, y, xS(64), yS(3))
+{
+ this->client = client;
+ this->window = window;
+}
+
+int ShapeWipeFeather::handle_event()
+{
+ float v = atof(get_text());
+ bclamp(v, SHAPE_FMIN, SHAPE_FMAX);
+ client->config.feather = v;
+ float sv = log(v)/M_LN2;
+ window->shape_fslider->update(sv);
+ client->send_configure_change();
+ return 1;
+}
+ShapeWipeFSlider::ShapeWipeFSlider(ShapeWipeMain *client,
+ ShapeWipeWindow *window, int x, int y, int w)
+ : BC_FSlider(x, y, 0, w, w, SHAPE_FLOG_MIN, SHAPE_FLOG_MAX,
+ log(bclip(client->config.feather, SHAPE_FMIN, SHAPE_FMAX))/M_LN2)
+{
+ this->client = client;
+ this->window = window;
+ set_precision(0.001);
+ set_pagination(0.01, 0.1);
+ enable_show_value(0);
+}
+int ShapeWipeFSlider::handle_event()
+{
+ float v = get_value();
+ float vv = exp(M_LN2*v);
+ client->config.feather = vv;
+ window->shape_feather->update(vv);
+ client->send_configure_change();
+ return 1;
+}
+ShapeWipeReset::ShapeWipeReset(ShapeWipeMain *client,
+ ShapeWipeWindow *window, int x, int y)
+ : BC_Button(x, y, client->get_theme()->get_image_set("reset_button"))
+{
+ this->client = client;
+ this->window = window;
+ set_tooltip(_("Reset feather"));
+}
+int ShapeWipeReset::handle_event()
+{
+ window->shape_fslider->update(SHAPE_FLOG_MIN);
+ float v = SHAPE_FMIN;
+ window->shape_feather->update(v);
+ client->config.feather = v;
+ client->send_configure_change();
+ return 1;
+}
ShapeWipeShape::ShapeWipeShape(ShapeWipeMain *client,
- ShapeWipeWindow *window,
- int x,
- int y,
- int text_w,
- int list_h)
- : BC_PopupTextBox(window,
- &window->shapes,
- client->shape_name,
- x,
- y,
- text_w,
- list_h)
+ ShapeWipeWindow *window, int x, int y,
+ int text_w, int list_h)
+ : BC_PopupTextBox(window, &window->shapes, client->config.shape_name,
+ x, y, text_w, list_h)
{
this->client = client;
this->window = window;
int ShapeWipeShape::handle_event()
{
- strcpy(client->shape_name, get_text());
+ strcpy(client->config.shape_name, get_text());
client->send_configure_change();
return 1;
}
-
-
-
-
-
-
ShapeWipeWindow::ShapeWipeWindow(ShapeWipeMain *plugin)
- : PluginClientWindow(plugin,
- 450,
- 125,
- 450,
- 125,
- 0)
+ : PluginClientWindow(plugin, xS(425), yS(215), xS(425), yS(215), 0)
{
this->plugin = plugin;
+ shape_feather = 0;
}
ShapeWipeWindow::~ShapeWipeWindow()
{
shapes.remove_all_objects();
+ delete shape_feather;
}
-
void ShapeWipeWindow::create_objects()
{
BC_Title *title = 0;
lock_window("ShapeWipeWindow::create_objects");
- int widget_border = plugin->get_theme()->widget_border;
- int window_border = plugin->get_theme()->window_border;
- int x = window_border, y = window_border;
+ int pad = xS(10), margin = xS(10);
+ int x = margin, y = margin;
+ int ww = get_w() - 2*margin;
plugin->init_shapes();
- for(int i = 0; i < plugin->shape_titles.size(); i++)
- {
+ for( int i=0; i<plugin->shape_titles.size(); ++i ) {
shapes.append(new BC_ListBoxItem(plugin->shape_titles.get(i)));
}
- add_subwindow(title = new BC_Title(x, y, _("Direction:")));
- x += title->get_w() + widget_border;
- add_subwindow(left = new ShapeWipeW2B(plugin,
- this,
- x,
- y));
- x += left->get_w() + widget_border;
- add_subwindow(right = new ShapeWipeB2W(plugin,
- this,
- x,
- y));
- x = window_border;
- y += right->get_h() + widget_border;
-
+ BC_TitleBar *bar;
+ add_subwindow(bar = new BC_TitleBar(x, y, ww, xS(20), yS(10),
+ _("Wipe"), MEDIUMFONT));
+ y += bar->get_h() + pad;
add_subwindow(title = new BC_Title(x, y, _("Shape:")));
- x += title->get_w() + widget_border;
-
-// add_subwindow(filename_widget = new
-// ShapeWipeFilename(plugin,
-// this,
-// plugin->filename,
-// x,
-// y));
-// x += 200;
-// add_subwindow(new ShapeWipeBrowseButton(
-// plugin,
-// this,
-// filename_widget,
-// x,
-// y));
-
- shape_text = new ShapeWipeShape(plugin,
- this,
- x,
- y,
- 150,
- 200);
+ int x1 = xS(85), x2 = xS(355), x3 = xS(386);
+ shape_text = new ShapeWipeShape(plugin, this, x1, y, x2-x1, yS(200));
shape_text->create_objects();
- x += shape_text->get_w() + widget_border;
- add_subwindow(new ShapeWipeTumble(plugin,
- this,
- x,
- y));
- x = window_border;
- y += shape_text->get_h() + widget_border;
-
- ShapeWipeAntiAlias *anti_alias;
- add_subwindow(anti_alias = new ShapeWipeAntiAlias(
- plugin,
- this,
- x,
- y));
- y += anti_alias->get_h() + widget_border;
+ add_subwindow(new ShapeWipeTumble(plugin, this, x3, y));
+ y += shape_text->get_h() + pad;
+
+ x = margin;
+ add_subwindow(title = new BC_Title(x, y, _("Feather:")));
+ x = x1;
+ shape_feather = new ShapeWipeFeather(plugin, this, x, y);
+ shape_feather->create_objects();
+ shape_feather->set_log_floatincrement(1);
+ x += shape_feather->get_w() + 2*pad;
+ add_subwindow(shape_fslider = new ShapeWipeFSlider(plugin, this, x, y, x2-x));
+ add_subwindow(shape_reset = new ShapeWipeReset(plugin, this, x3, y));
+ y += shape_fslider->get_h() + pad;
+
+ x = margin;
ShapeWipePreserveAspectRatio *aspect_ratio;
add_subwindow(aspect_ratio = new ShapeWipePreserveAspectRatio(
- plugin,
- this,
- x,
- y));
- y += aspect_ratio->get_h() + widget_border;
+ plugin, this, x, y));
+ y += aspect_ratio->get_h() + pad;
+
+ add_subwindow(bar = new BC_TitleBar(x, y, ww, xS(20), yS(10),
+ _("Direction"), MEDIUMFONT));
+ y += bar->get_h() + pad;
+ x = margin;
+ add_subwindow(left = new ShapeWipeW2B(plugin, this, x, y));
+ y += left->get_h();
+ add_subwindow(right = new ShapeWipeB2W(plugin, this, x, y));
show_window();
unlock_window();
void ShapeWipeWindow::next_shape()
{
- for(int i = 0; i < plugin->shape_titles.size(); i++)
- {
- if(!strcmp(plugin->shape_titles.get(i), plugin->shape_name))
- {
- i++;
- if(i >= plugin->shape_titles.size()) i = 0;
- strcpy(plugin->shape_name, plugin->shape_titles.get(i));
- shape_text->update(plugin->shape_name);
- break;
- }
+ ShapeWipeConfig &config = plugin->config;
+ int k = plugin->shape_titles.size();
+ while( --k>=0 && strcmp(plugin->shape_titles.get(k),config.shape_name) );
+
+ if( k >= 0 ) {
+ if( ++k >= plugin->shape_titles.size() ) k = 0;
+ strcpy(config.shape_name, plugin->shape_titles.get(k));
+ shape_text->update(config.shape_name);
}
client->send_configure_change();
}
void ShapeWipeWindow::prev_shape()
{
- for(int i = 0; i < plugin->shape_titles.size(); i++)
- {
- if(!strcmp(plugin->shape_titles.get(i), plugin->shape_name))
- {
- i--;
- if(i < 0) i = plugin->shape_titles.size() - 1;
- strcpy(plugin->shape_name, plugin->shape_titles.get(i));
- shape_text->update(plugin->shape_name);
- break;
- }
+ ShapeWipeConfig &config = plugin->config;
+ int k = plugin->shape_titles.size();
+ while( --k>=0 && strcmp(plugin->shape_titles.get(k),config.shape_name) );
+
+ if( k >= 0 ) {
+ if( --k < 0 ) k = plugin->shape_titles.size()-1;
+ strcpy(config.shape_name, plugin->shape_titles.get(k));
+ shape_text->update(config.shape_name);
}
client->send_configure_change();
}
-
-
-
ShapeWipeMain::ShapeWipeMain(PluginServer *server)
: PluginVClient(server)
{
- direction = 0;
- filename[0] = 0;
- last_read_filename[0] = '\0';
- strcpy(shape_name, DEFAULT_SHAPE);
+ input = 0;
+ output = 0;
+ engine = 0;
+ current_filename[0] = '\0';
current_name[0] = 0;
- pattern_image = NULL;
- min_value = (unsigned char)255;
- max_value = (unsigned char)0;
- antialias = 0;
- preserve_aspect = 0;
+ pattern_image = 0;
+ min_value = 255;
+ max_value = 0;
last_preserve_aspect = 0;
shapes_initialized = 0;
shape_paths.set_array_delete();
reset_pattern_image();
shape_paths.remove_all_objects();
shape_titles.remove_all_objects();
+ delete engine;
}
const char* ShapeWipeMain::plugin_title() { return N_("Shape Wipe"); }
NEW_WINDOW_MACRO(ShapeWipeMain, ShapeWipeWindow);
-
-
-void ShapeWipeMain::save_data(KeyFrame *keyframe)
+void ShapeWipeMain::read_data(KeyFrame *keyframe)
{
- FileXML output;
- output.set_shared_output(keyframe->xbuf);
- output.tag.set_title("SHAPEWIPE");
- output.tag.set_property("DIRECTION", direction);
- output.tag.set_property("ANTIALIAS", antialias);
- output.tag.set_property("PRESERVE_ASPECT", preserve_aspect);
- output.tag.set_property("FILENAME", filename);
- output.tag.set_property("SHAPE_NAME", shape_name);
- output.append_tag();
- output.tag.set_title("/SHAPEWIPE");
- output.append_tag();
- output.terminate_string();
+ config.read_xml(keyframe);
}
-
-void ShapeWipeMain::read_data(KeyFrame *keyframe)
+void ShapeWipeMain::save_data(KeyFrame *keyframe)
{
- FileXML input;
-
- input.set_shared_input(keyframe->xbuf);
-
- while(!input.read_tag())
- {
- if(input.tag.title_is("SHAPEWIPE"))
- {
- direction = input.tag.get_property("DIRECTION", direction);
- antialias = input.tag.get_property("ANTIALIAS", antialias);
- preserve_aspect = input.tag.get_property("PRESERVE_ASPECT", preserve_aspect);
- input.tag.get_property("FILENAME", filename);
- input.tag.get_property("SHAPE_NAME", shape_name);
- }
- }
+ config.save_xml(keyframe);
}
void ShapeWipeMain::init_shapes()
{
- if(!shapes_initialized)
- {
+ if( !shapes_initialized ) {
FileSystem fs;
fs.set_filter("*.png");
char shape_path[BCTEXTLEN];
sprintf(shape_path, "%s%s", get_plugin_dir(), SHAPE_SEARCHPATH);
fs.update(shape_path);
- for(int i = 0; i < fs.total_files(); i++)
- {
+ for( int i=0; i<fs.total_files(); ++i ) {
FileItem *file_item = fs.get_entry(i);
- if(!file_item->get_is_dir())
- {
+ if( !file_item->get_is_dir() ) {
shape_paths.append(cstrdup(file_item->get_path()));
char *ptr = cstrdup(file_item->get_name());
char *ptr2 = strrchr(ptr, '.');
return 1;
}
-int ShapeWipeMain::read_pattern_image(int new_frame_width, int new_frame_height)
+int ShapeWipeMain::read_pattern_image(char *shape_name,
+ int new_frame_width, int new_frame_height)
{
png_byte header[8];
int is_png;
- int row;
- int col;
- int scaled_row;
- int scaled_col;
+ int row, col;
int pixel_width;
unsigned char value;
png_uint_32 width;
png_uint_32 height;
png_byte color_type;
png_byte bit_depth;
- png_structp png_ptr;
- png_infop info_ptr;
- png_infop end_info;
- png_bytep *image;
+ png_structp png_ptr = 0;
+ png_infop info_ptr = 0;
+ png_infop end_info = 0;
+ png_bytep *image = 0;
+ FILE *fp = 0;
frame_width = new_frame_width;
frame_height = new_frame_height;
+ int ret = 0;
// Convert name to filename
- for(int i = 0; i < shape_paths.size(); i++)
- {
- if(!strcmp(shape_titles.get(i), shape_name))
- {
- strcpy(filename, shape_paths.get(i));
- break;
- }
- }
-
- FILE *fp = fopen(filename, "rb");
- if (!fp)
- {
- return 1;
- }
-
- fread(header, 1, 8, fp);
- is_png = !png_sig_cmp(header, 0, 8);
-
- if (!is_png)
- {
- fclose(fp);
- return 1;
- }
-
- png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
- NULL, NULL, NULL);
-
- if (!png_ptr)
- {
- fclose(fp);
- return 1;
+ int k = shape_paths.size();
+ while( --k>=0 && strcmp(shape_titles[k], shape_name) );
+ if( k < 0 ) ret = 1;
+ if( !ret ) {
+ strcpy(current_filename, shape_paths[k]);
+ fp = fopen(current_filename, "rb");
+ if( !fp ) ret = 1;
}
-
- /* Tell libpng we already checked the first 8 bytes */
- png_set_sig_bytes(png_ptr, 8);
-
- info_ptr = png_create_info_struct(png_ptr);
- if (!info_ptr)
- {
- png_destroy_read_struct(&png_ptr, NULL, NULL);
- fclose(fp);
- return 1;
- }
-
- end_info = png_create_info_struct(png_ptr);
- if (!end_info)
- {
- png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
- fclose(fp);
- return 1;
+ if( !ret ) {
+ fread(header, 1, 8, fp);
+ is_png = !png_sig_cmp(header, 0, 8);
+ if( !is_png ) ret = 1;
}
-
- png_init_io(png_ptr, fp);
- png_read_info(png_ptr, info_ptr);
-
- color_type = png_get_color_type(png_ptr, info_ptr);
- bit_depth = png_get_bit_depth(png_ptr, info_ptr);
- width = png_get_image_width (png_ptr, info_ptr);
- height = png_get_image_height(png_ptr, info_ptr);
-
- /* Skip the alpha channel if present
- * stripping alpha currently doesn't work in conjunction with
- * converting to grayscale in libpng */
- if (color_type & PNG_COLOR_MASK_ALPHA)
- pixel_width = 2;
- else
- pixel_width = 1;
-
- /* Convert 16 bit data to 8 bit */
- if (bit_depth == 16) png_set_strip_16(png_ptr);
-
- /* Expand to 1 pixel per byte if necessary */
- if (bit_depth < 8) png_set_packing(png_ptr);
-
- /* Convert to grayscale */
- if (color_type == PNG_COLOR_TYPE_RGB ||
- color_type == PNG_COLOR_TYPE_RGB_ALPHA)
- png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1);
-
- /* Allocate memory to hold the original png image */
- image = (png_bytep*)malloc(sizeof(png_bytep)*height);
- for (row = 0; row < (int)height; row++)
- {
- image[row] = (png_byte*)malloc(sizeof(png_byte)*width*pixel_width);
+ if( !ret ) {
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ if( !png_ptr ) ret = 1;
}
-
- /* Allocate memory for the pattern image that will actually be
- * used for the wipe */
- pattern_image = (unsigned char**)malloc(sizeof(unsigned char*)*frame_height);
-
-
- png_read_image(png_ptr, image);
- png_read_end(png_ptr, end_info);
-
- double row_factor, col_factor;
- double row_offset = 0.5, col_offset = 0.5; // for rounding
-
- if (preserve_aspect && aspect_w != 0 && aspect_h != 0)
- {
- row_factor = (height-1)/aspect_h;
- col_factor = (width-1)/aspect_w;
- if (row_factor < col_factor)
- col_factor = row_factor;
- else
- row_factor = col_factor;
- row_factor *= aspect_h/(double)(frame_height-1);
- col_factor *= aspect_w/(double)(frame_width-1);
-
- // center the pattern over the frame
- row_offset += (height-1-(frame_height-1)*row_factor)/2;
- col_offset += (width-1-(frame_width-1)*col_factor)/2;
+ if( !ret ) {
+ /* Tell libpng we already checked the first 8 bytes */
+ png_set_sig_bytes(png_ptr, 8);
+ info_ptr = png_create_info_struct(png_ptr);
+ if( !info_ptr ) ret = 1;
}
- else
- {
- // Stretch (or shrink) the pattern image to fill the frame
- row_factor = (double)(height-1)/(double)(frame_height-1);
- col_factor = (double)(width-1)/(double)(frame_width-1);
+ if( !ret ) {
+ end_info = png_create_info_struct(png_ptr);
+ if( !end_info ) ret = 1;
}
-
- for (scaled_row = 0; scaled_row < frame_height; scaled_row++)
- {
- row = (int)(row_factor*scaled_row + row_offset);
- pattern_image[scaled_row] = (unsigned char*)malloc(sizeof(unsigned char)*frame_width);
- for (scaled_col = 0; scaled_col < frame_width; scaled_col++)
- {
- col = (int)(col_factor*scaled_col + col_offset)*pixel_width;
- value = image[row][col];
- pattern_image[scaled_row][scaled_col] = value;
- if (value < min_value) min_value = value;
- if (value > max_value) max_value = value;
-
+ if( !ret ) {
+ png_init_io(png_ptr, fp);
+ png_read_info(png_ptr, info_ptr);
+
+ color_type = png_get_color_type(png_ptr, info_ptr);
+ bit_depth = png_get_bit_depth(png_ptr, info_ptr);
+ width = png_get_image_width (png_ptr, info_ptr);
+ height = png_get_image_height(png_ptr, info_ptr);
+
+ /* Skip the alpha channel if present
+ * stripping alpha currently doesn't work in conjunction with
+ * converting to grayscale in libpng */
+ pixel_width = color_type & PNG_COLOR_MASK_ALPHA ? 2 : 1;
+ /* Convert 16 bit data to 8 bit */
+ if( bit_depth == 16 ) png_set_strip_16(png_ptr);
+ /* Expand to 1 pixel per byte if necessary */
+ if( bit_depth < 8 ) png_set_packing(png_ptr);
+
+ /* Convert to grayscale */
+ if( color_type == PNG_COLOR_TYPE_RGB ||
+ color_type == PNG_COLOR_TYPE_RGB_ALPHA )
+ png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1);
+
+ /* Allocate memory to hold the original png image */
+ image = (png_bytep*)new png_bytep[height];
+ for( row=0; row<(int)height; ++row )
+ image[row] = new png_byte[width*pixel_width];
+
+ /* Allocate memory for the pattern image that will actually be
+ * used for the wipe */
+ pattern_image = new unsigned char*[frame_height];
+
+ png_read_image(png_ptr, image);
+ png_read_end(png_ptr, end_info);
+
+ double row_factor, col_factor;
+ double row_offset = 0.5, col_offset = 0.5; // for rounding
+
+ if( config.preserve_aspect && aspect_w && aspect_h ) {
+ row_factor = (height-1)/aspect_h;
+ col_factor = (width-1)/aspect_w;
+ if( row_factor < col_factor )
+ col_factor = row_factor;
+ else
+ row_factor = col_factor;
+ row_factor *= aspect_h/(double)(frame_height-1);
+ col_factor *= aspect_w/(double)(frame_width-1);
+
+ // center the pattern over the frame
+ row_offset += (height-1-(frame_height-1)*row_factor)/2;
+ col_offset += (width-1-(frame_width-1)*col_factor)/2;
+ }
+ else {
+ // Stretch (or shrink) the pattern image to fill the frame
+ row_factor = (double)(height-1)/(double)(frame_height-1);
+ col_factor = (double)(width-1)/(double)(frame_width-1);
+ }
+ // first, determine range min..max
+ for( int y=0; y<frame_height; ++y ) {
+ row = (int)(row_factor*y + row_offset);
+ for( int x=0; x<frame_width; ++x ) {
+ col = (int)(col_factor*x + col_offset)*pixel_width;
+ value = image[row][col];
+ if( value < min_value ) min_value = value;
+ if( value > max_value ) max_value = value;
+ }
+ }
+ int range = max_value - min_value;
+ if( !range ) range = 1;
+ // scale to fade normalized pattern_image
+ for( int y=0; y<frame_height; ++y ) {
+ row = (int)(row_factor*y + row_offset);
+ pattern_image[y] = new unsigned char[frame_width];
+ for( int x=0; x<frame_width; ++x ) {
+ col = (int)(col_factor*x + col_offset)*pixel_width;
+ value = image[row][col];
+ pattern_image[y][x] = 0xff*(value - min_value) / range;
+ }
}
}
- png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
- fclose(fp);
- /* Deallocate the original image as it is no longer needed */
- for (row = 0; row < (int)height; row++)
- {
- free(image[row]);
+ if( png_ptr || info_ptr || end_info )
+ png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+ if( fp )
+ fclose(fp);
+ if( image ) {
+ for( row=0; row<(int)height; ++row )
+ delete [] image[row];
+ delete [] image;
}
- free (image);
- return 0;
+ return ret;
}
void ShapeWipeMain::reset_pattern_image()
{
- int row;
- if (pattern_image != NULL)
- {
- for (row = 0; row < frame_height; row++)
- {
- free (pattern_image[row]);
- }
- free (pattern_image);
- pattern_image = NULL;
- min_value = (unsigned char)255;
- max_value = (unsigned char)0; // are recalc'd in read_pattern_image
+ if( pattern_image ) {
+ for( int y=0; y<frame_height; ++y )
+ delete [] pattern_image[y];
+ delete [] pattern_image; pattern_image = 0;
+ min_value = 255;
+ max_value = 0; // updated in read_pattern_image
}
}
-#define SHAPEWIPE(type, components) \
-{ \
-\
- type **in_rows = (type**)incoming->get_rows(); \
- type **out_rows = (type**)outgoing->get_rows(); \
- \
- type *in_row; \
- type *out_row; \
- \
- if( !direction ) { \
- for(j = 0; j < h; j++) { \
- in_row = (type*) in_rows[j]; \
- out_row = (type*)out_rows[j]; \
- pattern_row = pattern_image[j]; \
- \
- col_offset = 0; \
- for(k = 0; k < w; k++, col_offset += components ) { \
- value = pattern_row[k]; \
- if (value < threshold) continue; \
- out_row[col_offset] = in_row[col_offset]; \
- out_row[col_offset + 1] = in_row[col_offset + 1]; \
- out_row[col_offset + 2] = in_row[col_offset + 2]; \
- if(components == 4) \
- out_row[col_offset + 3] = in_row[col_offset + 3]; \
+#define SHAPEBLEND(type, components, tmp_type) { \
+ float scale = feather ? 1/feather : 0xff; \
+ type **in_rows = (type**)input->get_rows(); \
+ type **out_rows = (type**)output->get_rows(); \
+ if( !dir ) { \
+ for( int y=y1; y<y2; ++y ) { \
+ type *in_row = (type*) in_rows[y]; \
+ type *out_row = (type*)out_rows[y]; \
+ unsigned char *pattern_row = pattern_image[y]; \
+ for( int x=0; x<w; ++x ) { \
+ tmp_type d = (pattern_row[x] - threshold) * scale; \
+ if( d > 0xff ) d = 0xff; \
+ else if( d < -0xff ) d = -0xff; \
+ tmp_type a = (d + 0xff) / 2, b = 0xff - a; \
+ for( int i=0; i<components; ++i ) { \
+ type ic = in_row[i], oc = out_row[i]; \
+ out_row[i] = (ic * a + oc * b) / 0xff; \
+ } \
+ in_row += components; out_row += components; \
} \
} \
} \
else { \
- for(j = 0; j < h; j++) { \
- in_row = (type*) in_rows[j]; \
- out_row = (type*)out_rows[j]; \
- pattern_row = pattern_image[j]; \
- \
- col_offset = 0; \
- for(k = 0; k < w; k++, col_offset += components ) { \
- value = pattern_row[k]; \
- if (value > threshold) continue; \
- out_row[col_offset] = in_row[col_offset]; \
- out_row[col_offset + 1] = in_row[col_offset + 1]; \
- out_row[col_offset + 2] = in_row[col_offset + 2]; \
- if(components == 4) \
- out_row[col_offset + 3] = in_row[col_offset + 3]; \
+ for( int y=y1; y<y2; ++y ) { \
+ type *in_row = (type*) in_rows[y]; \
+ type *out_row = (type*)out_rows[y]; \
+ unsigned char *pattern_row = pattern_image[y]; \
+ for( int x=0; x<w; ++x ) { \
+ tmp_type d = (pattern_row[x] - threshold) * scale; \
+ if( d > 0xff ) d = 0xff; \
+ else if( d < -0xff ) d = -0xff; \
+ tmp_type b = (d + 0xff) / 2, a = 0xff - b; \
+ for( int i=0; i<components; ++i ) { \
+ type ic = in_row[i], oc = out_row[i]; \
+ out_row[i] = (ic * a + oc * b) / 0xff; \
+ } \
+ in_row += components; out_row += components; \
} \
} \
} \
}
-#define COMPARE1(x,y) \
-{ \
- if (pattern_image[x][y] <= threshold) opacity++; \
-}
-
-#define COMPARE2(x,y) \
-{ \
- if (pattern_image[x][y] >= threshold) opacity++; \
-}
-
-// components is always 4
-#define BLEND_ONLY_4_NORMAL(temp_type, type, max, chroma_offset,x,y) \
-{ \
- const int bits = sizeof(type) * 8; \
- temp_type blend_opacity = (temp_type)(alpha * ((temp_type)1 << bits) + 0.5); \
- temp_type blend_transparency = ((temp_type)1 << bits) - blend_opacity; \
- \
- col = y * 4; \
- type* in_row = (type*)incoming->get_rows()[x]; \
- type* output = (type*)outgoing->get_rows()[x]; \
- \
- output[col] = ((temp_type)in_row[col] * blend_opacity + output[col] * blend_transparency) >> bits; \
- output[col+1] = ((temp_type)in_row[col+1] * blend_opacity + output[col+1] * blend_transparency) >> bits; \
- output[col+2] = ((temp_type)in_row[col+2] * blend_opacity + output[col+2] * blend_transparency) >> bits; \
-}
-
-
-// components is always 3
-#define BLEND_ONLY_3_NORMAL(temp_type, type, max, chroma_offset,x,y) \
-{ \
- const int bits = sizeof(type) * 8; \
- temp_type blend_opacity = (temp_type)(alpha * ((temp_type)1 << bits) + 0.5); \
- temp_type blend_transparency = ((temp_type)1 << bits) - blend_opacity; \
- \
- col = y * 3; \
- type* in_row = (type*)incoming->get_rows()[x]; \
- type* output = (type*)outgoing->get_rows()[x]; \
- \
- output[col] = ((temp_type)in_row[col] * blend_opacity + output[col] * blend_transparency) >> bits; \
- output[col+1] = ((temp_type)in_row[col+1] * blend_opacity + output[col+1] * blend_transparency) >> bits; \
- output[col+2] = ((temp_type)in_row[col+2] * blend_opacity + output[col+2] * blend_transparency) >> bits; \
-}
-
-/* opacity is defined as opacity of incoming frame */
-#define BLEND(x,y,total) \
-{ \
- float pixel_opacity = (float)opacity / total; \
- float alpha = pixel_opacity; \
- float pixel_transparency = 1.0 - pixel_opacity; \
- int col; \
- \
- if (pixel_opacity > 0.0) \
- { \
- switch(incoming->get_color_model()) \
- { \
- case BC_RGB_FLOAT: \
- { \
- float *in_row = (float*)incoming->get_rows()[x]; \
- float *out_row = (float*)outgoing->get_rows()[x]; \
- col = y * 3; \
- out_row[col] = in_row[col] * pixel_opacity + \
- out_row[col] * pixel_transparency; \
- out_row[col+1] = in_row[col+1] * pixel_opacity + \
- out_row[col+1] * pixel_transparency; \
- out_row[col+2] = in_row[col+2] * pixel_opacity + \
- out_row[col+2] * pixel_transparency; \
- break; \
- } \
- case BC_RGBA_FLOAT: \
- { \
- float *in_row = (float*)incoming->get_rows()[x]; \
- float *out_row = (float*)outgoing->get_rows()[x]; \
- col = y * 4; \
- out_row[col] = in_row[col] * pixel_opacity + \
- out_row[col] * pixel_transparency; \
- out_row[col+1] = in_row[col+1] * pixel_opacity + \
- out_row[col+1] * pixel_transparency; \
- out_row[col+2] = in_row[col+2] * pixel_opacity + \
- out_row[col+2] * pixel_transparency; \
- break; \
- } \
- case BC_RGB888: \
- BLEND_ONLY_3_NORMAL(uint32_t, unsigned char, 0xff, 0,x,y); \
- break; \
- case BC_YUV888: \
- BLEND_ONLY_3_NORMAL(int32_t, unsigned char, 0xff, 0x80,x,y); \
- break; \
- case BC_RGBA8888: \
- BLEND_ONLY_4_NORMAL(uint32_t, unsigned char, 0xff, 0,x,y); \
- break; \
- case BC_YUVA8888: \
- BLEND_ONLY_4_NORMAL(int32_t, unsigned char, 0xff, 0x80,x,y); \
- break; \
- case BC_RGB161616: \
- BLEND_ONLY_3_NORMAL(uint64_t, uint16_t, 0xffff, 0,x,y); \
- break; \
- case BC_YUV161616: \
- BLEND_ONLY_3_NORMAL(int64_t, uint16_t, 0xffff, 0x8000,x,y); \
- break; \
- case BC_RGBA16161616: \
- BLEND_ONLY_4_NORMAL(uint64_t, uint16_t, 0xffff, 0,x,y); \
- break; \
- case BC_YUVA16161616: \
- BLEND_ONLY_4_NORMAL(int64_t, uint16_t, 0xffff, 0x8000,x,y); \
- break; \
- } \
- } \
-}
-
-int ShapeWipeMain::process_realtime(VFrame *incoming, VFrame *outgoing)
+int ShapeWipeMain::process_realtime(VFrame *input, VFrame *output)
{
- unsigned char *pattern_row;
- int col_offset;
- unsigned char threshold;
- unsigned char value;
- int j, k;
- int opacity;
-
+ this->input = input;
+ this->output = output;
+ int w = input->get_w();
+ int h = input->get_h();
init_shapes();
load_configuration();
- int w = incoming->get_w();
- int h = incoming->get_h();
-
- if (strncmp(filename, last_read_filename, BCTEXTLEN) ||
- strncmp(shape_name, current_name, BCTEXTLEN) ||
- preserve_aspect != last_preserve_aspect)
- {
+ if( strncmp(config.shape_name, current_name, BCTEXTLEN) ||
+ config.preserve_aspect != last_preserve_aspect ) {
reset_pattern_image();
}
-
- if (!pattern_image)
- {
- read_pattern_image(w, h);
- strncpy(last_read_filename, filename, BCTEXTLEN);
- last_preserve_aspect = preserve_aspect;
-
- if (pattern_image)
- {
- strncpy(last_read_filename, filename, BCTEXTLEN);
- strncpy(current_name, shape_name, BCTEXTLEN);
- last_preserve_aspect = preserve_aspect;
- }
- else {
- fprintf(stderr, _("Shape Wipe: cannot load shape %s\n"), filename);
- last_read_filename[0] = 0;
+ if ( !pattern_image ) {
+ if( read_pattern_image(config.shape_name, w, h) ) {
+ fprintf(stderr, _("Shape Wipe: cannot load shape %s\n"),
+ current_filename);
+ current_filename[0] = 0;
return 0;
}
+ strncpy(current_name, config.shape_name, BCTEXTLEN);
+ last_preserve_aspect = config.preserve_aspect;
}
- if (direction)
- {
- threshold = (unsigned char)(
- (float)PluginClient::get_source_position() /
- (float)PluginClient::get_total_len() *
- (float)(max_value - min_value))
- + min_value;
- }
- else
- {
- threshold = (unsigned char)((max_value - min_value) - (
- (float)PluginClient::get_source_position() /
- (float)PluginClient::get_total_len() *
- (float)(max_value - min_value)))
- + min_value;
+ float fade = (float)PluginClient::get_source_position() /
+ (float)PluginClient::get_total_len();
+ if( !config.direction ) fade = 1 - fade;
+ threshold = fade * 0xff;
+
+ int slices = w*h/0x40000+1;
+ int max_slices = BC_Resources::machine_cpus/2;
+ if( slices > max_slices ) slices = max_slices;
+ if( slices < 1 ) slices = 1;
+ if( engine && engine->get_total_clients() != slices ) {
+ delete engine; engine = 0;
}
+ if( !engine )
+ engine = new ShapeEngine(this, slices, slices);
- if (antialias)
- {
- if (direction)
- {
- /* Top left corner */
- opacity = 0;
- COMPARE1(0,0);
- COMPARE1(0,1);
- COMPARE1(1,0);
- COMPARE1(1,1);
- BLEND(0,0,4.0);
-
- /* Top edge */
- for (k = 1; k < w-1; k++)
- {
- opacity = 0;
- COMPARE1(0,k-1);
- COMPARE1(0,k);
- COMPARE1(0,k+1);
- COMPARE1(1,k-1);
- COMPARE1(1,k);
- COMPARE1(1,k+1);
- BLEND(0,k,6.0);
- }
+ engine->process_packages();
+ return 0;
+}
- /* Top right corner */
- opacity = 0;
- COMPARE1(0,w-1);
- COMPARE1(0,w-2);
- COMPARE1(1,w-1);
- COMPARE1(1,w-2);
- BLEND(0,w-1,4.0);
-
- /* Left edge */
- for (j = 1; j < h-1; j++)
- {
- opacity = 0;
- COMPARE1(j-1,0);
- COMPARE1(j,0);
- COMPARE1(j+1,0);
- COMPARE1(j-1,1);
- COMPARE1(j,1);
- COMPARE1(j+1,1);
- BLEND(j,0,6.0);
- }
- /* Middle */
- for (j = 1; j < h-1; j++)
- {
- for (k = 1; k < w-1; k++)
- {
- opacity = 0;
- COMPARE1(j-1,k-1);
- COMPARE1(j,k-1);
- COMPARE1(j+1,k-1);
- COMPARE1(j-1,k);
- COMPARE1(j,k);
- COMPARE1(j+1,k);
- COMPARE1(j-1,k+1);
- COMPARE1(j,k+1);
- COMPARE1(j+1,k+1);
- BLEND(j,k,9.0);
- }
- }
+ShapePackage::ShapePackage()
+ : LoadPackage()
+{
+}
- /* Right edge */
- for (j = 1; j < h-1; j++)
- {
- opacity = 0;
- COMPARE1(j-1,w-1);
- COMPARE1(j,w-1);
- COMPARE1(j+1,w-1);
- COMPARE1(j-1,w-2);
- COMPARE1(j,w-2);
- COMPARE1(j+1,w-2);
- BLEND(j,w-1,6.0);
- }
+ShapeUnit::ShapeUnit(ShapeEngine *server) : LoadClient(server)
+{
+ this->server = server;
+}
- /* Bottom left corner */
- opacity = 0;
- COMPARE1(h-1,0);
- COMPARE1(h-1,1);
- COMPARE1(h-2,0);
- COMPARE1(h-2,1);
- BLEND(h-1,0,4.0);
-
- /* Bottom edge */
- for (k = 1; k < w-1; k++)
- {
- opacity = 0;
- COMPARE1(h-1,k-1);
- COMPARE1(h-1,k);
- COMPARE1(h-1,k+1);
- COMPARE1(h-2,k-1);
- COMPARE1(h-2,k);
- COMPARE1(h-2,k+1);
- BLEND(h-1,k,6.0);
- }
+ShapeUnit::~ShapeUnit()
+{
+}
- /* Bottom right corner */
- opacity = 0;
- COMPARE1(h-1,w-1);
- COMPARE1(h-1,w-2);
- COMPARE1(h-2,w-1);
- COMPARE1(h-2,w-2);
- BLEND(h-1,w-1,4.0);
- }
- else
- {
- /* Top left corner */
- opacity = 0;
- COMPARE2(0,0);
- COMPARE2(0,1);
- COMPARE2(1,0);
- COMPARE2(1,1);
- BLEND(0,0,4.0);
-
- /* Top edge */
- for (k = 1; k < w-1; k++)
- {
- opacity = 0;
- COMPARE2(0,k-1);
- COMPARE2(0,k);
- COMPARE2(0,k+1);
- COMPARE2(1,k-1);
- COMPARE2(1,k);
- COMPARE2(1,k+1);
- BLEND(0,k,6.0);
- }
+void ShapeUnit::process_package(LoadPackage *package)
+{
+ VFrame *input = server->plugin->input;
+ VFrame *output = server->plugin->output;
+ int w = input->get_w();
+ int dir = server->plugin->config.direction;
+
+ unsigned char **pattern_image = server->plugin->pattern_image;
+ unsigned char threshold = server->plugin->threshold;
+ float feather = server->plugin->config.feather;
+ ShapePackage *pkg = (ShapePackage*)package;
+ int y1 = pkg->y1, y2 = pkg->y2;
+
+ switch(input->get_color_model()) {
+ case BC_RGB_FLOAT:
+ SHAPEBLEND(float, 3, float)
+ break;
+ case BC_RGB888:
+ case BC_YUV888:
+ SHAPEBLEND(unsigned char, 3, int)
+ break;
+ case BC_RGBA_FLOAT:
+ SHAPEBLEND(float, 4, float)
+ break;
+ case BC_RGBA8888:
+ case BC_YUVA8888:
+ SHAPEBLEND(unsigned char, 4, int)
+ break;
+ case BC_RGB161616:
+ case BC_YUV161616:
+ SHAPEBLEND(uint16_t, 3, int64_t)
+ break;
+ case BC_RGBA16161616:
+ case BC_YUVA16161616:
+ SHAPEBLEND(uint16_t, 4, int64_t)
+ break;
+ }
+}
- /* Top right corner */
- opacity = 0;
- COMPARE2(0,w-1);
- COMPARE2(0,w-2);
- COMPARE2(1,w-1);
- COMPARE2(1,w-2);
- BLEND(0,w-1,4.0);
-
- /* Left edge */
- for (j = 1; j < h-1; j++)
- {
- opacity = 0;
- COMPARE2(j-1,0);
- COMPARE2(j,0);
- COMPARE2(j+1,0);
- COMPARE2(j-1,1);
- COMPARE2(j,1);
- COMPARE2(j+1,1);
- BLEND(j,0,6.0);
- }
- /* Middle */
- for (j = 1; j < h-1; j++)
- {
- for (k = 1; k < w-1; k++)
- {
- opacity = 0;
- COMPARE2(j-1,k-1);
- COMPARE2(j,k-1);
- COMPARE2(j+1,k-1);
- COMPARE2(j-1,k);
- COMPARE2(j,k);
- COMPARE2(j+1,k);
- COMPARE2(j-1,k+1);
- COMPARE2(j,k+1);
- COMPARE2(j+1,k+1);
- BLEND(j,k,9.0);
- }
- }
+ShapeEngine::ShapeEngine(ShapeWipeMain *plugin,
+ int total_clients, int total_packages)
+ : LoadServer(total_clients, total_packages)
+{
+ this->plugin = plugin;
+}
- /* Right edge */
- for (j = 1; j < h-1; j++)
- {
- opacity = 0;
- COMPARE2(j-1,w-1);
- COMPARE2(j,w-1);
- COMPARE2(j+1,w-1);
- COMPARE2(j-1,w-2);
- COMPARE2(j,w-2);
- COMPARE2(j+1,w-2);
- BLEND(j,w-1,6.0);
- }
+ShapeEngine::~ShapeEngine()
+{
+}
- /* Bottom left corner */
- opacity = 0;
- COMPARE2(h-1,0);
- COMPARE2(h-1,1);
- COMPARE2(h-2,0);
- COMPARE2(h-2,1);
- BLEND(h-1,0,4.0);
-
- /* Bottom edge */
- for (k = 1; k < w-1; k++)
- {
- opacity = 0;
- COMPARE2(h-1,k-1);
- COMPARE2(h-1,k);
- COMPARE2(h-1,k+1);
- COMPARE2(h-2,k-1);
- COMPARE2(h-2,k);
- COMPARE2(h-2,k+1);
- BLEND(h-1,k,6.0);
- }
- /* Bottom right corner */
- opacity = 0;
- COMPARE2(h-1,w-1);
- COMPARE2(h-1,w-2);
- COMPARE2(h-2,w-1);
- COMPARE2(h-2,w-2);
- BLEND(h-1,w-1,4.0);
- }
- }
- else
- {
- switch(incoming->get_color_model())
- {
- case BC_RGB_FLOAT:
- SHAPEWIPE(float, 3)
- break;
- case BC_RGB888:
- case BC_YUV888:
- SHAPEWIPE(unsigned char, 3)
- break;
- case BC_RGBA_FLOAT:
- SHAPEWIPE(float, 4)
- break;
- case BC_RGBA8888:
- case BC_YUVA8888:
- SHAPEWIPE(unsigned char, 4)
- break;
- case BC_RGB161616:
- case BC_YUV161616:
- SHAPEWIPE(uint16_t, 3)
- break;
- case BC_RGBA16161616:
- case BC_YUVA16161616:
- SHAPEWIPE(uint16_t, 4)
- break;
- }
+void ShapeEngine::init_packages()
+{
+ int y = 0, h1 = plugin->input->get_h()-1;
+ int total_packages = get_total_packages();
+ for(int i = 0; i<total_packages; ) {
+ ShapePackage *pkg = (ShapePackage*)get_package(i++);
+ pkg->y1 = y;
+ y = h1 * i / total_packages;
+ pkg->y2 = y;
}
- return 0;
}
+
+LoadClient* ShapeEngine::new_client()
+{
+ return new ShapeUnit(this);
+}
+
+LoadPackage* ShapeEngine::new_package()
+{
+ return new ShapePackage;
+}
+