6 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, 61, "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 version
213 of giflib 4.1.0, which has 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 In previous versions of Imager only this test was careful about catching
223 the error, we now skip any tests that crashed or failed when the buggy
229 print "ok 14 # skip giflib3 doesn't support callbacks\n";
232 my $c = i_color_new(0,0,0,0);
234 my $im = Imager::ImgRaw::new(200, 200, 3);
235 _add_tags($im, gif_local_map=>1, gif_delay=>150, gif_loop=>10);
236 for my $x (0 .. 39) {
237 for my $y (0 .. 39) {
238 $c->set($x * 6, $y * 6, 32*$g+$x+$y, 255);
239 i_box_filled($im, $x*5, $y*5, $x*5+4, $y*5+4, $c);
244 # test giflib with multiple palettes
245 # (it was meant to test the NS loop extension too, but that's broken)
246 # this looks better with make_colors=>'addi', translate=>'errdiff'
247 # this test aims to overload the palette for each image, so the
248 # output looks moderately horrible
249 open FH, ">testout/t105_mult_pall.gif" or die "Cannot create file: $!";
251 if (i_writegif_gen(fileno(FH), { #make_colors=>'webmap',
257 print "not ok 15 # ", join(":", map $_->[1], Imager::i_errors()),"\n";
261 # regression test: giflib doesn't like 1 colour images
262 my $img1 = Imager::ImgRaw::new(100, 100, 3);
263 i_box_filled($img1, 0, 0, 100, 100, $red);
264 open FH, ">testout/t105_onecol.gif" or die $!;
266 if (i_writegif_gen(fileno(FH), { translate=>'giflib'}, $img1)) {
267 print "ok 16 # single colour write regression\n";
269 print "not ok 16 # single colour write regression\n";
274 # previously it was harder do write transparent images
275 # tests the improvements
276 my $timg = Imager::ImgRaw::new(20, 20, 4);
277 my $trans = i_color_new(255, 0, 0, 127);
278 i_box_filled($timg, 0, 0, 20, 20, $green);
279 i_box_filled($timg, 2, 2, 18, 18, $trans);
280 open FH, ">testout/t105_trans.gif" or die $!;
282 i_writegif_gen(fileno(FH), { make_colors=>'addi',
283 translate=>'closest',
285 }, $timg) or print "not ";
289 # some error handling tests
290 # open a file handle for read and try to save to it
291 # is this idea portable?
292 # whether or not it is, giflib segfaults on this <sigh>
293 #open FH, "<testout/t105_trans.gif" or die $!;
294 #binmode FH; # habit, I suppose
295 #if (i_writegif_gen(fileno(FH), {}, $timg)) {
296 # # this is meant to _fail_
297 # print "not ok 18 # writing to read-only should fail";
300 # print "ok 18 # ",Imager::_error_as_msg(),"\n";
304 # try to read a file of the wrong format - the script will do
305 open FH, "<t/t105gif.t"
306 or die "Cannot open this script!: $!";
308 my $im2 = i_readgif(fileno(FH));
310 # this should have failed
311 print "not ok 18 # giflib think script if a GIF file\n";
314 print "ok 18 # ",Imager::_error_as_msg(),"\n";
318 # try to save no images :)
319 open FH, ">testout/t105_none.gif"
320 or die "Cannot open testout/t105_none.gif: $!";
322 if (i_writegif_gen(fileno(FH), {}, "hello")) {
323 print "not ok 19 # shouldn't be able to save strings\n";
326 print "ok 19 # ",Imager::_error_as_msg(),"\n";
329 # try to read a truncated gif (no image descriptors)
330 read_failure('testimg/trimgdesc.gif', 20);
331 # file truncated just after the image descriptor tag
332 read_failure('testimg/trmiddesc.gif', 21);
333 # image has no colour map
334 read_failure('testimg/nocmap.gif', 22);
336 unless (-e $buggy_giflib_file) {
337 # image has a local colour map
338 open FH, "< testimg/loccmap.gif"
339 or die "Cannot open testimg/loccmap.gif: $!";
341 if (i_readgif(fileno(FH))) {
345 print "not ok 23 # failed to read image with only a local colour map";
349 # image has global and local colour maps
350 open FH, "< testimg/screen2.gif"
351 or die "Cannot open testimg/screen2.gif: $!";
353 my $ims = i_readgif(fileno(FH));
358 print "not ok 24 # ",Imager::_error_as_msg(),"\n";
361 open FH, "< testimg/expected.gif"
362 or die "Cannot open testimg/expected.gif: $!";
364 my $ime = i_readgif(fileno(FH));
370 print "not ok 25 # ",Imager::_error_as_msg(),"\n";
373 if (i_img_diff($ime, $ims)) {
374 print "not ok 26 # mismatch ",i_img_diff($ime, $ims),"\n";
376 open FH, "> testout/t105_screen2.gif"
377 or die "Cannot create testout/t105_screen.gif: $!";
379 i_writegifmc($ims, fileno(FH), 7)
380 or print "# could not save t105_screen.gif\n";
388 print "ok 26 # skipped\n";
391 # test reading a multi-image file into multiple images
392 open FH, "< testimg/screen2.gif"
393 or die "Cannot open testimg/screen2.gif: $!";
395 @imgs = Imager::i_readgif_multi(fileno(FH))
399 @imgs == 2 or print "not ";
401 for my $img (@imgs) {
402 unless (Imager::i_img_type($img) == 1) {
408 Imager::i_colorcount($imgs[0]) == 4 or print "not ";
410 Imager::i_colorcount($imgs[1]) == 2 or print "not ";
412 Imager::i_tags_find($imgs[0], "gif_left", 0) or print "not ";
414 my @tags = map {[ Imager::i_tags_get($imgs[1], $_) ]} 0..Imager::i_tags_count($imgs[1])-1;
415 my ($left) = grep $_->[0] eq 'gif_left', @tags;
416 $left && $left->[1] == 3 or print "not ";
419 # screen3.gif was saved with
420 open FH, "< testimg/screen3.gif"
421 or die "Cannot open testimg/screen3.gif: $!";
423 @imgs = Imager::i_readgif_multi(fileno(FH))
428 require 'Data/Dumper.pm';
429 Data::Dumper->import();
432 # build a big map of all tags for all images
437 map { join ",", map { defined() ? $_ : "undef" } Imager::i_tags_get($im, $_) }
438 0..Imager::i_tags_count($_)-1
441 my $dump = Dumper(\@tags);
443 print "# tags from gif\n", $dump;
446 # at this point @imgs should contain only paletted images
447 ok(35, Imager::i_img_type($imgs[0]) == 1, "imgs[0] not paletted");
448 ok(36, Imager::i_img_type($imgs[1]) == 1, "imgs[1] not paletted");
450 # see how we go saving it
451 open FH, ">testout/t105_pal.gif" or die $!;
453 ok(37, i_writegif_gen(fileno(FH), { make_colors=>'addi',
454 translate=>'closest',
456 }, @imgs), "write from paletted");
459 # make sure nothing bad happened
460 open FH, "< testout/t105_pal.gif" or die $!;
462 ok(38, (my @imgs2 = Imager::i_readgif_multi(fileno(FH))) == 2,
463 "re-reading saved paletted images");
464 ok(39, i_img_diff($imgs[0], $imgs2[0]) == 0, "imgs[0] mismatch");
465 ok(40, i_img_diff($imgs[1], $imgs2[1]) == 0, "imgs[1] mismatch");
469 print "ok $_ # skip see $buggy_giflib_file\n";
472 # test that the OO interface warns when we supply old options
475 local $SIG{__WARN__} = sub { push(@warns, "@_") };
477 my $ooim = Imager->new;
478 ok(41, $ooim->read(file=>"testout/t105.gif"), "read into object");
479 ok(42, $ooim->write(file=>"testout/t105_warn.gif", interlace=>1),
481 ok(43, grep(/Obsolete .* interlace .* gif_interlace/, @warns),
482 "check for warning");
483 init(warn_obsolete=>0);
485 ok(44, $ooim->write(file=>"testout/t105_warn.gif", interlace=>1),
487 ok(45, !grep(/Obsolete .* interlace .* gif_interlace/, @warns),
488 "check for warning");
491 # test that we get greyscale from 1 channel images
492 # we check for each makemap, and for each translate
493 print "# test writes of grayscale images - ticket #365\n";
495 my $ooim = Imager->new(xsize=>50, ysize=>50, channels=>1);
496 for (my $y = 0; $y < 50; $y += 10) {
497 $ooim->box(box=>[ 0, $y, 49, $y+9], color=>NC($y*5,0,0), filled=>1);
499 my $ooim3 = $ooim->convert(preset=>'rgb');
500 #$ooim3->write(file=>'testout/t105gray.ppm');
501 my %maxerror = ( mediancut => 51000,
506 for my $makemap (qw(mediancut addi)) {
507 print "# make_colors => $makemap\n";
508 ok($num++, $ooim->write(file=>"testout/t105gray-$makemap.gif",
509 make_colors=>$makemap,
511 "writing gif with makemap $makemap");
512 my $im2 = Imager->new;
513 if (ok($num++, $im2->read(file=>"testout/t105gray-$makemap.gif"),
514 "reading written grayscale gif")) {
515 my $diff = i_img_diff($ooim3->{IMG}, $im2->{IMG});
516 ok($num++, $diff <= $maxerror{$makemap}, "comparing images $diff");
517 #$im2->write(file=>"testout/t105gray-$makemap.ppm");
520 print "ok $num # skip\n";
524 for my $translate (qw(closest perturb errdiff)) {
525 print "# translate => $translate\n";
526 my @colors = map NC($_*50, $_*50, $_*50), 0..4;
527 ok($num++, $ooim->write(file=>"testout/t105gray-$translate.gif",
528 translate=>$translate,
532 "writing gif with translate $translate");
533 my $im2 = Imager->new;
534 if (ok($num++, $im2->read(file=>"testout/t105gray-$translate.gif"),
535 "reading written grayscale gif")) {
536 my $diff = i_img_diff($ooim3->{IMG}, $im2->{IMG});
537 ok($num++, $diff <= $maxerror{$translate}, "comparing images $diff");
538 #$im2->write(file=>"testout/t105gray-$translate.ppm");
541 print "ok $num # skip\n";
546 # try to write an image with no colors - should error
547 ok($num++, !$ooim->write(file=>"testout/t105nocolors.gif",
549 colors=>[], gifquant=>'gen'),
550 "write with no colors");
552 # try to write multiple with no colors, with separate maps
553 # I don't see a way to test this, since we don't have a mechanism
554 # to give the second image different quant options, we can't trigger
555 # a failure just for the second image
559 my ($num, $ok, $comment) = @_;
565 print "not ok $num # line ",(caller)[2],": $comment \n";
569 sub test_readgif_cb {
572 open FH, "<testimg/scale.gif" or die "Cannot open testimg/scale.gif";
574 my $img = i_readgif_callback(sub { my $tmp; read(FH, $tmp, $size) and $tmp });
579 # tests for reading bad gif files
581 my ($filename, $testnum) = @_;
583 open FH, "< $filename"
584 or die "Cannot open $filename: $!";
586 my ($result, $map) = i_readgif(fileno(FH));
588 print "not ok $testnum # this is an invalid file, we succeeded\n";
591 print "ok $testnum # ",Imager::_error_as_msg(),"\n";
599 for my $img (@imgs) {
600 $img->deltag(code=>0);
605 my ($img, %tags) = @_;
607 for my $key (keys %tags) {
608 Imager::i_tags_add($img, $key, 0, $tags{$key}, 0);
613 my ($testnum, $code, $count, $name) = @_;
616 $name ||= "gif$testnum";
619 my $script = "testout/$name.pl";
620 if (open SCRIPT, "> $script") {
621 print SCRIPT <<'PROLOG';
623 if (lc $^O eq 'mswin32') {
624 # avoid the dialog box that window's pops up on a GPF
625 # if you want to debug this stuff, I suggest you comment out the
628 require Win32API::File;
629 Win32API::File::SetErrorMode( Win32API::File::SEM_NOGPFAULTERRORBOX());
638 $perl = qq/"$perl"/ if $perl =~ / /;
640 print "# script: $script\n";
641 my $cmd = "$perl -Mblib $script";
642 print "# command: $cmd\n";
645 my @out = `$cmd`; # should work on DOS and Win32
657 unless ($count == $found) {
658 print "# didn't see enough ok/not ok\n";
664 return skip($testnum, $count, "could not create test script $script: $!");