]> git.imager.perl.org - imager.git/blame - PNG/t/10png.t
PNG re-work: improve error reporting for PNG read/write
[imager.git] / PNG / t / 10png.t
CommitLineData
352c64ed
TC
1#!perl -w
2use strict;
37a9be8e
TC
3use Imager qw(:all);
4use Test::More;
6d5c85a2 5use Imager::Test qw(test_image_raw test_image);
1cdc4cbd 6
38eab175
TC
7my $debug_writes = 1;
8
1d7e3124
TC
9-d "testout" or mkdir "testout";
10
9e35eed7 11init_log("testout/t102png.log",1);
1cdc4cbd 12
1d7e3124 13$Imager::formats{"png"}
3a64cddb 14 or plan skip_all => "No png support";
37a9be8e 15
38eab175 16plan tests => 39;
1cdc4cbd 17
352c64ed
TC
18my $green = i_color_new(0, 255, 0, 255);
19my $blue = i_color_new(0, 0, 255, 255);
20my $red = i_color_new(255, 0, 0, 255);
1cdc4cbd 21
37a9be8e 22my $img = test_image_raw();
1cdc4cbd
TC
23
24my $timg = Imager::ImgRaw::new(20, 20, 4);
25my $trans = i_color_new(255, 0, 0, 127);
26i_box_filled($timg, 0, 0, 20, 20, $green);
27i_box_filled($timg, 2, 2, 18, 18, $trans);
28
37a9be8e
TC
29Imager::i_tags_add($img, "i_xres", 0, "300", 0);
30Imager::i_tags_add($img, "i_yres", 0, undef, 200);
31# the following confuses the GIMP
32#Imager::i_tags_add($img, "i_aspect_only", 0, undef, 1);
33open(FH,">testout/t102.png") || die "cannot open testout/t102.png for writing\n";
34binmode(FH);
35my $IO = Imager::io_new_fd(fileno(FH));
1d7e3124 36ok(Imager::File::PNG::i_writepng_wiol($img, $IO), "write");
37a9be8e
TC
37close(FH);
38
39open(FH,"testout/t102.png") || die "cannot open testout/t102.png\n";
40binmode(FH);
41$IO = Imager::io_new_fd(fileno(FH));
1d7e3124 42my $cmpimg = Imager::File::PNG::i_readpng_wiol($IO);
37a9be8e
TC
43close(FH);
44ok($cmpimg, "read png");
45
46print "# png average mean square pixel difference: ",sqrt(i_img_diff($img,$cmpimg))/150*150,"\n";
47is(i_img_diff($img, $cmpimg), 0, "compare saved and original images");
48
49my %tags = map { Imager::i_tags_get($cmpimg, $_) }
50 0..Imager::i_tags_count($cmpimg) - 1;
51ok(abs($tags{i_xres} - 300) < 1, "i_xres: $tags{i_xres}");
52ok(abs($tags{i_yres} - 200) < 1, "i_yres: $tags{i_yres}");
53is($tags{i_format}, "png", "i_format: $tags{i_format}");
54
55open FH, "> testout/t102_trans.png"
56 or die "Cannot open testout/t102_trans.png: $!";
57binmode FH;
58$IO = Imager::io_new_fd(fileno(FH));
1d7e3124 59ok(Imager::File::PNG::i_writepng_wiol($timg, $IO), "write tranparent");
37a9be8e
TC
60close FH;
61
62open FH,"testout/t102_trans.png"
63 or die "cannot open testout/t102_trans.png\n";
64binmode(FH);
65$IO = Imager::io_new_fd(fileno(FH));
1d7e3124 66$cmpimg = Imager::File::PNG::i_readpng_wiol($IO);
37a9be8e
TC
67ok($cmpimg, "read transparent");
68close(FH);
69
70print "# png average mean square pixel difference: ",sqrt(i_img_diff($timg,$cmpimg))/150*150,"\n";
71is(i_img_diff($timg, $cmpimg), 0, "compare saved and original transparent");
72
73# REGRESSION TEST
74# png.c 1.1 would produce an incorrect image when loading images with
75# less than 8 bits/pixel with a transparent palette entry
76open FH, "< testimg/palette.png"
77 or die "cannot open testimg/palette.png: $!\n";
78binmode FH;
79$IO = Imager::io_new_fd(fileno(FH));
80# 1.1 may segfault here (it does with libefence)
1d7e3124 81my $pimg = Imager::File::PNG::i_readpng_wiol($IO);
37a9be8e
TC
82ok($pimg, "read transparent paletted image");
83close FH;
84
85open FH, "< testimg/palette_out.png"
86 or die "cannot open testimg/palette_out.png: $!\n";
87binmode FH;
88$IO = Imager::io_new_fd(fileno(FH));
1d7e3124 89my $poimg = Imager::File::PNG::i_readpng_wiol($IO);
37a9be8e
TC
90ok($poimg, "read palette_out image");
91close FH;
92if (!is(i_img_diff($pimg, $poimg), 0, "images the same")) {
93 print <<EOS;
d8bbe40d
TC
94# this tests a bug in Imager's png.c v1.1
95# if also tickles a bug in libpng before 1.0.5, so you may need to
96# upgrade libpng
97EOS
37a9be8e
TC
98}
99
100{ # check file limits are checked
101 my $limit_file = "testout/t102.png";
102 ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
103 my $im = Imager->new;
104 ok(!$im->read(file=>$limit_file),
105 "should fail read due to size limits");
106 print "# ",$im->errstr,"\n";
107 like($im->errstr, qr/image width/, "check message");
108
109 ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
110 ok(!$im->read(file=>$limit_file),
111 "should fail read due to size limits");
112 print "# ",$im->errstr,"\n";
113 like($im->errstr, qr/image height/, "check message");
114
115 ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
116 ok($im->read(file=>$limit_file),
117 "should succeed - just inside width limit");
118 ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
119 ok($im->read(file=>$limit_file),
120 "should succeed - just inside height limit");
121
122 # 150 x 150 x 3 channel image uses 67500 bytes
123 ok(Imager->set_file_limits(reset=>1, bytes=>67499),
124 "set bytes limit 67499");
125 ok(!$im->read(file=>$limit_file),
77157728 126 "should fail - too many bytes");
37a9be8e 127 print "# ",$im->errstr,"\n";
77157728 128 like($im->errstr, qr/storage size/, "check error message");
37a9be8e
TC
129 ok(Imager->set_file_limits(reset=>1, bytes=>67500),
130 "set bytes limit 67500");
131 ok($im->read(file=>$limit_file),
132 "should succeed - just inside bytes limit");
133 Imager->set_file_limits(reset=>1);
134}
e7ff1cf7 135
37a9be8e
TC
136{ # check if the read_multi fallback works
137 my @imgs = Imager->read_multi(file => 'testout/t102.png');
138 is(@imgs, 1, "check the image was loaded");
139 is(i_img_diff($img, $imgs[0]), 0, "check image matches");
140
141 # check the write_multi fallback
142 ok(Imager->write_multi({ file => 'testout/t102m.png', type => 'png' },
143 @imgs),
144 'test write_multi() callback');
145
146 # check that we fail if we actually write 2
147 ok(!Imager->write_multi({ file => 'testout/t102m.png', type => 'png' },
e7ff1cf7 148 @imgs, @imgs),
37a9be8e
TC
149 'test write_multi() callback failure');
150}
f245645a 151
6d5c85a2
TC
152{ # check close failures are handled correctly
153 my $im = test_image();
154 my $fail_close = sub {
155 Imager::i_push_error(0, "synthetic close failure");
156 return 0;
157 };
158 ok(!$im->write(type => "png", callback => sub { 1 },
159 closecb => $fail_close),
160 "check failing close fails");
161 like($im->errstr, qr/synthetic close failure/,
162 "check error message");
163}
164
37a9be8e
TC
165{
166 ok(grep($_ eq 'png', Imager->read_types), "check png in read types");
167 ok(grep($_ eq 'png', Imager->write_types), "check png in write types");
1cdc4cbd 168}
37a9be8e 169
38eab175
TC
170{ # read error reporting
171 my $im = Imager->new;
172 ok(!$im->read(file => "testimg/badcrc.png", type => "png"),
173 "read png with bad CRC chunk should fail");
174 is($im->errstr, "IHDR: CRC error", "check error message");
175}
176
177{ # write error reporting
178 my $im = test_image();
179 ok(!$im->write(type => "png", callback => limited_write(1), buffered => 0),
180 "write limited to 1 byte should fail");
181 is($im->errstr, "Write error on an iolayer source.: limit reached",
182 "check error message");
183}
184
185sub limited_write {
186 my ($limit) = @_;
187
188 return
189 sub {
190 my ($data) = @_;
191 $limit -= length $data;
192 if ($limit >= 0) {
193 print "# write of ", length $data, " bytes successful ($limit left)\n" if $debug_writes;
194 return 1;
195 }
196 else {
197 print "# write of ", length $data, " bytes failed\n";
198 Imager::i_push_error(0, "limit reached");
199 return;
200 }
201 };
202}