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; |