4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
22 #include "bcdisplayinfo.h"
25 #include "filesystem.h"
28 #include "overlayframe.h"
31 #include "shapewipe.h"
38 #define SHAPE_SEARCHPATH "/shapes"
39 #define DEFAULT_SHAPE "circle"
41 REGISTER_PLUGIN(ShapeWipeMain)
43 ShapeWipeW2B::ShapeWipeW2B(ShapeWipeMain *plugin,
44 ShapeWipeWindow *window,
49 plugin->direction == 0,
52 this->plugin = plugin;
53 this->window = window;
56 int ShapeWipeW2B::handle_event()
59 plugin->direction = 0;
60 window->right->update(0);
61 plugin->send_configure_change();
65 ShapeWipeB2W::ShapeWipeB2W(ShapeWipeMain *plugin,
66 ShapeWipeWindow *window,
71 plugin->direction == 1,
74 this->plugin = plugin;
75 this->window = window;
78 int ShapeWipeB2W::handle_event()
81 plugin->direction = 1;
82 window->left->update(0);
83 plugin->send_configure_change();
87 ShapeWipeAntiAlias::ShapeWipeAntiAlias(ShapeWipeMain *plugin,
88 ShapeWipeWindow *window,
91 : BC_CheckBox (x,y,plugin->antialias, _("Anti-aliasing"))
93 this->plugin = plugin;
94 this->window = window;
97 int ShapeWipeAntiAlias::handle_event()
99 plugin->antialias = get_value();
100 plugin->send_configure_change();
104 ShapeWipePreserveAspectRatio::ShapeWipePreserveAspectRatio(ShapeWipeMain *plugin,
105 ShapeWipeWindow *window,
108 : BC_CheckBox (x, y, plugin->preserve_aspect, _("Preserve shape aspect ratio"))
110 this->plugin = plugin;
111 this->window = window;
114 int ShapeWipePreserveAspectRatio::handle_event()
116 plugin->preserve_aspect = get_value();
117 plugin->send_configure_change();
126 ShapeWipeTumble::ShapeWipeTumble(ShapeWipeMain *client,
127 ShapeWipeWindow *window,
132 this->client = client;
133 this->window = window;
136 int ShapeWipeTumble::handle_up_event()
138 window->prev_shape();
142 int ShapeWipeTumble::handle_down_event()
144 window->next_shape();
154 ShapeWipeShape::ShapeWipeShape(ShapeWipeMain *client,
155 ShapeWipeWindow *window,
160 : BC_PopupTextBox(window,
168 this->client = client;
169 this->window = window;
172 int ShapeWipeShape::handle_event()
174 strcpy(client->shape_name, get_text());
175 client->send_configure_change();
186 ShapeWipeWindow::ShapeWipeWindow(ShapeWipeMain *plugin)
187 : PluginClientWindow(plugin,
194 this->plugin = plugin;
197 ShapeWipeWindow::~ShapeWipeWindow()
199 shapes.remove_all_objects();
204 void ShapeWipeWindow::create_objects()
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;
212 plugin->init_shapes();
213 for(int i = 0; i < plugin->shape_titles.size(); i++)
215 shapes.append(new BC_ListBoxItem(plugin->shape_titles.get(i)));
218 add_subwindow(title = new BC_Title(x, y, _("Direction:")));
219 x += title->get_w() + widget_border;
220 add_subwindow(left = new ShapeWipeW2B(plugin,
224 x += left->get_w() + widget_border;
225 add_subwindow(right = new ShapeWipeB2W(plugin,
230 y += right->get_h() + widget_border;
233 add_subwindow(title = new BC_Title(x, y, _("Shape:")));
234 x += title->get_w() + widget_border;
236 // add_subwindow(filename_widget = new
237 // ShapeWipeFilename(plugin,
243 // add_subwindow(new ShapeWipeBrowseButton(
250 shape_text = new ShapeWipeShape(plugin,
256 shape_text->create_objects();
257 x += shape_text->get_w() + widget_border;
258 add_subwindow(new ShapeWipeTumble(plugin,
263 y += shape_text->get_h() + widget_border;
265 ShapeWipeAntiAlias *anti_alias;
266 add_subwindow(anti_alias = new ShapeWipeAntiAlias(
271 y += anti_alias->get_h() + widget_border;
272 ShapeWipePreserveAspectRatio *aspect_ratio;
273 add_subwindow(aspect_ratio = new ShapeWipePreserveAspectRatio(
278 y += aspect_ratio->get_h() + widget_border;
284 void ShapeWipeWindow::next_shape()
286 for(int i = 0; i < plugin->shape_titles.size(); i++)
288 if(!strcmp(plugin->shape_titles.get(i), plugin->shape_name))
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);
297 client->send_configure_change();
300 void ShapeWipeWindow::prev_shape()
302 for(int i = 0; i < plugin->shape_titles.size(); i++)
304 if(!strcmp(plugin->shape_titles.get(i), plugin->shape_name))
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);
313 client->send_configure_change();
320 ShapeWipeMain::ShapeWipeMain(PluginServer *server)
321 : PluginVClient(server)
325 last_read_filename[0] = '\0';
326 strcpy(shape_name, DEFAULT_SHAPE);
328 pattern_image = NULL;
329 min_value = (unsigned char)255;
330 max_value = (unsigned char)0;
333 last_preserve_aspect = 0;
334 shapes_initialized = 0;
337 ShapeWipeMain::~ShapeWipeMain()
339 reset_pattern_image();
340 shape_paths.remove_all_objects();
341 shape_titles.remove_all_objects();
344 const char* ShapeWipeMain::plugin_title() { return _("Shape Wipe"); }
345 int ShapeWipeMain::is_transition() { return 1; }
346 int ShapeWipeMain::uses_gui() { return 1; }
348 NEW_WINDOW_MACRO(ShapeWipeMain, ShapeWipeWindow);
352 void ShapeWipeMain::save_data(KeyFrame *keyframe)
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);
363 output.tag.set_title("/SHAPEWIPE");
365 output.terminate_string();
368 void ShapeWipeMain::read_data(KeyFrame *keyframe)
372 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
374 while(!input.read_tag())
376 if(input.tag.title_is("SHAPEWIPE"))
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);
387 void ShapeWipeMain::init_shapes()
389 if(!shapes_initialized)
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);
397 for(int i = 0; i < fs.total_files(); i++)
399 FileItem *file_item = fs.get_entry(i);
400 if(!file_item->get_is_dir())
402 shape_paths.append(strdup(file_item->get_path()));
403 char *ptr = strdup(file_item->get_name());
404 char *ptr2 = strrchr(ptr, '.');
406 shape_titles.append(ptr);
410 shapes_initialized = 1;
415 int ShapeWipeMain::load_configuration()
417 read_data(get_prev_keyframe(get_source_position()));
421 int ShapeWipeMain::read_pattern_image(int new_frame_width, int new_frame_height)
439 frame_width = new_frame_width;
440 frame_height = new_frame_height;
442 // Convert name to filename
443 for(int i = 0; i < shape_paths.size(); i++)
445 if(!strcmp(shape_titles.get(i), shape_name))
447 strcpy(filename, shape_paths.get(i));
452 FILE *fp = fopen(filename, "rb");
458 fread(header, 1, 8, fp);
459 is_png = !png_sig_cmp(header, 0, 8);
467 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
476 /* Tell libpng we already checked the first 8 bytes */
477 png_set_sig_bytes(png_ptr, 8);
479 info_ptr = png_create_info_struct(png_ptr);
482 png_destroy_read_struct(&png_ptr, NULL, NULL);
487 end_info = png_create_info_struct(png_ptr);
490 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
495 png_init_io(png_ptr, fp);
496 png_read_info(png_ptr, info_ptr);
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);
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)
511 /* Convert 16 bit data to 8 bit */
512 if (bit_depth == 16) png_set_strip_16(png_ptr);
514 /* Expand to 1 pixel per byte if necessary */
515 if (bit_depth < 8) png_set_packing(png_ptr);
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);
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++)
526 image[row] = (png_byte*)malloc(sizeof(png_byte)*width*pixel_width);
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);
534 png_read_image(png_ptr, image);
535 png_read_end(png_ptr, end_info);
537 double row_factor, col_factor;
538 double row_offset = 0.5, col_offset = 0.5; // for rounding
540 if (preserve_aspect && aspect_w != 0 && aspect_h != 0)
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;
547 row_factor = col_factor;
548 row_factor *= aspect_h/(double)(frame_height-1);
549 col_factor *= aspect_w/(double)(frame_width-1);
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;
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);
562 for (scaled_row = 0; scaled_row < frame_height; scaled_row++)
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++)
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;
577 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
579 /* Deallocate the original image as it is no longer needed */
580 for (row = 0; row < (int)height; row++)
588 void ShapeWipeMain::reset_pattern_image()
591 if (pattern_image != NULL)
593 for (row = 0; row < frame_height; row++)
595 free (pattern_image[row]);
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
604 #define SHAPEWIPE(type, components) \
607 type **in_rows = (type**)incoming->get_rows(); \
608 type **out_rows = (type**)outgoing->get_rows(); \
613 for(j = 0; j < h; j++) \
615 in_row = (type*) in_rows[j]; \
616 out_row = (type*)out_rows[j]; \
617 pattern_row = pattern_image[j]; \
620 for(k = 0; k < w; k++) \
622 value = pattern_row[k]; \
623 if ((direction == 0 && value >= threshold) || \
624 (direction == 1 && value <= threshold)) \
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]; \
632 col_offset += components; \
637 #define COMPARE1(x,y) \
639 if (pattern_image[x][y] <= threshold) opacity++; \
642 #define COMPARE2(x,y) \
644 if (pattern_image[x][y] >= threshold) opacity++; \
647 // components is always 4
648 #define BLEND_ONLY_4_NORMAL(temp_type, type, max, chroma_offset,x,y) \
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; \
655 type* in_row = (type*)incoming->get_rows()[x]; \
656 type* output = (type*)outgoing->get_rows()[x]; \
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; \
664 // components is always 3
665 #define BLEND_ONLY_3_NORMAL(temp_type, type, max, chroma_offset,x,y) \
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; \
672 type* in_row = (type*)incoming->get_rows()[x]; \
673 type* output = (type*)outgoing->get_rows()[x]; \
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; \
680 /* opacity is defined as opacity of incoming frame */
681 #define BLEND(x,y,total) \
683 float pixel_opacity = (float)opacity / total; \
684 float alpha = pixel_opacity; \
685 float pixel_transparency = 1.0 - pixel_opacity; \
688 if (pixel_opacity > 0.0) \
690 switch(incoming->get_color_model()) \
694 float *in_row = (float*)incoming->get_rows()[x]; \
695 float *out_row = (float*)outgoing->get_rows()[x]; \
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; \
705 case BC_RGBA_FLOAT: \
707 float *in_row = (float*)incoming->get_rows()[x]; \
708 float *out_row = (float*)outgoing->get_rows()[x]; \
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; \
719 BLEND_ONLY_3_NORMAL(uint32_t, unsigned char, 0xff, 0,x,y); \
722 BLEND_ONLY_3_NORMAL(int32_t, unsigned char, 0xff, 0x80,x,y); \
725 BLEND_ONLY_4_NORMAL(uint32_t, unsigned char, 0xff, 0,x,y); \
728 BLEND_ONLY_4_NORMAL(int32_t, unsigned char, 0xff, 0x80,x,y); \
731 BLEND_ONLY_3_NORMAL(uint64_t, uint16_t, 0xffff, 0,x,y); \
734 BLEND_ONLY_3_NORMAL(int64_t, uint16_t, 0xffff, 0x8000,x,y); \
736 case BC_RGBA16161616: \
737 BLEND_ONLY_4_NORMAL(uint64_t, uint16_t, 0xffff, 0,x,y); \
739 case BC_YUVA16161616: \
740 BLEND_ONLY_4_NORMAL(int64_t, uint16_t, 0xffff, 0x8000,x,y); \
746 int ShapeWipeMain::process_realtime(VFrame *incoming, VFrame *outgoing)
748 unsigned char *pattern_row;
750 unsigned char threshold;
756 load_configuration();
758 int w = incoming->get_w();
759 int h = incoming->get_h();
761 if (strncmp(filename, last_read_filename, BCTEXTLEN) ||
762 strncmp(shape_name, current_name, BCTEXTLEN) ||
763 preserve_aspect != last_preserve_aspect)
765 reset_pattern_image();
770 read_pattern_image(w, h);
771 strncpy(last_read_filename, filename, BCTEXTLEN);
772 last_preserve_aspect = preserve_aspect;
776 strncpy(last_read_filename, filename, BCTEXTLEN);
777 strncpy(current_name, shape_name, BCTEXTLEN);
778 last_preserve_aspect = preserve_aspect;
781 fprintf(stderr, _("Shape Wipe: cannot load shape %s\n"), filename);
782 last_read_filename[0] = 0;
789 threshold = (unsigned char)(
790 (float)PluginClient::get_source_position() /
791 (float)PluginClient::get_total_len() *
792 (float)(max_value - min_value))
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)))
808 /* Top left corner */
817 for (k = 1; k < w-1; k++)
829 /* Top right corner */
838 for (j = 1; j < h-1; j++)
851 for (j = 1; j < h-1; j++)
853 for (k = 1; k < w-1; k++)
870 for (j = 1; j < h-1; j++)
882 /* Bottom left corner */
891 for (k = 1; k < w-1; k++)
903 /* Bottom right corner */
913 /* Top left corner */
922 for (k = 1; k < w-1; k++)
934 /* Top right corner */
943 for (j = 1; j < h-1; j++)
956 for (j = 1; j < h-1; j++)
958 for (k = 1; k < w-1; k++)
975 for (j = 1; j < h-1; j++)
987 /* Bottom left corner */
996 for (k = 1; k < w-1; k++)
1008 /* Bottom right corner */
1019 switch(incoming->get_color_model())
1026 SHAPEWIPE(unsigned char, 3)
1033 SHAPEWIPE(unsigned char, 4)
1037 SHAPEWIPE(uint16_t, 3)
1039 case BC_RGBA16161616:
1040 case BC_YUVA16161616:
1041 SHAPEWIPE(uint16_t, 4)