line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Image::Math::Constrain; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=pod |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 NAME |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
Image::Math::Constrain - Scaling math used in image size constraining (such |
8
|
|
|
|
|
|
|
as thumbnails) |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 SYNOPSIS |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
use Image::Math::Constrain; |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# Create the math object |
15
|
|
|
|
|
|
|
my $math = Image::Math::Constrain->new(64, 48); |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# Get the scaling values for an arbitrary image |
18
|
|
|
|
|
|
|
my $Image = My::Image->load("myimage.jpg"); |
19
|
|
|
|
|
|
|
my $scaling = $math->constrain($Image->width, $Image->height); |
20
|
|
|
|
|
|
|
die "Don't need to scale" if $scaling->{scale} == 1; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# Returns the three values as a list when called in array contect |
23
|
|
|
|
|
|
|
my ($width, $height, $scale) = $math->constrain(800, 600); |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
# There are lots of different ways to specify the constrain |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# Constrain based on width only |
28
|
|
|
|
|
|
|
$math = Image::Math::Constrain->new(100, 0); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Constrain based on height only |
31
|
|
|
|
|
|
|
$math = Image::Math::Constrain->new(0, 100); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Or you can provide the two values by ARRAY ref |
34
|
|
|
|
|
|
|
$math = Image::Math::Constrain->new( [ 64, 48 ] ); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# Constrain height and width by the same value |
37
|
|
|
|
|
|
|
$math = Image::Math::Constrain->new(100); |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
# Various string forms to do the same thing |
40
|
|
|
|
|
|
|
$math = Image::Math::Constrain->new('constrain(800x600)'); |
41
|
|
|
|
|
|
|
$math = Image::Math::Constrain->new('300x200'); |
42
|
|
|
|
|
|
|
$math = Image::Math::Constrain->new('300w200h'); |
43
|
|
|
|
|
|
|
$math = Image::Math::Constrain->new('100w'); |
44
|
|
|
|
|
|
|
$math = Image::Math::Constrain->new('100h'); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
# Serialises back to 'constrain(800x600)'. |
47
|
|
|
|
|
|
|
# You can use this to store the object if you wish. |
48
|
|
|
|
|
|
|
my $string = $math->as_string; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
=head1 DESCRIPTION |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
There are a number of different modules and systems that constrain image |
53
|
|
|
|
|
|
|
sizes, such as thumbnailing. Every one of these independantly implement |
54
|
|
|
|
|
|
|
the same logic. That is, given a width and/or height constraint, they |
55
|
|
|
|
|
|
|
check to see if the image is bigger than the constraint, and if so scale |
56
|
|
|
|
|
|
|
the image down proportionally so that it fits withint the constraints. |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Of course, they all do it slightly differnetly, and some do it better |
59
|
|
|
|
|
|
|
than others. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
C has been created specifically to implement |
62
|
|
|
|
|
|
|
this logic once, and implement it properly. Any module or script that |
63
|
|
|
|
|
|
|
does image size constraining or thumbnailing should probably be using |
64
|
|
|
|
|
|
|
this for its math. |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
=head1 METHODS |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=cut |
69
|
|
|
|
|
|
|
|
70
|
2
|
|
|
2
|
|
34646
|
use 5.005; |
|
2
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
77
|
|
71
|
2
|
|
|
2
|
|
11
|
use strict; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
122
|
|
72
|
|
|
|
|
|
|
use overload 'bool' => sub () { 1 }, |
73
|
2
|
|
|
2
|
|
1953
|
'""' => 'as_string'; |
|
2
|
|
|
|
|
1304
|
|
|
2
|
|
|
|
|
14
|
|
74
|
|
|
|
|
|
|
|
75
|
2
|
|
|
2
|
|
177
|
use vars qw{$VERSION}; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
115
|
|
76
|
|
|
|
|
|
|
BEGIN { |
77
|
2
|
|
|
2
|
|
2169
|
$VERSION = '1.02'; |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
##################################################################### |
85
|
|
|
|
|
|
|
# Constructor |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=pod |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head2 new $width, $height |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
-head2 new [ $width, $height ] |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=head1 new $width_and_height |
94
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=head2 new $string |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
The C constructor takes the dimentions to which you wish to |
98
|
|
|
|
|
|
|
constrain and creates a new math object. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
You can feed a number of different height/width pairs to this object, and |
101
|
|
|
|
|
|
|
it will returns the scaling you will need to do to shrink the image down |
102
|
|
|
|
|
|
|
to the constraints, and the final width and height of the image after |
103
|
|
|
|
|
|
|
scaling, at least one of which should match the constraint. |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
A value of zero is used to indicate that a dimension should not be |
106
|
|
|
|
|
|
|
constrained. Thus, C<-Enew(400, 0)> would indicate to constrain the |
107
|
|
|
|
|
|
|
width to 400 pixels, but to ignore the height (only changing it to keep |
108
|
|
|
|
|
|
|
the image proportional). |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
The constraint dimensions can be provided in a number of different |
111
|
|
|
|
|
|
|
formats. See the Synopsis for a quick list of these. To stay |
112
|
|
|
|
|
|
|
compatible with automated constraint generators, you B provide |
113
|
|
|
|
|
|
|
constrains as zero width and zero height, and the math object will not |
114
|
|
|
|
|
|
|
attempt to do any scaling, always returning the input width/height, |
115
|
|
|
|
|
|
|
and a scaling value of 1. |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
Once created, the object is fully Storable and re-usable and does not |
118
|
|
|
|
|
|
|
store any state information from a single calculation run. |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
Returns a new Image::Math::Constrain object, or C if the |
121
|
|
|
|
|
|
|
constraints have been defined wrongly. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
=cut |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
sub new { |
126
|
15
|
50
|
|
15
|
1
|
4224
|
my $class = ref $_[0] ? ref shift : shift; |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Expand a single argument |
129
|
15
|
100
|
|
|
|
45
|
if ( @_ == 1 ) { |
130
|
6
|
50
|
|
|
|
16
|
my $value = defined $_[0] ? shift : return undef; |
131
|
6
|
100
|
66
|
|
|
32
|
if ( ref $value eq 'ARRAY' and @$value == 2 ) { |
132
|
1
|
|
|
|
|
8
|
return $class->new(@$value); |
133
|
|
|
|
|
|
|
} |
134
|
5
|
50
|
|
|
|
13
|
return undef if ref $value; |
135
|
5
|
|
|
|
|
15
|
$value =~ s/\s//g; |
136
|
|
|
|
|
|
|
# constrain(800x600) |
137
|
5
|
100
|
|
|
|
21
|
return $class->new("$1", "$2") if $value =~ /^constrain\((\d+)x(\d+)\)$/; |
138
|
|
|
|
|
|
|
# 800x600 |
139
|
4
|
100
|
|
|
|
29
|
return $class->new("$1", "$2") if $value =~ /^(\d+)x(\d+)$/; |
140
|
|
|
|
|
|
|
# 800w600h |
141
|
2
|
100
|
|
|
|
16
|
return $class->new("$1", "$2") if $value =~ /^(\d+)w(\d+)h$/; |
142
|
|
|
|
|
|
|
# 800w (width only) |
143
|
1
|
50
|
|
|
|
13
|
return $class->new("$1", 0) if $value =~ /^(\d+)w$/; |
144
|
|
|
|
|
|
|
# 800h (height only) |
145
|
0
|
0
|
|
|
|
0
|
return $class->new(0, "$1") if $value =~ /^(\d+)h$/; |
146
|
|
|
|
|
|
|
# 800 (meaning 800x800) |
147
|
0
|
0
|
|
|
|
0
|
if ( $class->_non_neg_int($value) ) { |
148
|
0
|
|
|
|
|
0
|
return $class->new($value, $value); |
149
|
|
|
|
|
|
|
} |
150
|
0
|
|
|
|
|
0
|
return undef; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# The two argument form |
154
|
9
|
50
|
|
|
|
25
|
return undef unless @_ == 2; |
155
|
9
|
|
|
|
|
29
|
my $self = bless {}, $class; |
156
|
9
|
50
|
|
|
|
32
|
$self->{width} = $class->_non_neg_int($_[0]) ? shift : return undef; |
157
|
9
|
50
|
|
|
|
30
|
$self->{height} = $class->_non_neg_int($_[0]) ? shift : return undef; |
158
|
9
|
|
|
|
|
29
|
$self; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=pod |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=head2 width |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
The C method gets the width constraint for the object. |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
Returns a positive integer, or zero if there is no width constraint. |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=cut |
170
|
|
|
|
|
|
|
|
171
|
8
|
|
|
8
|
1
|
9218
|
sub width { $_[0]->{width} } |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=pod |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=head2 height |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
The C method gets the height constrain for the object. |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
Returns a positive integer, or zero if there is no height constraint. |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=cut |
182
|
|
|
|
|
|
|
|
183
|
8
|
|
|
8
|
1
|
39
|
sub height { $_[0]->{height} } |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
=pod |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=head2 as_string |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
The C method returns the constrain rule as a string in the |
190
|
|
|
|
|
|
|
format 'constrain(123x123)'. This string form is also supported by the |
191
|
|
|
|
|
|
|
constructor and so it provides a good way to serialise the constrain |
192
|
|
|
|
|
|
|
rule, should you ever need to do so. |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
As this value is not localisable, it should never really be shown to the |
195
|
|
|
|
|
|
|
user directly, unless you are sure you will never add i18n to your app. |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=cut |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub as_string { |
200
|
2
|
|
|
2
|
1
|
910
|
"constrain($_[0]->{width}x$_[0]->{height})"; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=pod |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=head2 constrain $width, $height |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
The C method takes the height and width of an image and |
208
|
|
|
|
|
|
|
applies the constrain math to them to get the final width, height |
209
|
|
|
|
|
|
|
and the scaling value needed in order to get the your image from |
210
|
|
|
|
|
|
|
it's current size to the final size. |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
The resulting size will be in proportion to the original (it will have |
213
|
|
|
|
|
|
|
the same aspect ratio) and will never be larger than the original. |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
When called in array context, returns the new dimensions and scaling value |
216
|
|
|
|
|
|
|
as a list, as in the following. |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
my ($width, $height, $scale) = $math->constrain(800, 600); |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
When called in scalar context, it returns a reference to a hash containing |
221
|
|
|
|
|
|
|
the keys 'width', 'height', and 'scale'. |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
my $hash = $math->constrain(800, 600); |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
print "New Width : $hash->{width}\n"; |
226
|
|
|
|
|
|
|
print "New Height : $hash->{height}\n"; |
227
|
|
|
|
|
|
|
print "Scaling By : $hash->{scalar}\n"; |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
Having been created correctly, the object will only return an error if the |
230
|
|
|
|
|
|
|
width and height arguments are not correct (are not positive integers). |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
In list context, returns a null list, so all three values will be C. |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
In scalar context, just returns C. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=cut |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub constrain { |
239
|
3
|
|
|
3
|
1
|
1700
|
my $self = shift; |
240
|
3
|
50
|
|
|
|
10
|
my $width = $self->_pos_int($_[0]) ? shift : return; |
241
|
3
|
50
|
|
|
|
8
|
my $height = $self->_pos_int($_[0]) ? shift : return; |
242
|
3
|
50
|
33
|
|
|
11
|
unless ( $self->{width} or $self->{height} ) { |
243
|
0
|
|
|
|
|
0
|
return $self->_ret_val(wantarray, $width, $height, 1); |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# Determine the prefered scaling in both dimensions |
247
|
3
|
100
|
66
|
|
|
20
|
my $w_scale = ($self->{width} and $self->{width} < $width) |
248
|
|
|
|
|
|
|
? ($self->{width} / $width) : 1; |
249
|
3
|
100
|
66
|
|
|
13
|
my $h_scale = ($self->{height} and $self->{height} < $height) |
250
|
|
|
|
|
|
|
? ($self->{height} / $height) : 1; |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# Do we need to scale? |
253
|
3
|
100
|
66
|
|
|
15
|
if ( $w_scale == 1 and $h_scale == 1 ) { |
254
|
1
|
|
|
|
|
4
|
return $self->_ret_val(wantarray, $width, $height, 1); |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# Use the smaller scaling value to scale the dimentions |
258
|
2
|
50
|
|
|
|
4
|
my $scale = $w_scale < $h_scale ? $w_scale : $h_scale; |
259
|
2
|
|
|
|
|
4
|
$width *= $scale; |
260
|
2
|
|
|
|
|
2
|
$height *= $scale; |
261
|
|
|
|
|
|
|
|
262
|
2
|
|
|
|
|
6
|
$self->_ret_val(wantarray, $width, $height, $scale); |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
##################################################################### |
270
|
|
|
|
|
|
|
# Support Methods |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# Validate a non-negative integer |
273
|
|
|
|
|
|
|
sub _non_neg_int { |
274
|
18
|
50
|
|
18
|
|
42
|
my $value = defined $_[1] ? $_[1] : return ''; |
275
|
18
|
50
|
|
|
|
39
|
return '' if ref $value; |
276
|
18
|
100
|
|
|
|
47
|
return 1 if $value eq '0'; |
277
|
15
|
|
|
|
|
427
|
!! $value =~ /^[1-9]\d*$/; |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
# Validate a positive integer |
281
|
|
|
|
|
|
|
sub _pos_int { |
282
|
6
|
50
|
|
6
|
|
10
|
my $value = defined $_[1] ? $_[1] : return ''; |
283
|
6
|
50
|
|
|
|
12
|
return '' if ref $value; |
284
|
6
|
|
|
|
|
25
|
!! $value =~ /^[1-9]\d*$/; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# Return as either a list or HASH reference |
288
|
|
|
|
|
|
|
sub _ret_val { |
289
|
3
|
|
|
3
|
|
3
|
my $self = shift; |
290
|
3
|
100
|
|
|
|
15
|
shift(@_) ? @_ # wantarray |
291
|
|
|
|
|
|
|
: { width => shift, height => shift, scale => shift }; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
1; |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
=pod |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=head1 TO DO |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
- Write more special-case unit tests |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
=head1 SUPPORT |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
Bugs should always be submitted via the CPAN bug tracker |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
L |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
For other issues, contact the maintainer |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=head1 AUTHORS |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
Adam Kennedy Eadamk@cpan.orgE |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
Thank you to Phase N (L) for permitting |
315
|
|
|
|
|
|
|
the open sourcing and release of this distribution. |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=head1 COPYRIGHT |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
Copyright 2004 - 2008 Adam Kennedy. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
This program is free software; you can redistribute |
322
|
|
|
|
|
|
|
it and/or modify it under the same terms as Perl itself. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
The full text of the license can be found in the |
325
|
|
|
|
|
|
|
LICENSE file included with this module. |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=cut |