r125: This commit was manufactured by cvs2svn to create tag 'r1_1_7-last'.
[cinelerra_cv/mob.git] / hvirtual / plugins / zoomblur / zoomblur.C
blob06337aad43990ed56bedee60823bb68013bfec80
1 #include <math.h>
2 #include <stdint.h>
3 #include <string.h>
5 #include "bcdisplayinfo.h"
6 #include "clip.h"
7 #include "defaults.h"
8 #include "filexml.h"
9 #include "keyframe.h"
10 #include "loadbalance.h"
11 #include "picon_png.h"
12 #include "pluginvclient.h"
13 #include "vframe.h"
15 #include <libintl.h>
16 #define _(String) gettext(String)
17 #define gettext_noop(String) String
18 #define N_(String) gettext_noop (String)
21 class ZoomBlurMain;
22 class ZoomBlurEngine;
28 class ZoomBlurConfig
30 public:
31         ZoomBlurConfig();
33         int equivalent(ZoomBlurConfig &that);
34         void copy_from(ZoomBlurConfig &that);
35         void interpolate(ZoomBlurConfig &prev, 
36                 ZoomBlurConfig &next, 
37                 long prev_frame, 
38                 long next_frame, 
39                 long current_frame);
41         int x;
42         int y;
43         int radius;
44         int steps;
45         int r;
46         int g;
47         int b;
48         int a;
53 class ZoomBlurSize : public BC_ISlider
55 public:
56         ZoomBlurSize(ZoomBlurMain *plugin, 
57                 int x, 
58                 int y, 
59                 int *output,
60                 int min,
61                 int max);
62         int handle_event();
63         ZoomBlurMain *plugin;
64         int *output;
67 class ZoomBlurToggle : public BC_CheckBox
69 public:
70         ZoomBlurToggle(ZoomBlurMain *plugin, 
71                 int x, 
72                 int y, 
73                 int *output,
74                 char *string);
75         int handle_event();
76         ZoomBlurMain *plugin;
77         int *output;
80 class ZoomBlurWindow : public BC_Window
82 public:
83         ZoomBlurWindow(ZoomBlurMain *plugin, int x, int y);
84         ~ZoomBlurWindow();
86         int create_objects();
87         int close_event();
89         ZoomBlurSize *x, *y, *radius, *steps;
90         ZoomBlurToggle *r, *g, *b, *a;
91         ZoomBlurMain *plugin;
96 PLUGIN_THREAD_HEADER(ZoomBlurMain, ZoomBlurThread, ZoomBlurWindow)
99 class ZoomBlurMain : public PluginVClient
101 public:
102         ZoomBlurMain(PluginServer *server);
103         ~ZoomBlurMain();
105         int process_realtime(VFrame *input_ptr, VFrame *output_ptr);
106         int is_realtime();
107         int load_defaults();
108         int save_defaults();
109         void save_data(KeyFrame *keyframe);
110         void read_data(KeyFrame *keyframe);
111         void update_gui();
113         PLUGIN_CLASS_MEMBERS(ZoomBlurConfig, ZoomBlurThread)
115         void delete_tables();
116         VFrame *input, *output, *temp;
117         ZoomBlurEngine *engine;
118         int **scale_y_table;
119         int **scale_x_table;
120         int table_entries;
121         int need_reconfigure;
122         int *accum;
125 class ZoomBlurPackage : public LoadPackage
127 public:
128         ZoomBlurPackage();
129         int y1, y2;
132 class ZoomBlurUnit : public LoadClient
134 public:
135         ZoomBlurUnit(ZoomBlurEngine *server, ZoomBlurMain *plugin);
136         void process_package(LoadPackage *package);
137         ZoomBlurEngine *server;
138         ZoomBlurMain *plugin;
141 class ZoomBlurEngine : public LoadServer
143 public:
144         ZoomBlurEngine(ZoomBlurMain *plugin, 
145                 int total_clients, 
146                 int total_packages);
147         void init_packages();
148         LoadClient* new_client();
149         LoadPackage* new_package();
150         ZoomBlurMain *plugin;
171 REGISTER_PLUGIN(ZoomBlurMain)
175 ZoomBlurConfig::ZoomBlurConfig()
177         x = 50;
178         y = 50;
179         radius = 10;
180         steps = 10;
181         r = 1;
182         g = 1;
183         b = 1;
184         a = 1;
187 int ZoomBlurConfig::equivalent(ZoomBlurConfig &that)
189         return 
190                 x == that.x &&
191                 y == that.y &&
192                 radius == that.radius &&
193                 steps == that.steps &&
194                 r == that.r &&
195                 g == that.g &&
196                 b == that.b &&
197                 a == that.a;
200 void ZoomBlurConfig::copy_from(ZoomBlurConfig &that)
202         x = that.x;
203         y = that.y;
204         radius = that.radius;
205         steps = that.steps;
206         r = that.r;
207         g = that.g;
208         b = that.b;
209         a = that.a;
212 void ZoomBlurConfig::interpolate(ZoomBlurConfig &prev, 
213         ZoomBlurConfig &next, 
214         long prev_frame, 
215         long next_frame, 
216         long current_frame)
218         double next_scale = (double)(current_frame - prev_frame) / (next_frame - prev_frame);
219         double prev_scale = (double)(next_frame - current_frame) / (next_frame - prev_frame);
220         this->x = (int)(prev.x * prev_scale + next.x * next_scale + 0.5);
221         this->y = (int)(prev.y * prev_scale + next.y * next_scale + 0.5);
222         this->radius = (int)(prev.radius * prev_scale + next.radius * next_scale + 0.5);
223         this->steps = (int)(prev.steps * prev_scale + next.steps * next_scale + 0.5);
224         r = prev.r;
225         g = prev.g;
226         b = prev.b;
227         a = prev.a;
238 PLUGIN_THREAD_OBJECT(ZoomBlurMain, ZoomBlurThread, ZoomBlurWindow)
242 ZoomBlurWindow::ZoomBlurWindow(ZoomBlurMain *plugin, int x, int y)
243  : BC_Window(plugin->gui_string, 
244         x,
245         y,
246         230, 
247         340, 
248         230, 
249         340, 
250         0, 
251         1)
253         this->plugin = plugin; 
256 ZoomBlurWindow::~ZoomBlurWindow()
260 int ZoomBlurWindow::create_objects()
262         int x = 10, y = 10;
264         add_subwindow(new BC_Title(x, y, _("X:")));
265         y += 20;
266         add_subwindow(this->x = new ZoomBlurSize(plugin, x, y, &plugin->config.x, 0, 100));
267         y += 30;
268         add_subwindow(new BC_Title(x, y, _("Y:")));
269         y += 20;
270         add_subwindow(this->y = new ZoomBlurSize(plugin, x, y, &plugin->config.y, 0, 100));
271         y += 30;
272         add_subwindow(new BC_Title(x, y, _("Radius:")));
273         y += 20;
274         add_subwindow(radius = new ZoomBlurSize(plugin, x, y, &plugin->config.radius, -100, 100));
275         y += 30;
276         add_subwindow(new BC_Title(x, y, _("Steps:")));
277         y += 20;
278         add_subwindow(steps = new ZoomBlurSize(plugin, x, y, &plugin->config.steps, 1, 100));
279         y += 30;
280         add_subwindow(r = new ZoomBlurToggle(plugin, x, y, &plugin->config.r, _("Red")));
281         y += 30;
282         add_subwindow(g = new ZoomBlurToggle(plugin, x, y, &plugin->config.g, _("Green")));
283         y += 30;
284         add_subwindow(b = new ZoomBlurToggle(plugin, x, y, &plugin->config.b, _("Blue")));
285         y += 30;
286         add_subwindow(a = new ZoomBlurToggle(plugin, x, y, &plugin->config.a, _("Alpha")));
287         y += 30;
289         show_window();
290         flush();
291         return 0;
294 int ZoomBlurWindow::close_event()
296 // Set result to 1 to indicate a plugin side close
297         set_done(1);
298         return 1;
310 ZoomBlurToggle::ZoomBlurToggle(ZoomBlurMain *plugin, 
311         int x, 
312         int y, 
313         int *output, 
314         char *string)
315  : BC_CheckBox(x, y, *output, string)
317         this->plugin = plugin;
318         this->output = output;
321 int ZoomBlurToggle::handle_event()
323         *output = get_value();
324         plugin->send_configure_change();
325         return 1;
334 ZoomBlurSize::ZoomBlurSize(ZoomBlurMain *plugin, 
335         int x, 
336         int y, 
337         int *output,
338         int min,
339         int max)
340  : BC_ISlider(x, y, 0, 200, 200, min, max, *output)
342         this->plugin = plugin;
343         this->output = output;
345 int ZoomBlurSize::handle_event()
347         *output = get_value();
348         plugin->send_configure_change();
349         return 1;
361 ZoomBlurMain::ZoomBlurMain(PluginServer *server)
362  : PluginVClient(server)
364         PLUGIN_CONSTRUCTOR_MACRO
365         engine = 0;
366         scale_x_table = 0;
367         scale_y_table = 0;
368         table_entries = 0;
369         accum = 0;
370         need_reconfigure = 1;
371         temp = 0;
374 ZoomBlurMain::~ZoomBlurMain()
376         PLUGIN_DESTRUCTOR_MACRO
377         if(engine) delete engine;
378         delete_tables();
379         if(accum) delete [] accum;
380         if(temp) delete temp;
383 char* ZoomBlurMain::plugin_title() { return _("Zoom Blur"); }
384 int ZoomBlurMain::is_realtime() { return 1; }
387 NEW_PICON_MACRO(ZoomBlurMain)
389 SHOW_GUI_MACRO(ZoomBlurMain, ZoomBlurThread)
391 SET_STRING_MACRO(ZoomBlurMain)
393 RAISE_WINDOW_MACRO(ZoomBlurMain)
395 LOAD_CONFIGURATION_MACRO(ZoomBlurMain, ZoomBlurConfig)
397 void ZoomBlurMain::delete_tables()
399         if(scale_x_table)
400         {
401                 for(int i = 0; i < table_entries; i++)
402                         delete [] scale_x_table[i];
403                 delete [] scale_x_table;
404         }
406         if(scale_y_table)
407         {
408                 for(int i = 0; i < table_entries; i++)
409                         delete [] scale_y_table[i];
410                 delete [] scale_y_table;
411         }
412         scale_x_table = 0;
413         scale_y_table = 0;
414         table_entries = 0;
417 int ZoomBlurMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
419         need_reconfigure |= load_configuration();
421         if(!engine) engine = new ZoomBlurEngine(this,
422                 get_project_smp() + 1,
423                 get_project_smp() + 1);
424         if(!accum) accum = new int[input_ptr->get_w() * 
425                 input_ptr->get_h() *
426                 cmodel_components(input_ptr->get_color_model())];
428         this->input = input_ptr;
429         this->output = output_ptr;
432         if(input_ptr->get_rows()[0] == output_ptr->get_rows()[0])
433         {
434                 if(!temp) temp = new VFrame(0,
435                         input_ptr->get_w(),
436                         input_ptr->get_h(),
437                         input_ptr->get_color_model());
438                 temp->copy_from(input_ptr);
439                 this->input = temp;
440         }
442 // Generate tables here.  The same table is used by many packages to render
443 // each horizontal stripe.  Need to cover the entire output range in  each
444 // table to avoid green borders
445         if(need_reconfigure)
446         {
447                 float w = input->get_w();
448                 float h = input->get_h();
449                 float center_x = (float)config.x / 100 * w;
450                 float center_y = (float)config.y / 100 * h;
451                 float radius = (float)(100 + config.radius) / 100;
452                 float min_w, min_h;
453                 float max_w, max_h;
454                 int steps = config.steps ? config.steps : 1;
455                 float min_x1;
456                 float min_y1;
457                 float min_x2;
458                 float min_y2;
459                 float max_x1;
460                 float max_y1;
461                 float max_x2;
462                 float max_y2;
463                 
465 // printf("ZoomBlurMain::process_realtime 1 %d %d\n", 
466 // config.x,
467 // config.y);
469                 center_x = (center_x - w / 2) * (1.0 - radius) + w / 2;
470                 center_y = (center_y - h / 2) * (1.0 - radius) + h / 2;
471                 min_w = w * radius;
472                 min_h = h * radius;
473                 max_w = w;
474                 max_h = h;
475                 min_x1 = center_x - min_w / 2;
476                 min_y1 = center_y - min_h / 2;
477                 min_x2 = center_x + min_w / 2;
478                 min_y2 = center_y + min_h / 2;
479                 max_x1 = 0;
480                 max_y1 = 0;
481                 max_x2 = w;
482                 max_y2 = h;
484 // printf("ZoomBlurMain::process_realtime 2 w=%f radius=%f center_x=%f\n", 
485 // w,
486 // radius,
487 // center_x);
490 // Dimensions of outermost rectangle
492                 delete_tables();
493                 scale_x_table = new int*[config.steps];
494                 scale_y_table = new int*[config.steps];
495                 table_entries = config.steps;
498                 for(int i = 0; i < config.steps; i++)
499                 {
500                         float fraction = (float)i / config.steps;
501                         float inv_fraction = 1.0 - fraction;
502                         float out_x1 = min_x1 * fraction + max_x1 * inv_fraction;
503                         float out_x2 = min_x2 * fraction + max_x2 * inv_fraction;
504                         float out_y1 = min_y1 * fraction + max_y1 * inv_fraction;
505                         float out_y2 = min_y2 * fraction + max_y2 * inv_fraction;
506                         float out_w = out_x2 - out_x1;
507                         float out_h = out_y2 - out_y1;
508                         if(out_w < 0) out_w = 0;
509                         if(out_h < 0) out_h = 0;
510                         float scale_x = (float)w / out_w;
511                         float scale_y = (float)h / out_h;
513                         int *x_table;
514                         int *y_table;
515                         scale_y_table[i] = y_table = new int[(int)(h + 1)];
516                         scale_x_table[i] = x_table = new int[(int)(w + 1)];
518                         for(int j = 0; j < h; j++)
519                         {
520                                 y_table[j] = (int)((j - out_y1) * scale_y);
521                         }
522                         for(int j = 0; j < w; j++)
523                         {
524                                 x_table[j] = (int)((j - out_x1) * scale_x);
525 //printf("ZoomBlurMain::process_realtime %d %d\n", j, x_table[j]);
526                         }
527                 }
528                 need_reconfigure = 0;
529         }
531         bzero(accum, input_ptr->get_w() * 
532                 input_ptr->get_h() *
533                 cmodel_components(input_ptr->get_color_model()) *
534                 sizeof(int));
535         engine->process_packages();
536         return 0;
540 void ZoomBlurMain::update_gui()
542         if(thread)
543         {
544                 load_configuration();
545                 thread->window->lock_window();
546                 thread->window->x->update(config.x);
547                 thread->window->y->update(config.y);
548                 thread->window->radius->update(config.radius);
549                 thread->window->steps->update(config.steps);
550                 thread->window->r->update(config.r);
551                 thread->window->g->update(config.g);
552                 thread->window->b->update(config.b);
553                 thread->window->a->update(config.a);
554                 thread->window->unlock_window();
555         }
559 int ZoomBlurMain::load_defaults()
561         char directory[1024], string[1024];
562 // set the default directory
563         sprintf(directory, "%szoomblur.rc", BCASTDIR);
565 // load the defaults
566         defaults = new Defaults(directory);
567         defaults->load();
569         config.x = defaults->get("X", config.x);
570         config.y = defaults->get("Y", config.y);
571         config.radius = defaults->get("RADIUS", config.radius);
572         config.steps = defaults->get("STEPS", config.steps);
573         config.r = defaults->get("R", config.r);
574         config.g = defaults->get("G", config.g);
575         config.b = defaults->get("B", config.b);
576         config.a = defaults->get("A", config.a);
577         return 0;
581 int ZoomBlurMain::save_defaults()
583         defaults->update("X", config.x);
584         defaults->update("Y", config.y);
585         defaults->update("RADIUS", config.radius);
586         defaults->update("STEPS", config.steps);
587         defaults->update("R", config.r);
588         defaults->update("G", config.g);
589         defaults->update("B", config.b);
590         defaults->update("A", config.a);
591         defaults->save();
592         return 0;
597 void ZoomBlurMain::save_data(KeyFrame *keyframe)
599         FileXML output;
601 // cause data to be stored directly in text
602         output.set_shared_string(keyframe->data, MESSAGESIZE);
603         output.tag.set_title("ZOOMBLUR");
605         output.tag.set_property("X", config.x);
606         output.tag.set_property("Y", config.y);
607         output.tag.set_property("RADIUS", config.radius);
608         output.tag.set_property("STEPS", config.steps);
609         output.tag.set_property("R", config.r);
610         output.tag.set_property("G", config.g);
611         output.tag.set_property("B", config.b);
612         output.tag.set_property("A", config.a);
613         output.append_tag();
614         output.terminate_string();
617 void ZoomBlurMain::read_data(KeyFrame *keyframe)
619         FileXML input;
621         input.set_shared_string(keyframe->data, strlen(keyframe->data));
623         int result = 0;
625         while(!result)
626         {
627                 result = input.read_tag();
629                 if(!result)
630                 {
631                         if(input.tag.title_is("ZOOMBLUR"))
632                         {
633                                 config.x = input.tag.get_property("X", config.x);
634                                 config.y = input.tag.get_property("Y", config.y);
635                                 config.radius = input.tag.get_property("RADIUS", config.radius);
636                                 config.steps = input.tag.get_property("STEPS", config.steps);
637                                 config.r = input.tag.get_property("R", config.r);
638                                 config.g = input.tag.get_property("G", config.g);
639                                 config.b = input.tag.get_property("B", config.b);
640                                 config.a = input.tag.get_property("A", config.a);
641                         }
642                 }
643         }
651 ZoomBlurPackage::ZoomBlurPackage()
652  : LoadPackage()
659 ZoomBlurUnit::ZoomBlurUnit(ZoomBlurEngine *server, 
660         ZoomBlurMain *plugin)
661  : LoadClient(server)
663         this->plugin = plugin;
664         this->server = server;
668 #define BLEND_LAYER(COMPONENTS, TYPE, MAX, DO_YUV) \
669 { \
670         const int chroma_offset = (DO_YUV ? ((MAX + 1) / 2) : 0); \
671         for(int j = pkg->y1; j < pkg->y2; j++) \
672         { \
673                 int *out_row = plugin->accum + COMPONENTS * w * j; \
674                 int in_y = y_table[j]; \
676 /* Blend image */ \
677                 if(in_y >= 0 && in_y < h) \
678                 { \
679                         TYPE *in_row = (TYPE*)plugin->input->get_rows()[in_y]; \
680                         for(int k = 0; k < w; k++) \
681                         { \
682                                 int in_x = x_table[k]; \
683 /* Blend pixel */ \
684                                 if(in_x >= 0 && in_x < w) \
685                                 { \
686                                         int in_offset = in_x * COMPONENTS; \
687                                         *out_row++ += in_row[in_offset]; \
688                                         if(DO_YUV) \
689                                         { \
690                                                 *out_row++ += in_row[in_offset + 1]; \
691                                                 *out_row++ += in_row[in_offset + 2]; \
692                                         } \
693                                         else \
694                                         { \
695                                                 *out_row++ += (int)in_row[in_offset + 1]; \
696                                                 *out_row++ += (int)in_row[in_offset + 2]; \
697                                         } \
698                                         if(COMPONENTS == 4) \
699                                                 *out_row++ += in_row[in_offset + 3]; \
700                                 } \
701 /* Blend nothing */ \
702                                 else \
703                                 { \
704                                         out_row++; \
705                                         if(DO_YUV) \
706                                         { \
707                                                 *out_row++ += chroma_offset; \
708                                                 *out_row++ += chroma_offset; \
709                                         } \
710                                         else \
711                                         { \
712                                                 out_row += 2; \
713                                         } \
714                                         if(COMPONENTS == 4) out_row++; \
715                                 } \
716                         } \
717                 } \
718                 else \
719                 if(DO_YUV) \
720                 { \
721                         for(int k = 0; k < w; k++) \
722                         { \
723                                 out_row++; \
724                                 *out_row++ += chroma_offset; \
725                                 *out_row++ += chroma_offset; \
726                                 if(COMPONENTS == 4) out_row++; \
727                         } \
728                 } \
729         } \
731 /* Copy to output */ \
732         if(i == plugin->config.steps - 1) \
733         { \
734                 for(int j = pkg->y1; j < pkg->y2; j++) \
735                 { \
736                         int *in_row = plugin->accum + COMPONENTS * w * j; \
737                         TYPE *in_backup = (TYPE*)plugin->input->get_rows()[j]; \
738                         TYPE *out_row = (TYPE*)plugin->output->get_rows()[j]; \
739                         for(int k = 0; k < w; k++) \
740                         { \
741                                 if(do_r) \
742                                 { \
743                                         *out_row++ = (*in_row++ * fraction) >> 16; \
744                                         in_backup++; \
745                                 } \
746                                 else \
747                                 { \
748                                         *out_row++ = *in_backup++; \
749                                         in_row++; \
750                                 } \
752                                 if(DO_YUV) \
753                                 { \
754                                         if(do_g) \
755                                         { \
756                                                 *out_row++ = ((*in_row++ * fraction) >> 16); \
757                                                 in_backup++; \
758                                         } \
759                                         else \
760                                         { \
761                                                 *out_row++ = *in_backup++; \
762                                                 in_row++; \
763                                         } \
765                                         if(do_b) \
766                                         { \
767                                                 *out_row++ = ((*in_row++ * fraction) >> 16); \
768                                                 in_backup++; \
769                                         } \
770                                         else \
771                                         { \
772                                                 *out_row++ = *in_backup++; \
773                                                 in_row++; \
774                                         } \
775                                 } \
776                                 else \
777                                 { \
778                                         if(do_g) \
779                                         { \
780                                                 *out_row++ = (*in_row++ * fraction) >> 16; \
781                                                 in_backup++; \
782                                         } \
783                                         else \
784                                         { \
785                                                 *out_row++ = *in_backup++; \
786                                                 in_row++; \
787                                         } \
789                                         if(do_b) \
790                                         { \
791                                                 *out_row++ = (*in_row++ * fraction) >> 16; \
792                                                 in_backup++; \
793                                         } \
794                                         else \
795                                         { \
796                                                 *out_row++ = *in_backup++; \
797                                                 in_row++; \
798                                         } \
799                                 } \
801                                 if(COMPONENTS == 4) \
802                                 { \
803                                         if(do_a) \
804                                         { \
805                                                 *out_row++ = (*in_row++ * fraction) >> 16; \
806                                                 in_backup++; \
807                                         } \
808                                         else \
809                                         { \
810                                                 *out_row++ = *in_backup++; \
811                                                 in_row++; \
812                                         } \
813                                 } \
814                         } \
815                 } \
816         } \
819 void ZoomBlurUnit::process_package(LoadPackage *package)
821         ZoomBlurPackage *pkg = (ZoomBlurPackage*)package;
822         int h = plugin->output->get_h();
823         int w = plugin->output->get_w();
824         int do_r = plugin->config.r;
825         int do_g = plugin->config.g;
826         int do_b = plugin->config.b;
827         int do_a = plugin->config.a;
829         int fraction = 0x10000 / plugin->config.steps;
830         for(int i = 0; i < plugin->config.steps; i++)
831         {
832                 int *x_table = plugin->scale_x_table[i];
833                 int *y_table = plugin->scale_y_table[i];
835                 switch(plugin->input->get_color_model())
836                 {
837                         case BC_RGB888:
838                                 BLEND_LAYER(3, uint8_t, 0xff, 0)
839                                 break;
840                         case BC_RGBA8888:
841                                 BLEND_LAYER(4, uint8_t, 0xff, 0)
842                                 break;
843                         case BC_RGB161616:
844                                 BLEND_LAYER(3, uint16_t, 0xffff, 0)
845                                 break;
846                         case BC_RGBA16161616:
847                                 BLEND_LAYER(4, uint16_t, 0xffff, 0)
848                                 break;
849                         case BC_YUV888:
850                                 BLEND_LAYER(3, uint8_t, 0xff, 1)
851                                 break;
852                         case BC_YUVA8888:
853                                 BLEND_LAYER(4, uint8_t, 0xff, 1)
854                                 break;
855                         case BC_YUV161616:
856                                 BLEND_LAYER(3, uint16_t, 0xffff, 1)
857                                 break;
858                         case BC_YUVA16161616:
859                                 BLEND_LAYER(4, uint16_t, 0xffff, 1)
860                                 break;
861                 }
862         }
870 ZoomBlurEngine::ZoomBlurEngine(ZoomBlurMain *plugin, 
871         int total_clients, 
872         int total_packages)
873  : LoadServer(total_clients, total_packages)
875         this->plugin = plugin;
878 void ZoomBlurEngine::init_packages()
880         int package_h = (int)((float)plugin->output->get_h() / 
881                         total_packages + 1);
882         int y1 = 0;
883         for(int i = 0; i < total_packages; i++)
884         {
885                 ZoomBlurPackage *package = (ZoomBlurPackage*)packages[i];
886                 package->y1 = y1;
887                 package->y2 = y1 + package_h;
888                 package->y1 = MIN(plugin->output->get_h(), package->y1);
889                 package->y2 = MIN(plugin->output->get_h(), package->y2);
890                 y1 = package->y2;
891         }
894 LoadClient* ZoomBlurEngine::new_client()
896         return new ZoomBlurUnit(this, plugin);
899 LoadPackage* ZoomBlurEngine::new_package()
901         return new ZoomBlurPackage;