Perl x Open Food Facts Hackathon: Paris, France - May 24-25 Learn more

package SWF::Builder;
use strict;
use Carp;
our $VERSION = '0.12';
my $SFTAG = SWF::Element::Tag::ShowFrame->new;
sub new {
my $class = shift;
my $self = bless {
_root =>
SWF::Builder::Movie::Root->new(@_)
}, $class;
$self;
}
sub AUTOLOAD {
my $self = shift;
my $sub = $SWF::Builder::AUTOLOAD;
return if $sub =~/::DESTROY$/;
$sub =~ s/.+:://;
croak "Can't locate object method \"$sub\" via package \"".ref($self).'" (perhaps you forgot to load "'.ref($self).'"?)' unless $self->{_root}->can($sub);
$self->{_root}->$sub(@_);
}
sub DESTROY {
my $r = shift->{_root};
$r->_destroy if defined $r;
}
####
use Carp;
sub new {
my ($class, $parent, $lower) = @_;
my $self = bless {
_parent => $parent,
_depth => SWF::Element::Depth->new,
}, $class;
$lower ||= $self;
$self->{_lower} = $lower;
$self->{_upper} = $lower->{_upper}||$lower;
$lower->{_upper} = $lower->{_upper}{_lower} = $self;
return $self;
}
sub set_depth {
my ($self, $n) = @_;
$self->{_depth}->configure($n);
$n+1;
}
sub _destroy {
my $self = shift;
while(my $lower = $self->{_lower}) {
%$self = ();
$self = $lower;
}
}
####
@SWF::Builder::_FrameList::ISA = qw/ SWF::Element::Array::TAGARRAY /;
sub pack {
my ($self, $stream) = @_;
for my $frame (@$self) {
for my $tag (@$frame) {
$tag->pack($stream);
}
$SFTAG->pack($stream);
}
SWF::Element::Tag::End->new->pack($stream);
}
####
use Carp;
sub new {
my $class = shift;
my $self = bless {
_frame_list => SWF::Builder::_FrameList->new,
_streamsoundf => 0,
}, $class;
$self->{_depth_list} = SWF::Builder::Depth->new($self);
$self;
}
sub new_shape {
shift->_new_character(SWF::Builder::Character::Shape::Def->new);
}
sub new_static_text {
shift->_new_character(SWF::Builder::Character::Text::Def->new(@_));
}
*new_text = \&new_static_text;
sub new_dynamic_text {
my $s =shift;
my $q = SWF::Builder::Character::DynamicText->new(@_);
$s->_new_character($q);
}
sub new_edit_text {
shift->_new_character(SWF::Builder::Character::EditText::Def->new(@_));
}
sub new_html_text {
shift->_new_character(SWF::Builder::Character::HTMLText->new(@_));
}
sub new_text_area {
shift->_new_character(SWF::Builder::Character::TextArea->new(@_));
}
sub new_input_field {
shift->_new_character(SWF::Builder::Character::InputField->new(@_));
}
sub new_password_field {
shift->_new_character(SWF::Builder::Character::PasswordField->new(@_));
}
sub new_font {
shift->_new_character(SWF::Builder::Character::Font::Def->new(@_));
}
sub new_movie_clip {
shift->_new_character(SWF::Builder::Character::MovieClip::Def->new);
}
*new_mc = \&new_movie_clip;
sub new_gradient {
SWF::Builder::Gradient->new;
}
sub new_jpeg {
my $self = shift;
unshift @_, 'JPEGFile' if @_==1;
$self->_new_character(SWF::Builder::Character::Bitmap::JPEG->new(@_));
}
sub new_bitmap {
my $self = shift;
$self->_new_character(SWF::Builder::Character::Bitmap::Lossless->new(@_));
}
sub new_sound {
my $self = shift;
$self->_new_character(SWF::Builder::Character::Sound::Def->new(@_));
}
sub import_asset {
my $self = shift;
my $i = $self->_new_character(SWF::Builder::Character::Imported->new(@_));
$self->_depends($i, 1); # set 'depend' flag by force because imported assets may be used only by ActionScript.
$i;
}
#sub shape_tween {
# require SWF::Builder::Character::MorphShape;
#
# my $self = shift;
#
# $self->_new_character(SWF::Builder::Character::MorphShape->shape_tween(@_));
#}
sub _new_character {
my ($parent, $self) = @_;
push @{$parent->{_root}{_character_IDs}}, $self->{ID};
push @{$parent->{_root}{_to_destroy}}, $self;
$self->{_parent} = $parent;
$self->{_root} = $parent->{_root};
return $self;
}
sub frame_action {
my ($self, $frame) = @_;
my $tag = SWF::Element::Tag::DoAction->new;
push @{$self->{_frame_list}[$frame-1]}, $tag;
my $action = SWF::Builder::ActionScript->new(Version => $self->{_root}{_version});
$tag->Actions($action->{_actions});
return $action;
}
sub frame_label {
my ($self, $frame, $label, $anchor) = @_;
utf2bin($label);
push @{$self->{_frame_list}[$frame-1]}, SWF::Element::Tag::FrameLabel->new(Name => $label, NamedAnchorFlag => $anchor);
}
sub set_depth {
my $self = shift;
my $n = 1;
my $depth = $self->{_depth_list}{_upper};
while ($depth != $self->{_depth_list}) {
$n = $depth->set_depth($n);
$depth = $depth->{_upper};
}
}
sub _destroy {
my $self = shift;
$self->{_depth_list}->_destroy;
%$self = ();
}
####
use Carp;
use base qw/ SWF::Builder::Movie SWF::Builder::ExElement::Color::AddColor /;
sub new {
my $class = shift;
my %param = @_;
my $version = $param{Version} || 6;
my $self = $class->SUPER::new;
$self->{_file} = SWF::File->new
( undef,
Version => $version,
FrameRate => $param{FrameRate},
FrameSize => [ map {$_*20} @{$param{FrameSize}}],
);
$self->{_backgroundcolor} = $param{BackgroundColor};
$self->{_root} = $self;
$self->{_character_IDs} = [];
$self->{_ID_seed} = 1;
$self->{_target_path} = '_root';
$self->{_to_destroy} = [];
$self->{_version} = $version;
$self->{_as_namespace} = {};
$self->{_init_action} = undef;
$self->{_auto_namer} = 1;
$self->_init_is_alpha;
$self;
}
sub pack {
my ($self, $stream) = @_;
for my $id (@{$self->{_character_IDs}}) {
$id->configure(undef);
}
$self->set_depth;
$self->{_frame_list}->pack($stream);
}
our $EMPTY_SPRITE = SWF::Element::Tag::DefineSprite->new(ControlTags=>[SWF::Element::Tag::ShowFrame->new]);
sub save {
my ($self, $file) = @_;
my $stream = $self->{_file};
$self->{_is_alpha}->configure(0);
SWF::Element::Tag::SetBackgroundColor->new(BackgroundColor => $self->_add_color($self->{_backgroundcolor}))->pack($stream);
$self->{_ID_seed} = 1;
if (keys %{$self->{_as_namespace}}) {
my $action = SWF::Builder::ActionScript->new(Version => $self->{_version});
$action->compile(_create_namespace_initializer('', '_global', $self->{_as_namespace}));
# SWF::Element::Tag::DefineSprite->new(SpriteID => $self->{_ID_seed}, ControlTags=>[SWF::Element::Tag::ShowFrame->new])->pack($stream);
SWF::Element::Tag::DefineSprite->new(SpriteID => $self->{_ID_seed}, ControlTags=>[SWF::Element::Tag::End->new])->pack($stream);
SWF::Element::Tag::DoInitAction->new(SpriteID => $self->{_ID_seed}++, Actions => $action->{_actions})->pack($stream);
}
if ($self->{_init_action}) {
# SWF::Element::Tag::DefineSprite->new(SpriteID => $self->{_ID_seed}, ControlTags=>[SWF::Element::Tag::ShowFrame->new])->pack($stream);
SWF::Element::Tag::DefineSprite->new(SpriteID => $self->{_ID_seed}, ControlTags=>[SWF::Element::Tag::End->new])->pack($stream);
SWF::Element::Tag::DoInitAction->new(SpriteID => $self->{_ID_seed}++, Actions => $self->{_init_action}{_actions})->pack($stream);
}
$self->pack($stream);
$stream->close($file);
}
sub _create_namespace_initializer {
my ($as, $pname, $namehash) = @_;
for my $name (keys %$namehash) {
my $n = "$pname.$name";
$as .= <<ASEND;
//#
if (eval('$n') == undefined) {
set('$n', new Object());
}
ASEND
#//
$as = _create_namespace_initializer($as, $n, $namehash->{$name});
}
return $as;
}
sub use_namespace {
my $self = shift;
while (my $name = shift) {
my $ns = $self->{_as_namespace};
my @n = split /\./, $name;
for my $n (@n) {
$ns->{$n} ||= {};
$ns = $ns->{$n};
}
}
}
sub init_action {
my $self = shift;
$self->{_init_action} ||= SWF::Builder::ActionScript->new(Version => $self->{_version});
}
sub auto_namer {
shift->{_auto_namer} = 1;
}
sub no_namer {
shift->{_auto_namer} = 0;
}
sub _depends {
my ($self, $char, $frame) = @_;
push @{$self->{_frame_list}[$frame-1]}, $char;
}
sub FrameRate {
my $self = shift;
$self->{_file}->FrameRate(@_);
}
sub FrameSize {
my $self = shift;
$self->{_file}->FrameSize(map {$_*20} @_);
}
sub BackgroundColor {
my ($self, $color) = @_;
$self->{_backgroundcolor} = $color if defined $color;
$self->{_backgroundcolor};
}
sub compress {
my $self = shift;
$self->{_file}->compress(@_);
}
sub _destroy {
my $self = shift;
undef $self->{_root};
for (@{$self->{_to_destroy}}) {
$_->_destroy;
}
$self->SUPER::_destroy;
}
# Autoload methods go after =cut, and are processed by the autosplit program.
1;
__END__
# Below is the stub of documentation for your module. You better edit it!
=head1 NAME
SWF::Builder - Create SWF movie.
=head1 SYNOPSIS
use SWF::Builder;
my $movie = SWF::Builder->new
( FrameRate => 15,
FrameSize => [0, 0, 400, 400],
BackgroundColor => 'ffffff'
);
my $shape = $movie->new_shape # red triangle.
->fillstyle('ff0000')
->linestyle(1, '000000')
->moveto(0,-11)
->lineto(10,6)
->lineto(-10,6)
->lineto(0,-11);
my $instance = $shape->place;
for (my $x = 0; $x < 400; $x++) {
$instance->rotate(15)->moveto($x,200);
}
$movie->save('triangle.swf');
=head1 DESCRIPTION
I<SWF::Builder> is a wrapper of I<SWF::File>.
It provides an easy way to create SWF6 movie.
The SWF movie consists a dictionary of character definitions and
a hierarchical group of movie clips. You create a movie by following steps:
=over 4
=item 1.
create a '_root' movie by SWF::Builder->new.
=item 2.
define characters such as shapes, fonts, texts, movieclips, and so on,
by $movie->new_XXX methods.
=item 3.
get a display instance of the character by $char->place.
=item 4.
move, scale, and rotate the instance every frame.
=item 5.
repeat 2-4 if you need.
=item 6.
save the whole movie by $movie->save.
=back
=head2 '_root' movie
The '_root' movie is a top of the movie clip hierarchy.
It has properties of the whole SWF movie. It also has character constructors
and other methods for movie. See the next section for details.
=over 4
=item $movie = SWF::Builder->new( [FrameRate => $rate, FrameSize => [$xmin, $ymin, $xmax, $ymax], BackgroundColor => $color, Version => $version] )
creates a new '_root' movie. It can take three optional named parameters.
FrameRate is a frame count per second. FrameSize is a box size of frames,
which is an array reference of the coordinates of top-left and bottom-right
of the box in pixels.
BackgroundColor is a background color of the movie. It can take a six-figure
hexadecimal string, an array reference of R, G, and B value, an array reference
of named parameters such as [Red => 255], and SWF::Element::RGB object.
Version is a version number of the SWF. It must be 6 and above.
=item $movie->FrameRate( $rate )
=item $movie->FrameSize( $xmin, $ymin, $xmax, $ymax )
=item $movie->BackgroundColor( $color )
sets the property. See SWF::Builder->new.
=item $movie->use_namespace( $namespace )
prepares ActionScript namespace. For example, $movie->use_namespace('SWF.Builder')
creates movieclip which is initialized by
if (_global.SWF == undefined) {
_global.SWF = new Object();
}
if (_global.SWF.Builder == undefined) {
_global.SWF.Builder = new Object();
}
and place the movieclip on the first frame of the root movie.
=item $movie->init_action
returns SWF::Builder::ActionScript object to initialize the root movie.
=item $movie->no_namer
=item $movie->auto_namer
deactivate/reactivate auto_namer. Default is active.
Auto_namer gives a suitable name to characters and display instances automatically
when their name is referred before being named explicitly.
=item $movie->save( $filename )
saves the movie.
=back
=head2 Character constructors
=over 4
=item $mc->new_shape
returns a new shape (type: Shape).
See L<SWF::Builder::Character::Shape> for the detail.
=item $mc->new_font( $fontfile [, $fontname] )
returns a new font (type: Font).
$fontfile is a font file name. It should be a TrueType font file (ttf/ttc).
Optional $fontname is a font name referred by HTMLs in dynamic texts.
It is taken from the TrueType file if not defined.
See L<SWF::Builder::Character::Font> for the detail.
=item $mc->new_static_text( [$font, $text] )
returns a new static text (type: Text), which is fixed by authoring and cannot
be changed at playing time.
See L<SWF::Builder::Character::Text> for the detail of a text.
=item $mc->new_edit_text( [$font, $text] )
=item $mc->new_dynamic_text( [$font, $text] )
=item $mc->new_html_text( [$html] )
=item $mc->new_text_area( $width, $height )
=item $mc->new_input_field( [$length] )
=item $mc->new_password_field( [$length] )
return new dynamic editable text variations (type: EditText).
See L<SWF::Builder::Character::EditText> for the detail.
=item $mc->new_movie_clip
=item $mc->new_mc
returns a new movie clip (type: MovieClip).
See L<SWF::Builder::Character::MovieClip> for the detail.
=item $mc->new_gradient
returns a new gradient object.
See L<SWF::Builder::Gradient> and L<SWF::Builder::Character::Shape> for the detail.
=item $mc->new_jpeg( ... )
returns a new JPEG bitmap (type: Bitmap).
See L<SWF::Builder::Character::Bitmap> for the detail.
=item $mc->new_bitmap( $obj [, $type] )
returns a new lossless bitmap (type: Bitmap).
See L<SWF::Builder::Character::Bitmap> for the detail.
=item $mc->import_asset( $url, $name [, $type] )
returns a character which will be imported from the SWF movie in $url by $name.
This method does not actually import a character but put
an 'ImportAssets' tag on the movie.
$type is a type of the character, such as 'Shape', 'Font', 'Text',
'EditText', 'MovieClip', and 'Bitmap'. Default is 'MovieClip'.
This method does not check whether a character which has $name and $type is actually exported.
=back
=head2 Other methods for movies
Here describe other common methods for root movie and movie clips.
=over 4
=item $mc->frame_action( $frame )
returns SWF::Builder::ActionScript object for a frame action.
=item $mc->frame_label( $frame, $label [, $anchorflag] )
gives $label to $frame to which ActionScripts can refer.
If the $anchorflag is set to 1, it is accessible as an
HTML anchor.
=back
=head2 Characters
Display and export methods of characters are described here.
See SWF::Builder::Character::* for details of type-specific methods.
=over 4
=item $disp_i = $char->place( [ MovieClip => $mc, Frame => $frame, above => $another_i, below => $another_i, clip_with => $mask_i ] )
places the character on $mc and returns the display instance.
It can take four optional named parameters.
'MovieClip'(MC) is a parent movie clip on which the character is placed.
The movie clip must be under the same root movie with the movie clip
in which the character is defined.
If MC is not set, the character is placed on the movie clip in which
it is defined.
'Frame' is a first frame number on which the character is placed. Default is 1.
You can set the relative depth of the new instance by 'above' and 'below'.
'clip_with' is a mask instance with which the character is clipped.
Font character can't place itself.
=item $mask_i = $char->place_as_mask( [ MovieClip => $mc, Frame => $frame, above => $another_i, below => $another_i ] )
places the character on $mc as the mask object (clipping layer)
and returns the mask instance.
It can take optional parameters as same as 'place' method except 'clip_with'.
You can handle the mask instance as same as the display instance.
Only Shape and Text characters can place_as_mask.
=item $char->name( [$name] )
gives a name to the character, exports the character, and returns the name.
ActionScripts can refer the character as $name.
Other movies can import the character by $name.
When you call 'name' method without a parameter, the method simply returns the name
of the character. If the character is not named yet and auto_namer is active,
auto_namer gives a suitable name.
=item $char->export_asset( [$name] )
Same as $char->name.
=back
=head2 Display instances
It is necessary to get the display instance to show the defined character.
Each instance has its own timeline tied to the parent movie clip
and the current frame to move, to rotate, etc.
=over 4
=item $disp_i->name( [$name] )
gives a name to the display instance to which ActionScripts can refer and returns the name.
When you call 'name' method without a parameter, the method simply returns the name
of the character. If the character is not named yet and auto_namer is active,
auto_namer gives a suitable name.
=item $fobj = $disp_i->frame( $frame )
gets the specified frame object of the display instance and sets the current
frame of the display instance to $frame.
Moving, rotating, scaling, and any other matrix transforming of
the display instance are handled in a frame by frame via a frame object.
When a frame object is not specified, the 'current frame object' kept by the
display item is used. The current frame is counted up after it is used.
=item $fobj/$disp_i->moveto( $x, $y )
moves the display item to ($x, $y) at the (current) frame.
=item $fobj/$disp_i->r_moveto( $dx, $dy )
moves the display item relatively ( to (former X + $x, former Y + $y)).
=item $fobj/$disp_i->scale( $xscale [, $yscale] )
magnifies/reduces the display item at the (current) frame.
The scaling effect is accumulative.
=item $fobj/$disp_i->rotate( $angle )
rotates the display item at the (current) frame.
The rotation angle is accumulative.
=item $fobj/$disp_i->reset
resets the rotation and scaling at the (current) frame.
=item $fobj/$disp_i->remove
removes the display instance from the parent movie clip at the (current) frame.
=item $fobj/$disp_i->matrix
gets the transformation matrix of the display instance at the (current) frame.
The result is an SWF::Element::MATRIX object.
=item $fobj/$disp_i->frame_action
=item $fobj/$disp_i->frame_label( $label [, $anchorflag] )
same as those for movie clips, setting the frame number to that of the frame object.
=back
=head1 COPYRIGHT
Copyright 2003 Yasuhiro Sasama (ySas), <ysas@nmt.ne.jp>
This library is free software; you can redistribute it
and/or modify it under the same terms as Perl itself.
=cut