line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# HUUUUUUUUUUGE heap of undocumented crap I don't have time to |
2
|
|
|
|
|
|
|
# figure out right now. |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
# col is documented |
5
|
|
|
|
|
|
|
# col_all is documented |
6
|
|
|
|
|
|
|
# col_any is documented |
7
|
|
|
|
|
|
|
# col_none is documented |
8
|
|
|
|
|
|
|
# column is documented |
9
|
|
|
|
|
|
|
# column_all is documented |
10
|
|
|
|
|
|
|
# column_any is documented |
11
|
|
|
|
|
|
|
# column_none is documented |
12
|
|
|
|
|
|
|
# region is documented |
13
|
|
|
|
|
|
|
# region_all is documented |
14
|
|
|
|
|
|
|
# region_any is documented |
15
|
|
|
|
|
|
|
# region_none is documented |
16
|
|
|
|
|
|
|
# row is documented |
17
|
|
|
|
|
|
|
# row_all is documented |
18
|
|
|
|
|
|
|
# row_any is documented |
19
|
|
|
|
|
|
|
# row_none is documented |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
package Test::Image; |
22
|
12
|
|
|
12
|
|
12303
|
use base qw(Exporter); |
|
12
|
|
|
|
|
23
|
|
|
12
|
|
|
|
|
1261
|
|
23
|
|
|
|
|
|
|
|
24
|
12
|
|
|
12
|
|
71
|
use strict; |
|
12
|
|
|
|
|
23
|
|
|
12
|
|
|
|
|
501
|
|
25
|
12
|
|
|
12
|
|
72
|
use warnings; |
|
12
|
|
|
|
|
24
|
|
|
12
|
|
|
|
|
516
|
|
26
|
|
|
|
|
|
|
|
27
|
12
|
|
|
12
|
|
63
|
use vars qw($VERSION @STANDARD_PLUGINS @EXPORT); |
|
12
|
|
|
|
|
27
|
|
|
12
|
|
|
|
|
978
|
|
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
$VERSION = "0.02"; |
30
|
|
|
|
|
|
|
|
31
|
12
|
|
|
12
|
|
69
|
use Carp qw(croak); |
|
12
|
|
|
|
|
22
|
|
|
12
|
|
|
|
|
775
|
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# get the Test::Builder singleton |
34
|
12
|
|
|
12
|
|
78
|
use Test::Builder; |
|
12
|
|
|
|
|
19
|
|
|
12
|
|
|
|
|
1079
|
|
35
|
|
|
|
|
|
|
my $tester = Test::Builder->new(); |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# set up the color names |
38
|
|
|
|
|
|
|
my %NameTable; |
39
|
|
|
|
|
|
|
eval "use Graphics::ColorNames"; |
40
|
|
|
|
|
|
|
unless ($@) { |
41
|
|
|
|
|
|
|
tie %NameTable, "Graphics::ColorNames", "X"; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
# use module pluggable, but if that's not installed, just fake it by |
45
|
|
|
|
|
|
|
# installing a subroutine that just returns the known plugins we ship with |
46
|
|
|
|
|
|
|
@STANDARD_PLUGINS = qw( |
47
|
|
|
|
|
|
|
Test::Image::Plugin::TestingImage |
48
|
|
|
|
|
|
|
Test::Image::Plugin::Imlib2 |
49
|
|
|
|
|
|
|
); |
50
|
|
|
|
|
|
|
# TODO - Module::Pluggable insists on compiling MacOS metadata files. |
51
|
|
|
|
|
|
|
# this makes actually hacking on this module incredibly painful. Until |
52
|
|
|
|
|
|
|
# (a) M::P is better, and (b) we actually _have_ any plugins, I don't want |
53
|
|
|
|
|
|
|
# this - tinsam 2006/06 |
54
|
|
|
|
|
|
|
#eval "use Module::Pluggable require => 1"; |
55
|
|
|
|
|
|
|
#if ($@) { |
56
|
12
|
|
|
12
|
|
13355
|
use Test::Image::Plugin::TestingImage; |
|
12
|
|
|
|
|
27
|
|
|
12
|
|
|
|
|
626
|
|
57
|
12
|
|
|
12
|
|
8255
|
use Test::Image::Plugin::Imlib2; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
*plugins = sub { @STANDARD_PLUGINS }; |
59
|
|
|
|
|
|
|
#} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
# for Module::Build::Kwalitee, to explain that plugins is documented |
62
|
|
|
|
|
|
|
# plugins is documented |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# We use number compare for some of the comparison operations with size |
65
|
|
|
|
|
|
|
# we load it with eval to allow these tests to automatically skip |
66
|
|
|
|
|
|
|
# if it's not installed |
67
|
|
|
|
|
|
|
eval "use Number::Compare"; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head1 NAME |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
Test::Image - test an image |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head1 SYNOPSIS |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
use Test::More plan => 1; |
76
|
|
|
|
|
|
|
use Test::Image; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# create a new image tester |
79
|
|
|
|
|
|
|
my $i = Test::Image->new(Image::Imlib2->new("foo.jpg")); |
80
|
|
|
|
|
|
|
ok($i, "image ok"); |
81
|
|
|
|
|
|
|
$i->size(400,300); # (see also $i->width, $i->height) |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# you can check pixels using names, rgb hex, or rgb decimal |
84
|
|
|
|
|
|
|
$i->pixel(10,10,"white"); # 10, 10 is white |
85
|
|
|
|
|
|
|
$i->pixel(10,10,"ffffff"); # 10, 10 is white |
86
|
|
|
|
|
|
|
$i->pixel(10,10,[255,255,255]); # 10, 10 is white |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
$i->pixel_not(10,10,"white"); # 10, 10 isn't white |
89
|
|
|
|
|
|
|
$i->pixel_not(10,10,"ffffff"); # 10, 10 isn't white |
90
|
|
|
|
|
|
|
$i->pixel_not(10,10,[255,255,255]); # 10, 10 isn't white |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
# you can use multiple posibilities too |
93
|
|
|
|
|
|
|
# check pixel is red, white or blue: |
94
|
|
|
|
|
|
|
$i->pixel(10,10,["red", "white", "blue"]); |
95
|
|
|
|
|
|
|
$i->pixel(10,10,["ff0000", "ffffff", "0000ff"]); |
96
|
|
|
|
|
|
|
$i->pixel(10,10,[[255,0,0], [255,255,255], [0,0,255]]); |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# check that the pixel isn't red white or blue: |
99
|
|
|
|
|
|
|
$i->pixel_not(10,10,["red", "white", "blue"]); |
100
|
|
|
|
|
|
|
$i->pixel_not(10,10,["ff0000", "ffffff", "0000ff"]); |
101
|
|
|
|
|
|
|
$i->pixel_not(10,10,[[255,0,0], [255,255,255], [0,0,255]]); |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# row functions (or replace "row" with "col" or "column" for column tests) |
104
|
|
|
|
|
|
|
# you can use multiple colours |
105
|
|
|
|
|
|
|
$i->row(10, "white"); # row 11 is all white |
106
|
|
|
|
|
|
|
$i->row_all(10, "white"); # row 11 is all white |
107
|
|
|
|
|
|
|
$i->row_any(10, "white"); # row 11 has a white pixel |
108
|
|
|
|
|
|
|
$i->row_none(10, "white"); # row 11 has no white pixels |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
# likewise for the whole image (again can use multiple colours) |
111
|
|
|
|
|
|
|
$i->all("white"); # whole image is white |
112
|
|
|
|
|
|
|
$i->any("white"); # whole image has a white pixel |
113
|
|
|
|
|
|
|
$i->none("white"); # whole image has no white pixels |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# finally regions (you can use _all, _any or _none too) |
116
|
|
|
|
|
|
|
# check the 10x10 region starting at 40,30 |
117
|
|
|
|
|
|
|
$i->region(40, 30, "r10", "r10", "white"); |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=head1 DESCRIPTION |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
This modules is a C compatible testing module for testing |
122
|
|
|
|
|
|
|
images. |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Calling the methods of this module prints out Test Anything Protocol |
125
|
|
|
|
|
|
|
output designed to be processed by Test::Harness during a C |
126
|
|
|
|
|
|
|
or C<./Build test>. This module 'plays nice' with other test modules |
127
|
|
|
|
|
|
|
also crafted with Test::Builder. For example, you can happily use this |
128
|
|
|
|
|
|
|
module in conjunction with Test::More, Test::Exception, |
129
|
|
|
|
|
|
|
Test::DatabaseRow, etc, and not have to worry about your test numbers |
130
|
|
|
|
|
|
|
getting confused. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
All methods take an optional description as the last arguement. For example: |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
$i->width(400); # prints "ok 1 - image width" |
135
|
|
|
|
|
|
|
$i->width(400, "1st width"); # prints "ok 2 - 1st width" |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
=head2 Constructing |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=over |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item new($image) |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
The constructor takes one arguement, the image you want to test. By default |
144
|
|
|
|
|
|
|
we only support B and B objects, but you can provide |
145
|
|
|
|
|
|
|
further plugins for other image formats by following the PLUGINS guide below. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=back |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=cut |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub new { |
152
|
|
|
|
|
|
|
my $class = shift; |
153
|
|
|
|
|
|
|
my $newimage = shift; |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
unless (defined $newimage) { |
156
|
|
|
|
|
|
|
croak "No image passed"; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
my $self = bless {}, $class; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
# find a plugin that will handle the image |
162
|
|
|
|
|
|
|
foreach (__PACKAGE__->plugins) { |
163
|
|
|
|
|
|
|
my $plugin_instance = $_->new( $newimage ); |
164
|
|
|
|
|
|
|
next unless $plugin_instance; |
165
|
|
|
|
|
|
|
$self->{image} = $plugin_instance; |
166
|
|
|
|
|
|
|
return $self; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# couldn't find a plugin that matches |
170
|
|
|
|
|
|
|
croak "No plugin found for image passed"; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=head2 Image Size |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
There are various tests that can be used to check the magnitude of the |
176
|
|
|
|
|
|
|
image: |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# check that fred.png is 100 by 300 pixels big |
179
|
|
|
|
|
|
|
my $i = Test::Image->new(Image::Imlib2->new( "fred.png" )); |
180
|
|
|
|
|
|
|
$i->size(100,300) |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
If you have C installed, then you can use non |
183
|
|
|
|
|
|
|
absolute values, and you can use magnitudes. |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# image is at least 300x200 |
186
|
|
|
|
|
|
|
$i->size(">=300", ">=200"); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# It's a five megapixel image! |
189
|
|
|
|
|
|
|
$i->total_pixels(">=5M"); |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
See L for more info. If you do not have |
192
|
|
|
|
|
|
|
C installed, these style of tests will be |
193
|
|
|
|
|
|
|
automatically skipped. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=over |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=item width($w_pixels) |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
Test the width of the image |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=item height($h_pixels) |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
Test the height of the image |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=item size($w_pixels, $h_pixels) |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
Test the width and the height of the image at the same time |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=item total_size($pixels) |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
Test the total number of pixels in the image (i.e. width x height) |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=back |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=cut |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
sub width { |
218
|
|
|
|
|
|
|
my $self = shift; |
219
|
|
|
|
|
|
|
return $self->_wh_test("width", "wide", @_); |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
sub height { |
223
|
|
|
|
|
|
|
my $self = shift; |
224
|
|
|
|
|
|
|
return $self->_wh_test("height", "tall", @_); |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub total_size { |
228
|
|
|
|
|
|
|
my $self = shift; |
229
|
|
|
|
|
|
|
return $self->_wh_test("total size", "in total", @_); |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
sub _wh_test { |
233
|
|
|
|
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1; |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
my $self = shift; |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# these first two values are just things that, since we're |
238
|
|
|
|
|
|
|
# using the same subroutine for width and height, that we |
239
|
|
|
|
|
|
|
# can use to call the right thing. They're not set by |
240
|
|
|
|
|
|
|
# the user. |
241
|
|
|
|
|
|
|
my $type = shift; # width/height/total size |
242
|
|
|
|
|
|
|
my $what = shift; # wide/tall |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
my $wanted = shift; |
245
|
|
|
|
|
|
|
my $description = @_ ? shift : |
246
|
|
|
|
|
|
|
"image $type"; |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
# get the actual value |
249
|
|
|
|
|
|
|
my $got = $type eq "total size" |
250
|
|
|
|
|
|
|
? $self->{image}->width * $self->{image}->height |
251
|
|
|
|
|
|
|
: $self->{image}->$type; |
252
|
|
|
|
|
|
|
my $got_pixels = $got == 1 ? "pixel" : "pixels"; |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# hmm, should we be doing a number compare test here? |
255
|
|
|
|
|
|
|
my $wanted_pixels; |
256
|
|
|
|
|
|
|
if ($wanted !~ /^\d+$/) { |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# skip if we don't have that installed |
259
|
|
|
|
|
|
|
unless ($INC{"Number/Compare.pm"}) { |
260
|
|
|
|
|
|
|
$tester->skip("No Number::Compare"); |
261
|
|
|
|
|
|
|
return 1; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# use number compare to do the actual comparison |
265
|
|
|
|
|
|
|
my $compare = Number::Compare->new($wanted); |
266
|
|
|
|
|
|
|
if ($compare->($got)) { |
267
|
|
|
|
|
|
|
$tester->ok(1,$description); |
268
|
|
|
|
|
|
|
return 1; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# we've failed |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# munge the values for the error message |
274
|
|
|
|
|
|
|
$wanted = "'$wanted'"; |
275
|
|
|
|
|
|
|
$wanted_pixels = "pixels"; # N::C tests are always plural |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
} else { |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# plain old number |
280
|
|
|
|
|
|
|
if ($wanted == $got) { |
281
|
|
|
|
|
|
|
$tester->ok(1,$description); |
282
|
|
|
|
|
|
|
return 1; |
283
|
|
|
|
|
|
|
} |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
$wanted_pixels = $wanted == 1 ? "pixel" : "pixels"; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# both failure cases fall through to here |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
$tester->ok(0, $description); |
291
|
|
|
|
|
|
|
$tester->diag("Image $got $got_pixels $what, not $wanted $wanted_pixels as expected"); |
292
|
|
|
|
|
|
|
return 0; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
sub size { |
296
|
|
|
|
|
|
|
my $self = shift; |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
my $wanted_w = shift; |
299
|
|
|
|
|
|
|
my $wanted_h = shift; |
300
|
|
|
|
|
|
|
my $description = @_ ? shift : |
301
|
|
|
|
|
|
|
"image size"; |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# get the actual values from the image |
304
|
|
|
|
|
|
|
my $got_w = $self->{image}->width; |
305
|
|
|
|
|
|
|
my $got_h = $self->{image}->height; |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
# check if we're using a complicated value that isn't |
308
|
|
|
|
|
|
|
# just a normal number, and test differently if we are |
309
|
|
|
|
|
|
|
if ($wanted_w !~ /^\d+$/ || $wanted_h !~ /^\d+$/) { |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# skip if we don't have that installed |
312
|
|
|
|
|
|
|
unless ($INC{"Number/Compare.pm"}) { |
313
|
|
|
|
|
|
|
$tester->skip("No Number::Compare"); |
314
|
|
|
|
|
|
|
return 1; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
# use number compare to do the tests. Note that one |
318
|
|
|
|
|
|
|
# of these two may be just a plain old number. That's |
319
|
|
|
|
|
|
|
# fine! Number::Compare will cope with that. |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
my $compare_w = Number::Compare->new($wanted_w); |
322
|
|
|
|
|
|
|
my $compare_h = Number::Compare->new($wanted_h); |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
if ($compare_w->($got_w) && $compare_h->($got_h)) { |
325
|
|
|
|
|
|
|
$tester->ok(1,$description); |
326
|
|
|
|
|
|
|
return 1; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
# we've failed! |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
# put the things that aren't numbers in quotes. This |
332
|
|
|
|
|
|
|
# just makes them look better when we print out the |
333
|
|
|
|
|
|
|
# error message below |
334
|
|
|
|
|
|
|
$wanted_w = "'$wanted_w'" unless $wanted_w =~ /^\d+$/; |
335
|
|
|
|
|
|
|
$wanted_h = "'$wanted_h'" unless $wanted_h =~ /^\d+$/; |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
} else { |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
# just a plain old number, we can do that without Number::Compare |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
if ($wanted_w == $got_w && $wanted_h == $got_h) { |
342
|
|
|
|
|
|
|
$tester->ok(1,$description); |
343
|
|
|
|
|
|
|
return 1; |
344
|
|
|
|
|
|
|
} |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# both failure cases will fall through to give the same error message |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
$tester->ok(0, $description); |
350
|
|
|
|
|
|
|
$tester->diag("Image size ($got_w,$got_h) not ($wanted_w,$wanted_h) as expected"); |
351
|
|
|
|
|
|
|
return 0; |
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=head2 Color specification |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
The testing system can cope with multiple definitions of color. You can |
357
|
|
|
|
|
|
|
use an arrayref containing the red, green and blue values (between 0 and 255:) |
358
|
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
my $red = [255,0,0]; |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
You can specify the value in hex if you want too: |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
my $red = "ff0000"; |
364
|
|
|
|
|
|
|
my $red = "FF0000"; # it's case insensitive |
365
|
|
|
|
|
|
|
my $red = "ff0000"; # you can put a # at the start if you want |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
If you install the B module from CPAN then you can |
368
|
|
|
|
|
|
|
use the name of the color in the "X" color scheme. |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
my $red = "red"; |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
Finally you can specify more than one colour by using an array ref containing |
373
|
|
|
|
|
|
|
the other forms. |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
my $rwab = ["red", "white", "blue"]; |
376
|
|
|
|
|
|
|
my $rwab = ["ff0000", "ffffff", "0000ff"]; |
377
|
|
|
|
|
|
|
my $rwab = [[255,0,0], [255,255,255], [0,0,255]]; |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
=head2 Checking Single Pixels |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
The simple C test can be used to check the color of a given |
382
|
|
|
|
|
|
|
pixel either is or isn't a particular color (or set of colors) |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# check the pixel at 40, 30 is red |
385
|
|
|
|
|
|
|
$i->pixel(40, 30, [255,0,0]) |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# check the pixel at 40, 30 is red or white |
388
|
|
|
|
|
|
|
$i->pixel(40, 30, [[255,0,0], [255,255,255]]) |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# check the pixel at 40, 30 isn't red |
391
|
|
|
|
|
|
|
$i->pixel_not(40, 30, [255,0,0]) |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
# check the pixel at 40, 30 isn't red or white |
394
|
|
|
|
|
|
|
$i->pixel_not(40, 30, [[255,0,0], [255,255,255]]) |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
This will fail if the pixel isn't the correct color, or the pixel is outside |
397
|
|
|
|
|
|
|
the image. |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
You can also use negative numbers to indicate coordinates relative the far |
400
|
|
|
|
|
|
|
sides of the image in a similar manner to Perl arrays. For example: |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
$i->pixel(-1,-2, "red"); |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
Is the same for a 400x300 image as: |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
$i->pixel(399,298, "red"); |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=cut |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
# pixel is documented |
411
|
|
|
|
|
|
|
sub pixel { |
412
|
|
|
|
|
|
|
my $self = shift; |
413
|
|
|
|
|
|
|
my $image = $self->{image}; |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
my $x = shift; |
416
|
|
|
|
|
|
|
my $y = shift; |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
# cope with negative coords |
419
|
|
|
|
|
|
|
$x = $self->{image}->width + $x if $x < 0; |
420
|
|
|
|
|
|
|
$y = $self->{image}->height + $y if $y < 0; |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
my $wanted_color = shift; |
423
|
|
|
|
|
|
|
my $description = @_ ? shift : "pixel test"; |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
my ($test,@colors) = _ctest($wanted_color); |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
my ($r, $g, $b) = $image->color_at($x, $y); |
428
|
|
|
|
|
|
|
unless (defined $r) { |
429
|
|
|
|
|
|
|
$tester->ok(0, $description); |
430
|
|
|
|
|
|
|
$tester->diag("Coords ($x, $y) outside of image"); |
431
|
|
|
|
|
|
|
return 0; |
432
|
|
|
|
|
|
|
}; |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
unless ($test->($r,$g,$b)) { |
435
|
|
|
|
|
|
|
$tester->ok(0, $description); |
436
|
|
|
|
|
|
|
$tester->diag("Pixel ($x, $y):"); |
437
|
|
|
|
|
|
|
$tester->diag(" got: "._color($r,$g,$b)); |
438
|
|
|
|
|
|
|
$tester->diag(" expected: ". |
439
|
|
|
|
|
|
|
join(" or\n ", @colors)); |
440
|
|
|
|
|
|
|
return 0; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
$tester->ok(1, $description); |
444
|
|
|
|
|
|
|
return 1; |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
# pixel_not is documented |
448
|
|
|
|
|
|
|
sub pixel_not { |
449
|
|
|
|
|
|
|
my $self = shift; |
450
|
|
|
|
|
|
|
my $image = $self->{image}; |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
my $x = shift; |
453
|
|
|
|
|
|
|
my $y = shift; |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
# cope with negative coords |
456
|
|
|
|
|
|
|
$x = $self->{image}->width + $x if $x < 0; |
457
|
|
|
|
|
|
|
$y = $self->{image}->height + $y if $y < 0; |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
my $wanted_color = shift; |
460
|
|
|
|
|
|
|
my $description = @_ ? shift : "pixel not test"; |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
my ($test) = _ctest($wanted_color); |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
my ($r, $g, $b) = $image->color_at($x, $y); |
465
|
|
|
|
|
|
|
unless (defined $r) { |
466
|
|
|
|
|
|
|
$tester->ok(0, $description); |
467
|
|
|
|
|
|
|
$tester->diag("Coords ($x, $y) outside of image"); |
468
|
|
|
|
|
|
|
return 0; |
469
|
|
|
|
|
|
|
}; |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
unless (!$test->($r,$g,$b)) { |
472
|
|
|
|
|
|
|
$tester->ok(0, $description); |
473
|
|
|
|
|
|
|
$tester->diag("Pixel ($x, $y) unexpectedly "._color($r,$g,$b)); |
474
|
|
|
|
|
|
|
return 0; |
475
|
|
|
|
|
|
|
} |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
$tester->ok(1, $description); |
478
|
|
|
|
|
|
|
return 1; |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
sub _munge_value { |
482
|
|
|
|
|
|
|
my $self = shift; |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
my $thingy = shift; # width or height |
485
|
|
|
|
|
|
|
my $value = shift; |
486
|
|
|
|
|
|
|
my $original = $value; |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# simple case where it's just a number |
489
|
|
|
|
|
|
|
if ($value =~ /^\d+$/) { |
490
|
|
|
|
|
|
|
return ($value, $value); |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
if ($value =~ s/(-\d+)$//) { |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
# calculate what that number should have been |
496
|
|
|
|
|
|
|
my $temp_value = $self->{image}->$thingy + $1; |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
# okay, if it was just a negative number, we're done |
499
|
|
|
|
|
|
|
return ($temp_value, $temp_value) if !length($value); |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
$value =~ tr[<>][><]; # reverse the greater than if any |
502
|
|
|
|
|
|
|
$value .= $temp_value; # and attach back the number part |
503
|
|
|
|
|
|
|
} |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
foreach (qw( <0 <-1 )) { |
506
|
|
|
|
|
|
|
die "You can't have a constraint of '$_'" if $value eq $_; |
507
|
|
|
|
|
|
|
} |
508
|
|
|
|
|
|
|
|
509
|
|
|
|
|
|
|
if ($value =~ /^[<][=](\d+)$/) |
510
|
|
|
|
|
|
|
{ return (0, $1) } |
511
|
|
|
|
|
|
|
if ($value =~ /^[<](\d+)$/) |
512
|
|
|
|
|
|
|
{ return (0, $1 - 1) } |
513
|
|
|
|
|
|
|
if ($value =~ /^[>][=](\d+)$/) |
514
|
|
|
|
|
|
|
{ return ($1, $self->{image}->$thingy - 1) } |
515
|
|
|
|
|
|
|
if ($value =~ /^[>](\d+)$/) |
516
|
|
|
|
|
|
|
{ return ($1 + 1, $self->{image}->$thingy - 1) } |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
die "Constraint '$value' makes no sense!"; |
519
|
|
|
|
|
|
|
} |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
sub row { |
522
|
|
|
|
|
|
|
my $self = shift; |
523
|
|
|
|
|
|
|
$self->_row("all", "row test", @_); |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
sub col { |
527
|
|
|
|
|
|
|
my $self = shift; |
528
|
|
|
|
|
|
|
$self->_column("all", "column test", @_); |
529
|
|
|
|
|
|
|
} |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
sub column { |
532
|
|
|
|
|
|
|
my $self = shift; |
533
|
|
|
|
|
|
|
$self->_column("all", "column test", @_); |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# "row_all" is a synonym for "row" |
537
|
|
|
|
|
|
|
sub row_all { |
538
|
|
|
|
|
|
|
my $self = shift; |
539
|
|
|
|
|
|
|
$self->_row("all", "row test", @_); |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
# "column_all" is a synonym for "column" |
543
|
|
|
|
|
|
|
sub column_all { |
544
|
|
|
|
|
|
|
my $self = shift; |
545
|
|
|
|
|
|
|
$self->_column("all", "column test", @_); |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
# "col_all" is a synonym for "column" |
549
|
|
|
|
|
|
|
sub col_all { |
550
|
|
|
|
|
|
|
my $self = shift; |
551
|
|
|
|
|
|
|
$self->_column("all", "column test", @_); |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
sub row_none { |
555
|
|
|
|
|
|
|
my $self = shift; |
556
|
|
|
|
|
|
|
$self->_row("none", "row none test", @_); |
557
|
|
|
|
|
|
|
} |
558
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
sub column_none { |
560
|
|
|
|
|
|
|
my $self = shift; |
561
|
|
|
|
|
|
|
$self->_column("none", "column none test", @_); |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
sub col_none { |
565
|
|
|
|
|
|
|
my $self = shift; |
566
|
|
|
|
|
|
|
$self->_column("none", "column none test", @_); |
567
|
|
|
|
|
|
|
} |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
sub row_any { |
570
|
|
|
|
|
|
|
my $self = shift; |
571
|
|
|
|
|
|
|
$self->_row("any", "row any test", @_); |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
sub column_any { |
575
|
|
|
|
|
|
|
my $self = shift; |
576
|
|
|
|
|
|
|
$self->_column("any", "column any test", @_); |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
sub col_any { |
580
|
|
|
|
|
|
|
my $self = shift; |
581
|
|
|
|
|
|
|
$self->_column("any", "column any test", @_); |
582
|
|
|
|
|
|
|
} |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
sub _row { |
585
|
|
|
|
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1; |
586
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
my $self = shift; |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# values defined in the methods |
590
|
|
|
|
|
|
|
my $mode = shift; |
591
|
|
|
|
|
|
|
my $default_description = shift; |
592
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
# user supplied values |
594
|
|
|
|
|
|
|
my $row = shift; |
595
|
|
|
|
|
|
|
my $color = shift; |
596
|
|
|
|
|
|
|
my $description = @_ ? shift : $default_description; |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
# work out what rows we're looking at |
599
|
|
|
|
|
|
|
my ($y1, $y2) = $self->_munge_value("height",$row); |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
$self->_region( |
602
|
|
|
|
|
|
|
x1 => 0, x2 => $self->{image}->width - 1, |
603
|
|
|
|
|
|
|
y1 => $y1, y2 => $y2, |
604
|
|
|
|
|
|
|
color => $color, |
605
|
|
|
|
|
|
|
description => $description, |
606
|
|
|
|
|
|
|
mode => $mode, |
607
|
|
|
|
|
|
|
) |
608
|
|
|
|
|
|
|
} |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
sub _column { |
611
|
|
|
|
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1; |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
my $self = shift; |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
# values defined in the methods |
616
|
|
|
|
|
|
|
my $mode = shift; |
617
|
|
|
|
|
|
|
my $default_description = shift; |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
# user supplied values |
620
|
|
|
|
|
|
|
my $column = shift; |
621
|
|
|
|
|
|
|
my $color = shift; |
622
|
|
|
|
|
|
|
my $description = @_ ? shift : $default_description; |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
# work out what columns we're looking at |
625
|
|
|
|
|
|
|
my ($x1, $x2) = $self->_munge_value("width",$column); |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
$self->_region( |
628
|
|
|
|
|
|
|
y1 => 0, y2 => $self->{image}->height - 1, |
629
|
|
|
|
|
|
|
x1 => $x1, x2 => $x2, |
630
|
|
|
|
|
|
|
color => $color, |
631
|
|
|
|
|
|
|
description => $description, |
632
|
|
|
|
|
|
|
mode => $mode, |
633
|
|
|
|
|
|
|
) |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
# this tests a region. It's the routine that all the other pixel based |
637
|
|
|
|
|
|
|
# tests (apart from the basic "pixel" and "pixel_not" tests call |
638
|
|
|
|
|
|
|
sub _region { |
639
|
|
|
|
|
|
|
# increase the T::B::Level so that errors come from the right line |
640
|
|
|
|
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1; |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
my $self = shift; |
643
|
|
|
|
|
|
|
my %args = @_; |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
my $image = $self->{image}; |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
my $description = $args{description}; |
648
|
|
|
|
|
|
|
my $wanted_color = $args{color}; |
649
|
|
|
|
|
|
|
my $mode = $args{mode}; |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
# get the coords. x1 is the smaller x coord, x2 is the largest. |
652
|
|
|
|
|
|
|
# same thing for y coords. x1 and x2 are inclusive. |
653
|
|
|
|
|
|
|
my ($x1, $x2, $y1, $y2) = map { $args{ $_ } } |
654
|
|
|
|
|
|
|
qw( x1 x2 y1 y2); |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
# get a test for this color |
657
|
|
|
|
|
|
|
my ($test,@colors) = _ctest($wanted_color); |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
# loop left -> right, top->bottom through our region |
660
|
|
|
|
|
|
|
my ($i, $j); |
661
|
|
|
|
|
|
|
for ($j = $y1; $j <= $y2; $j++) { |
662
|
|
|
|
|
|
|
for ($i = $x1; $i <= $x2; $i++) { |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
# grab a pixel |
665
|
|
|
|
|
|
|
my ($r, $g, $b) = $image->color_at($i, $j); |
666
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
# check it's inside |
668
|
|
|
|
|
|
|
# this should be probably rolled out of the loop |
669
|
|
|
|
|
|
|
unless (defined $r) { |
670
|
|
|
|
|
|
|
$tester->ok(0, $description); |
671
|
|
|
|
|
|
|
$tester->diag("Coords ($i, $j) outside of image"); |
672
|
|
|
|
|
|
|
return 0; |
673
|
|
|
|
|
|
|
}; |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
# this should probably be totally unrolled |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
if ($mode eq "none" && $test->($r,$g,$b)) { |
678
|
|
|
|
|
|
|
$tester->ok(0, $description); |
679
|
|
|
|
|
|
|
$tester->diag("Pixel ($i, $j) unexpectedly "._color($r,$g,$b)); |
680
|
|
|
|
|
|
|
return 0; |
681
|
|
|
|
|
|
|
} |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
if ($mode eq "all" && !$test->($r,$g,$b)) { |
684
|
|
|
|
|
|
|
$tester->ok(0, $description); |
685
|
|
|
|
|
|
|
$tester->diag("Pixel ($i, $j):"); |
686
|
|
|
|
|
|
|
$tester->diag(" got: "._color($r,$g,$b)); |
687
|
|
|
|
|
|
|
$tester->diag(" expected: ". |
688
|
|
|
|
|
|
|
join(" or\n ", @colors)); |
689
|
|
|
|
|
|
|
return 0; |
690
|
|
|
|
|
|
|
} |
691
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
if ($mode eq "any" && $test->($r,$g,$b)) { |
693
|
|
|
|
|
|
|
$tester->ok(1, $description); |
694
|
|
|
|
|
|
|
return 1; |
695
|
|
|
|
|
|
|
} |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
} |
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
if ($mode eq "any") { |
700
|
|
|
|
|
|
|
$tester->ok(0, $description); |
701
|
|
|
|
|
|
|
$tester->diag("No pixel correct color"); |
702
|
|
|
|
|
|
|
$tester->diag(" expected: ". |
703
|
|
|
|
|
|
|
join(" or\n ", @colors)); |
704
|
|
|
|
|
|
|
return 0; |
705
|
|
|
|
|
|
|
} |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
# got this far? must have succeeded |
708
|
|
|
|
|
|
|
$tester->ok(1, $description); |
709
|
|
|
|
|
|
|
return 1; |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
|
712
|
|
|
|
|
|
|
# this returns a function that can check for the passed colour |
713
|
|
|
|
|
|
|
# so calling |
714
|
|
|
|
|
|
|
# |
715
|
|
|
|
|
|
|
# my $foo = _ctest([255,0,0]) |
716
|
|
|
|
|
|
|
# $foo->(255,0,0); # check red is red |
717
|
|
|
|
|
|
|
# |
718
|
|
|
|
|
|
|
# my $foo = _ctest([255,0,0], [0,255,0]) |
719
|
|
|
|
|
|
|
# $foo->(255,0,0); # check red is red or blue |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
# TODO: make this function only use arrayrefs. Make sure we do colour |
722
|
|
|
|
|
|
|
# conversion *before* we hand things to it (much much more efficent |
723
|
|
|
|
|
|
|
# in the case we have multiple links) |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
sub _ctest { |
726
|
|
|
|
|
|
|
my $tests = shift; |
727
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
# note that this is very careful to allow things like objects that |
729
|
|
|
|
|
|
|
# stringify to be used okay |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
# case where we don't pass an array |
732
|
|
|
|
|
|
|
# meaning it's "white" or "ffffff" or something |
733
|
|
|
|
|
|
|
unless (ref $tests && ref($tests) eq "ARRAY") { |
734
|
|
|
|
|
|
|
my ($wr, $wg, $wb) = _rgb($tests); |
735
|
|
|
|
|
|
|
return sub { $wr == $_[0] && $wg == $_[1] && $wb == $_[2] }, "[$wr,$wg,$wb]"; |
736
|
|
|
|
|
|
|
} |
737
|
|
|
|
|
|
|
|
738
|
|
|
|
|
|
|
# case where we pass an array and the first element looks like a number |
739
|
|
|
|
|
|
|
# meaning it's [255,0,0], etc |
740
|
|
|
|
|
|
|
if (ref $tests && ref($tests) eq "ARRAY" && defined $tests->[0] && $tests->[0] =~ /^\d+$/) { |
741
|
|
|
|
|
|
|
my ($wr, $wg, $wb) = _rgb($tests); |
742
|
|
|
|
|
|
|
return sub { $wr == $_[0] && $wg == $_[1] && $wb == $_[2] }, "[$wr,$wg,$wb]"; |
743
|
|
|
|
|
|
|
} |
744
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
# must be an array of tests then. Try them each in turn. |
746
|
|
|
|
|
|
|
my @colors = map { [ _rgb( $_ ) ] } @$tests; |
747
|
|
|
|
|
|
|
return sub { |
748
|
|
|
|
|
|
|
foreach (@colors) { |
749
|
|
|
|
|
|
|
return 1 if $_->[0] == $_[0] && $_->[1] == $_[1] && $_->[2] == $_[2]; |
750
|
|
|
|
|
|
|
} |
751
|
|
|
|
|
|
|
return 0; |
752
|
|
|
|
|
|
|
}, map { _color(@$_) } @colors; |
753
|
|
|
|
|
|
|
} |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
# return the color in $r, $g, $b for what's passed in |
757
|
|
|
|
|
|
|
# you can pass in "#ff0000", or "ff0000", or [255,0,0] or "red" |
758
|
|
|
|
|
|
|
sub _rgb { |
759
|
|
|
|
|
|
|
my $value = shift; |
760
|
|
|
|
|
|
|
unless (defined $value) |
761
|
|
|
|
|
|
|
{ croak "Undef passed as expected color" } |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
return @$value if ref $value && ref $value eq "ARRAY"; |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
# convert from hex and return if we can |
766
|
|
|
|
|
|
|
if ($value =~ /^#?([a-fA-F09]{2})([a-fA-F09]{2})([a-fA-F09]{2})$/) { |
767
|
|
|
|
|
|
|
return hex $1, hex $2, hex $3; # loop unrolled by hand :-) |
768
|
|
|
|
|
|
|
} |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
if (!$INC{"Graphics/ColorNames.pm"}) |
771
|
|
|
|
|
|
|
{ die "Can't determine color for '$value': Graphics::ColorNames not installed" } |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
my $hex = $NameTable{ $value }; |
774
|
|
|
|
|
|
|
die "Can't determine color for '$value'" unless $hex; |
775
|
|
|
|
|
|
|
return Graphics::ColorNames::hex2tuple($hex); |
776
|
|
|
|
|
|
|
} |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
# return a string that describes the colour |
779
|
|
|
|
|
|
|
sub _color { |
780
|
|
|
|
|
|
|
my ($r, $g, $b) = @_; |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
my $string = ""; |
783
|
|
|
|
|
|
|
if ($INC{"Graphics::ColorNames"}) { |
784
|
|
|
|
|
|
|
# TODO: modify $string so it has the colour name in here |
785
|
|
|
|
|
|
|
} |
786
|
|
|
|
|
|
|
return "$string\[$r,$g,$b]"; |
787
|
|
|
|
|
|
|
} |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
sub region { |
790
|
|
|
|
|
|
|
my $self = shift; |
791
|
|
|
|
|
|
|
return $self->_r("all","image region", @_); |
792
|
|
|
|
|
|
|
} |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
sub region_all { |
795
|
|
|
|
|
|
|
my $self = shift; |
796
|
|
|
|
|
|
|
return $self->_r("all","image region", @_); |
797
|
|
|
|
|
|
|
} |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
sub region_any { |
800
|
|
|
|
|
|
|
my $self = shift; |
801
|
|
|
|
|
|
|
return $self->_r("any","image region any", @_); |
802
|
|
|
|
|
|
|
} |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
sub region_none { |
805
|
|
|
|
|
|
|
my $self = shift; |
806
|
|
|
|
|
|
|
return $self->_r("none","image region none", @_); |
807
|
|
|
|
|
|
|
} |
808
|
|
|
|
|
|
|
|
809
|
|
|
|
|
|
|
sub _r { |
810
|
|
|
|
|
|
|
local $Test::Builder::Level = $Test::Builder::Level + 1; |
811
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
my $self = shift; |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
# values defined in the methods |
815
|
|
|
|
|
|
|
my $mode = shift; |
816
|
|
|
|
|
|
|
my $default_description = shift; |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
# user supplied values |
819
|
|
|
|
|
|
|
my ($x1, $y1, $x2, $y2) = splice(@_, 0, 4); |
820
|
|
|
|
|
|
|
my $color = shift; |
821
|
|
|
|
|
|
|
my $description = @_ ? shift : |
822
|
|
|
|
|
|
|
"image region"; |
823
|
|
|
|
|
|
|
|
824
|
|
|
|
|
|
|
# convert negative coords into positive ones |
825
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
foreach ($x1, $x2) { |
827
|
|
|
|
|
|
|
$_ = $self->{image}->width + $_ if $_ < 0; |
828
|
|
|
|
|
|
|
} |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
foreach ($y1, $y2) { |
831
|
|
|
|
|
|
|
$_ = $self->{image}->height + $_ if $_ < 0; |
832
|
|
|
|
|
|
|
} |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
# make ?1 smaller than ?2 if it's not |
835
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
if ($x2 < $x1) |
837
|
|
|
|
|
|
|
{ ($x1, $x2) = ($x2, $x1) } |
838
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
if ($y2 < $y1) |
840
|
|
|
|
|
|
|
{ ($y1, $y2) = ($y2, $y1) } |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
# examine the region |
843
|
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
$self->_region( |
845
|
|
|
|
|
|
|
y1 => $y1, |
846
|
|
|
|
|
|
|
y2 => $y2, |
847
|
|
|
|
|
|
|
x1 => $x1, |
848
|
|
|
|
|
|
|
x2 => $x2, |
849
|
|
|
|
|
|
|
color => $color, |
850
|
|
|
|
|
|
|
description => $description, |
851
|
|
|
|
|
|
|
mode => $mode, |
852
|
|
|
|
|
|
|
) |
853
|
|
|
|
|
|
|
} |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
=head1 PLUGINS |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
This module can be extended to allow you to test arbitary image formats. |
858
|
|
|
|
|
|
|
To do this you need to implement a module called Test::Image::Plugin::* |
859
|
|
|
|
|
|
|
which supports the following methods: |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
=over |
862
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
=item new( $image ) |
864
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
A constructor. Return an object if you're prepared to handle the image |
866
|
|
|
|
|
|
|
that's passed in. Return C if the image isn't something you'll |
867
|
|
|
|
|
|
|
handle (hopefully some other plugin will.) |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
=item width |
870
|
|
|
|
|
|
|
|
871
|
|
|
|
|
|
|
=item height |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
Instance methods. These methods should return the width and height of |
874
|
|
|
|
|
|
|
the image. |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
=item color_at($x, $y) |
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
Instance method should return a three element list that contains the |
879
|
|
|
|
|
|
|
red, green and blue value. This should return the empty list if the |
880
|
|
|
|
|
|
|
pixel specified is outside the image. |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
=back |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
In order for these plugins to work you must first install |
885
|
|
|
|
|
|
|
C from CPAN. If you're writing C plugin |
886
|
|
|
|
|
|
|
and distributing it on CPAN, you should add C to your |
887
|
|
|
|
|
|
|
required modules in C / C |
888
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
=head1 BUGS |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
If you don't have module compare installed and you pass a string to |
892
|
|
|
|
|
|
|
any of the image size routines that isn't just a plain old number |
893
|
|
|
|
|
|
|
then that test will be skipped if you don't have C |
894
|
|
|
|
|
|
|
installed, even if that string is just junk. This is to allow this |
895
|
|
|
|
|
|
|
module to be compatible with future improvements to C. |
896
|
|
|
|
|
|
|
You are encouraged to have C installed when |
897
|
|
|
|
|
|
|
developing tests on your own system. |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
We should probably automatically skip named colors if you don't |
900
|
|
|
|
|
|
|
have C installed. We don't yet. |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
Please report any further bugs you find via the CPAN RT system. |
903
|
|
|
|
|
|
|
L |
904
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
=head1 OTHER BUGS |
906
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
British Airways doesn't have TIVO like movies like Virgin Atlantic. Or, |
908
|
|
|
|
|
|
|
if it does, it doesn't have it on B flight, and that's all I really |
909
|
|
|
|
|
|
|
care about at the momement. |
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
In the movie "Failure To Launch" taking of your facemask while playing |
912
|
|
|
|
|
|
|
paintball is insanely dangerous. It also makes me want to shout lock |
913
|
|
|
|
|
|
|
off! when Ace is belaying. I don't find climbing accidents funny. |
914
|
|
|
|
|
|
|
Who's belaying the guy from Alias in that scene anyway? Despite |
915
|
|
|
|
|
|
|
all that, I quite enjoyed the film. |
916
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
I somehow ended up with some of my sister in law's music in iTunes and |
918
|
|
|
|
|
|
|
now when I'm coding I sometime randomly get some Christina Aguilera. |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
Coding on a plane is very hard to do, as you don't have the arm room |
921
|
|
|
|
|
|
|
to type properly. |
922
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
This said, I don't get a chance to listen to my entire Chemical |
924
|
|
|
|
|
|
|
Brothers collection in one go uninterrupted very often. |
925
|
|
|
|
|
|
|
|
926
|
|
|
|
|
|
|
=head1 AUTHOR |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
Written by Mark Fowler, Emark@twoshortplanks.comE. Please see |
929
|
|
|
|
|
|
|
L for details of how to contact me. |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
Copyright Fotango 2006-2007. All rights reserved. |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
This module is free software; you can redistribute it and/or modify it under |
934
|
|
|
|
|
|
|
the same terms as Perl itself. |
935
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
=head1 SEE ALSO |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
L, for an alternative way of testing GD Images. |
939
|
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
L |
941
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
L |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
=cut |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
1; |
947
|
|
|
|
|
|
|
|