File Coverage

blib/lib/SDLx/Sprite/Animated.pm
Criterion Covered Total %
statement 181 192 94.2
branch 54 70 77.1
condition 5 12 41.6
subroutine 32 34 94.1
pod 0 18 0.0
total 272 326 83.4


line stmt bran cond sub pod time code
1             package SDLx::Sprite::Animated;
2 2     2   2003 use strict;
  2         5  
  2         49  
3 2     2   10 use warnings;
  2         5  
  2         58  
4              
5 2     2   8 use Scalar::Util 'refaddr';
  2         4  
  2         79  
6 2     2   10 use SDL;
  2         9  
  2         104  
7 2     2   15 use SDL::Video;
  2         5  
  2         290  
8 2     2   329 use SDL::Rect;
  2         4  
  2         693  
9 2     2   345 use SDLx::Sprite;
  2         5  
  2         68  
10 2     2   14 use SDLx::Validate;
  2         5  
  2         80  
11              
12 2     2   13 use base 'SDLx::Sprite';
  2         4  
  2         4142  
13              
14             our $VERSION = 2.548;
15              
16             # inside out
17             my %_ticks;
18             my %_width;
19             my %_height;
20             my %_step_x;
21             my %_step_y;
22             my %_offset_x;
23             my %_offset_y;
24             my %_type;
25             my %_max_loops;
26             my %_ticks_per_frame;
27             my %_current_frame;
28             my %_current_loop;
29             my %_sequences;
30             my %_sequence;
31             my %_started;
32             my %_direction;
33              
34             sub new {
35 10     10 0 12427 my ( $class, %options ) = @_;
36              
37 10         17 my ( $w, $h );
38 10 100 33     39 if ( exists $options{clip} ) {
    100          
    50          
39 2         10 ( $w, $h ) = ( $options{clip}->w, $options{clip}->h );
40             } elsif ( exists $options{rect} ) {
41 7         29 ( $w, $h ) = ( $options{rect}->w, $options{rect}->h );
42             } elsif ( exists $options{width} && $options{height} ) {
43 1         3 ( $w, $h ) = ( $options{width}, $options{height} );
44             }
45              
46 10         48 my $self = $class->SUPER::new(%options);
47              
48 10         28 $self->_store_geometry( $w, $h );
49              
50 10 100       27 $self->step_x( exists $options{step_x} ? $options{step_x} : $self->clip->w );
51 10 100       24 $self->step_y( exists $options{step_y} ? $options{step_y} : $self->clip->h );
52 10 100       31 $_offset_x{ refaddr $self} = exists $options{clip} ? $options{clip}->x : 0;
53 10 100       28 $_offset_y{ refaddr $self} = exists $options{clip} ? $options{clip}->y : 0;
54              
55 10 100       29 $self->max_loops( exists $options{max_loops} ? $options{max_loops} : 0 );
56 10 50       28 $self->ticks_per_frame( exists $options{ticks_per_frame} ? $options{ticks_per_frame} : 1 );
57 10 100       28 $self->type( exists $options{type} ? $options{type} : 'circular' );
58              
59 10 100       15 if ( exists $options{sequences} ) {
60 1         4 $_sequences{ refaddr $self} = $options{sequences};
61             } else {
62 9         21 $self->_init_default_sequence();
63             }
64 10 100       25 $self->sequence( $options{sequence} ) if exists $options{sequence};
65              
66 10         21 $_ticks{ refaddr $self} = 0;
67 10         18 $_direction{ refaddr $self} = 1;
68              
69 10         34 return $self;
70             }
71              
72             sub DESTROY {
73 10     10   1868 my $self = shift;
74 10         23 delete $_ticks{ refaddr $self};
75 10         22 delete $_width{ refaddr $self};
76 10         19 delete $_height{ refaddr $self};
77 10         16 delete $_step_x{ refaddr $self};
78 10         16 delete $_step_y{ refaddr $self};
79 10         20 delete $_offset_x{ refaddr $self};
80 10         17 delete $_offset_y{ refaddr $self};
81 10         24 delete $_type{ refaddr $self};
82 10         18 delete $_max_loops{ refaddr $self};
83 10         20 delete $_ticks_per_frame{ refaddr $self};
84 10         21 delete $_current_frame{ refaddr $self};
85 10         17 delete $_current_loop{ refaddr $self};
86 10         35 delete $_sequences{ refaddr $self};
87 10         23 delete $_sequence{ refaddr $self};
88 10         19 delete $_started{ refaddr $self};
89 10         45 delete $_direction{ refaddr $self};
90             }
91              
92             sub load {
93 1     1 0 1022 my $self = shift;
94 1         2 my $image = shift;
95 1         7 $self->SUPER::load($image);
96 1         3 $self->_restore_geometry;
97 1         3 $self->_init_default_sequence;
98 1         4 return $self;
99             }
100              
101             sub _init_default_sequence {
102 10     10   12 my $self = shift;
103              
104 10         21 my $max_x = int( ( $self->surface->w - $_offset_x{ refaddr $self} ) / $self->step_x );
105 10         23 my $max_y = int( ( $self->surface->h - $_offset_y{ refaddr $self} ) / $self->step_y );
106              
107 10         13 my @sequence;
108 10         23 foreach my $y ( 0 .. $max_y - 1 ) {
109 27         35 foreach my $x ( 0 .. $max_x - 1 ) {
110 129         193 push @sequence, [ $x, $y ];
111             }
112             }
113 10         32 $_sequences{ refaddr $self} = { 'default' => \@sequence };
114 10         22 $self->sequence('default');
115             }
116              
117             sub _store_geometry {
118 10     10   22 my ( $self, $w, $h ) = @_;
119              
120 10         29 $_width{ refaddr $self} = $w;
121 10         22 $_height{ refaddr $self} = $h;
122              
123 10         24 $self->_restore_geometry;
124             }
125              
126             sub _restore_geometry {
127 13     13   15 my $self = shift;
128              
129 13 50       45 $self->clip->w( $_width{ refaddr $self} ) if exists $_width{ refaddr $self};
130 13 50       41 $self->clip->h( $_height{ refaddr $self} ) if exists $_height{ refaddr $self};
131 13 50       46 $self->rect->w( $_width{ refaddr $self} ) if exists $_width{ refaddr $self};
132 13 50       37 $self->rect->h( $_height{ refaddr $self} ) if exists $_height{ refaddr $self};
133             }
134              
135             sub step_y {
136 20     20 0 33 my ( $self, $step_y ) = @_;
137              
138 20 100       30 if ($step_y) {
139 10         20 $_step_y{ refaddr $self} = $step_y;
140             }
141              
142 20         44 return $_step_y{ refaddr $self};
143             }
144              
145             sub step_x {
146 20     20 0 31 my ( $self, $step_x ) = @_;
147              
148 20 100       38 if ($step_x) {
149 10         21 $_step_x{ refaddr $self} = $step_x;
150             }
151              
152 20         51 return $_step_x{ refaddr $self};
153             }
154              
155             sub type {
156 10     10 0 23 my ( $self, $type ) = @_;
157              
158 10 50       22 if ($type) {
159 10         25 $_type{ refaddr $self} = lc $type;
160             }
161              
162 10         20 return $_type{ refaddr $self};
163             }
164              
165             sub max_loops {
166 10     10 0 15 my $self = shift;
167              
168 10 50       26 if (@_) {
169 10         21 $_max_loops{ refaddr $self} = shift;
170             }
171              
172 10         20 return $_max_loops{ refaddr $self};
173             }
174              
175             sub ticks_per_frame {
176 10     10 0 16 my ( $self, $ticks ) = @_;
177              
178 10 50       19 if ($ticks) {
179 10         16 $_ticks_per_frame{ refaddr $self} = $ticks;
180             }
181              
182 10         18 return $_ticks_per_frame{ refaddr $self};
183             }
184              
185             sub current_frame {
186 22     22 0 629 my ( $self, $frame ) = @_;
187 22         103 return $_current_frame{ refaddr $self};
188             }
189              
190             sub current_loop {
191 12     12 0 557 my ($self) = @_;
192 12         67 return $_current_loop{ refaddr $self };
193             }
194              
195             sub set_sequences {
196 6     6 0 44 my ( $self, %sequences ) = @_;
197              
198             # TODO: Validate sequences.
199 6         26 $_sequences{ refaddr $self} = \%sequences;
200              
201 6         11 return $self;
202             }
203              
204             sub sequence {
205 18     18 0 2110 my ( $self, $sequence ) = @_;
206 18         38 my $me = refaddr $self;
207              
208 18 50       36 if ($sequence) {
209              
210 18 50       35 if ( !defined( $_sequences{ $me }{$sequence} ) ) {
211 0         0 warn 'Unknown sequence: ', $sequence;
212 0         0 return;
213             }
214 18         28 $_sequence{ $me } = $sequence;
215 18         23 $_current_frame{ $me } = 1;
216 18         23 $_current_loop{ $me } = 1;
217 18         21 $_direction{ $me } = 1;
218 18         36 $self->_update_clip;
219             }
220              
221 18         36 return $_sequence{ $me };
222             }
223              
224             sub _sequence {
225 181     181   215 my $self = shift;
226 181         612 return $_sequences{ refaddr $self}{ $_sequence{ refaddr $self} };
227             }
228              
229             sub _frame {
230 72     72   82 my $self = shift;
231 72         103 return $self->_sequence->[ $_current_frame{ refaddr $self} - 1 ];
232             }
233              
234             sub next {
235 43     43 0 35820 my $self = shift;
236 43         105 my $me = refaddr $self;
237              
238 43 50       51 return if @{ $self->_sequence } == 1;
  43         158  
239              
240 43 100 100     135 return if $_max_loops{ $me } && $_current_loop{ $me } > $_max_loops{ $me };
241              
242 42         72 my $next_frame = ( $_current_frame{ $me } - 1 + $_direction{ $me } ) % @{ $self->_sequence };
  42         75  
243              
244 42 100       88 if ( $next_frame == 0 ) {
245 10 100       30 $_current_loop{ $me }++ if $_type{ $me } eq 'circular';
246              
247 10 100       19 if ( $_type{ $me } eq 'reverse' ) {
248              
249 3 100       7 if ( $_direction{ $me } == 1 ) {
250 2         3 $next_frame = @{ $self->_sequence } - 2;
  2         6  
251             } else {
252 1         2 $_current_loop{ $me }++;
253             }
254              
255 3         5 $_direction{ $me } *= -1;
256             }
257             }
258 42         63 $_current_frame{ $me } = $next_frame + 1;
259              
260 42         93 $self->_update_clip;
261              
262 42         79 return $self;
263             }
264              
265             sub previous {
266 11     11 0 2171 my $self = shift;
267              
268 11 50 33     46 return if $_max_loops{ refaddr $self} && $_current_loop{ refaddr $self } > $_max_loops{ refaddr $self};
269              
270 11         24 $_ticks{ refaddr $self} = 0;
271              
272 11 50       15 return if @{ $self->_sequence } == 1;
  11         18  
273              
274 11         79 my $previous_frame = ( $_current_frame{ refaddr $self} - 1 - $_direction{ refaddr $self} ) % @{ $self->_sequence };
  11         22  
275              
276 11 100       23 if ( $previous_frame == 0 ) {
277 5 100       19 if ( $_type{ refaddr $self} eq 'reverse' ) {
278              
279 3 100       10 if ( $_direction{ refaddr $self} == -1 ) {
280 1         3 $previous_frame = 1;
281             }
282              
283 3         5 $_direction{ refaddr $self} *= -1;
284             }
285             }
286 11         23 $_current_frame{ refaddr $self} = $previous_frame + 1;
287              
288 11         26 $self->_update_clip;
289              
290 11         22 return $self;
291             }
292              
293             sub reset {
294 1     1 0 4 my $self = shift;
295              
296 1         6 $self->stop;
297 1         4 $_current_frame{ refaddr $self} = 1;
298 1         6 $self->_update_clip;
299              
300 1         7 return $self;
301             }
302              
303             sub start {
304 0     0 0 0 my $self = shift;
305              
306 0         0 $_started{ refaddr $self} = 1;
307              
308 0         0 return $self;
309             }
310              
311             sub stop {
312 1     1 0 3 my $self = shift;
313              
314 1         6 $_started{ refaddr $self} = 0;
315              
316 1         3 return $self;
317             }
318              
319             sub _update_clip {
320 72     72   98 my $self = shift;
321              
322 72         172 my $clip = $self->clip;
323 72         135 my $frame = $self->_frame;
324              
325 72         282 $clip->x( $_offset_x{ refaddr $self} + $frame->[0] * $_step_x{ refaddr $self} );
326 72         222 $clip->y( $_offset_y{ refaddr $self} + $frame->[1] * $_step_y{ refaddr $self} );
327             }
328              
329             sub alpha_key {
330 2     2 0 10 my $self = shift;
331 2         9 $self->SUPER::alpha_key(@_);
332 2         4 $self->_restore_geometry;
333 2         7 return $self;
334             }
335              
336             sub draw {
337 0     0 0   my ( $self, $surface ) = @_;
338              
339 0           $surface = SDLx::Validate::surface($surface);
340              
341 0           $_ticks{ refaddr $self}++;
342 0 0 0       $self->next if $_started{ refaddr $self} && $_ticks{ refaddr $self} % $_ticks_per_frame{ refaddr $self} == 0;
343              
344 0           SDL::Video::blit_surface( $self->surface, $self->clip, $surface, $self->rect );
345              
346 0           return $self;
347             }
348              
349             1;
350