Andrew contribution to add preference method for Fast/Slow speed
[goodguy/cinelerra.git] / cinelerra-5.1 / plugins / shapewipe / shapewipe.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "bchash.h"
24 #include "cstrdup.h"
25 #include "edl.inc"
26 #include "filesystem.h"
27 #include "filexml.h"
28 #include "language.h"
29 #include "overlayframe.h"
30 #include "theme.h"
31 #include "vframe.h"
32 #include "shapewipe.h"
33
34 #include <png.h>
35 #include <math.h>
36 #include <stdint.h>
37 #include <fcntl.h>
38 #include <string.h>
39
40 #define SHAPE_SEARCHPATH "/shapes"
41 #define DEFAULT_SHAPE "circle"
42 // feather slider range log2 = -10 .. -1 == 9.8e-4 .. 0.5
43 #define SHAPE_FLOG_MIN -10.
44 #define SHAPE_FLOG_MAX -1.
45 #define SHAPE_FMIN expf(M_LN2*SHAPE_FLOG_MIN)
46 #define SHAPE_FMAX expf(M_LN2*SHAPE_FLOG_MAX)
47
48 REGISTER_PLUGIN(ShapeWipeMain)
49
50 ShapeWipeConfig::ShapeWipeConfig()
51 {
52         direction = 0;
53         feather = SHAPE_FMIN;
54         preserve_aspect = 0;
55         strcpy(shape_name, DEFAULT_SHAPE);
56 }
57 ShapeWipeConfig::~ShapeWipeConfig()
58 {
59 }
60
61 void ShapeWipeConfig::read_xml(KeyFrame *keyframe)
62 {
63         FileXML input;
64         input.set_shared_input(keyframe->xbuf);
65
66         while( !input.read_tag() ) {
67                 if( input.tag.title_is("SHAPEWIPE") ) {
68                         direction = input.tag.get_property("DIRECTION", direction);
69                         feather = input.tag.get_property("FEATHER", feather);
70                         preserve_aspect = input.tag.get_property("PRESERVE_ASPECT", preserve_aspect);
71                         input.tag.get_property("SHAPE_NAME", shape_name);
72                 }
73         }
74 }
75 void ShapeWipeConfig::save_xml(KeyFrame *keyframe)
76 {
77         FileXML output;
78         output.set_shared_output(keyframe->xbuf);
79         output.tag.set_title("SHAPEWIPE");
80         output.tag.set_property("DIRECTION", direction);
81         output.tag.set_property("FEATHER", feather);
82         output.tag.set_property("PRESERVE_ASPECT", preserve_aspect);
83         output.tag.set_property("SHAPE_NAME", shape_name);
84         output.append_tag();
85         output.tag.set_title("/SHAPEWIPE");
86         output.append_tag();
87         output.terminate_string();
88 }
89
90
91 ShapeWipeW2B::ShapeWipeW2B(ShapeWipeMain *plugin,
92         ShapeWipeWindow *window, int x, int y)
93  : BC_Radial(x, y, plugin->config.direction == 0, _("White to Black"))
94 {
95         this->plugin = plugin;
96         this->window = window;
97 }
98
99 int ShapeWipeW2B::handle_event()
100 {
101         update(1);
102         plugin->config.direction = 0;
103         window->right->update(0);
104         plugin->send_configure_change();
105         return 0;
106 }
107
108 ShapeWipeB2W::ShapeWipeB2W(ShapeWipeMain *plugin,
109         ShapeWipeWindow *window, int x, int y)
110  : BC_Radial(x, y, plugin->config.direction == 1, _("Black to White"))
111 {
112         this->plugin = plugin;
113         this->window = window;
114 }
115
116 int ShapeWipeB2W::handle_event()
117 {
118         update(1);
119         plugin->config.direction = 1;
120         window->left->update(0);
121         plugin->send_configure_change();
122         return 0;
123 }
124
125
126 ShapeWipePreserveAspectRatio::ShapeWipePreserveAspectRatio(ShapeWipeMain *plugin,
127         ShapeWipeWindow *window, int x, int y)
128  : BC_CheckBox (x, y, plugin->config.preserve_aspect, _("Preserve shape aspect ratio"))
129 {
130         this->plugin = plugin;
131         this->window = window;
132 }
133
134 int ShapeWipePreserveAspectRatio::handle_event()
135 {
136         plugin->config.preserve_aspect = get_value();
137         plugin->send_configure_change();
138         return 0;
139 }
140
141
142 ShapeWipeTumble::ShapeWipeTumble(ShapeWipeMain *client,
143         ShapeWipeWindow *window, int x, int y)
144  : BC_Tumbler(x, y)
145 {
146         this->client = client;
147         this->window = window;
148         set_increment(0.01);
149 }
150
151 int ShapeWipeTumble::handle_up_event()
152 {
153         window->prev_shape();
154         return 1;
155 }
156
157 int ShapeWipeTumble::handle_down_event()
158 {
159         window->next_shape();
160         return 0;
161 }
162
163
164 ShapeWipeFeather::ShapeWipeFeather(ShapeWipeMain *client,
165                 ShapeWipeWindow *window, int x, int y)
166  : BC_TumbleTextBox(window,
167                 bclip(client->config.feather, SHAPE_FMIN, SHAPE_FMAX),
168                 SHAPE_FMIN, SHAPE_FMAX, x, y, xS(64), yS(3))
169 {
170         this->client = client;
171         this->window = window;
172 }
173
174 int ShapeWipeFeather::handle_event()
175 {
176         float v = atof(get_text());
177         bclamp(v, SHAPE_FMIN, SHAPE_FMAX);
178         client->config.feather = v;
179         float sv = log(v)/M_LN2;
180         window->shape_fslider->update(sv);
181         client->send_configure_change();
182         return 1;
183 }
184
185 ShapeWipeFSlider::ShapeWipeFSlider(ShapeWipeMain *client,
186                 ShapeWipeWindow *window, int x, int y, int w)
187  : BC_FSlider(x, y, 0, w, w, SHAPE_FLOG_MIN, SHAPE_FLOG_MAX,
188         log(bclip(client->config.feather, SHAPE_FMIN, SHAPE_FMAX))/M_LN2)
189 {
190         this->client = client;
191         this->window = window;
192         set_precision(0.001);
193         set_pagination(0.01, 0.1);
194         enable_show_value(0);
195 }
196
197 int ShapeWipeFSlider::handle_event()
198 {
199         float v = get_value();
200         float vv = exp(M_LN2*v);
201         client->config.feather = vv;
202         window->shape_feather->update(vv);
203         client->send_configure_change();
204         return 1;
205 }
206
207 ShapeWipeReset::ShapeWipeReset(ShapeWipeMain *client,
208                 ShapeWipeWindow *window, int x, int y)
209  : BC_Button(x, y, client->get_theme()->get_image_set("reset_button"))
210 {
211         this->client = client;
212         this->window = window;
213         set_tooltip(_("Reset feather"));
214 }
215
216 int ShapeWipeReset::handle_event()
217 {
218         window->shape_fslider->update(SHAPE_FLOG_MIN);
219         float v = SHAPE_FMIN;
220         window->shape_feather->update(v);
221         client->config.feather = v;
222         client->send_configure_change();
223         return 1;
224 }
225
226 ShapeWipeShape::ShapeWipeShape(ShapeWipeMain *client,
227                 ShapeWipeWindow *window, int x, int y,
228                 int text_w, int list_h)
229  : BC_PopupTextBox(window, &window->shapes, client->config.shape_name,
230         x, y, text_w, list_h)
231 {
232         this->client = client;
233         this->window = window;
234 }
235
236 int ShapeWipeShape::handle_event()
237 {
238         strcpy(client->config.shape_name, get_text());
239         client->send_configure_change();
240         return 1;
241 }
242
243
244 ShapeWipeWindow::ShapeWipeWindow(ShapeWipeMain *plugin)
245  : PluginClientWindow(plugin, xS(425), yS(215), xS(425), yS(215), 0)
246 {
247         this->plugin = plugin;
248         shape_feather = 0;
249 }
250
251 ShapeWipeWindow::~ShapeWipeWindow()
252 {
253         shapes.remove_all_objects();
254         delete shape_feather;
255 }
256
257
258 void ShapeWipeWindow::create_objects()
259 {
260         BC_Title *title = 0;
261         lock_window("ShapeWipeWindow::create_objects");
262         int pad = xS(10), margin = xS(10);
263         int x = margin, y = margin;
264         int ww = get_w() - 2*margin;
265
266         plugin->init_shapes();
267         for( int i=0; i<plugin->shape_titles.size(); ++i ) {
268                 shapes.append(new BC_ListBoxItem(plugin->shape_titles.get(i)));
269         }
270
271         BC_TitleBar *bar;
272         add_subwindow(bar = new BC_TitleBar(x, y, ww, xS(20), yS(10),
273                 _("Wipe"), MEDIUMFONT));
274         y += bar->get_h() + pad;
275
276         add_subwindow(title = new BC_Title(x, y, _("Shape:")));
277         int x1 = xS(85), x2 = xS(355), x3 = xS(386);
278         shape_text = new ShapeWipeShape(plugin, this, x1, y, x2-x1, yS(200));
279         shape_text->create_objects();
280         add_subwindow(new ShapeWipeTumble(plugin, this, x3, y));
281         y += shape_text->get_h() + pad;
282
283         x = margin;
284         add_subwindow(title = new BC_Title(x, y, _("Feather:")));
285         x = x1;
286         shape_feather = new ShapeWipeFeather(plugin, this, x, y);
287         shape_feather->create_objects();
288         shape_feather->set_log_floatincrement(1);
289         x += shape_feather->get_w() + 2*pad;
290         add_subwindow(shape_fslider = new ShapeWipeFSlider(plugin, this, x, y, x2-x));
291         add_subwindow(shape_reset = new ShapeWipeReset(plugin, this, x3, y));
292         y += shape_fslider->get_h() + pad;
293
294         x = margin;
295         ShapeWipePreserveAspectRatio *aspect_ratio;
296         add_subwindow(aspect_ratio = new ShapeWipePreserveAspectRatio(
297                 plugin, this, x, y));
298         y += aspect_ratio->get_h() + pad;
299
300         add_subwindow(bar = new BC_TitleBar(x, y, ww, xS(20), yS(10),
301                 _("Direction"), MEDIUMFONT));
302         y += bar->get_h() + pad;
303         x = margin;
304         add_subwindow(left = new ShapeWipeW2B(plugin, this, x, y));
305         y += left->get_h();
306         add_subwindow(right = new ShapeWipeB2W(plugin, this, x, y));
307
308         show_window();
309         unlock_window();
310 }
311
312 void ShapeWipeWindow::next_shape()
313 {
314         ShapeWipeConfig &config = plugin->config;
315         int k = plugin->shape_titles.size();
316         while( --k>=0 && strcmp(plugin->shape_titles.get(k),config.shape_name) );
317
318         if( k >= 0 ) {
319                 if( ++k >= plugin->shape_titles.size() ) k = 0;
320                 strcpy(config.shape_name, plugin->shape_titles.get(k));
321                 shape_text->update(config.shape_name);
322         }
323         client->send_configure_change();
324 }
325
326 void ShapeWipeWindow::prev_shape()
327 {
328         ShapeWipeConfig &config = plugin->config;
329         int k = plugin->shape_titles.size();
330         while( --k>=0 && strcmp(plugin->shape_titles.get(k),config.shape_name) );
331
332         if( k >= 0 ) {
333                 if( --k < 0 ) k = plugin->shape_titles.size()-1;
334                 strcpy(config.shape_name, plugin->shape_titles.get(k));
335                 shape_text->update(config.shape_name);
336         }
337         client->send_configure_change();
338 }
339
340
341 ShapeWipeMain::ShapeWipeMain(PluginServer *server)
342  : PluginVClient(server)
343 {
344         input = 0;
345         output = 0;
346         engine = 0;
347         current_filename[0] = '\0';
348         current_name[0] = 0;
349         pattern_image = 0;
350         last_preserve_aspect = 0;
351         shapes_initialized = 0;
352         shape_paths.set_array_delete();
353         shape_titles.set_array_delete();
354 }
355
356 ShapeWipeMain::~ShapeWipeMain()
357 {
358         reset_pattern_image();
359         shape_paths.remove_all_objects();
360         shape_titles.remove_all_objects();
361         delete engine;
362 }
363
364 const char* ShapeWipeMain::plugin_title() { return N_("Shape Wipe"); }
365 int ShapeWipeMain::is_transition() { return 1; }
366 int ShapeWipeMain::uses_gui() { return 1; }
367
368 NEW_WINDOW_MACRO(ShapeWipeMain, ShapeWipeWindow);
369
370 void ShapeWipeMain::read_data(KeyFrame *keyframe)
371 {
372         config.read_xml(keyframe);
373 }
374 void ShapeWipeMain::save_data(KeyFrame *keyframe)
375 {
376         config.save_xml(keyframe);
377 }
378
379 void ShapeWipeMain::init_shapes()
380 {
381         if( !shapes_initialized ) {
382                 FileSystem fs;
383                 fs.set_filter("[*.png][*.jpg]");
384                 char shape_path[BCTEXTLEN];
385                 sprintf(shape_path, "%s%s", get_plugin_dir(), SHAPE_SEARCHPATH);
386                 fs.update(shape_path);
387
388                 for( int i=0; i<fs.total_files(); ++i ) {
389                         FileItem *file_item = fs.get_entry(i);
390                         if( !file_item->get_is_dir() ) {
391                                 shape_paths.append(cstrdup(file_item->get_path()));
392                                 char *ptr = cstrdup(file_item->get_name());
393                                 char *ptr2 = strrchr(ptr, '.');
394                                 if(ptr2) *ptr2 = 0;
395                                 shape_titles.append(ptr);
396                         }
397                 }
398
399                 shapes_initialized = 1;
400         }
401 }
402
403 int ShapeWipeMain::load_configuration()
404 {
405         read_data(get_prev_keyframe(get_source_position()));
406         return 1;
407 }
408
409 int ShapeWipeMain::read_pattern_image(char *shape_name,
410                 int frame_width, int frame_height)
411 {
412         VFrame *pattern = 0;
413         int ret = 0, fd = -1;
414         int is_png = 0;
415         unsigned char header[10];
416 // Convert name to filename
417         int k = shape_paths.size(), hsz = sizeof(header);
418         while( --k>=0 && strcmp(shape_titles[k], shape_name) );
419         if( k < 0 ) ret = 1;
420         if( !ret ) {
421                 strcpy(current_filename, shape_paths[k]);
422                 fd = ::open(current_filename, O_RDONLY);
423                 if( fd < 0 || read(fd,header,hsz) != hsz ) ret = 1;
424         }
425         if( !ret ) {
426                 is_png = !png_sig_cmp(header, 0, hsz);
427                 if( !is_png && strncmp("JFIF", (char*)header+6, 4) ) ret = 1;
428         }
429         if( !ret ) {
430                 lseek(fd, 0, SEEK_SET);
431                 pattern = is_png ?
432                         VFramePng::vframe_png(fd, 1, 1) :
433                         VFrameJpeg::vframe_jpeg(fd, 1, 1, BC_GREY8);
434                 if( !pattern ) ret = 1;
435         }
436         if( fd >= 0 ) {
437                 close(fd);  fd = -1;
438         }
439         if( !ret ) {
440                 int width = pattern->get_w(), height = pattern->get_h();
441                 double row_factor, col_factor;
442                 double row_offset = 0.5, col_offset = 0.5;      // for rounding
443
444                 if( config.preserve_aspect && aspect_w && aspect_h ) {
445                         row_factor = (height-1)/aspect_h;
446                         col_factor = (width-1)/aspect_w;
447                         if( row_factor < col_factor )
448                                 col_factor = row_factor;
449                         else
450                                 row_factor = col_factor;
451                         row_factor *= aspect_h/(double)(frame_height-1);
452                         col_factor *= aspect_w/(double)(frame_width-1);
453
454                         // center the pattern over the frame
455                         row_offset += (height-1-(frame_height-1)*row_factor)/2;
456                         col_offset += (width-1-(frame_width-1)*col_factor)/2;
457                 }
458                 else {
459                         // Stretch (or shrink) the pattern image to fill the frame
460                         row_factor = (double)(height-1)/(double)(frame_height-1);
461                         col_factor = (double)(width-1)/(double)(frame_width-1);
462                 }
463                 int out_w = width * col_factor, out_h = height * row_factor;
464                 if( out_w != width || out_h != height ) {
465                         VFrame *new_pattern = new VFrame(frame_width, frame_height, BC_GREY8);
466                         new_pattern->transfer_from(pattern, 0, col_offset,row_offset,
467                                 frame_width*col_factor, frame_height*row_factor);
468                         delete pattern;  pattern = new_pattern;
469                 }
470                 unsigned char **rows = pattern->get_rows();
471                 unsigned char min = 0xff, max = 0x00;
472                 // first, determine range min..max
473                 for( int y=0; y<frame_height; ++y ) {
474                         unsigned char *row = rows[y];
475                         for( int x=0; x<frame_width; ++x ) {
476                                 unsigned char value = row[x];
477                                 if( value < min ) min = value;
478                                 if( value > max ) max = value;
479                         }
480                 }
481                 if( min > max ) min = max;
482                 int range = max - min;
483                 if( !range ) range = 1;
484                 pattern_image = new  unsigned char*[frame_height];
485                 // scale to fade normalized pattern_image
486                 for( int y=0; y<frame_height; ++y ) {
487                         unsigned char *row = rows[y];
488                         pattern_image[y] = new unsigned char[frame_width];
489                         for( int x=0; x<frame_width; ++x ) {
490                                 unsigned char value = row[x];
491                                 pattern_image[y][x] = 0xff*(value-min) / range;
492                         }
493                 }
494                 this->frame_width = frame_width;
495                 this->frame_height = frame_height;
496         }
497
498         return ret;
499 }
500
501 void ShapeWipeMain::reset_pattern_image()
502 {
503         if( pattern_image ) {
504                 for( int y=0; y<frame_height; ++y )
505                         delete [] pattern_image[y];
506                 delete [] pattern_image;  pattern_image = 0;
507         }
508 }
509
510 #define SHAPEBLEND(type, components, tmp_type) { \
511         float scale = feather ? 1/feather : 0xff; \
512         type  **in_rows = (type**)input->get_rows(); \
513         type **out_rows = (type**)output->get_rows(); \
514         if( !dir ) { \
515                 for( int y=y1; y<y2; ++y ) { \
516                         type *in_row = (type*) in_rows[y]; \
517                         type *out_row = (type*)out_rows[y]; \
518                         unsigned char *pattern_row = pattern_image[y]; \
519                         for( int x=0; x<w; ++x ) { \
520                                 tmp_type d = (pattern_row[x] - threshold) * scale; \
521                                 if( d > 0xff ) d = 0xff; \
522                                 else if( d < -0xff ) d = -0xff; \
523                                 tmp_type a = (d + 0xff) / 2, b = 0xff - a; \
524                                 for( int i=0; i<components; ++i ) { \
525                                         type ic = in_row[i], oc = out_row[i]; \
526                                         out_row[i] = (ic * a + oc * b) / 0xff; \
527                                 } \
528                                 in_row += components; out_row += components; \
529                         } \
530                 } \
531         } \
532         else { \
533                 for( int y=y1; y<y2; ++y ) { \
534                         type *in_row = (type*) in_rows[y]; \
535                         type *out_row = (type*)out_rows[y]; \
536                         unsigned char *pattern_row = pattern_image[y]; \
537                         for( int x=0; x<w; ++x ) { \
538                                 tmp_type d = (pattern_row[x] - threshold) * scale; \
539                                 if( d > 0xff ) d = 0xff; \
540                                 else if( d < -0xff ) d = -0xff; \
541                                 tmp_type b = (d + 0xff) / 2, a = 0xff - b; \
542                                 for( int i=0; i<components; ++i ) { \
543                                         type ic = in_row[i], oc = out_row[i]; \
544                                         out_row[i] = (ic * a + oc * b) / 0xff; \
545                                 } \
546                                 in_row += components; out_row += components; \
547                         } \
548                 } \
549         } \
550 }
551
552 int ShapeWipeMain::process_realtime(VFrame *input, VFrame *output)
553 {
554         this->input = input;
555         this->output = output;
556         int w = input->get_w();
557         int h = input->get_h();
558         init_shapes();
559         load_configuration();
560
561         if( strncmp(config.shape_name, current_name, BCTEXTLEN) ||
562             config.preserve_aspect != last_preserve_aspect ) {
563                 reset_pattern_image();
564         }
565         if ( !pattern_image ) {
566                 if( read_pattern_image(config.shape_name, w, h) ) {
567                         fprintf(stderr, _("Shape Wipe: cannot load shape %s\n"),
568                                 current_filename);
569                         current_filename[0] = 0;
570                         return 0;
571                 }
572                 strncpy(current_name, config.shape_name, BCTEXTLEN);
573                 last_preserve_aspect = config.preserve_aspect;
574         }
575
576         float fade = (float)PluginClient::get_source_position() /
577                 (float)PluginClient::get_total_len();
578         if( !config.direction ) fade = 1 - fade;
579         threshold = fade * 0xff;
580
581         int slices = w*h/0x40000+1;
582         int max_slices = BC_Resources::machine_cpus/2;
583         if( slices > max_slices ) slices = max_slices;
584         if( slices < 1 ) slices = 1;
585         if( engine && engine->get_total_clients() != slices ) {
586                 delete engine;  engine = 0;
587         }
588         if( !engine )
589                 engine = new ShapeEngine(this, slices, slices);
590
591         engine->process_packages();
592         return 0;
593 }
594
595
596 ShapePackage::ShapePackage()
597  : LoadPackage()
598 {
599 }
600
601 ShapeUnit::ShapeUnit(ShapeEngine *server) : LoadClient(server)
602 {
603         this->server = server;
604 }
605
606 ShapeUnit::~ShapeUnit()
607 {
608 }
609
610 void ShapeUnit::process_package(LoadPackage *package)
611 {
612         VFrame *input = server->plugin->input;
613         VFrame *output = server->plugin->output;
614         int w = input->get_w();
615         int dir = server->plugin->config.direction;
616
617         unsigned char **pattern_image = server->plugin->pattern_image;
618         unsigned char threshold = server->plugin->threshold;
619         float feather = server->plugin->config.feather;
620         ShapePackage *pkg = (ShapePackage*)package;
621         int y1 = pkg->y1, y2 = pkg->y2;
622
623         switch(input->get_color_model()) {
624         case BC_RGB_FLOAT:
625                 SHAPEBLEND(float, 3, float)
626                 break;
627         case BC_RGB888:
628         case BC_YUV888:
629                 SHAPEBLEND(unsigned char, 3, int)
630                 break;
631         case BC_RGBA_FLOAT:
632                 SHAPEBLEND(float, 4, float)
633                 break;
634         case BC_RGBA8888:
635         case BC_YUVA8888:
636                 SHAPEBLEND(unsigned char, 4, int)
637                 break;
638         case BC_RGB161616:
639         case BC_YUV161616:
640                 SHAPEBLEND(uint16_t, 3, int64_t)
641                 break;
642         case BC_RGBA16161616:
643         case BC_YUVA16161616:
644                 SHAPEBLEND(uint16_t, 4, int64_t)
645                 break;
646         }
647 }
648
649
650 ShapeEngine::ShapeEngine(ShapeWipeMain *plugin,
651         int total_clients, int total_packages)
652  : LoadServer(total_clients, total_packages)
653 {
654         this->plugin = plugin;
655 }
656
657 ShapeEngine::~ShapeEngine()
658 {
659 }
660
661
662 void ShapeEngine::init_packages()
663 {
664         int y = 0, h1 = plugin->input->get_h()-1;
665         int total_packages = get_total_packages();
666         for(int i = 0; i<total_packages; ) {
667                 ShapePackage *pkg = (ShapePackage*)get_package(i++);
668                 pkg->y1 = y;
669                 y = h1 * i / total_packages;
670                 pkg->y2 = y;
671         }
672 }
673
674 LoadClient* ShapeEngine::new_client()
675 {
676         return new ShapeUnit(this);
677 }
678
679 LoadPackage* ShapeEngine::new_package()
680 {
681         return new ShapePackage;
682 }
683