tool tipinfo on effect plugins, updated plugin tipinfo, tweaks to layouts
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / shapewipe / shapewipe.C
index 9a38a916b03de086ae3f4cfa33c3779d26a13f33..e372f30ad271f2656d2ba6aef3e801316105d3b2 100644 (file)
 
 #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;
@@ -57,20 +98,15 @@ ShapeWipeW2B::ShapeWipeW2B(ShapeWipeMain *plugin,
 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;
@@ -79,34 +115,16 @@ ShapeWipeB2W::ShapeWipeB2W(ShapeWipeMain *plugin,
 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;
@@ -114,24 +132,19 @@ ShapeWipePreserveAspectRatio::ShapeWipePreserveAspectRatio(ShapeWipeMain *plugin
 
 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()
@@ -147,24 +160,73 @@ int ShapeWipeTumble::handle_down_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, 64, 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;
@@ -172,111 +234,75 @@ ShapeWipeShape::ShapeWipeShape(ShapeWipeMain *client,
 
 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, 425, 215, 425, 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 = 10, margin = 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, 20, 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 = 85, x2 = 355, x3 = 386;
+       shape_text = new ShapeWipeShape(plugin, this, x1, y, x2-x1, 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, 20, 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();
@@ -284,53 +310,44 @@ void ShapeWipeWindow::create_objects()
 
 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();
@@ -342,6 +359,7 @@ ShapeWipeMain::~ShapeWipeMain()
        reset_pattern_image();
        shape_paths.remove_all_objects();
        shape_titles.remove_all_objects();
+       delete engine;
 }
 
 const char* ShapeWipeMain::plugin_title() { return N_("Shape Wipe"); }
@@ -350,58 +368,27 @@ int ShapeWipeMain::uses_gui() { return 1; }
 
 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, '.');
@@ -421,643 +408,309 @@ int ShapeWipeMain::load_configuration()
        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;
-               }
+       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;
        }
-
-       FILE *fp = fopen(filename, "rb");
-       if (!fp)
-       {
-               return 1;
+       if( !ret ) {
+               fread(header, 1, 8, fp);
+               is_png = !png_sig_cmp(header, 0, 8);
+               if( !is_png ) ret = 1;
        }
-
-       fread(header, 1, 8, fp);
-       is_png = !png_sig_cmp(header, 0, 8);
-
-       if (!is_png)
-       {
-               fclose(fp);
-               return 1;
+       if( !ret ) {
+               png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+               if( !png_ptr ) ret = 1;
        }
-
-       png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
-               NULL, NULL, NULL);
-
-       if (!png_ptr)
-       {
-               fclose(fp);
-               return 1;
+       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;
        }
-
-       /* 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;
+       if( !ret ) {
+               end_info = png_create_info_struct(png_ptr);
+               if( !end_info ) ret = 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;
-       }
-
-       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);
-       }
-
-       /* 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;
-       }
-       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);
-       }
-
-       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]; \
-                       } \
-               } \
-       } \
-       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]; \
+#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(); \
+       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; \
                } \
        } \
 }
 
-#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();
+
+       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;
+}
+