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