6 BEGIN { require "t/testtools.pl"; }
8 my $buggy_giflib_file = "buggy_giflib.txt";
12 init_log("testout/t105gif.log",1);
14 my $green=i_color_new(0,255,0,255);
15 my $blue=i_color_new(0,0,255,255);
16 my $red=i_color_new(255,0,0,255);
18 my $img=Imager::ImgRaw::new(150,150,3);
20 i_box_filled($img,70,25,130,125,$green);
21 i_box_filled($img,20,25,80,125,$blue);
22 i_arc($img,75,75,30,0,361,$red);
23 i_conv($img,[0.1, 0.2, 0.4, 0.2, 0.1]);
25 my $timg = Imager::ImgRaw::new(20, 20, 4);
26 my $trans = i_color_new(255, 0, 0, 127);
27 i_box_filled($timg, 0, 0, 20, 20, $green);
28 i_box_filled($timg, 2, 2, 18, 18, $trans);
30 if (!i_has_format("gif")) {
31 skipn(1, 69, "no gif support");
33 open(FH,">testout/t105.gif") || die "Cannot open testout/t105.gif\n";
35 i_writegifmc($img,fileno(FH),6) || die "Cannot write testout/t105.gif\n";
40 open(FH,"testout/t105.gif") || die "Cannot open testout/t105.gif\n";
42 $img=i_readgif(fileno(FH)) || die "Cannot read testout/t105.gif\n";
47 open(FH,"testout/t105.gif") || die "Cannot open testout/t105.gif\n";
49 ($img, my $palette)=i_readgif(fileno(FH));
50 $img || die "Cannot read testout/t105.gif\n";
53 $palette=''; # just to skip a warning.
57 # check that reading interlaced/non-interlaced versions of
58 # the same GIF produce the same image
59 # I could replace this with code that used Imager's built-in
60 # image comparison code, but I know this code revealed the error
61 open(FH, "<testimg/scalei.gif") || die "Cannot open testimg/scalei.gif";
63 my ($imgi) = i_readgif(fileno(FH));
64 $imgi || die "Cannot read testimg/scalei.gif";
67 open FH, "<testimg/scale.gif" or die "Cannot open testimg/scale.gif";
69 my ($imgni) = i_readgif(fileno(FH));
70 $imgni or die "Cannot read testimg/scale.gif";
74 open FH, ">testout/t105i.ppm" or die "Cannot create testout/t105i.ppm";
76 my $IO = Imager::io_new_fd( fileno(FH) );
77 i_writeppm_wiol($imgi, $IO) or die "Cannot write testout/t105i.ppm";
81 open FH, ">testout/t105ni.ppm" or die "Cannot create testout/t105ni.ppm";
83 $IO = Imager::io_new_fd( fileno(FH) );
84 i_writeppm_wiol($imgni, $IO) or die "Cannot write testout/t105ni.ppm";
88 open FH, "<testout/t105i.ppm" or die "Cannot open testout/t105i.ppm";
89 my $datai = do { local $/; <FH> };
92 open FH, "<testout/t105ni.ppm" or die "Cannot open testout/t105ni.ppm";
93 my $datani = do { local $/; <FH> };
95 if ($datai eq $datani) {
102 my $gifver = Imager::i_giflib_version();
103 if ($gifver >= 4.0) {
104 # reading with a callback
105 # various sizes to make sure the buffering works
107 open FH, "<testimg/scale.gif" or die "Cannot open testimg/scale.gif";
109 # no callback version in giflib3, so don't overwrite a good image
110 my $img2 = i_readgif_callback(sub { my $tmp; read(FH, $tmp, $_[0]) and $tmp });
112 print $img ? "ok 7\n" : "not ok 7\n";
114 print test_readgif_cb(1) ? "ok 8\n" : "not ok 8\n";
115 print test_readgif_cb(512) ? "ok 9\n" : "not ok 9\n";
116 print test_readgif_cb(1024) ? "ok 10\n" : "not ok 10\n";
120 print "ok $_ # skip giflib3 doesn't support callbacks\n";
123 open FH, ">testout/t105_mc.gif" or die "Cannot open testout/t105_mc.gif";
125 i_writegifmc($img, fileno(FH), 7) or print "not ";
130 # test webmap, custom errdiff map
131 # (looks fairly awful)
132 open FH, ">testout/t105_gen.gif" or die $!;
134 i_writegif_gen(fileno(FH), { make_colors=>'webmap',
135 translate=>'errdiff',
139 errdiff_map=>[0, 1, 1, 0]}, $img)
144 print "# the following tests are fairly slow\n";
146 # test animation, mc_addi, error diffusion, ordered transparency
148 my $sortagreen = i_color_new(0, 255, 0, 63);
150 my $im = Imager::ImgRaw::new(200, 200, 4);
151 _add_tags($im, gif_delay=>50, gif_disposal=>2);
152 for my $j (0..$i-1) {
153 my $fill = i_color_new(0, 128, 0, 255 * ($i-$j)/$i);
154 i_box_filled($im, 0, $j*40, 199, $j*40+40, $fill);
156 i_box_filled($im, 0, $i*40, 199, 199, $blue);
159 my @gif_delays = (50) x 5;
160 my @gif_disposal = (2) x 5;
161 open FH, ">testout/t105_anim.gif" or die $!;
163 i_writegif_gen(fileno(FH), { make_colors=>'addi',
164 translate=>'closest',
165 gif_delays=>\@gif_delays,
166 gif_disposal=>\@gif_disposal,
167 gif_positions=> [ map [ $_*10, $_*10 ], 0..4 ],
168 gif_user_input=>[ 1, 0, 1, 0, 1 ],
170 tr_orddith=>'dot8'}, @imgs)
171 or die "Cannot write anim gif";
175 my $can_write_callback = 0;
176 unlink $buggy_giflib_file;
177 if ($gifver >= 4.0) {
178 ++$can_write_callback;
179 my $good = ext_test(14, <<'ENDOFCODE');
181 require "t/testtools.pl";
182 my $timg = test_img();
183 my @gif_delays = (50) x 5;
184 my @gif_disposal = (2) x 5;
185 my @imgs = ($timg) x 5;
186 open FH, "> testout/t105_anim_cb.gif" or die $!;
188 i_writegif_callback(sub {
192 { make_colors=>'webmap',
193 translate=>'closest',
194 gif_delays=>\@gif_delays,
195 gif_disposal=>\@gif_disposal,
197 tr_orddith=>'dot8'}, @imgs)
198 or die "Cannot write anim gif";
204 $can_write_callback = 0;
205 print "not ok 14 # see $buggy_giflib_file\n";
206 print STDERR "\nprobable buggy giflib - skipping tests that depend on a good giflib\n";
207 print STDERR "see $buggy_giflib_file for more information\n";
208 open FLAG, "> $buggy_giflib_file" or die;
210 This file is created by t105gif.t when test 14 fails.
212 This failure usually indicates you\'re using the original versions
213 of giflib 4.1.0 - 4.1.3, which have a few bugs that Imager tickles.
215 You can apply the patch from:
217 http://www.develop-help.com/imager/giflib.patch
219 or you can just install Imager as is, if you only need to write GIFs to
220 files or file descriptors (such as sockets).
222 One hunk of this patch is rejected (correctly) with giflib 4.1.3,
223 since one bug that the patch fixes is fixed in 4.1.3.
225 If you don't feel comfortable with that apply the patch file that
226 belongs to the following patch entry on sourceforge:
228 https://sourceforge.net/tracker/index.php?func=detail&aid=981255&group_id=102202&atid=631306
230 In previous versions of Imager only this test was careful about catching
231 the error, we now skip any tests that crashed or failed when the buggy
237 print "ok 14 # skip giflib3 doesn't support callbacks\n";
240 my $c = i_color_new(0,0,0,0);
242 my $im = Imager::ImgRaw::new(200, 200, 3);
243 _add_tags($im, gif_local_map=>1, gif_delay=>150, gif_loop=>10);
244 for my $x (0 .. 39) {
245 for my $y (0 .. 39) {
246 $c->set($x * 6, $y * 6, 32*$g+$x+$y, 255);
247 i_box_filled($im, $x*5, $y*5, $x*5+4, $y*5+4, $c);
252 # test giflib with multiple palettes
253 # (it was meant to test the NS loop extension too, but that's broken)
254 # this looks better with make_colors=>'addi', translate=>'errdiff'
255 # this test aims to overload the palette for each image, so the
256 # output looks moderately horrible
257 open FH, ">testout/t105_mult_pall.gif" or die "Cannot create file: $!";
259 if (i_writegif_gen(fileno(FH), { #make_colors=>'webmap',
265 print "not ok 15 # ", join(":", map $_->[1], Imager::i_errors()),"\n";
269 # regression test: giflib doesn't like 1 colour images
270 my $img1 = Imager::ImgRaw::new(100, 100, 3);
271 i_box_filled($img1, 0, 0, 100, 100, $red);
272 open FH, ">testout/t105_onecol.gif" or die $!;
274 if (i_writegif_gen(fileno(FH), { translate=>'giflib'}, $img1)) {
275 print "ok 16 # single colour write regression\n";
277 print "not ok 16 # single colour write regression\n";
282 # previously it was harder do write transparent images
283 # tests the improvements
284 my $timg = Imager::ImgRaw::new(20, 20, 4);
285 my $trans = i_color_new(255, 0, 0, 127);
286 i_box_filled($timg, 0, 0, 20, 20, $green);
287 i_box_filled($timg, 2, 2, 18, 18, $trans);
288 open FH, ">testout/t105_trans.gif" or die $!;
290 i_writegif_gen(fileno(FH), { make_colors=>'addi',
291 translate=>'closest',
293 }, $timg) or print "not ";
297 # some error handling tests
298 # open a file handle for read and try to save to it
299 # is this idea portable?
300 # whether or not it is, giflib segfaults on this <sigh>
301 #open FH, "<testout/t105_trans.gif" or die $!;
302 #binmode FH; # habit, I suppose
303 #if (i_writegif_gen(fileno(FH), {}, $timg)) {
304 # # this is meant to _fail_
305 # print "not ok 18 # writing to read-only should fail";
308 # print "ok 18 # ",Imager::_error_as_msg(),"\n";
312 # try to read a file of the wrong format - the script will do
313 open FH, "<t/t105gif.t"
314 or die "Cannot open this script!: $!";
316 my $im2 = i_readgif(fileno(FH));
318 # this should have failed
319 print "not ok 18 # giflib think script if a GIF file\n";
322 print "ok 18 # ",Imager::_error_as_msg(),"\n";
326 # try to save no images :)
327 open FH, ">testout/t105_none.gif"
328 or die "Cannot open testout/t105_none.gif: $!";
330 if (i_writegif_gen(fileno(FH), {}, "hello")) {
331 print "not ok 19 # shouldn't be able to save strings\n";
334 print "ok 19 # ",Imager::_error_as_msg(),"\n";
337 # try to read a truncated gif (no image descriptors)
338 read_failure('testimg/trimgdesc.gif', 20);
339 # file truncated just after the image descriptor tag
340 read_failure('testimg/trmiddesc.gif', 21);
341 # image has no colour map
342 read_failure('testimg/nocmap.gif', 22);
344 unless (-e $buggy_giflib_file) {
345 # image has a local colour map
346 open FH, "< testimg/loccmap.gif"
347 or die "Cannot open testimg/loccmap.gif: $!";
349 if (i_readgif(fileno(FH))) {
353 print "not ok 23 # failed to read image with only a local colour map";
357 # image has global and local colour maps
358 open FH, "< testimg/screen2.gif"
359 or die "Cannot open testimg/screen2.gif: $!";
361 my $ims = i_readgif(fileno(FH));
366 print "not ok 24 # ",Imager::_error_as_msg(),"\n";
369 open FH, "< testimg/expected.gif"
370 or die "Cannot open testimg/expected.gif: $!";
372 my $ime = i_readgif(fileno(FH));
378 print "not ok 25 # ",Imager::_error_as_msg(),"\n";
381 if (i_img_diff($ime, $ims)) {
382 print "not ok 26 # mismatch ",i_img_diff($ime, $ims),"\n";
384 open FH, "> testout/t105_screen2.gif"
385 or die "Cannot create testout/t105_screen.gif: $!";
387 i_writegifmc($ims, fileno(FH), 7)
388 or print "# could not save t105_screen.gif\n";
396 print "ok 26 # skipped\n";
399 # test reading a multi-image file into multiple images
400 open FH, "< testimg/screen2.gif"
401 or die "Cannot open testimg/screen2.gif: $!";
403 @imgs = Imager::i_readgif_multi(fileno(FH))
407 @imgs == 2 or print "not ";
409 for my $img (@imgs) {
410 unless (Imager::i_img_type($img) == 1) {
416 Imager::i_colorcount($imgs[0]) == 4 or print "not ";
418 Imager::i_colorcount($imgs[1]) == 2 or print "not ";
420 Imager::i_tags_find($imgs[0], "gif_left", 0) or print "not ";
422 my @tags = map {[ Imager::i_tags_get($imgs[1], $_) ]} 0..Imager::i_tags_count($imgs[1])-1;
423 my ($left) = grep $_->[0] eq 'gif_left', @tags;
424 $left && $left->[1] == 3 or print "not ";
427 # screen3.gif was saved with
428 open FH, "< testimg/screen3.gif"
429 or die "Cannot open testimg/screen3.gif: $!";
431 @imgs = Imager::i_readgif_multi(fileno(FH))
436 require 'Data/Dumper.pm';
437 Data::Dumper->import();
440 # build a big map of all tags for all images
445 map { join ",", map { defined() ? $_ : "undef" } Imager::i_tags_get($im, $_) }
446 0..Imager::i_tags_count($_)-1
449 my $dump = Dumper(\@tags);
451 print "# tags from gif\n", $dump;
454 # at this point @imgs should contain only paletted images
455 ok(35, Imager::i_img_type($imgs[0]) == 1, "imgs[0] not paletted");
456 ok(36, Imager::i_img_type($imgs[1]) == 1, "imgs[1] not paletted");
458 # see how we go saving it
459 open FH, ">testout/t105_pal.gif" or die $!;
461 ok(37, i_writegif_gen(fileno(FH), { make_colors=>'addi',
462 translate=>'closest',
464 }, @imgs), "write from paletted");
467 # make sure nothing bad happened
468 open FH, "< testout/t105_pal.gif" or die $!;
470 ok(38, (my @imgs2 = Imager::i_readgif_multi(fileno(FH))) == 2,
471 "re-reading saved paletted images");
472 ok(39, i_img_diff($imgs[0], $imgs2[0]) == 0, "imgs[0] mismatch");
473 ok(40, i_img_diff($imgs[1], $imgs2[1]) == 0, "imgs[1] mismatch");
477 print "ok $_ # skip see $buggy_giflib_file\n";
480 # test that the OO interface warns when we supply old options
483 local $SIG{__WARN__} = sub { push(@warns, "@_") };
485 my $ooim = Imager->new;
486 ok(41, $ooim->read(file=>"testout/t105.gif"), "read into object");
487 ok(42, $ooim->write(file=>"testout/t105_warn.gif", interlace=>1),
489 ok(43, grep(/Obsolete .* interlace .* gif_interlace/, @warns),
490 "check for warning");
491 init(warn_obsolete=>0);
493 ok(44, $ooim->write(file=>"testout/t105_warn.gif", interlace=>1),
495 ok(45, !grep(/Obsolete .* interlace .* gif_interlace/, @warns),
496 "check for warning");
499 # test that we get greyscale from 1 channel images
500 # we check for each makemap, and for each translate
501 print "# test writes of grayscale images - ticket #365\n";
503 my $ooim = Imager->new(xsize=>50, ysize=>50, channels=>1);
504 for (my $y = 0; $y < 50; $y += 10) {
505 $ooim->box(box=>[ 0, $y, 49, $y+9], color=>NC($y*5,0,0), filled=>1);
507 my $ooim3 = $ooim->convert(preset=>'rgb');
508 #$ooim3->write(file=>'testout/t105gray.ppm');
509 my %maxerror = ( mediancut => 51000,
514 for my $makemap (qw(mediancut addi)) {
515 print "# make_colors => $makemap\n";
516 ok($num++, $ooim->write(file=>"testout/t105gray-$makemap.gif",
517 make_colors=>$makemap,
519 "writing gif with makemap $makemap");
520 my $im2 = Imager->new;
521 if (ok($num++, $im2->read(file=>"testout/t105gray-$makemap.gif"),
522 "reading written grayscale gif")) {
523 my $diff = i_img_diff($ooim3->{IMG}, $im2->{IMG});
524 ok($num++, $diff <= $maxerror{$makemap}, "comparing images $diff");
525 #$im2->write(file=>"testout/t105gray-$makemap.ppm");
528 print "ok $num # skip\n";
532 for my $translate (qw(closest perturb errdiff)) {
533 print "# translate => $translate\n";
534 my @colors = map NC($_*50, $_*50, $_*50), 0..4;
535 ok($num++, $ooim->write(file=>"testout/t105gray-$translate.gif",
536 translate=>$translate,
540 "writing gif with translate $translate");
541 my $im2 = Imager->new;
542 if (ok($num++, $im2->read(file=>"testout/t105gray-$translate.gif"),
543 "reading written grayscale gif")) {
544 my $diff = i_img_diff($ooim3->{IMG}, $im2->{IMG});
545 ok($num++, $diff <= $maxerror{$translate}, "comparing images $diff");
546 #$im2->write(file=>"testout/t105gray-$translate.ppm");
549 print "ok $num # skip\n";
554 # try to write an image with no colors - should error
555 ok($num++, !$ooim->write(file=>"testout/t105nocolors.gif",
557 colors=>[], gifquant=>'gen'),
558 "write with no colors");
560 # try to write multiple with no colors, with separate maps
561 # I don't see a way to test this, since we don't have a mechanism
562 # to give the second image different quant options, we can't trigger
563 # a failure just for the second image
565 # check that the i_format tag is set for both multiple and single
568 my @anim = Imager->read_multi(file=>"testout/t105_anim.gif");
569 okn($num++, @anim == 5, "check we got all the images");
570 for my $frame (@anim) {
571 my ($type) = $frame->tags(name=>'i_format');
572 isn($num++, $type, 'gif', "check i_format for animation frame");
575 my $im = Imager->new;
576 okn($num++, $im->read(file=>"testout/t105.gif"), "read some gif");
577 my ($type) = $im->tags(name=>'i_format');
578 isn($num++, $type, 'gif', 'check i_format for single image read');
583 my ($num, $ok, $comment) = @_;
589 print "not ok $num # line ",(caller)[2],": $comment \n";
593 sub test_readgif_cb {
596 open FH, "<testimg/scale.gif" or die "Cannot open testimg/scale.gif";
598 my $img = i_readgif_callback(sub { my $tmp; read(FH, $tmp, $size) and $tmp });
603 # tests for reading bad gif files
605 my ($filename, $testnum) = @_;
607 open FH, "< $filename"
608 or die "Cannot open $filename: $!";
610 my ($result, $map) = i_readgif(fileno(FH));
612 print "not ok $testnum # this is an invalid file, we succeeded\n";
615 print "ok $testnum # ",Imager::_error_as_msg(),"\n";
623 for my $img (@imgs) {
624 $img->deltag(code=>0);
629 my ($img, %tags) = @_;
631 for my $key (keys %tags) {
632 Imager::i_tags_add($img, $key, 0, $tags{$key}, 0);
637 my ($testnum, $code, $count, $name) = @_;
640 $name ||= "gif$testnum";
643 my $script = "testout/$name.pl";
644 if (open SCRIPT, "> $script") {
645 print SCRIPT <<'PROLOG';
647 if (lc $^O eq 'mswin32') {
648 # avoid the dialog box that window's pops up on a GPF
649 # if you want to debug this stuff, I suggest you comment out the
652 require Win32API::File;
653 Win32API::File::SetErrorMode( Win32API::File::SEM_NOGPFAULTERRORBOX());
662 $perl = qq/"$perl"/ if $perl =~ / /;
664 print "# script: $script\n";
665 my $cmd = "$perl -Mblib $script";
666 print "# command: $cmd\n";
669 my @out = `$cmd`; # should work on DOS and Win32
681 unless ($count == $found) {
682 print "# didn't see enough ok/not ok\n";
688 return skip($testnum, $count, "could not create test script $script: $!");