dynamic keyframes, textbox rework, andrea ffmpeg.opts, perpetual chkpt undo, lv2...
[goodguy/history.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 <string.h>
38
39 #define SHAPE_SEARCHPATH "/shapes"
40 #define DEFAULT_SHAPE "circle"
41
42 REGISTER_PLUGIN(ShapeWipeMain)
43
44 ShapeWipeW2B::ShapeWipeW2B(ShapeWipeMain *plugin,
45         ShapeWipeWindow *window,
46         int x,
47         int y)
48  : BC_Radial(x,
49                 y,
50                 plugin->direction == 0,
51                 _("White to Black"))
52 {
53         this->plugin = plugin;
54         this->window = window;
55 }
56
57 int ShapeWipeW2B::handle_event()
58 {
59         update(1);
60         plugin->direction = 0;
61         window->right->update(0);
62         plugin->send_configure_change();
63         return 0;
64 }
65
66 ShapeWipeB2W::ShapeWipeB2W(ShapeWipeMain *plugin,
67         ShapeWipeWindow *window,
68         int x,
69         int y)
70  : BC_Radial(x,
71                 y,
72                 plugin->direction == 1,
73                 _("Black to White"))
74 {
75         this->plugin = plugin;
76         this->window = window;
77 }
78
79 int ShapeWipeB2W::handle_event()
80 {
81         update(1);
82         plugin->direction = 1;
83         window->left->update(0);
84         plugin->send_configure_change();
85         return 0;
86 }
87
88 ShapeWipeAntiAlias::ShapeWipeAntiAlias(ShapeWipeMain *plugin,
89         ShapeWipeWindow *window,
90         int x,
91         int y)
92  : BC_CheckBox (x,y,plugin->antialias, _("Anti-aliasing"))
93 {
94         this->plugin = plugin;
95         this->window = window;
96 }
97
98 int ShapeWipeAntiAlias::handle_event()
99 {
100         plugin->antialias = get_value();
101         plugin->send_configure_change();
102         return 0;
103 }
104
105 ShapeWipePreserveAspectRatio::ShapeWipePreserveAspectRatio(ShapeWipeMain *plugin,
106         ShapeWipeWindow *window,
107         int x,
108         int y)
109  : BC_CheckBox (x, y, plugin->preserve_aspect, _("Preserve shape aspect ratio"))
110 {
111         this->plugin = plugin;
112         this->window = window;
113 }
114
115 int ShapeWipePreserveAspectRatio::handle_event()
116 {
117         plugin->preserve_aspect = get_value();
118         plugin->send_configure_change();
119         return 0;
120 }
121
122
123
124
125
126
127 ShapeWipeTumble::ShapeWipeTumble(ShapeWipeMain *client,
128         ShapeWipeWindow *window,
129         int x,
130         int y)
131  : BC_Tumbler(x, y)
132 {
133         this->client = client;
134         this->window = window;
135 }
136
137 int ShapeWipeTumble::handle_up_event()
138 {
139         window->prev_shape();
140         return 1;
141 }
142
143 int ShapeWipeTumble::handle_down_event()
144 {
145         window->next_shape();
146         return 0;
147 }
148
149
150
151
152
153
154
155 ShapeWipeShape::ShapeWipeShape(ShapeWipeMain *client,
156         ShapeWipeWindow *window,
157         int x,
158         int y,
159         int text_w,
160         int list_h)
161  : BC_PopupTextBox(window,
162         &window->shapes,
163         client->shape_name,
164         x,
165         y,
166         text_w,
167         list_h)
168 {
169         this->client = client;
170         this->window = window;
171 }
172
173 int ShapeWipeShape::handle_event()
174 {
175         strcpy(client->shape_name, get_text());
176         client->send_configure_change();
177         return 1;
178 }
179
180
181
182
183
184
185
186
187 ShapeWipeWindow::ShapeWipeWindow(ShapeWipeMain *plugin)
188  : PluginClientWindow(plugin,
189         450,
190         125,
191         450,
192         125,
193         0)
194 {
195         this->plugin = plugin;
196 }
197
198 ShapeWipeWindow::~ShapeWipeWindow()
199 {
200         shapes.remove_all_objects();
201 }
202
203
204
205 void ShapeWipeWindow::create_objects()
206 {
207         BC_Title *title = 0;
208         lock_window("ShapeWipeWindow::create_objects");
209         int widget_border = plugin->get_theme()->widget_border;
210         int window_border = plugin->get_theme()->window_border;
211         int x = window_border, y = window_border;
212
213         plugin->init_shapes();
214         for(int i = 0; i < plugin->shape_titles.size(); i++)
215         {
216                 shapes.append(new BC_ListBoxItem(plugin->shape_titles.get(i)));
217         }
218
219         add_subwindow(title = new BC_Title(x, y, _("Direction:")));
220         x += title->get_w() + widget_border;
221         add_subwindow(left = new ShapeWipeW2B(plugin,
222                 this,
223                 x,
224                 y));
225         x += left->get_w() + widget_border;
226         add_subwindow(right = new ShapeWipeB2W(plugin,
227                 this,
228                 x,
229                 y));
230         x = window_border;
231         y += right->get_h() + widget_border;
232
233
234         add_subwindow(title = new BC_Title(x, y, _("Shape:")));
235         x += title->get_w() + widget_border;
236
237 //      add_subwindow(filename_widget = new
238 //      ShapeWipeFilename(plugin,
239 //              this,
240 //              plugin->filename,
241 //              x,
242 //              y));
243 //      x += 200;
244 //      add_subwindow(new ShapeWipeBrowseButton(
245 //              plugin,
246 //              this,
247 //              filename_widget,
248 //              x,
249 //              y));
250
251         shape_text = new ShapeWipeShape(plugin,
252                 this,
253                 x,
254                 y,
255                 150,
256                 200);
257         shape_text->create_objects();
258         x += shape_text->get_w() + widget_border;
259         add_subwindow(new ShapeWipeTumble(plugin,
260                 this,
261                 x,
262                 y));
263         x = window_border;
264         y += shape_text->get_h() + widget_border;
265
266         ShapeWipeAntiAlias *anti_alias;
267         add_subwindow(anti_alias = new ShapeWipeAntiAlias(
268                 plugin,
269                 this,
270                 x,
271                 y));
272         y += anti_alias->get_h() + widget_border;
273         ShapeWipePreserveAspectRatio *aspect_ratio;
274         add_subwindow(aspect_ratio = new ShapeWipePreserveAspectRatio(
275                 plugin,
276                 this,
277                 x,
278                 y));
279         y += aspect_ratio->get_h() + widget_border;
280
281         show_window();
282         unlock_window();
283 }
284
285 void ShapeWipeWindow::next_shape()
286 {
287         for(int i = 0; i < plugin->shape_titles.size(); i++)
288         {
289                 if(!strcmp(plugin->shape_titles.get(i), plugin->shape_name))
290                 {
291                         i++;
292                         if(i >= plugin->shape_titles.size()) i = 0;
293                         strcpy(plugin->shape_name, plugin->shape_titles.get(i));
294                         shape_text->update(plugin->shape_name);
295                         break;
296                 }
297         }
298         client->send_configure_change();
299 }
300
301 void ShapeWipeWindow::prev_shape()
302 {
303         for(int i = 0; i < plugin->shape_titles.size(); i++)
304         {
305                 if(!strcmp(plugin->shape_titles.get(i), plugin->shape_name))
306                 {
307                         i--;
308                         if(i < 0) i = plugin->shape_titles.size() - 1;
309                         strcpy(plugin->shape_name, plugin->shape_titles.get(i));
310                         shape_text->update(plugin->shape_name);
311                         break;
312                 }
313         }
314         client->send_configure_change();
315 }
316
317
318
319
320
321 ShapeWipeMain::ShapeWipeMain(PluginServer *server)
322  : PluginVClient(server)
323 {
324         direction = 0;
325         filename[0] = 0;
326         last_read_filename[0] = '\0';
327         strcpy(shape_name, DEFAULT_SHAPE);
328         current_name[0] = 0;
329         pattern_image = NULL;
330         min_value = (unsigned char)255;
331         max_value = (unsigned char)0;
332         antialias = 0;
333         preserve_aspect = 0;
334         last_preserve_aspect = 0;
335         shapes_initialized = 0;
336         shape_paths.set_array_delete();
337         shape_titles.set_array_delete();
338 }
339
340 ShapeWipeMain::~ShapeWipeMain()
341 {
342         reset_pattern_image();
343         shape_paths.remove_all_objects();
344         shape_titles.remove_all_objects();
345 }
346
347 const char* ShapeWipeMain::plugin_title() { return N_("Shape Wipe"); }
348 int ShapeWipeMain::is_transition() { return 1; }
349 int ShapeWipeMain::uses_gui() { return 1; }
350
351 NEW_WINDOW_MACRO(ShapeWipeMain, ShapeWipeWindow);
352
353
354
355 void ShapeWipeMain::save_data(KeyFrame *keyframe)
356 {
357         FileXML output;
358         output.set_shared_output(keyframe->xbuf);
359         output.tag.set_title("SHAPEWIPE");
360         output.tag.set_property("DIRECTION", direction);
361         output.tag.set_property("ANTIALIAS", antialias);
362         output.tag.set_property("PRESERVE_ASPECT", preserve_aspect);
363         output.tag.set_property("FILENAME", filename);
364         output.tag.set_property("SHAPE_NAME", shape_name);
365         output.append_tag();
366         output.tag.set_title("/SHAPEWIPE");
367         output.append_tag();
368         output.terminate_string();
369 }
370
371 void ShapeWipeMain::read_data(KeyFrame *keyframe)
372 {
373         FileXML input;
374
375         input.set_shared_input(keyframe->xbuf);
376
377         while(!input.read_tag())
378         {
379                 if(input.tag.title_is("SHAPEWIPE"))
380                 {
381                         direction = input.tag.get_property("DIRECTION", direction);
382                         antialias = input.tag.get_property("ANTIALIAS", antialias);
383                         preserve_aspect = input.tag.get_property("PRESERVE_ASPECT", preserve_aspect);
384                         input.tag.get_property("FILENAME", filename);
385                         input.tag.get_property("SHAPE_NAME", shape_name);
386                 }
387         }
388 }
389
390 void ShapeWipeMain::init_shapes()
391 {
392         if(!shapes_initialized)
393         {
394                 FileSystem fs;
395                 fs.set_filter("*.png");
396                 char shape_path[BCTEXTLEN];
397                 sprintf(shape_path, "%s%s", get_plugin_dir(), SHAPE_SEARCHPATH);
398                 fs.update(shape_path);
399
400                 for(int i = 0; i < fs.total_files(); i++)
401                 {
402                         FileItem *file_item = fs.get_entry(i);
403                         if(!file_item->get_is_dir())
404                         {
405                                 shape_paths.append(cstrdup(file_item->get_path()));
406                                 char *ptr = cstrdup(file_item->get_name());
407                                 char *ptr2 = strrchr(ptr, '.');
408                                 if(ptr2) *ptr2 = 0;
409                                 shape_titles.append(ptr);
410                         }
411                 }
412
413                 shapes_initialized = 1;
414         }
415 }
416
417
418 int ShapeWipeMain::load_configuration()
419 {
420         read_data(get_prev_keyframe(get_source_position()));
421         return 1;
422 }
423
424 int ShapeWipeMain::read_pattern_image(int new_frame_width, int new_frame_height)
425 {
426         png_byte header[8];
427         int is_png;
428         int row;
429         int col;
430         int scaled_row;
431         int scaled_col;
432         int pixel_width;
433         unsigned char value;
434         png_uint_32 width;
435         png_uint_32 height;
436         png_byte color_type;
437         png_byte bit_depth;
438         png_structp png_ptr;
439         png_infop info_ptr;
440         png_infop end_info;
441         png_bytep *image;
442         frame_width = new_frame_width;
443         frame_height = new_frame_height;
444
445 // Convert name to filename
446         for(int i = 0; i < shape_paths.size(); i++)
447         {
448                 if(!strcmp(shape_titles.get(i), shape_name))
449                 {
450                         strcpy(filename, shape_paths.get(i));
451                         break;
452                 }
453         }
454
455         FILE *fp = fopen(filename, "rb");
456         if (!fp)
457         {
458                 return 1;
459         }
460
461         fread(header, 1, 8, fp);
462         is_png = !png_sig_cmp(header, 0, 8);
463
464         if (!is_png)
465         {
466                 fclose(fp);
467                 return 1;
468         }
469
470         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
471                 NULL, NULL, NULL);
472
473         if (!png_ptr)
474         {
475                 fclose(fp);
476                 return 1;
477         }
478
479         /* Tell libpng we already checked the first 8 bytes */
480         png_set_sig_bytes(png_ptr, 8);
481
482         info_ptr = png_create_info_struct(png_ptr);
483         if (!info_ptr)
484         {
485                 png_destroy_read_struct(&png_ptr, NULL, NULL);
486                 fclose(fp);
487                 return 1;
488         }
489
490         end_info = png_create_info_struct(png_ptr);
491         if (!end_info)
492         {
493                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
494                 fclose(fp);
495                 return 1;
496         }
497
498         png_init_io(png_ptr, fp);
499         png_read_info(png_ptr, info_ptr);
500
501         color_type = png_get_color_type(png_ptr, info_ptr);
502         bit_depth = png_get_bit_depth(png_ptr, info_ptr);
503         width  = png_get_image_width (png_ptr, info_ptr);
504         height = png_get_image_height(png_ptr, info_ptr);
505
506         /* Skip the alpha channel if present
507         * stripping alpha currently doesn't work in conjunction with
508         * converting to grayscale in libpng */
509         if (color_type & PNG_COLOR_MASK_ALPHA)
510                 pixel_width = 2;
511         else
512                 pixel_width = 1;
513
514         /* Convert 16 bit data to 8 bit */
515         if (bit_depth == 16) png_set_strip_16(png_ptr);
516
517         /* Expand to 1 pixel per byte if necessary */
518         if (bit_depth < 8) png_set_packing(png_ptr);
519
520         /* Convert to grayscale */
521         if (color_type == PNG_COLOR_TYPE_RGB ||
522                 color_type == PNG_COLOR_TYPE_RGB_ALPHA)
523         png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1);
524
525         /* Allocate memory to hold the original png image */
526         image = (png_bytep*)malloc(sizeof(png_bytep)*height);
527         for (row = 0; row < (int)height; row++)
528         {
529                 image[row] = (png_byte*)malloc(sizeof(png_byte)*width*pixel_width);
530         }
531
532         /* Allocate memory for the pattern image that will actually be
533         * used for the wipe */
534         pattern_image = (unsigned char**)malloc(sizeof(unsigned char*)*frame_height);
535
536
537         png_read_image(png_ptr, image);
538         png_read_end(png_ptr, end_info);
539
540         double row_factor, col_factor;
541         double row_offset = 0.5, col_offset = 0.5;      // for rounding
542
543         if (preserve_aspect && aspect_w != 0 && aspect_h != 0)
544         {
545                 row_factor = (height-1)/aspect_h;
546                 col_factor = (width-1)/aspect_w;
547                 if (row_factor < col_factor)
548                         col_factor = row_factor;
549                 else
550                         row_factor = col_factor;
551                 row_factor *= aspect_h/(double)(frame_height-1);
552                 col_factor *= aspect_w/(double)(frame_width-1);
553
554                 // center the pattern over the frame
555                 row_offset += (height-1-(frame_height-1)*row_factor)/2;
556                 col_offset += (width-1-(frame_width-1)*col_factor)/2;
557         }
558         else
559         {
560                 // Stretch (or shrink) the pattern image to fill the frame
561                 row_factor = (double)(height-1)/(double)(frame_height-1);
562                 col_factor = (double)(width-1)/(double)(frame_width-1);
563         }
564
565         for (scaled_row = 0; scaled_row < frame_height; scaled_row++)
566         {
567                 row = (int)(row_factor*scaled_row + row_offset);
568                 pattern_image[scaled_row] = (unsigned char*)malloc(sizeof(unsigned char)*frame_width);
569                 for (scaled_col = 0; scaled_col < frame_width; scaled_col++)
570                 {
571                         col = (int)(col_factor*scaled_col + col_offset)*pixel_width;
572                         value = image[row][col];
573                         pattern_image[scaled_row][scaled_col] = value;
574                         if (value < min_value) min_value = value;
575                         if (value > max_value) max_value = value;
576
577                 }
578         }
579
580         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
581         fclose(fp);
582         /* Deallocate the original image as it is no longer needed */
583         for (row = 0; row < (int)height; row++)
584         {
585                 free(image[row]);
586         }
587         free (image);
588         return 0;
589 }
590
591 void ShapeWipeMain::reset_pattern_image()
592 {
593         int row;
594         if (pattern_image != NULL)
595         {
596                 for (row = 0; row < frame_height; row++)
597                 {
598                         free (pattern_image[row]);
599                 }
600                 free (pattern_image);
601                 pattern_image = NULL;
602                 min_value = (unsigned char)255;
603                 max_value = (unsigned char)0;   // are recalc'd in read_pattern_image
604         }
605 }
606
607 #define SHAPEWIPE(type, components) \
608 { \
609 \
610         type  **in_rows = (type**)incoming->get_rows(); \
611         type **out_rows = (type**)outgoing->get_rows(); \
612         \
613         type *in_row; \
614         type *out_row; \
615         \
616         if( !direction ) { \
617                 for(j = 0; j < h; j++) { \
618                         in_row = (type*) in_rows[j]; \
619                         out_row = (type*)out_rows[j]; \
620                         pattern_row = pattern_image[j]; \
621                         \
622                         col_offset = 0; \
623                         for(k = 0; k < w; k++, col_offset += components ) { \
624                                 value = pattern_row[k]; \
625                                 if (value < threshold) continue; \
626                                 out_row[col_offset]     = in_row[col_offset]; \
627                                 out_row[col_offset + 1] = in_row[col_offset + 1]; \
628                                 out_row[col_offset + 2] = in_row[col_offset + 2]; \
629                                 if(components == 4) \
630                                         out_row[col_offset + 3] = in_row[col_offset + 3]; \
631                         } \
632                 } \
633         } \
634         else { \
635                 for(j = 0; j < h; j++) { \
636                         in_row = (type*) in_rows[j]; \
637                         out_row = (type*)out_rows[j]; \
638                         pattern_row = pattern_image[j]; \
639                         \
640                         col_offset = 0; \
641                         for(k = 0; k < w; k++, col_offset += components ) { \
642                                 value = pattern_row[k]; \
643                                 if (value > threshold) continue; \
644                                 out_row[col_offset]     = in_row[col_offset]; \
645                                 out_row[col_offset + 1] = in_row[col_offset + 1]; \
646                                 out_row[col_offset + 2] = in_row[col_offset + 2]; \
647                                 if(components == 4) \
648                                         out_row[col_offset + 3] = in_row[col_offset + 3]; \
649                         } \
650                 } \
651         } \
652 }
653
654 #define COMPARE1(x,y) \
655 { \
656         if (pattern_image[x][y] <= threshold) opacity++; \
657 }
658
659 #define COMPARE2(x,y) \
660 { \
661         if (pattern_image[x][y] >= threshold) opacity++; \
662 }
663
664 // components is always 4
665 #define BLEND_ONLY_4_NORMAL(temp_type, type, max, chroma_offset,x,y) \
666 { \
667         const int bits = sizeof(type) * 8; \
668         temp_type blend_opacity = (temp_type)(alpha * ((temp_type)1 << bits) + 0.5); \
669         temp_type blend_transparency = ((temp_type)1 << bits) - blend_opacity; \
670  \
671         col = y * 4; \
672         type* in_row = (type*)incoming->get_rows()[x]; \
673         type* output = (type*)outgoing->get_rows()[x]; \
674  \
675         output[col] = ((temp_type)in_row[col] * blend_opacity + output[col] * blend_transparency) >> bits; \
676         output[col+1] = ((temp_type)in_row[col+1] * blend_opacity + output[col+1] * blend_transparency) >> bits; \
677         output[col+2] = ((temp_type)in_row[col+2] * blend_opacity + output[col+2] * blend_transparency) >> bits; \
678 }
679
680
681 // components is always 3
682 #define BLEND_ONLY_3_NORMAL(temp_type, type, max, chroma_offset,x,y) \
683 { \
684         const int bits = sizeof(type) * 8; \
685         temp_type blend_opacity = (temp_type)(alpha * ((temp_type)1 << bits) + 0.5); \
686         temp_type blend_transparency = ((temp_type)1 << bits) - blend_opacity; \
687  \
688         col = y * 3; \
689         type* in_row = (type*)incoming->get_rows()[x]; \
690         type* output = (type*)outgoing->get_rows()[x]; \
691  \
692         output[col] = ((temp_type)in_row[col] * blend_opacity + output[col] * blend_transparency) >> bits; \
693         output[col+1] = ((temp_type)in_row[col+1] * blend_opacity + output[col+1] * blend_transparency) >> bits; \
694         output[col+2] = ((temp_type)in_row[col+2] * blend_opacity + output[col+2] * blend_transparency) >> bits; \
695 }
696
697 /* opacity is defined as opacity of incoming frame */
698 #define BLEND(x,y,total) \
699 { \
700         float pixel_opacity = (float)opacity / total; \
701         float alpha = pixel_opacity; \
702         float pixel_transparency = 1.0 - pixel_opacity; \
703         int col; \
704  \
705         if (pixel_opacity > 0.0) \
706         { \
707                 switch(incoming->get_color_model()) \
708                 { \
709                 case BC_RGB_FLOAT: \
710                 { \
711                         float  *in_row = (float*)incoming->get_rows()[x]; \
712                         float *out_row = (float*)outgoing->get_rows()[x]; \
713                         col = y * 3; \
714                         out_row[col] = in_row[col] * pixel_opacity + \
715                                 out_row[col] * pixel_transparency; \
716                         out_row[col+1] = in_row[col+1] * pixel_opacity + \
717                                 out_row[col+1] * pixel_transparency; \
718                         out_row[col+2] = in_row[col+2] * pixel_opacity + \
719                                 out_row[col+2] * pixel_transparency; \
720                         break; \
721                 } \
722                 case BC_RGBA_FLOAT: \
723                 { \
724                         float  *in_row = (float*)incoming->get_rows()[x]; \
725                         float *out_row = (float*)outgoing->get_rows()[x]; \
726                         col = y * 4; \
727                         out_row[col] = in_row[col] * pixel_opacity + \
728                                 out_row[col] * pixel_transparency; \
729                         out_row[col+1] = in_row[col+1] * pixel_opacity + \
730                                 out_row[col+1] * pixel_transparency; \
731                         out_row[col+2] = in_row[col+2] * pixel_opacity + \
732                                 out_row[col+2] * pixel_transparency; \
733                         break; \
734                 } \
735                 case BC_RGB888: \
736                         BLEND_ONLY_3_NORMAL(uint32_t, unsigned char, 0xff, 0,x,y); \
737                         break; \
738                 case BC_YUV888: \
739                         BLEND_ONLY_3_NORMAL(int32_t, unsigned char, 0xff, 0x80,x,y); \
740                         break; \
741                 case BC_RGBA8888: \
742                         BLEND_ONLY_4_NORMAL(uint32_t, unsigned char, 0xff, 0,x,y); \
743                         break; \
744                 case BC_YUVA8888: \
745                         BLEND_ONLY_4_NORMAL(int32_t, unsigned char, 0xff, 0x80,x,y); \
746                         break; \
747                 case BC_RGB161616: \
748                         BLEND_ONLY_3_NORMAL(uint64_t, uint16_t, 0xffff, 0,x,y); \
749                         break; \
750                 case BC_YUV161616: \
751                         BLEND_ONLY_3_NORMAL(int64_t, uint16_t, 0xffff, 0x8000,x,y); \
752                         break; \
753                 case BC_RGBA16161616: \
754                         BLEND_ONLY_4_NORMAL(uint64_t, uint16_t, 0xffff, 0,x,y); \
755                         break; \
756                 case BC_YUVA16161616: \
757                         BLEND_ONLY_4_NORMAL(int64_t, uint16_t, 0xffff, 0x8000,x,y); \
758                         break; \
759       } \
760    } \
761 }
762
763 int ShapeWipeMain::process_realtime(VFrame *incoming, VFrame *outgoing)
764 {
765         unsigned char *pattern_row;
766         int col_offset;
767         unsigned char threshold;
768         unsigned char value;
769         int j, k;
770         int opacity;
771
772         init_shapes();
773         load_configuration();
774
775         int w = incoming->get_w();
776         int h = incoming->get_h();
777
778         if (strncmp(filename, last_read_filename, BCTEXTLEN) ||
779                 strncmp(shape_name, current_name, BCTEXTLEN) ||
780                 preserve_aspect != last_preserve_aspect)
781         {
782                 reset_pattern_image();
783         }
784
785         if (!pattern_image)
786         {
787                 read_pattern_image(w, h);
788                 strncpy(last_read_filename, filename, BCTEXTLEN);
789                 last_preserve_aspect = preserve_aspect;
790
791                 if (pattern_image)
792                 {
793                         strncpy(last_read_filename, filename, BCTEXTLEN);
794                         strncpy(current_name, shape_name, BCTEXTLEN);
795                         last_preserve_aspect = preserve_aspect;
796                 }
797                 else {
798                         fprintf(stderr, _("Shape Wipe: cannot load shape %s\n"), filename);
799                         last_read_filename[0] = 0;
800                         return 0;
801                 }
802         }
803
804         if (direction)
805         {
806                 threshold = (unsigned char)(
807                                 (float)PluginClient::get_source_position() /
808                                 (float)PluginClient::get_total_len() *
809                                 (float)(max_value - min_value))
810                         + min_value;
811         }
812         else
813         {
814                 threshold = (unsigned char)((max_value - min_value) - (
815                                 (float)PluginClient::get_source_position() /
816                                 (float)PluginClient::get_total_len() *
817                                 (float)(max_value - min_value)))
818                         + min_value;
819         }
820
821         if (antialias)
822         {
823                 if (direction)
824                 {
825                         /* Top left corner */
826                         opacity = 0;
827                         COMPARE1(0,0);
828                         COMPARE1(0,1);
829                         COMPARE1(1,0);
830                         COMPARE1(1,1);
831                         BLEND(0,0,4.0);
832
833                         /* Top edge */
834                         for (k = 1; k < w-1; k++)
835                         {
836                                 opacity = 0;
837                                 COMPARE1(0,k-1);
838                                 COMPARE1(0,k);
839                                 COMPARE1(0,k+1);
840                                 COMPARE1(1,k-1);
841                                 COMPARE1(1,k);
842                                 COMPARE1(1,k+1);
843                                 BLEND(0,k,6.0);
844                         }
845
846                         /* Top right corner */
847                         opacity = 0;
848                         COMPARE1(0,w-1);
849                         COMPARE1(0,w-2);
850                         COMPARE1(1,w-1);
851                         COMPARE1(1,w-2);
852                         BLEND(0,w-1,4.0);
853
854                         /* Left edge */
855                         for (j = 1; j < h-1; j++)
856                         {
857                                 opacity = 0;
858                                 COMPARE1(j-1,0);
859                                 COMPARE1(j,0);
860                                 COMPARE1(j+1,0);
861                                 COMPARE1(j-1,1);
862                                 COMPARE1(j,1);
863                                 COMPARE1(j+1,1);
864                                 BLEND(j,0,6.0);
865                         }
866
867                         /* Middle */
868                         for (j = 1; j < h-1; j++)
869                         {
870                                 for (k = 1; k < w-1; k++)
871                                 {
872                                         opacity = 0;
873                                         COMPARE1(j-1,k-1);
874                                         COMPARE1(j,k-1);
875                                         COMPARE1(j+1,k-1);
876                                         COMPARE1(j-1,k);
877                                         COMPARE1(j,k);
878                                         COMPARE1(j+1,k);
879                                         COMPARE1(j-1,k+1);
880                                         COMPARE1(j,k+1);
881                                         COMPARE1(j+1,k+1);
882                                         BLEND(j,k,9.0);
883                                 }
884                         }
885
886                         /* Right edge */
887                         for (j = 1; j < h-1; j++)
888                         {
889                                 opacity = 0;
890                                 COMPARE1(j-1,w-1);
891                                 COMPARE1(j,w-1);
892                                 COMPARE1(j+1,w-1);
893                                 COMPARE1(j-1,w-2);
894                                 COMPARE1(j,w-2);
895                                 COMPARE1(j+1,w-2);
896                                 BLEND(j,w-1,6.0);
897                         }
898
899                         /* Bottom left corner */
900                         opacity = 0;
901                         COMPARE1(h-1,0);
902                         COMPARE1(h-1,1);
903                         COMPARE1(h-2,0);
904                         COMPARE1(h-2,1);
905                         BLEND(h-1,0,4.0);
906
907                         /* Bottom edge */
908                         for (k = 1; k < w-1; k++)
909                         {
910                                 opacity = 0;
911                                 COMPARE1(h-1,k-1);
912                                 COMPARE1(h-1,k);
913                                 COMPARE1(h-1,k+1);
914                                 COMPARE1(h-2,k-1);
915                                 COMPARE1(h-2,k);
916                                 COMPARE1(h-2,k+1);
917                                 BLEND(h-1,k,6.0);
918                         }
919
920                         /* Bottom right corner */
921                         opacity = 0;
922                         COMPARE1(h-1,w-1);
923                         COMPARE1(h-1,w-2);
924                         COMPARE1(h-2,w-1);
925                         COMPARE1(h-2,w-2);
926                         BLEND(h-1,w-1,4.0);
927                 }
928                 else
929                 {
930                         /* Top left corner */
931                         opacity = 0;
932                         COMPARE2(0,0);
933                         COMPARE2(0,1);
934                         COMPARE2(1,0);
935                         COMPARE2(1,1);
936                         BLEND(0,0,4.0);
937
938                         /* Top edge */
939                         for (k = 1; k < w-1; k++)
940                         {
941                                 opacity = 0;
942                                 COMPARE2(0,k-1);
943                                 COMPARE2(0,k);
944                                 COMPARE2(0,k+1);
945                                 COMPARE2(1,k-1);
946                                 COMPARE2(1,k);
947                                 COMPARE2(1,k+1);
948                                 BLEND(0,k,6.0);
949                         }
950
951                         /* Top right corner */
952                         opacity = 0;
953                         COMPARE2(0,w-1);
954                         COMPARE2(0,w-2);
955                         COMPARE2(1,w-1);
956                         COMPARE2(1,w-2);
957                         BLEND(0,w-1,4.0);
958
959                         /* Left edge */
960                         for (j = 1; j < h-1; j++)
961                         {
962                                 opacity = 0;
963                                 COMPARE2(j-1,0);
964                                 COMPARE2(j,0);
965                                 COMPARE2(j+1,0);
966                                 COMPARE2(j-1,1);
967                                 COMPARE2(j,1);
968                                 COMPARE2(j+1,1);
969                                 BLEND(j,0,6.0);
970                         }
971
972                         /* Middle */
973                         for (j = 1; j < h-1; j++)
974                         {
975                                 for (k = 1; k < w-1; k++)
976                                 {
977                                         opacity = 0;
978                                         COMPARE2(j-1,k-1);
979                                         COMPARE2(j,k-1);
980                                         COMPARE2(j+1,k-1);
981                                         COMPARE2(j-1,k);
982                                         COMPARE2(j,k);
983                                         COMPARE2(j+1,k);
984                                         COMPARE2(j-1,k+1);
985                                         COMPARE2(j,k+1);
986                                         COMPARE2(j+1,k+1);
987                                         BLEND(j,k,9.0);
988                                 }
989                         }
990
991                         /* Right edge */
992                         for (j = 1; j < h-1; j++)
993                         {
994                                 opacity = 0;
995                                 COMPARE2(j-1,w-1);
996                                 COMPARE2(j,w-1);
997                                 COMPARE2(j+1,w-1);
998                                 COMPARE2(j-1,w-2);
999                                 COMPARE2(j,w-2);
1000                                 COMPARE2(j+1,w-2);
1001                                 BLEND(j,w-1,6.0);
1002                         }
1003
1004                         /* Bottom left corner */
1005                         opacity = 0;
1006                         COMPARE2(h-1,0);
1007                         COMPARE2(h-1,1);
1008                         COMPARE2(h-2,0);
1009                         COMPARE2(h-2,1);
1010                         BLEND(h-1,0,4.0);
1011
1012                         /* Bottom edge */
1013                         for (k = 1; k < w-1; k++)
1014                         {
1015                                 opacity = 0;
1016                                 COMPARE2(h-1,k-1);
1017                                 COMPARE2(h-1,k);
1018                                 COMPARE2(h-1,k+1);
1019                                 COMPARE2(h-2,k-1);
1020                                 COMPARE2(h-2,k);
1021                                 COMPARE2(h-2,k+1);
1022                                 BLEND(h-1,k,6.0);
1023                         }
1024
1025                         /* Bottom right corner */
1026                         opacity = 0;
1027                         COMPARE2(h-1,w-1);
1028                         COMPARE2(h-1,w-2);
1029                         COMPARE2(h-2,w-1);
1030                         COMPARE2(h-2,w-2);
1031                         BLEND(h-1,w-1,4.0);
1032                 }
1033         }
1034         else
1035         {
1036                 switch(incoming->get_color_model())
1037                 {
1038                 case BC_RGB_FLOAT:
1039                         SHAPEWIPE(float, 3)
1040                         break;
1041                 case BC_RGB888:
1042                 case BC_YUV888:
1043                         SHAPEWIPE(unsigned char, 3)
1044                         break;
1045                 case BC_RGBA_FLOAT:
1046                         SHAPEWIPE(float, 4)
1047                         break;
1048                 case BC_RGBA8888:
1049                 case BC_YUVA8888:
1050                         SHAPEWIPE(unsigned char, 4)
1051                         break;
1052                 case BC_RGB161616:
1053                 case BC_YUV161616:
1054                         SHAPEWIPE(uint16_t, 3)
1055                         break;
1056                 case BC_RGBA16161616:
1057                 case BC_YUVA16161616:
1058                         SHAPEWIPE(uint16_t, 4)
1059                         break;
1060                 }
1061         }
1062         return 0;
1063 }