r499: This commit was manufactured by cvs2svn to create tag 'r1_2_1-last'.
[cinelerra_cv/mob.git] / hvirtual / plugins / shapewipe / shapewipe.C
blobe72d6a014750494d8e30a6b5228a133887bd5a4b
1 #include "bcdisplayinfo.h"
2 #include "defaults.h"
3 #include "edl.inc"
4 #include "filexml.h"
5 #include "language.h"
6 #include "overlayframe.h"
7 #include "picon_png.h"
8 #include "vframe.h"
9 #include "shapewipe.h"
11 #include <png.h>
12 #include <math.h>
13 #include <stdint.h>
14 #include <string.h>
16 REGISTER_PLUGIN(ShapeWipeMain)
18 ShapeWipeW2B::ShapeWipeW2B(ShapeWipeMain *plugin, 
19         ShapeWipeWindow *window,
20         int x,
21         int y)
22  : BC_Radial(x, 
23                 y, 
24                 plugin->direction == 0, 
25                 _("White to Black"))
27         this->plugin = plugin;
28         this->window = window;
31 int ShapeWipeW2B::handle_event()
33         update(1);
34         plugin->direction = 0;
35         window->right->update(0);
36         plugin->send_configure_change();
37         return 0;
40 ShapeWipeB2W::ShapeWipeB2W(ShapeWipeMain *plugin, 
41         ShapeWipeWindow *window,
42         int x,
43         int y)
44  : BC_Radial(x, 
45                 y, 
46                 plugin->direction == 1, 
47                 _("Black to White"))
49         this->plugin = plugin;
50         this->window = window;
53 int ShapeWipeB2W::handle_event()
55         update(1);
56         plugin->direction = 1;
57         window->left->update(0);
58         plugin->send_configure_change();
59         return 0;
62 ShapeWipeAntiAlias::ShapeWipeAntiAlias(ShapeWipeMain *plugin,
63         ShapeWipeWindow *window,
64         int x,
65         int y)
66  : BC_CheckBox (x,y,plugin->antialias, _("Anti-aliasing"))
68         this->plugin = plugin;
69         this->window = window;
72 int ShapeWipeAntiAlias::handle_event()
74         plugin->antialias = get_value();
75         plugin->send_configure_change();
76         return 0;
79 ShapeWipePreserveAspectRatio::ShapeWipePreserveAspectRatio(ShapeWipeMain *plugin,
80         ShapeWipeWindow *window,
81         int x,
82         int y)
83  : BC_CheckBox (x,y,plugin->preserve_aspect, _("Preserve shape aspect ratio"))
85         this->plugin = plugin;
86         this->window = window;
89 int ShapeWipePreserveAspectRatio::handle_event()
91         plugin->preserve_aspect = get_value();
92         plugin->send_configure_change();
93         return 0;
96 ShapeWipeFilename::ShapeWipeFilename(
97         ShapeWipeMain *plugin,
98         ShapeWipeWindow *window,
99         char *value,
100         int x,
101         int y)
102  : BC_TextBox(x,y,180,1, value)
104         this->plugin = plugin;
105         this->window = window;
106         this->value = value;
109 int ShapeWipeFilename::handle_event()
111         value = get_text();
112         strcpy(plugin->filename, get_text());
113         plugin->send_configure_change();
114         return 0;
117 ShapeWipeBrowseButton::ShapeWipeBrowseButton(
118         ShapeWipeMain *plugin,
119         ShapeWipeWindow *window,
120         ShapeWipeFilename *filename,
121         int x,
122         int y)
123  : BC_GenericButton(x,y,_("Browse..."))
125         this->plugin = plugin;
126         this->window = window;
127         this->filename = filename;
130 int ShapeWipeBrowseButton::handle_event()
132         int result;
133         ShapeWipeLoad window(filename, filename->get_text());
134         window.create_objects();
135         window.update_filter("*.png");
136         result = window.run_window();
138         if (!result)
139         {
140                 filename->update(window.get_submitted_path());
141                 strcpy(plugin->filename, window.get_submitted_path());
142                 plugin->send_configure_change();
143         }
145         return 0;
148 ShapeWipeLoad::ShapeWipeLoad(
149         ShapeWipeFilename *filename, 
150         char *init_directory)
151  : BC_FileBox(
152         1,
153         1,
154         init_directory, 
155         _("Choose Shape"), 
156         _("Choose a Wipe Shape"))
158    this->filename = filename;
161 ShapeWipeWindow::ShapeWipeWindow(ShapeWipeMain *plugin, int x, int y)
162  : BC_Window(plugin->gui_string, 
163         x, 
164         y, 
165         450, 
166         125, 
167         450, 
168         125, 
169         0, 
170         0,
171         1)
173         this->plugin = plugin;
176 int ShapeWipeWindow::close_event()
178         set_done(1);
179         return 1;
182 void ShapeWipeWindow::create_objects()
184         int x = 10, y = 10;
185         add_subwindow(new BC_Title(x, y, _("Direction:")));
186         x += 100;
187         add_subwindow(left = new ShapeWipeW2B(plugin, 
188                 this,
189                 x,
190                 y));
191         x += 200;
192         add_subwindow(right = new ShapeWipeB2W(plugin, 
193                 this,
194                 x,
195                 y));
196         x = 10; y += 25;
197         add_subwindow(new BC_Title(x, y, _("Shape:")));
198         x += 100;
200         add_subwindow(filename_widget = new 
201         ShapeWipeFilename(plugin, 
202                 this,
203                 plugin->filename,
204                 x,
205                 y));
206         x += 200;
207         add_subwindow(new ShapeWipeBrowseButton(
208                 plugin, 
209                 this,
210                 filename_widget,
211                 x,
212                 y));
213         x = 110; y += 25;
214         add_subwindow(new ShapeWipeAntiAlias(
215                 plugin, 
216                 this,
217                 x,
218                 y));
219         x = 110; y += 25;
220         add_subwindow(new ShapeWipePreserveAspectRatio(
221                 plugin, 
222                 this,
223                 x,
224                 y));
225         show_window();
226         flush();
229 PLUGIN_THREAD_OBJECT(ShapeWipeMain, ShapeWipeThread, ShapeWipeWindow)
231 ShapeWipeMain::ShapeWipeMain(PluginServer *server)
232  : PluginVClient(server)
234         direction = 0;
235         strcpy(filename, DEFAULT_SHAPE);        // is defined by a -D compiler instruction
236         last_read_filename[0] = '\0';
237         pattern_image = NULL;
238         min_value = 256;
239         max_value = 0;
240         antialias = 0;
241         preserve_aspect = 0;
242         last_preserve_aspect = 0;
243         PLUGIN_CONSTRUCTOR_MACRO
246 ShapeWipeMain::~ShapeWipeMain()
248         reset_pattern_image();
249         PLUGIN_DESTRUCTOR_MACRO
252 char* ShapeWipeMain::plugin_title() { return N_("Shape Wipe"); }
253 int ShapeWipeMain::is_video() { return 1; }
254 int ShapeWipeMain::is_transition() { return 1; }
255 int ShapeWipeMain::uses_gui() { return 1; }
257 SHOW_GUI_MACRO(ShapeWipeMain, ShapeWipeThread);
258 SET_STRING_MACRO(ShapeWipeMain)
259 RAISE_WINDOW_MACRO(ShapeWipeMain)
262 VFrame* ShapeWipeMain::new_picon()
264         return new VFrame(picon_png);
267 int ShapeWipeMain::load_defaults()
269         char directory[BCTEXTLEN];
270 // set the default directory
271         sprintf(directory, "%sshapewipe.rc", BCASTDIR);
273 // load the defaults
274         defaults = new Defaults(directory);
275         defaults->load();
277         direction = defaults->get("DIRECTION", direction);
278         antialias = defaults->get("ANTIALIAS", antialias);
279         preserve_aspect = defaults->get("PRESERVE_ASPECT", preserve_aspect);
280         defaults->get("FILENAME", filename);
281         return 0;
284 int ShapeWipeMain::save_defaults()
286         defaults->update("DIRECTION", direction);
287         defaults->update("ANTIALIAS", antialias);
288         defaults->update("PRESERVE_ASPECT", preserve_aspect);
289         defaults->update("FILENAME", filename);
290         defaults->save();
291         return 0;
294 void ShapeWipeMain::save_data(KeyFrame *keyframe)
296         FileXML output;
297         output.set_shared_string(keyframe->data, MESSAGESIZE);
298         output.tag.set_title("SHAPEWIPE");
299         output.tag.set_property("DIRECTION", direction);
300         output.tag.set_property("ANTIALIAS", antialias);
301         output.tag.set_property("PRESERVE_ASPECT", preserve_aspect);
302         output.tag.set_property("FILENAME", filename);
303         output.append_tag();
304         output.terminate_string();
307 void ShapeWipeMain::read_data(KeyFrame *keyframe)
309         FileXML input;
311         input.set_shared_string(keyframe->data, strlen(keyframe->data));
313         while(!input.read_tag())
314         {
315                 if(input.tag.title_is("SHAPEWIPE"))
316                 {
317                         direction = input.tag.get_property("DIRECTION", direction);
318                         antialias = input.tag.get_property("ANTIALIAS", antialias);
319                         preserve_aspect = input.tag.get_property("PRESERVE_ASPECT", preserve_aspect);
320                         input.tag.get_property("FILENAME", filename);
321                 }
322         }
325 void ShapeWipeMain::load_configuration()
327         read_data(get_prev_keyframe(get_source_position()));
330 int ShapeWipeMain::read_pattern_image(int new_frame_width, int new_frame_height)
332         png_byte header[8];
333         int is_png;
334         int row;
335         int col;
336         int scaled_row;
337         int scaled_col;
338         int pixel_width;
339         unsigned char value;
340         png_uint_32 width;
341         png_uint_32 height;
342         png_byte color_type;
343         png_byte bit_depth;
344         png_structp png_ptr;
345         png_infop info_ptr;
346         png_infop end_info;
347         png_bytep *image;
348         frame_width = new_frame_width;
349         frame_height = new_frame_height;
351         FILE *fp = fopen(filename, "rb");
352         if (!fp)
353         {
354                 return 1;
355         }
357         fread(header, 1, 8, fp);
358         is_png = !png_sig_cmp(header, 0, 8);
360         if (!is_png)
361         {
362                 return 1;
363         }
365         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
366                 png_voidp_NULL, png_error_ptr_NULL, png_error_ptr_NULL);
368         if (!png_ptr)
369         {
370                 return 1;
371         }
373         /* Tell libpng we already checked the first 8 bytes */
374         png_set_sig_bytes(png_ptr, 8);
376         info_ptr = png_create_info_struct(png_ptr);
377         if (!info_ptr)
378         {
379                 png_destroy_read_struct(&png_ptr, png_infopp_NULL, png_infopp_NULL);
380                 return 1;
381         }
383         end_info = png_create_info_struct(png_ptr);
384         if (!end_info)
385         {
386                 png_destroy_read_struct(&png_ptr, &info_ptr, png_infopp_NULL);
387                 return 1;
388         }
390         png_init_io(png_ptr, fp);
391         png_read_info(png_ptr, info_ptr);
393         color_type = png_get_color_type(png_ptr, info_ptr);
394         bit_depth = png_get_bit_depth(png_ptr, info_ptr);
395         width  = png_get_image_width (png_ptr, info_ptr);
396         height = png_get_image_height(png_ptr, info_ptr);
398         /* Skip the alpha channel if present 
399         * stripping alpha currently doesn't work in conjunction with 
400         * converting to grayscale in libpng */
401         if (color_type & PNG_COLOR_MASK_ALPHA)
402                 pixel_width = 2;
403         else
404                 pixel_width = 1;
406         /* Convert 16 bit data to 8 bit */
407         if (bit_depth == 16) png_set_strip_16(png_ptr);
409         /* Expand to 1 pixel per byte if necessary */
410         if (bit_depth < 8) png_set_packing(png_ptr);
412         /* Convert to grayscale */
413         if (color_type == PNG_COLOR_TYPE_RGB
414                 || color_type == PNG_COLOR_TYPE_RGB_ALPHA)
415                 png_set_rgb_to_gray_fixed(png_ptr, 1, -1, -1);
417         /* Allocate memory to hold the original png image */
418         image = (png_bytep*)malloc(sizeof(png_bytep)*height);
419         for (row = 0; row < height; row++)
420         {
421                 image[row] = (png_byte*)malloc(sizeof(png_byte)*width*pixel_width);
422         }
424         /* Allocate memory for the pattern image that will actually be
425         * used for the wipe */
426         pattern_image = (unsigned char**)malloc(sizeof(unsigned char*)*frame_height);
429         png_read_image(png_ptr, image);
430         png_read_end(png_ptr, end_info);
432         double row_factor, col_factor;
433         double row_offset = 0.5, col_offset = 0.5;      // for rounding
435         if (preserve_aspect && aspect_w != 0 && aspect_h != 0)
436         {
437                 row_factor = (height-1)/aspect_h;
438                 col_factor = (width-1)/aspect_w;
439                 if (row_factor < col_factor)
440                         col_factor = row_factor;
441                 else
442                         row_factor = col_factor;
443                 row_factor *= aspect_h/(double)(frame_height-1);
444                 col_factor *= aspect_w/(double)(frame_width-1);
446                 // center the pattern over the frame
447                 row_offset += (height-1-(frame_height-1)*row_factor)/2;
448                 col_offset += (width-1-(frame_width-1)*col_factor)/2;
449         }
450         else
451         {
452                 // Stretch (or shrink) the pattern image to fill the frame
453                 row_factor = (double)(height-1)/(double)(frame_height-1);
454                 col_factor = (double)(width-1)/(double)(frame_width-1);
455         }
457         for (scaled_row = 0; scaled_row < frame_height; scaled_row++)
458         {
459                 row = (int)(row_factor*scaled_row + row_offset);
460                 pattern_image[scaled_row] = (unsigned char*)malloc(sizeof(unsigned char)*frame_width);
461                 for (scaled_col = 0; scaled_col < frame_width; scaled_col++)
462                 {
463                         col = (int)(col_factor*scaled_col + col_offset)*pixel_width;
464                         value = image[row][col];
465                         pattern_image[scaled_row][scaled_col] = value;
466                         if (value < min_value) min_value = value;
467                         if (value > max_value) max_value = value;
469                 }
470         }
472         png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
473         fclose(fp);
474         /* Deallocate the original image as it is no longer needed */
475         for (row = 0; row < height; row++)
476         {
477                 free(image[row]);
478         }
479         free (image);
480         return 0;
483 void ShapeWipeMain::reset_pattern_image()
485         int row;
486         if (pattern_image != NULL)
487         {
488                 for (row = 0; row < frame_height; row++)
489                 {
490                         free (pattern_image[row]);
491                 }
492                 free (pattern_image);
493                 pattern_image = NULL;
494                 min_value = 256, max_value = 0; // are recalc'd in read_pattern_image
495         }
498 #define SHAPEWIPE(type, components) \
499 { \
501         type  **in_rows = (type**)incoming->get_rows(); \
502         type **out_rows = (type**)outgoing->get_rows(); \
503         \
504         type *in_row; \
505         type *out_row; \
506         \
507         for(j = 0; j < h; j++) \
508         { \
509                 in_row = (type*) in_rows[j]; \
510                 out_row = (type*)out_rows[j]; \
511                 pattern_row = pattern_image[j]; \
512                 \
513                 col_offset = 0; \
514                 for(k = 0; k < w; k++) \
515                 { \
516                         value = pattern_row[k]; \
517                         if ((direction == 0 && value >= threshold) || \
518                         (direction == 1 && value <= threshold)) \
519                         { \
520                                 out_row[col_offset]     = in_row[col_offset]; \
521                                 out_row[col_offset + 1] = in_row[col_offset + 1]; \
522                                 out_row[col_offset + 2] = in_row[col_offset + 2]; \
523                                 if(components == 4) \
524                                         out_row[col_offset + 3] = in_row[col_offset + 3]; \
525                         } \
526                         col_offset += components; \
527                 } \
528         } \
531 #define COMPARE1(x,y) \
532 { \
533         if (pattern_image[x][y] <= threshold) opacity++; \
536 #define COMPARE2(x,y) \
537 { \
538         if (pattern_image[x][y] >= threshold) opacity++; \
541 // components is always 4
542 #define BLEND_ONLY_4_NORMAL(temp_type, type, max, chroma_offset,x,y) \
543 { \
544         const int bits = sizeof(type) * 8; \
545         temp_type blend_opacity = (temp_type)(alpha * ((temp_type)1 << bits) + 0.5); \
546         temp_type blend_transparency = ((temp_type)1 << bits) - blend_opacity; \
548         col = y * 4; \
549         type* in_row = (type*)incoming->get_rows()[x]; \
550         type* output = (type*)outgoing->get_rows()[x]; \
552         output[col] = ((temp_type)in_row[col] * blend_opacity + output[col] * blend_transparency) >> bits; \
553         output[col+1] = ((temp_type)in_row[col+1] * blend_opacity + output[col+1] * blend_transparency) >> bits; \
554         output[col+2] = ((temp_type)in_row[col+2] * blend_opacity + output[col+2] * blend_transparency) >> bits; \
558 // components is always 3
559 #define BLEND_ONLY_3_NORMAL(temp_type, type, max, chroma_offset,x,y) \
560 { \
561         const int bits = sizeof(type) * 8; \
562         temp_type blend_opacity = (temp_type)(alpha * ((temp_type)1 << bits) + 0.5); \
563         temp_type blend_transparency = ((temp_type)1 << bits) - blend_opacity; \
565         col = y * 3; \
566         type* in_row = (type*)incoming->get_rows()[x]; \
567         type* output = (type*)outgoing->get_rows()[x]; \
569         output[col] = ((temp_type)in_row[col] * blend_opacity + output[col] * blend_transparency) >> bits; \
570         output[col+1] = ((temp_type)in_row[col+1] * blend_opacity + output[col+1] * blend_transparency) >> bits; \
571         output[col+2] = ((temp_type)in_row[col+2] * blend_opacity + output[col+2] * blend_transparency) >> bits; \
574 /* opacity is defined as opacity of incoming frame */
575 #define BLEND(x,y,total) \
576 { \
577         float pixel_opacity = (float)opacity / total; \
578         float alpha = pixel_opacity; \
579         float pixel_transparency = 1.0 - pixel_opacity; \
580         int col; \
582         if (pixel_opacity > 0.0) \
583         { \
584                 switch(incoming->get_color_model()) \
585                 { \
586                 case BC_RGB_FLOAT: \
587                 { \
588                         float  *in_row = (float*)incoming->get_rows()[x]; \
589                         float *out_row = (float*)outgoing->get_rows()[x]; \
590                         col = y * 3; \
591                         out_row[col] = in_row[col] * pixel_opacity + \
592                                 out_row[col] * pixel_transparency; \
593                         out_row[col+1] = in_row[col+1] * pixel_opacity + \
594                                 out_row[col+1] * pixel_transparency; \
595                         out_row[col+2] = in_row[col+2] * pixel_opacity + \
596                                 out_row[col+2] * pixel_transparency; \
597                         break; \
598                 } \
599                 case BC_RGBA_FLOAT: \
600                 { \
601                         float  *in_row = (float*)incoming->get_rows()[x]; \
602                         float *out_row = (float*)outgoing->get_rows()[x]; \
603                         col = y * 4; \
604                         out_row[col] = in_row[col] * pixel_opacity + \
605                                 out_row[col] * pixel_transparency; \
606                         out_row[col+1] = in_row[col+1] * pixel_opacity + \
607                                 out_row[col+1] * pixel_transparency; \
608                         out_row[col+2] = in_row[col+2] * pixel_opacity + \
609                                 out_row[col+2] * pixel_transparency; \
610                         break; \
611                 } \
612                 case BC_RGB888: \
613                         BLEND_ONLY_3_NORMAL(uint32_t, unsigned char, 0xff, 0,x,y); \
614                         break; \
615                 case BC_YUV888: \
616                         BLEND_ONLY_3_NORMAL(int32_t, unsigned char, 0xff, 0x80,x,y); \
617                         break; \
618                 case BC_RGBA8888: \
619                         BLEND_ONLY_4_NORMAL(uint32_t, unsigned char, 0xff, 0,x,y); \
620                         break; \
621                 case BC_YUVA8888: \
622                         BLEND_ONLY_4_NORMAL(int32_t, unsigned char, 0xff, 0x80,x,y); \
623                         break; \
624                 case BC_RGB161616: \
625                         BLEND_ONLY_3_NORMAL(uint64_t, uint16_t, 0xffff, 0,x,y); \
626                         break; \
627                 case BC_YUV161616: \
628                         BLEND_ONLY_3_NORMAL(int64_t, uint16_t, 0xffff, 0x8000,x,y); \
629                         break; \
630                 case BC_RGBA16161616: \
631                         BLEND_ONLY_4_NORMAL(uint64_t, uint16_t, 0xffff, 0,x,y); \
632                         break; \
633                 case BC_YUVA16161616: \
634                         BLEND_ONLY_4_NORMAL(int64_t, uint16_t, 0xffff, 0x8000,x,y); \
635                         break; \
636       } \
637    } \
640 int ShapeWipeMain::process_realtime(VFrame *incoming, VFrame *outgoing)
642         unsigned char *pattern_row;
643         int col_offset;
644         unsigned char threshold;
645         unsigned char value;
646         int i,j,k;
647         int opacity;
649         load_configuration();
651         int w = incoming->get_w();
652         int h = incoming->get_h();
654         if (strncmp(filename,last_read_filename,BCTEXTLEN)
655                 || preserve_aspect != last_preserve_aspect)
656         {
657                 reset_pattern_image();
658         }
660         if (!pattern_image) {
661                 read_pattern_image(w, h);
662                 strncpy(last_read_filename, filename, BCTEXTLEN);
663                 last_preserve_aspect = preserve_aspect;
664         }
666         if (!pattern_image)
667         {
668                 fprintf(stderr, "Shape Wipe: cannot load shape %s\n", filename);
669                 return 0;
670         }
672         if (direction)
673         {
674                 threshold = (unsigned char)(
675                                 (float)PluginClient::get_source_position() /
676                                 (float)PluginClient::get_total_len() * 
677                                 (float)(max_value - min_value))
678                         + min_value;
679         }
680         else
681         {
682                 threshold = (unsigned char)((max_value - min_value) - ( 
683                                 (float)PluginClient::get_source_position() /
684                                 (float)PluginClient::get_total_len() * 
685                                 (float)(max_value - min_value)))
686                         + min_value;
687         }
689         if (antialias)
690         {
691                 if (direction)
692                 {
693                         /* Top left corner */
694                         opacity = 0;
695                         COMPARE1(0,0);
696                         COMPARE1(0,1);
697                         COMPARE1(1,0);
698                         COMPARE1(1,1);
699                         BLEND(0,0,4.0);
701                         /* Top edge */
702                         for (k = 1; k < w-1; k++)
703                         {
704                                 opacity = 0;
705                                 COMPARE1(0,k-1);
706                                 COMPARE1(0,k);
707                                 COMPARE1(0,k+1);
708                                 COMPARE1(1,k-1);
709                                 COMPARE1(1,k);
710                                 COMPARE1(1,k+1);
711                                 BLEND(0,k,6.0);
712                         }
714                         /* Top right corner */
715                         opacity = 0;
716                         COMPARE1(0,w-1);
717                         COMPARE1(0,w-2);
718                         COMPARE1(1,w-1);
719                         COMPARE1(1,w-2);
720                         BLEND(0,w-1,4.0);
722                         /* Left edge */
723                         for (j = 1; j < h-1; j++)
724                         {
725                                 opacity = 0;
726                                 COMPARE1(j-1,0);
727                                 COMPARE1(j,0);
728                                 COMPARE1(j+1,0);
729                                 COMPARE1(j-1,1);
730                                 COMPARE1(j,1);
731                                 COMPARE1(j+1,1);
732                                 BLEND(j,0,6.0);
733                         }
735                         /* Middle */
736                         for (j = 1; j < h-1; j++)
737                         {
738                                 for (k = 1; k < w-1; k++)
739                                 {
740                                         opacity = 0;
741                                         COMPARE1(j-1,k-1);
742                                         COMPARE1(j,k-1);
743                                         COMPARE1(j+1,k-1);
744                                         COMPARE1(j-1,k);
745                                         COMPARE1(j,k);
746                                         COMPARE1(j+1,k);
747                                         COMPARE1(j-1,k+1);
748                                         COMPARE1(j,k+1);
749                                         COMPARE1(j+1,k+1);
750                                         BLEND(j,k,9.0);
751                                 }
752                         }
754                         /* Right edge */
755                         for (j = 1; j < h-1; j++)
756                         {
757                                 opacity = 0;
758                                 COMPARE1(j-1,w-1);
759                                 COMPARE1(j,w-1);
760                                 COMPARE1(j+1,w-1);
761                                 COMPARE1(j-1,w-2);
762                                 COMPARE1(j,w-2);
763                                 COMPARE1(j+1,w-2);
764                                 BLEND(j,w-1,6.0);
765                         }
767                         /* Bottom left corner */
768                         opacity = 0;
769                         COMPARE1(h-1,0);
770                         COMPARE1(h-1,1);
771                         COMPARE1(h-2,0);
772                         COMPARE1(h-2,1);
773                         BLEND(h-1,0,4.0);
775                         /* Bottom edge */
776                         for (k = 1; k < w-1; k++)
777                         {
778                                 opacity = 0;
779                                 COMPARE1(h-1,k-1);
780                                 COMPARE1(h-1,k);
781                                 COMPARE1(h-1,k+1);
782                                 COMPARE1(h-2,k-1);
783                                 COMPARE1(h-2,k);
784                                 COMPARE1(h-2,k+1);
785                                 BLEND(h-1,k,6.0);
786                         }
788                         /* Bottom right corner */
789                         opacity = 0;
790                         COMPARE1(h-1,w-1);
791                         COMPARE1(h-1,w-2);
792                         COMPARE1(h-2,w-1);
793                         COMPARE1(h-2,w-2);
794                         BLEND(h-1,w-1,4.0);
795                 }
796                 else
797                 {
798                         /* Top left corner */
799                         opacity = 0;
800                         COMPARE2(0,0);
801                         COMPARE2(0,1);
802                         COMPARE2(1,0);
803                         COMPARE2(1,1);
804                         BLEND(0,0,4.0);
806                         /* Top edge */
807                         for (k = 1; k < w-1; k++)
808                         {
809                                 opacity = 0;
810                                 COMPARE2(0,k-1);
811                                 COMPARE2(0,k);
812                                 COMPARE2(0,k+1);
813                                 COMPARE2(1,k-1);
814                                 COMPARE2(1,k);
815                                 COMPARE2(1,k+1);
816                                 BLEND(0,k,6.0);
817                         }
819                         /* Top right corner */
820                         opacity = 0;
821                         COMPARE2(0,w-1);
822                         COMPARE2(0,w-2);
823                         COMPARE2(1,w-1);
824                         COMPARE2(1,w-2);
825                         BLEND(0,w-1,4.0);
827                         /* Left edge */
828                         for (j = 1; j < h-1; j++)
829                         {
830                                 opacity = 0;
831                                 COMPARE2(j-1,0);
832                                 COMPARE2(j,0);
833                                 COMPARE2(j+1,0);
834                                 COMPARE2(j-1,1);
835                                 COMPARE2(j,1);
836                                 COMPARE2(j+1,1);
837                                 BLEND(j,0,6.0);
838                         }
840                         /* Middle */
841                         for (j = 1; j < h-1; j++)
842                         {
843                                 for (k = 1; k < w-1; k++)
844                                 {
845                                         opacity = 0;
846                                         COMPARE2(j-1,k-1);
847                                         COMPARE2(j,k-1);
848                                         COMPARE2(j+1,k-1);
849                                         COMPARE2(j-1,k);
850                                         COMPARE2(j,k);
851                                         COMPARE2(j+1,k);
852                                         COMPARE2(j-1,k+1);
853                                         COMPARE2(j,k+1);
854                                         COMPARE2(j+1,k+1);
855                                         BLEND(j,k,9.0);
856                                 }
857                         }
859                         /* Right edge */
860                         for (j = 1; j < h-1; j++)
861                         {
862                                 opacity = 0;
863                                 COMPARE2(j-1,w-1);
864                                 COMPARE2(j,w-1);
865                                 COMPARE2(j+1,w-1);
866                                 COMPARE2(j-1,w-2);
867                                 COMPARE2(j,w-2);
868                                 COMPARE2(j+1,w-2);
869                                 BLEND(j,w-1,6.0);
870                         }
872                         /* Bottom left corner */
873                         opacity = 0;
874                         COMPARE2(h-1,0);
875                         COMPARE2(h-1,1);
876                         COMPARE2(h-2,0);
877                         COMPARE2(h-2,1);
878                         BLEND(h-1,0,4.0);
880                         /* Bottom edge */
881                         for (k = 1; k < w-1; k++)
882                         {
883                                 opacity = 0;
884                                 COMPARE2(h-1,k-1);
885                                 COMPARE2(h-1,k);
886                                 COMPARE2(h-1,k+1);
887                                 COMPARE2(h-2,k-1);
888                                 COMPARE2(h-2,k);
889                                 COMPARE2(h-2,k+1);
890                                 BLEND(h-1,k,6.0);
891                         }
893                         /* Bottom right corner */
894                         opacity = 0;
895                         COMPARE2(h-1,w-1);
896                         COMPARE2(h-1,w-2);
897                         COMPARE2(h-2,w-1);
898                         COMPARE2(h-2,w-2);
899                         BLEND(h-1,w-1,4.0);
900                 }
901         }
902         else
903         {
904                 switch(incoming->get_color_model())
905                 {
906                 case BC_RGB_FLOAT:
907                         SHAPEWIPE(float, 3)
908                         break;
909                 case BC_RGB888:
910                 case BC_YUV888:
911                         SHAPEWIPE(unsigned char, 3)
912                         break;
913                 case BC_RGBA_FLOAT:
914                         SHAPEWIPE(float, 4)
915                         break;
916                 case BC_RGBA8888:
917                 case BC_YUVA8888:
918                         SHAPEWIPE(unsigned char, 4)
919                         break;
920                 case BC_RGB161616:
921                 case BC_YUV161616:
922                         SHAPEWIPE(uint16_t, 3)
923                         break;
924                 case BC_RGBA16161616:
925                 case BC_YUVA16161616:
926                         SHAPEWIPE(uint16_t, 4)
927                         break;
928                 }
929         }
930         return 0;