File Coverage

lib/CSS/SpriteBuilder/Sprite.pm
Criterion Covered Total %
statement 85 128 66.4
branch 18 62 29.0
condition 9 35 25.7
subroutine 19 22 86.3
pod 2 13 15.3
total 133 260 51.1


line stmt bran cond sub pod time code
1             package CSS::SpriteBuilder::Sprite;
2              
3 4     4   33 use warnings;
  4         8  
  4         112  
4 4     4   21 use strict;
  4         7  
  4         123  
5 4     4   21 use File::Glob ":glob";
  4         7  
  4         2158  
6 4     4   23 use File::Spec;
  4         7  
  4         86  
7 4     4   17 use Cwd ();
  4         7  
  4         54  
8 4     4   17 use CSS::SpriteBuilder::Constants;
  4         5  
  4         342  
9 4     4   1622 use CSS::SpriteBuilder::SpriteItem;
  4         10  
  4         140  
10 4     4   26 use base 'CSS::SpriteBuilder::ImageDriver::Auto';
  4         6  
  4         7668  
11              
12 0     0 0 0 sub max { $_[ $_[0] < $_[1] ] }
13              
14             sub new {
15 3     3 1 18 my ($class, @args) = @_;
16              
17 3         42 my $self = $class->SUPER::new(
18             source_dir => undef,
19             source_images => undef,
20             target_file => undef,
21              
22             image_quality => DEF_IMAGE_QUALITY,
23             max_image_size => DEF_MAX_IMAGE_SIZE,
24             max_image_width => DEF_MAX_IMAGE_WIDTH,
25             max_image_height => DEF_MAX_IMAGE_HEIGHT,
26             max_sprite_width => DEF_MAX_SPRITE_WIDTH,
27             max_sprite_height => DEF_MAX_SPRITE_HEIGHT,
28             margin => DEF_MARGIN,
29             transparent_color => undef,
30             is_background => 0,
31             layout => DEF_LAYOUT,
32             css_selector_prefix => DEF_CSS_SELECTOR_PREFIX,
33             css_url_prefix => '',
34             is_add_timestamp => 1,
35              
36             _sprite_no => 1,
37             _repeated_images => [],
38             _sprites => {},
39             _x => 0,
40             _y => 0,
41              
42             @args,
43             );
44              
45 3 50       7 unless ( grep { $_ eq $self->{layout} } @{ LAYOUT_LIST() } ) {
  9         26  
  3         8  
46 0         0 die "Invalid 'layout' parameter: $self->{layout}";
47             }
48              
49 3         10 return $self;
50             }
51              
52 6     6 0 114 sub target_file { sprintf( $_[0]->{target_file}, $_[0]->{_sprite_no} ) }
53              
54             sub _init {
55 0     0   0 my ($self, $image) = @_;
56              
57 0 0       0 die "The 'target_file' parameter is not defined" unless $self->{target_file};
58 0 0       0 die "The 'source_images' parameter is not defined" unless $self->{source_images};
59              
60 0         0 $self->SUPER::_init($image);
61              
62 0         0 $self->{_x} = $self->{_y} = 0;
63              
64 0         0 return;
65             }
66              
67             sub build {
68 3     3 0 6 my ($self) = @_;
69              
70 3         8 my $images = $self->search_images();
71 3         6 foreach my $image (@$images) {
72 3         9 $self->process_image($image);
73             }
74              
75 3         10 $self->write();
76              
77 3         50 return $self->{_sprites};
78             }
79              
80             sub search_images {
81 3     3 0 4 my ($self) = @_;
82              
83 3         5 my $save_dir;
84 3 50       17 if ( $self->{source_dir} ) {
85 3         27 $save_dir = Cwd::getcwd();
86 3 50       89 chdir $self->{source_dir} or die "Cannot chdir to $self->{source_dir}: $!\n";
87             }
88              
89 3 50 33     23 my @images = grep {
      50        
      33        
      33        
      33        
      33        
      33        
90 3         5 ( !$self->{max_image_size} || -s $_->source_file() <= $self->{max_image_size} )
91             && ( !$self->{max_image_width} || $_->width() <= $self->{max_image_width} )
92             && ( !$self->{max_image_height} || $_->height() <= $self->{max_image_height} )
93             && ( $_->width() <= $self->{max_sprite_width} && $_->height <= $self->{max_sprite_height}
94             or die "The image resolution is bigger than maximum configured value for '". $_->source_file() ."'"
95             )
96             } map {
97 3         7 my $image_opts = $_;
98 3         17 map {
99 3         100 CSS::SpriteBuilder::SpriteItem->new(
100             %$image_opts,
101             source_file => $_,
102             )
103             } bsd_glob( $image_opts->{file}, File::Glob::GLOB_ERR );
104 3         5 } @{ $self->{source_images} };
105              
106 3 50       14 if ($save_dir) {
107 3 50       64 chdir $save_dir or die "Cannot chdir to $save_dir: $!\n";
108             }
109              
110 3 50       13 return wantarray ? @images: \@images;
111             }
112              
113             sub process_image {
114 3     3 0 4 my ($self, $image) = @_;
115              
116 3         10 $self->allocate_space($image);
117              
118 3         8 $self->align_image($image);
119              
120 3         8 $self->save_info($image);
121              
122 3 50 33     18 $self->save_repeated_images($image)
123             if $self->{layout} ne PACKED_LAYOUT && $image->{is_repeat};
124              
125 3         9 $self->post_process($image);
126             }
127              
128             sub align_image {
129 3     3 0 4 my ($self, $image) = @_;
130              
131 3 50       10 if ( $self->is_blank() ) {
132 3         9 $self->reset($image);
133             }
134             else {
135 0 0       0 $self->{_x} += $self->{margin} if $self->{_x};
136              
137 0         0 $self->extent(
138             max($self->width(), $self->{_x} + $image->width() ),
139             max($self->height(), $self->{_y} + $image->height()),
140             );
141              
142 0         0 $self->composite( $image, $self->{_x}, $self->{_y} );
143             }
144              
145 3         6 return;
146             }
147              
148             sub allocate_space {
149 3     3 0 4 my ($self, $image) = @_;
150              
151 3 50       25 return if $self->is_blank();
152              
153 0 0       0 my $x_margin = $self->{_x} == 0 ? 0 : $self->{margin};
154              
155 0 0 0     0 if (
156             $self->{layout} eq VERTICAL_LAYOUT
157             || ($self->{_x} + $image->width() + $x_margin) > $self->{max_sprite_width}
158             ) {
159 0         0 $self->{_x} = 0;
160 0         0 $self->{_y} = $self->height() + $self->{margin};
161             }
162              
163 0 0 0     0 if (
      0        
164             $self->{_y} > 0 && (
165             $self->{layout} eq HORIZONTAL_LAYOUT
166             || ($self->{_y} + $image->height() + $self->{margin}) > $self->{max_sprite_height}
167             )
168             ) {
169 0         0 $self->write();
170             }
171              
172 0         0 return;
173             }
174              
175             sub save_info {
176 3     3 0 5 my ($self, $image) = @_;
177              
178 3         7 my (undef, undef, $filename) = File::Spec->splitpath( $self->target_file() );
179 3         9 my $sprite_image = $self->{css_url_prefix} . $filename;
180              
181 3 50       14 $sprite_image .= '?' . time() if $self->{is_add_timestamp};
182              
183 3 50 33     6 push @{ $self->{_sprites}{$sprite_image} }, {
  3 50       21  
    0          
    50          
184             image => $image->source_file(),
185             width => $image->width(),
186             height => $image->height(),
187             x => $self->{_x},
188             y => $self->{_y},
189             selector => $image->get_css_selector( $self->{css_selector_prefix} ),
190             is_background => (
191             defined $image->is_background()
192             ? $image->is_background()
193             : $self->{is_background}
194             ) ? 1 : 0,
195             repeat => $self->{layout} ne PACKED_LAYOUT && $image->is_repeat()
196             ? $self->{layout} eq HORIZONTAL_LAYOUT ? 'y' : 'x'
197             : 'no'
198             ,
199             };
200              
201 3         9 return;
202             }
203              
204             sub save_repeated_images {
205 0     0 0 0 my ($self, $image) = @_;
206              
207 0         0 push @{ $self->{_repeated_images} }, {
  0         0  
208             image => $image,
209             x => $self->{_x},
210             y => $self->{_y},
211             };
212              
213 0         0 return;
214             }
215              
216             sub post_process {
217 3     3 0 6 my ($self, $image) = @_;
218              
219 3 50       16 $self->{ $self->{layout} eq VERTICAL_LAYOUT ? '_y' : '_x' } += $image->width;
220              
221 3         12 return;
222             }
223              
224             sub write {
225 3     3 1 5 my ($self) = @_;
226              
227 3 50       9 return if $self->is_blank();
228              
229 3         26 $self->process_repeated_images();
230              
231 3 50       10 $self->set_transparent_color( $self->{transparent_color} )
232             if $self->{transparent_color};
233              
234 3         30 $self->set_quality( $self->{image_quality} );
235              
236 3         7 $self->SUPER::write( $self->target_file() );
237              
238 3         5 $self->{_sprite_no}++;
239              
240 3         11 $self->reset();
241              
242 3         6 return;
243             }
244              
245             sub process_repeated_images {
246 3     3 0 5 my ($self) = @_;
247              
248 3 50       5 return unless scalar @{ $self->{_repeated_images} };
  3         15  
249              
250 0 0         my $meter = $self->{layout} eq VERTICAL_LAYOUT ? 'width' : 'height';
251 0           my $original_sprite_size = $self->$meter();
252 0 0         my $max_sprite_size = $self->{ $self->{layout} eq VERTICAL_LAYOUT ? 'max_sprite_width' : 'max_sprite_height' };
253 0           my @images_size = reverse sort map { $_->{image}->$meter() } @{ $self->{_repeated_images} };
  0            
  0            
254 0           my $max_image_size = shift @images_size;
255              
256 0 0         for (
257             my $k = int($original_sprite_size / $max_image_size) + ($original_sprite_size % $max_image_size ? 1 : 0);
258             $k * $max_image_size <= $max_sprite_size;
259             $k++
260             ) {
261 0           my $extented_sprite_size = $k * $max_image_size;
262 0 0         unless (grep { $extented_sprite_size % $_ } @images_size) {
  0            
263 0 0         $self->extent(
264             $meter eq 'width'
265             ? ( $extented_sprite_size, $self->height() )
266             : ( $self->width() , $extented_sprite_size )
267             );
268              
269 0           foreach my $image_info (@{ $self->{_repeated_images} }) {
  0            
270 0           my $image = $image_info->{image};
271 0           my $size = $image->$meter();
272              
273 0 0         while (($image_info->{ $self->{layout} eq VERTICAL_LAYOUT ? 'x' : 'y' } += $size) < $extented_sprite_size) {
274 0           $self->composite( $image, $image_info->{x}, $image_info->{y} )
275             }
276             }
277              
278 0           return;
279             }
280             }
281              
282 0           $self->{_repeated_images} = [];
283              
284 0           die "Can't process repeated images";
285             }
286              
287             1;