5 use Test::More tests => 69;
7 BEGIN { require "t/testtools.pl"; }
9 $SIG{__DIE__} = sub { confess @_ };
11 my $buggy_giflib_file = "buggy_giflib.txt";
13 init_log("testout/t105gif.log",1);
15 my $green=i_color_new(0,255,0,255);
16 my $blue=i_color_new(0,0,255,255);
17 my $red=i_color_new(255,0,0,255);
19 my $img=Imager::ImgRaw::new(150,150,3);
21 i_box_filled($img,70,25,130,125,$green);
22 i_box_filled($img,20,25,80,125,$blue);
23 i_arc($img,75,75,30,0,361,$red);
24 i_conv($img,[0.1, 0.2, 0.4, 0.2, 0.1]);
26 my $timg = Imager::ImgRaw::new(20, 20, 4);
27 my $trans = i_color_new(255, 0, 0, 127);
28 i_box_filled($timg, 0, 0, 20, 20, $green);
29 i_box_filled($timg, 2, 2, 18, 18, $trans);
34 unless (i_has_format("gif")) {
36 ok(!$im->read(file=>"testimg/scale.gif"), "should fail to read gif");
37 is($im->errstr, "format 'gif' not supported", "check no gif message");
38 $im = Imager->new(xsize=>2, ysize=>2);
39 ok(!$im->write(file=>"testout/nogif.gif"), "should fail to write gif");
40 is($im->errstr, 'format not supported', "check no gif message");
41 skip("no gif support", 65);
43 open(FH,">testout/t105.gif") || die "Cannot open testout/t105.gif\n";
45 ok(i_writegifmc($img,fileno(FH),6), "write low") or
46 die "Cannot write testout/t105.gif\n";
49 open(FH,"testout/t105.gif") || die "Cannot open testout/t105.gif\n";
51 ok($img=i_readgif(fileno(FH)), "read low")
52 or die "Cannot read testout/t105.gif\n";
55 open(FH,"testout/t105.gif") || die "Cannot open testout/t105.gif\n";
57 ($img, my $palette)=i_readgif(fileno(FH));
58 ok($img, "read palette") or die "Cannot read testout/t105.gif\n";
61 $palette=''; # just to skip a warning.
63 # check that reading interlaced/non-interlaced versions of
64 # the same GIF produce the same image
65 # I could replace this with code that used Imager's built-in
66 # image comparison code, but I know this code revealed the error
67 open(FH, "<testimg/scalei.gif") || die "Cannot open testimg/scalei.gif";
69 my ($imgi) = i_readgif(fileno(FH));
70 ok($imgi, "read interlaced") or die "Cannot read testimg/scalei.gif";
72 open FH, "<testimg/scale.gif" or die "Cannot open testimg/scale.gif";
74 my ($imgni) = i_readgif(fileno(FH));
75 ok($imgni, "read normal") or die "Cannot read testimg/scale.gif";
78 open FH, ">testout/t105i.ppm" or die "Cannot create testout/t105i.ppm";
80 my $IO = Imager::io_new_fd( fileno(FH) );
81 i_writeppm_wiol($imgi, $IO)
82 or die "Cannot write testout/t105i.ppm";
85 open FH, ">testout/t105ni.ppm" or die "Cannot create testout/t105ni.ppm";
87 $IO = Imager::io_new_fd( fileno(FH) );
88 i_writeppm_wiol($imgni, $IO)
89 or die "Cannot write testout/t105ni.ppm";
93 open FH, "<testout/t105i.ppm" or die "Cannot open testout/t105i.ppm";
94 my $datai = do { local $/; <FH> };
97 open FH, "<testout/t105ni.ppm" or die "Cannot open testout/t105ni.ppm";
98 my $datani = do { local $/; <FH> };
100 is($datai, $datani, "images match");
102 my $gifver = Imager::i_giflib_version();
105 skip("giflib3 doesn't support callbacks", 4) if $gifver >= 4.0;
106 # reading with a callback
107 # various sizes to make sure the buffering works
109 open FH, "<testimg/scale.gif" or die "Cannot open testimg/scale.gif";
111 # no callback version in giflib3, so don't overwrite a good image
112 my $img2 = i_readgif_callback(sub { my $tmp; read(FH, $tmp, $_[0]) and $tmp });
114 ok($img, "reading with a callback");
116 ok(test_readgif_cb(1), "read callback 1 char buffer");
117 ok(test_readgif_cb(512), "read callback 512 char buffer");
118 ok(test_readgif_cb(1024), "read callback 1024 char buffer");
120 open FH, ">testout/t105_mc.gif" or die "Cannot open testout/t105_mc.gif";
122 ok(i_writegifmc($img, fileno(FH), 7), "writegifmc");
126 # test webmap, custom errdiff map
127 # (looks fairly awful)
128 open FH, ">testout/t105_gen.gif" or die $!;
130 ok(i_writegif_gen(fileno(FH), { make_colors=>'webmap',
131 translate=>'errdiff',
135 errdiff_map=>[0, 1, 1, 0]}, $img),
136 "webmap, custom errdif map");
139 print "# the following tests are fairly slow\n";
141 # test animation, mc_addi, error diffusion, ordered transparency
143 my $sortagreen = i_color_new(0, 255, 0, 63);
145 my $im = Imager::ImgRaw::new(200, 200, 4);
146 _add_tags($im, gif_delay=>50, gif_disposal=>2);
147 for my $j (0..$i-1) {
148 my $fill = i_color_new(0, 128, 0, 255 * ($i-$j)/$i);
149 i_box_filled($im, 0, $j*40, 199, $j*40+40, $fill);
151 i_box_filled($im, 0, $i*40, 199, 199, $blue);
154 my @gif_delays = (50) x 5;
155 my @gif_disposal = (2) x 5;
156 open FH, ">testout/t105_anim.gif" or die $!;
158 ok(i_writegif_gen(fileno(FH), { make_colors=>'addi',
159 translate=>'closest',
160 gif_delays=>\@gif_delays,
161 gif_disposal=>\@gif_disposal,
162 gif_positions=> [ map [ $_*10, $_*10 ], 0..4 ],
163 gif_user_input=>[ 1, 0, 1, 0, 1 ],
165 'tr_orddith'=>'dot8'}, @imgs),
169 my $can_write_callback = 0;
170 unlink $buggy_giflib_file;
171 if ($gifver >= 4.0) {
172 ++$can_write_callback;
173 my $good = ext_test(14, <<'ENDOFCODE');
175 require "t/testtools.pl";
176 my $timg = test_img();
177 my @gif_delays = (50) x 5;
178 my @gif_disposal = (2) x 5;
179 my @imgs = ($timg) x 5;
180 open FH, "> testout/t105_anim_cb.gif" or die $!;
182 i_writegif_callback(sub {
186 { make_colors=>'webmap',
187 translate=>'closest',
188 gif_delays=>\@gif_delays,
189 gif_disposal=>\@gif_disposal,
191 tr_orddith=>'dot8'}, @imgs)
192 or die "Cannot write anim gif";
198 $can_write_callback = 0;
199 print "not ok 14 # see $buggy_giflib_file\n";
200 print STDERR "\nprobable buggy giflib - skipping tests that depend on a good giflib\n";
201 print STDERR "see $buggy_giflib_file for more information\n";
202 open FLAG, "> $buggy_giflib_file" or die;
204 This file is created by t105gif.t when test 14 fails.
206 This failure usually indicates you\'re using the original versions
207 of giflib 4.1.0 - 4.1.3, which have a few bugs that Imager tickles.
209 You can apply the patch from:
211 http://www.develop-help.com/imager/giflib.patch
213 or you can just install Imager as is, if you only need to write GIFs to
214 files or file descriptors (such as sockets).
216 One hunk of this patch is rejected (correctly) with giflib 4.1.3,
217 since one bug that the patch fixes is fixed in 4.1.3.
219 If you don't feel comfortable with that apply the patch file that
220 belongs to the following patch entry on sourceforge:
222 https://sourceforge.net/tracker/index.php?func=detail&aid=981255&group_id=102202&atid=631306
224 In previous versions of Imager only this test was careful about catching
225 the error, we now skip any tests that crashed or failed when the buggy
231 print "ok 14 # skip giflib3 doesn't support callbacks\n";
234 my $c = i_color_new(0,0,0,0);
236 my $im = Imager::ImgRaw::new(200, 200, 3);
237 _add_tags($im, gif_local_map=>1, gif_delay=>150, gif_loop=>10);
238 for my $x (0 .. 39) {
239 for my $y (0 .. 39) {
240 $c->set($x * 6, $y * 6, 32*$g+$x+$y, 255);
241 i_box_filled($im, $x*5, $y*5, $x*5+4, $y*5+4, $c);
246 # test giflib with multiple palettes
247 # (it was meant to test the NS loop extension too, but that's broken)
248 # this looks better with make_colors=>'addi', translate=>'errdiff'
249 # this test aims to overload the palette for each image, so the
250 # output looks moderately horrible
251 open FH, ">testout/t105_mult_pall.gif" or die "Cannot create file: $!";
253 ok(i_writegif_gen(fileno(FH), { #make_colors=>'webmap',
255 }, @imgs), "write multiple palettes")
256 or print "# ", join(":", map $_->[1], Imager::i_errors()),"\n";
259 # regression test: giflib doesn't like 1 colour images
260 my $img1 = Imager::ImgRaw::new(100, 100, 3);
261 i_box_filled($img1, 0, 0, 100, 100, $red);
262 open FH, ">testout/t105_onecol.gif" or die $!;
264 ok(i_writegif_gen(fileno(FH), { translate=>'giflib'}, $img1),
265 "single colour write regression");
269 # previously it was harder do write transparent images
270 # tests the improvements
271 my $timg = Imager::ImgRaw::new(20, 20, 4);
272 my $trans = i_color_new(255, 0, 0, 127);
273 i_box_filled($timg, 0, 0, 20, 20, $green);
274 i_box_filled($timg, 2, 2, 18, 18, $trans);
275 open FH, ">testout/t105_trans.gif" or die $!;
277 ok(i_writegif_gen(fileno(FH), { make_colors=>'addi',
278 translate=>'closest',
280 }, $timg), "write transparent");
283 # some error handling tests
284 # open a file handle for read and try to save to it
285 # is this idea portable?
286 # whether or not it is, giflib segfaults on this <sigh>
287 #open FH, "<testout/t105_trans.gif" or die $!;
288 #binmode FH; # habit, I suppose
289 #if (i_writegif_gen(fileno(FH), {}, $timg)) {
290 # # this is meant to _fail_
291 # print "not ok 18 # writing to read-only should fail";
294 # print "ok 18 # ",Imager::_error_as_msg(),"\n";
298 # try to read a file of the wrong format - the script will do
299 open FH, "<t/t105gif.t"
300 or die "Cannot open this script!: $!";
302 ok(!i_readgif(fileno(FH)),
303 "read test script as gif should fail ". Imager::_error_as_msg());
306 # try to save no images :)
307 open FH, ">testout/t105_none.gif"
308 or die "Cannot open testout/t105_none.gif: $!";
310 if (ok(!i_writegif_gen(fileno(FH), {}, "hello"), "shouldn't be able to write a string as a gif")) {
311 print "# ",Imager::_error_as_msg(),"\n";
314 # try to read a truncated gif (no image descriptors)
315 read_failure('testimg/trimgdesc.gif');
316 # file truncated just after the image descriptor tag
317 read_failure('testimg/trmiddesc.gif');
318 # image has no colour map
319 read_failure('testimg/nocmap.gif');
323 skip("see $buggy_giflib_file", 8) if -e $buggy_giflib_file;
324 # image has a local colour map
325 open FH, "< testimg/loccmap.gif"
326 or die "Cannot open testimg/loccmap.gif: $!";
328 ok(i_readgif(fileno(FH)), "read an image with only a local colour map");
331 # image has global and local colour maps
332 open FH, "< testimg/screen2.gif"
333 or die "Cannot open testimg/screen2.gif: $!";
335 my $ims = i_readgif(fileno(FH));
336 unless (ok($ims, "read an image with global and local colour map")) {
337 print "# ",Imager::_error_as_msg(),"\n";
341 open FH, "< testimg/expected.gif"
342 or die "Cannot open testimg/expected.gif: $!";
344 my $ime = i_readgif(fileno(FH));
346 ok($ime, "reading testimg/expected.gif");
349 skip("could not read one or both of expected.gif or loccamp.gif", 1)
350 unless $ims and $ime;
351 unless (is(i_img_diff($ime, $ims), 0,
352 "compare loccmap and expected")) {
354 open FH, "> testout/t105_screen2.gif"
355 or die "Cannot create testout/t105_screen.gif: $!";
357 i_writegifmc($ims, fileno(FH), 7)
358 or print "# could not save t105_screen.gif\n";
363 # test reading a multi-image file into multiple images
364 open FH, "< testimg/screen2.gif"
365 or die "Cannot open testimg/screen2.gif: $!";
367 @imgs = Imager::i_readgif_multi(fileno(FH));
368 ok(@imgs, "read multi-image file into multiple images");
370 is(@imgs, 2, "should be 2 images");
372 for my $img (@imgs) {
373 unless (Imager::i_img_type($img) == 1) {
378 ok($paletted, "both images should be paletted");
379 is(Imager::i_colorcount($imgs[0]), 4, "4 colours in first image");
380 is(Imager::i_colorcount($imgs[1]), 2, "2 colours in second image");
381 ok(Imager::i_tags_find($imgs[0], "gif_left", 0),
382 "gif_left tag should be there");
383 my @tags = map {[ Imager::i_tags_get($imgs[1], $_) ]} 0..Imager::i_tags_count($imgs[1])-1;
384 my ($left) = grep $_->[0] eq 'gif_left', @tags;
385 ok($left && $left->[1] == 3, "check gif_left value");
387 # screen3.gif was saved with
388 open FH, "< testimg/screen3.gif"
389 or die "Cannot open testimg/screen3.gif: $!";
391 @imgs = Imager::i_readgif_multi(fileno(FH));
392 ok(@imgs, "read screen3.gif");
395 require 'Data/Dumper.pm';
396 Data::Dumper->import();
399 # build a big map of all tags for all images
404 map { join ",", map { defined() ? $_ : "undef" } Imager::i_tags_get($im, $_) }
405 0..Imager::i_tags_count($_)-1
408 my $dump = Dumper(\@tags);
410 print "# tags from gif\n", $dump;
413 # at this point @imgs should contain only paletted images
414 ok(Imager::i_img_type($imgs[0]) == 1, "imgs[0] paletted");
415 ok(Imager::i_img_type($imgs[1]) == 1, "imgs[1] paletted");
417 # see how we go saving it
418 open FH, ">testout/t105_pal.gif" or die $!;
420 ok(i_writegif_gen(fileno(FH), { make_colors=>'addi',
421 translate=>'closest',
423 }, @imgs), "write from paletted");
426 # make sure nothing bad happened
427 open FH, "< testout/t105_pal.gif" or die $!;
429 ok((my @imgs2 = Imager::i_readgif_multi(fileno(FH))) == 2,
430 "re-reading saved paletted images");
431 ok(i_img_diff($imgs[0], $imgs2[0]) == 0, "imgs[0] mismatch");
432 ok(i_img_diff($imgs[1], $imgs2[1]) == 0, "imgs[1] mismatch");
435 # test that the OO interface warns when we supply old options
438 local $SIG{__WARN__} = sub { push(@warns, "@_") };
440 my $ooim = Imager->new;
441 ok($ooim->read(file=>"testout/t105.gif"), "read into object");
442 ok($ooim->write(file=>"testout/t105_warn.gif", interlace=>1),
444 ok(grep(/Obsolete .* interlace .* gif_interlace/, @warns),
445 "check for warning");
446 init(warn_obsolete=>0);
448 ok($ooim->write(file=>"testout/t105_warn.gif", interlace=>1),
450 ok(!grep(/Obsolete .* interlace .* gif_interlace/, @warns),
451 "check for warning");
454 # test that we get greyscale from 1 channel images
455 # we check for each makemap, and for each translate
456 print "# test writes of grayscale images - ticket #365\n";
457 my $ooim = Imager->new(xsize=>50, ysize=>50, channels=>1);
458 for (my $y = 0; $y < 50; $y += 10) {
459 $ooim->box(box=>[ 0, $y, 49, $y+9], color=>NC($y*5,0,0), filled=>1);
461 my $ooim3 = $ooim->convert(preset=>'rgb');
462 #$ooim3->write(file=>'testout/t105gray.ppm');
463 my %maxerror = ( mediancut => 51000,
468 for my $makemap (qw(mediancut addi)) {
469 print "# make_colors => $makemap\n";
470 ok( $ooim->write(file=>"testout/t105gray-$makemap.gif",
471 make_colors=>$makemap,
473 "writing gif with makemap $makemap");
474 my $im2 = Imager->new;
475 if (ok($im2->read(file=>"testout/t105gray-$makemap.gif"),
476 "reading written grayscale gif")) {
477 my $diff = i_img_diff($ooim3->{IMG}, $im2->{IMG});
478 ok($diff <= $maxerror{$makemap}, "comparing images $diff");
479 #$im2->write(file=>"testout/t105gray-$makemap.ppm");
482 SKIP: { skip("could not get test image", 1); }
485 for my $translate (qw(closest perturb errdiff)) {
486 print "# translate => $translate\n";
487 my @colors = map NC($_*50, $_*50, $_*50), 0..4;
488 ok($ooim->write(file=>"testout/t105gray-$translate.gif",
489 translate=>$translate,
493 "writing gif with translate $translate");
494 my $im2 = Imager->new;
495 if (ok($im2->read(file=>"testout/t105gray-$translate.gif"),
496 "reading written grayscale gif")) {
497 my $diff = i_img_diff($ooim3->{IMG}, $im2->{IMG});
498 ok($diff <= $maxerror{$translate}, "comparing images $diff");
499 #$im2->write(file=>"testout/t105gray-$translate.ppm");
502 SKIP: { skip("could not load test image", 1) }
506 # try to write an image with no colors - should error
507 ok(!$ooim->write(file=>"testout/t105nocolors.gif",
509 colors=>[], gifquant=>'gen'),
510 "write with no colors");
512 # try to write multiple with no colors, with separate maps
513 # I don't see a way to test this, since we don't have a mechanism
514 # to give the second image different quant options, we can't trigger
515 # a failure just for the second image
517 # check that the i_format tag is set for both multiple and single
520 my @anim = Imager->read_multi(file=>"testout/t105_anim.gif");
521 ok(@anim == 5, "check we got all the images");
522 for my $frame (@anim) {
523 my ($type) = $frame->tags(name=>'i_format');
524 is($type, 'gif', "check i_format for animation frame");
527 my $im = Imager->new;
528 ok($im->read(file=>"testout/t105.gif"), "read some gif");
529 my ($type) = $im->tags(name=>'i_format');
530 is($type, 'gif', 'check i_format for single image read');
534 sub test_readgif_cb {
537 open FH, "<testimg/scale.gif" or die "Cannot open testimg/scale.gif";
539 my $img = i_readgif_callback(sub { my $tmp; read(FH, $tmp, $size) and $tmp });
544 # tests for reading bad gif files
548 open FH, "< $filename"
549 or die "Cannot open $filename: $!";
551 my ($result, $map) = i_readgif(fileno(FH));
552 ok(!$result, "attempt to read invalid image $filename ".Imager::_error_as_msg());
559 for my $img (@imgs) {
560 $img->deltag(code=>0);
565 my ($img, %tags) = @_;
567 for my $key (keys %tags) {
568 Imager::i_tags_add($img, $key, 0, $tags{$key}, 0);
573 my ($testnum, $code, $count, $name) = @_;
576 $name ||= "gif$testnum";
579 my $script = "testout/$name.pl";
580 if (open SCRIPT, "> $script") {
581 print SCRIPT <<'PROLOG';
583 if (lc $^O eq 'mswin32') {
584 # avoid the dialog box that window's pops up on a GPF
585 # if you want to debug this stuff, I suggest you comment out the
588 require Win32API::File;
589 Win32API::File::SetErrorMode( Win32API::File::SEM_NOGPFAULTERRORBOX());
598 $perl = qq/"$perl"/ if $perl =~ / /;
600 print "# script: $script\n";
601 my $cmd = "$perl -Mblib $script";
602 print "# command: $cmd\n";
605 my @out = `$cmd`; # should work on DOS and Win32
608 if (/^not ok\s+(?:\d+\s*)?#(.*)/ || /^not ok/) {
614 elsif (/^ok\s+(?:\d+\s*)?#(.*)/ || /^ok/) {
620 unless ($count == $found) {
621 print "# didn't see enough ok/not ok\n";
627 return skip("could not create test script $script: $!");