]> git.imager.perl.org - imager.git/blob - t/t023palette.t
ec464fb15c7e5b6744785ad2d87c663407849342
[imager.git] / t / t023palette.t
1 #!perl -w
2 # some of this is tested in t01introvert.t too
3 use strict;
4 use Test::More tests => 107;
5 BEGIN { use_ok("Imager"); }
6
7 use Imager::Test qw(image_bounds_checks);
8
9 sub isbin($$$);
10
11 my $img = Imager->new(xsize=>50, ysize=>50, type=>'paletted');
12
13 ok($img, "paletted image created");
14
15 is($img->type, 'paletted', "got a paletted image");
16
17 my $black = Imager::Color->new(0,0,0);
18 my $red = Imager::Color->new(255,0,0);
19 my $green = Imager::Color->new(0,255,0);
20 my $blue = Imager::Color->new(0,0,255);
21
22 my $white = Imager::Color->new(255,255,255);
23
24 # add some color
25 my $blacki = $img->addcolors(colors=>[ $black, $red, $green, $blue ]);
26
27 print "# blacki $blacki\n";
28 ok(defined $blacki && $blacki == 0, "we got the first color");
29
30 ok($img->colorcount() == 4, "should have 4 colors");
31 my ($redi, $greeni, $bluei) = 1..3;
32
33 my @all = $img->getcolors;
34 ok(@all == 4, "all colors is 4");
35 coloreq($all[0], $black, "first black");
36 coloreq($all[1], $red, "then red");
37 coloreq($all[2], $green, "then green");
38 coloreq($all[3], $blue, "and finally blue");
39
40 # keep this as an assignment, checking for scalar context
41 # we don't want the last color, otherwise if the behaviour changes to
42 # get all up to the last (count defaulting to size-index) we'd get a
43 # false positive
44 my $one_color = $img->getcolors(start=>$redi);
45 ok($one_color->isa('Imager::Color'), "check scalar context");
46 coloreq($one_color, $red, "and that it's what we want");
47
48 # make sure we can find colors
49 ok(!defined($img->findcolor(color=>$white)), 
50     "shouldn't be able to find white");
51 ok($img->findcolor(color=>$black) == $blacki, "find black");
52 ok($img->findcolor(color=>$red) == $redi, "find red");
53 ok($img->findcolor(color=>$green) == $greeni, "find green");
54 ok($img->findcolor(color=>$blue) == $bluei, "find blue");
55
56 # various failure tests for setcolors
57 ok(!defined($img->setcolors(start=>-1, colors=>[$white])),
58     "expect failure: low index");
59 ok(!defined($img->setcolors(start=>1, colors=>[])),
60     "expect failure: no colors");
61 ok(!defined($img->setcolors(start=>5, colors=>[$white])),
62     "expect failure: high index");
63
64 # set the green index to white
65 ok($img->setcolors(start => $greeni, colors => [$white]),
66     "set a color");
67 # and check it
68 coloreq(scalar($img->getcolors(start=>$greeni)), $white,
69         "make sure it was set");
70 ok($img->findcolor(color=>$white) == $greeni, "and that we can find it");
71 ok(!defined($img->findcolor(color=>$green)), "and can't find the old color");
72
73 # write a few colors
74 ok(scalar($img->setcolors(start=>$redi, colors=>[ $green, $red])),
75            "save multiple");
76 coloreq(scalar($img->getcolors(start=>$redi)), $green, "first of multiple");
77 coloreq(scalar($img->getcolors(start=>$greeni)), $red, "second of multiple");
78
79 # put it back
80 $img->setcolors(start=>$red, colors=>[$red, $green]);
81
82 # draw on the image, make sure it stays paletted when it should
83 ok($img->box(color=>$red, filled=>1), "fill with red");
84 is($img->type, 'paletted', "paletted after fill");
85 ok($img->box(color=>$green, filled=>1, xmin=>10, ymin=>10,
86               xmax=>40, ymax=>40), "green box");
87 is($img->type, 'paletted', 'still paletted after box');
88 # an AA line will almost certainly convert the image to RGB, don't use
89 # an AA line here
90 ok($img->line(color=>$blue, x1=>10, y1=>10, x2=>40, y2=>40),
91     "draw a line");
92 is($img->type, 'paletted', 'still paletted after line');
93
94 # draw with white - should convert to direct
95 ok($img->box(color=>$white, filled=>1, xmin=>20, ymin=>20, 
96               xmax=>30, ymax=>30), "white box");
97 is($img->type, 'direct', "now it should be direct");
98
99 # various attempted to make a paletted image from our now direct image
100 my $palimg = $img->to_paletted;
101 ok($palimg, "we got an image");
102 # they should be the same pixel for pixel
103 ok(Imager::i_img_diff($img->{IMG}, $palimg->{IMG}) == 0, "same pixels");
104
105 # strange case: no color picking, and no colors
106 # this was causing a segmentation fault
107 $palimg = $img->to_paletted(colors=>[ ], make_colors=>'none');
108 ok(!defined $palimg, "to paletted with an empty palette is an error");
109 print "# ",$img->errstr,"\n";
110 ok(scalar($img->errstr =~ /no colors available for translation/),
111     "and got the correct msg");
112
113 ok(!Imager->new(xsize=>1, ysize=>-1, type=>'paletted'), 
114     "fail on -ve height");
115 cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
116        "and correct error message");
117 ok(!Imager->new(xsize=>-1, ysize=>1, type=>'paletted'), 
118     "fail on -ve width");
119 cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
120        "and correct error message");
121 ok(!Imager->new(xsize=>-1, ysize=>-1, type=>'paletted'), 
122     "fail on -ve width/height");
123 cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
124        "and correct error message");
125
126 ok(!Imager->new(xsize=>1, ysize=>1, type=>'paletted', channels=>0),
127     "fail on 0 channels");
128 cmp_ok(Imager->errstr, '=~', qr/Channels must be positive and <= 4/,
129        "and correct error message");
130 ok(!Imager->new(xsize=>1, ysize=>1, type=>'paletted', channels=>5),
131     "fail on 5 channels");
132 cmp_ok(Imager->errstr, '=~', qr/Channels must be positive and <= 4/,
133        "and correct error message");
134
135 {
136   # https://rt.cpan.org/Ticket/Display.html?id=8213
137   # check for handling of memory allocation of very large images
138   # only test this on 32-bit machines - on a 64-bit machine it may
139   # result in trying to allocate 4Gb of memory, which is unfriendly at
140   # least and may result in running out of memory, causing a different
141   # type of exit
142   use Config;
143  SKIP:
144   {
145     skip("don't want to allocate 4Gb", 10)
146       unless $Config{intsize} == 4;
147
148     my $uint_range = 256 ** $Config{intsize};
149     my $dim1 = int(sqrt($uint_range))+1;
150     
151     my $im_b = Imager->new(xsize=>$dim1, ysize=>$dim1, channels=>1, type=>'paletted');
152     is($im_b, undef, "integer overflow check - 1 channel");
153     
154     $im_b = Imager->new(xisze=>$dim1, ysize=>1, channels=>1, type=>'paletted');
155     ok($im_b, "but same width ok");
156     $im_b = Imager->new(xisze=>1, ysize=>$dim1, channels=>1, type=>'paletted');
157     ok($im_b, "but same height ok");
158     cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
159            "check the error message");
160
161     # do a similar test with a 3 channel image, so we're sure we catch
162     # the same case where the third dimension causes the overflow
163     # for paletted images the third dimension can't cause an overflow
164     # but make sure we didn't anything too dumb in the checks
165     my $dim3 = $dim1;
166     
167     $im_b = Imager->new(xsize=>$dim3, ysize=>$dim3, channels=>3, type=>'paletted');
168     is($im_b, undef, "integer overflow check - 3 channel");
169     
170     $im_b = Imager->new(xsize=>$dim3, ysize=>1, channels=>3, type=>'paletted');
171     ok($im_b, "but same width ok");
172     $im_b = Imager->new(xsize=>1, ysize=>$dim3, channels=>3, type=>'paletted');
173     ok($im_b, "but same height ok");
174
175     cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
176            "check the error message");
177
178     # test the scanline allocation check
179     # divide by 2 to get int range, by 3 so that the image (one byte/pixel)
180     # doesn't integer overflow, but the scanline of i_color (4/pixel) does
181     my $dim4 = $uint_range / 2 / 3;
182     my $im_o = Imager->new(xsize=>$dim4, ysize=>1, channels=>3, type=>'paletted');
183     is($im_o, undef, "integer overflow check - scanline size");
184     cmp_ok(Imager->errstr, '=~', 
185            qr/integer overflow calculating scanline allocation/,
186            "check error message");
187   }
188 }
189
190 { # http://rt.cpan.org/NoAuth/Bug.html?id=9672
191   my $warning;
192   local $SIG{__WARN__} = 
193     sub { 
194       $warning = "@_";
195       my $printed = $warning;
196       $printed =~ s/\n$//;
197       $printed =~ s/\n/\n\#/g; 
198       print "# ",$printed, "\n";
199     };
200   my $img = Imager->new(xsize=>10, ysize=>10);
201   $img->to_paletted();
202   cmp_ok($warning, '=~', 'void', "correct warning");
203   cmp_ok($warning, '=~', 't023palette\\.t', "correct file");
204 }
205
206 { # http://rt.cpan.org/NoAuth/Bug.html?id=12676
207   # setcolors() has a fencepost error
208   my $img = Imager->new(xsize=>10, ysize=>10, type=>'paletted');
209
210   is($img->addcolors(colors=>[ $black, $red ]), "0 but true",
211      "add test colors");
212   ok($img->setcolors(start=>1, colors=>[ $green ]), "set the last color");
213   ok(!$img->setcolors(start=>2, colors=>[ $black ]), 
214      "set after the last color");
215 }
216
217 { # https://rt.cpan.org/Ticket/Display.html?id=20056
218   # added named color support to addcolor/setcolor
219   my $img = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
220   is($img->addcolors(colors => [ qw/000000 FF0000/ ]), "0 but true",
221      "add colors as strings instead of objects");
222   my @colors = $img->getcolors;
223   iscolor($colors[0], $black, "check first color");
224   iscolor($colors[1], $red, "check second color");
225   ok($img->setcolors(colors => [ qw/00FF00 0000FF/ ]),
226      "setcolors as strings instead of objects");
227   @colors = $img->getcolors;
228   iscolor($colors[0], $green, "check first color");
229   iscolor($colors[1], $blue, "check second color");
230
231   # make sure we handle bad colors correctly
232   is($img->colorcount, 2, "start from a known state");
233   is($img->addcolors(colors => [ 'XXFGXFXGXFX' ]), undef,
234      "fail to add unknown color");
235   is($img->errstr, 'No color named XXFGXFXGXFX found', 'check error message');
236   is($img->setcolors(colors => [ 'XXFGXFXGXFXZ' ]), undef,
237      "fail to set to unknown color");
238   is($img->errstr, 'No color named XXFGXFXGXFXZ found', 'check error message');
239 }
240
241 { # https://rt.cpan.org/Ticket/Display.html?id=20338
242   # OO interface to i_glin/i_plin
243   my $im = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
244   is($im->addcolors(colors => [ "#000", "#F00", "#0F0", "#00F" ]), "0 but true",
245      "add some test colors")
246     or print "# ", $im->errstr, "\n";
247   # set a pixel to check
248   $im->setpixel(x => 1, 'y' => 0, color => "#0F0");
249   is_deeply([ $im->getscanline('y' => 0, type=>'index') ],
250             [ 0, 2, (0) x 8 ], "getscanline index in list context");
251   isbin($im->getscanline('y' => 0, type=>'index'),
252         "\x00\x02" . "\x00" x 8,
253         "getscanline index in scalar context");
254   is($im->setscanline('y' => 0, pixels => [ 1, 2, 0, 3 ], type => 'index'),
255      4, "setscanline with list");
256   is($im->setscanline('y' => 0, x => 4, pixels => pack("C*", 3, 2, 1, 0, 3),
257                       type => 'index'),
258      5, "setscanline with pv");
259   is_deeply([ $im->getscanline(type => 'index', 'y' => 0) ],
260             [ 1, 2, 0, 3, 3, 2, 1, 0, 3, 0 ],
261             "check values set");
262   eval { # should croak on OOR index
263     $im->setscanline('y' => 1, pixels => [ 255 ], type=>'index');
264   };
265   ok($@, "croak on setscanline() to invalid index");
266   eval { # same again with pv
267     $im->setscanline('y' => 1, pixels => "\xFF", type => 'index');
268   };
269   ok($@, "croak on setscanline() with pv to invalid index");
270 }
271
272 {
273   print "# make_colors => mono\n";
274   # test mono make_colors
275   my $imrgb = Imager->new(xsize => 10, ysize => 10);
276   $imrgb->setpixel(x => 0, 'y' => 0, color => '#FFF');
277   $imrgb->setpixel(x => 1, 'y' => 0, color => '#FF0');
278   $imrgb->setpixel(x => 2, 'y' => 0, color => '#000');
279   my $mono = $imrgb->to_paletted(make_colors => 'mono',
280                                    translate => 'closest');
281   is($mono->type, 'paletted', "check we get right image type");
282   is($mono->colorcount, 2, "only 2 colors");
283   my @colors = $mono->getcolors;
284   iscolor($colors[0], $black, "check first entry");
285   iscolor($colors[1], $white, "check second entry");
286   my @pixels = $mono->getscanline(x => 0, 'y' => 0, width => 3, type=>'index');
287   is($pixels[0], 1, "check white pixel");
288   is($pixels[1], 1, "check yellow pixel");
289   is($pixels[2], 0, "check black pixel");
290 }
291
292 { # check bounds checking
293   my $im = Imager->new(xsize => 10, ysize => 10, type=>'paletted');
294   ok($im->addcolors(colors => [ $black ]), "add color of pixel bounds check writes");
295
296   image_bounds_checks($im);
297 }
298
299 sub iscolor {
300   my ($c1, $c2, $msg) = @_;
301
302   my $builder = Test::Builder->new;
303   my @c1 = $c1->rgba;
304   my @c2 = $c2->rgba;
305   if (!$builder->ok($c1[0] == $c2[0] && $c1[1] == $c2[1] && $c1[2] == $c2[2],
306                     $msg)) {
307     $builder->diag(<<DIAG);
308       got color: [ @c1 ]
309  expected color: [ @c2 ]
310 DIAG
311   }
312 }
313
314 sub isbin ($$$) {
315   my ($got, $expected, $msg) = @_;
316
317   my $builder = Test::Builder->new;
318   if (!$builder->ok($got eq $expected, $msg)) {
319     (my $got_dec = $got) =~ s/([^ -~])/sprintf("\\x%02x", ord $1)/ge;
320     (my $exp_dec = $expected)  =~ s/([^ -~])/sprintf("\\x%02x", ord $1)/ge;
321     $builder->diag(<<DIAG);
322       got: "$got_dec"
323  expected: "$exp_dec"
324 DIAG
325   }
326 }
327
328 sub coloreq {
329   my ($left, $right, $comment) = @_;
330
331   my ($rl, $gl, $bl, $al) = $left->rgba;
332   my ($rr, $gr, $br, $ar) = $right->rgba;
333
334   print "# comparing color($rl,$gl,$bl,$al) with ($rr,$gr,$br,$ar)\n";
335   ok($rl == $rr && $gl == $gr && $bl == $br && $al == $ar,
336       $comment);
337 }
338