Commit | Line | Data |
---|---|---|
66614d6e TC |
1 | #!perl -w |
2 | use strict; | |
20adc63d | 3 | use Imager qw(:all); |
6e4af7d4 TC |
4 | use Test::More tests => 94; |
5 | use Imager::Test qw(is_color_close3); | |
20adc63d TC |
6 | |
7 | init_log("testout/t101jpeg.log",1); | |
8 | ||
66614d6e TC |
9 | my $green=i_color_new(0,255,0,255); |
10 | my $blue=i_color_new(0,0,255,255); | |
11 | my $red=i_color_new(255,0,0,255); | |
20adc63d | 12 | |
66614d6e TC |
13 | my $img=Imager::ImgRaw::new(150,150,3); |
14 | my $cmpimg=Imager::ImgRaw::new(150,150,3); | |
20adc63d TC |
15 | |
16 | i_box_filled($img,70,25,130,125,$green); | |
17 | i_box_filled($img,20,25,80,125,$blue); | |
18 | i_arc($img,75,75,30,0,361,$red); | |
19 | i_conv($img,[0.1, 0.2, 0.4, 0.2, 0.1]); | |
20 | ||
21 | i_has_format("jpeg") && print "# has jpeg\n"; | |
22 | if (!i_has_format("jpeg")) { | |
66614d6e TC |
23 | # previously we'd crash if we tried to save/read an image via the OO |
24 | # interface when there was no jpeg support | |
25 | SKIP: | |
26 | { | |
27 | my $im = Imager->new; | |
28 | ok(!$im->read(file=>"testimg/base.jpg"), "should fail to read jpeg"); | |
29 | cmp_ok($im->errstr, '=~', qr/format 'jpeg' not supported/, "check no jpeg message"); | |
30 | $im = Imager->new(xsize=>2, ysize=>2); | |
31 | ok(!$im->write(file=>"testout/nojpeg.jpg"), "should fail to write jpeg"); | |
f245645a TC |
32 | cmp_ok($im->errstr, '=~', qr/format 'jpeg' not supported/, "check no jpeg message"); |
33 | ok(!grep($_ eq 'jpeg', Imager->read_types), "check jpeg not in read types"); | |
34 | ok(!grep($_ eq 'jpeg', Imager->write_types), "check jpeg not in write types"); | |
6e4af7d4 | 35 | skip("no jpeg support", 88); |
cf692b64 | 36 | } |
20adc63d | 37 | } else { |
e2cb7e23 | 38 | open(FH,">testout/t101.jpg") || die "cannot open testout/t101.jpg for writing\n"; |
20adc63d | 39 | binmode(FH); |
66614d6e TC |
40 | my $IO = Imager::io_new_fd(fileno(FH)); |
41 | ok(i_writejpeg_wiol($img,$IO,30), "write jpeg low level"); | |
20adc63d TC |
42 | close(FH); |
43 | ||
dd55acc8 | 44 | open(FH, "testout/t101.jpg") || die "cannot open testout/t101.jpg\n"; |
20adc63d | 45 | binmode(FH); |
dd55acc8 AMH |
46 | $IO = Imager::io_new_fd(fileno(FH)); |
47 | ($cmpimg,undef) = i_readjpeg_wiol($IO); | |
20adc63d TC |
48 | close(FH); |
49 | ||
cf692b64 TC |
50 | my $diff = sqrt(i_img_diff($img,$cmpimg))/150*150; |
51 | print "# jpeg average mean square pixel difference: ",$diff,"\n"; | |
66614d6e | 52 | ok($cmpimg, "read jpeg low level"); |
cf692b64 | 53 | |
66614d6e | 54 | ok($diff < 10000, "difference between original and jpeg within bounds"); |
cf692b64 | 55 | |
47911724 | 56 | Imager::i_log_entry("Starting 4\n", 1); |
cf692b64 | 57 | my $imoo = Imager->new; |
66614d6e | 58 | ok($imoo->read(file=>'testout/t101.jpg'), "read jpeg OO"); |
6d54291b | 59 | |
66614d6e | 60 | ok($imoo->write(file=>'testout/t101_oo.jpg'), "write jpeg OO"); |
47911724 | 61 | Imager::i_log_entry("Starting 5\n", 1); |
cf692b64 | 62 | my $oocmp = Imager->new; |
66614d6e | 63 | ok($oocmp->read(file=>'testout/t101_oo.jpg'), "read jpeg OO for comparison"); |
cf692b64 TC |
64 | |
65 | $diff = sqrt(i_img_diff($imoo->{IMG},$oocmp->{IMG}))/150*150; | |
66 | print "# OO image difference $diff\n"; | |
66614d6e | 67 | ok($diff < 10000, "difference between original and jpeg within bounds"); |
f873cb01 TC |
68 | |
69 | # write failure test | |
70 | open FH, "< testout/t101.jpg" or die "Cannot open testout/t101.jpg: $!"; | |
71 | binmode FH; | |
66614d6e | 72 | ok(!$imoo->write(fd=>fileno(FH), type=>'jpeg'), 'failure handling'); |
f873cb01 TC |
73 | close FH; |
74 | print "# ",$imoo->errstr,"\n"; | |
2c2c832a TC |
75 | |
76 | # check that the i_format tag is set | |
77 | my @fmt = $imoo->tags(name=>'i_format'); | |
66614d6e | 78 | is($fmt[0], 'jpeg', 'i_format tag'); |
77157728 TC |
79 | |
80 | { # check file limits are checked | |
81 | my $limit_file = "testout/t101.jpg"; | |
82 | ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149"); | |
83 | my $im = Imager->new; | |
84 | ok(!$im->read(file=>$limit_file), | |
85 | "should fail read due to size limits"); | |
86 | print "# ",$im->errstr,"\n"; | |
87 | like($im->errstr, qr/image width/, "check message"); | |
88 | ||
89 | ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149"); | |
90 | ok(!$im->read(file=>$limit_file), | |
91 | "should fail read due to size limits"); | |
92 | print "# ",$im->errstr,"\n"; | |
93 | like($im->errstr, qr/image height/, "check message"); | |
94 | ||
95 | ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150"); | |
96 | ok($im->read(file=>$limit_file), | |
97 | "should succeed - just inside width limit"); | |
98 | ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150"); | |
99 | ok($im->read(file=>$limit_file), | |
100 | "should succeed - just inside height limit"); | |
101 | ||
102 | # 150 x 150 x 3 channel image uses 67500 bytes | |
103 | ok(Imager->set_file_limits(reset=>1, bytes=>67499), | |
104 | "set bytes limit 67499"); | |
105 | ok(!$im->read(file=>$limit_file), | |
106 | "should fail - too many bytes"); | |
107 | print "# ",$im->errstr,"\n"; | |
108 | like($im->errstr, qr/storage size/, "check error message"); | |
109 | ok(Imager->set_file_limits(reset=>1, bytes=>67500), | |
110 | "set bytes limit 67500"); | |
111 | ok($im->read(file=>$limit_file), | |
112 | "should succeed - just inside bytes limit"); | |
113 | Imager->set_file_limits(reset=>1); | |
114 | } | |
f7450478 TC |
115 | |
116 | SKIP: | |
117 | { | |
118 | # we don't test them all | |
119 | my %expected_tags = | |
120 | ( | |
121 | exif_date_time_original => "2005:11:25 00:00:00", | |
122 | exif_flash => 0, | |
123 | exif_image_description => "Imager Development Notes", | |
124 | exif_make => "Canon", | |
125 | exif_model => "CanoScan LiDE 35", | |
126 | exif_resolution_unit => 2, | |
127 | exif_resolution_unit_name => "inches", | |
128 | exif_user_comment => " Part of notes from reworking i_arc() and friends.", | |
129 | exif_white_balance => 0, | |
130 | exif_white_balance_name => "Auto white balance", | |
131 | ); | |
132 | ||
133 | # exif tests | |
134 | Imager::i_exif_enabled() | |
135 | or skip("no exif support", scalar keys %expected_tags); | |
136 | ||
137 | my $im = Imager->new; | |
138 | $im->read(file=>"testimg/exiftest.jpg") | |
139 | or skip("Could not read test image:".$im->errstr, scalar keys %expected_tags); | |
140 | ||
141 | for my $key (keys %expected_tags) { | |
142 | is($expected_tags{$key}, $im->tags(name => $key), | |
143 | "test value of exif tag $key"); | |
144 | } | |
145 | } | |
6d54291b TC |
146 | |
147 | { | |
148 | # tests that the density values are set and read correctly | |
149 | # tests jpeg_comment too | |
150 | my @density_tests = | |
151 | ( | |
152 | [ 't101cm100.jpg', | |
153 | { | |
154 | jpeg_density_unit => 2, | |
155 | i_xres => 254, | |
156 | i_yres => 254 | |
157 | }, | |
158 | { | |
159 | jpeg_density_unit => 2, | |
160 | i_xres => 254, | |
161 | i_yres => 254, | |
162 | i_aspect_only => undef, | |
163 | }, | |
164 | ], | |
165 | [ | |
166 | 't101xonly.jpg', | |
167 | { | |
168 | i_xres => 100, | |
169 | }, | |
170 | { | |
171 | i_xres => 100, | |
172 | i_yres => 100, | |
173 | jpeg_density_unit => 1, | |
174 | i_aspect_only => undef, | |
175 | }, | |
176 | ], | |
177 | [ | |
178 | 't101yonly.jpg', | |
179 | { | |
180 | i_yres => 100, | |
181 | }, | |
182 | { | |
183 | i_xres => 100, | |
184 | i_yres => 100, | |
185 | jpeg_density_unit => 1, | |
186 | i_aspect_only => undef, | |
187 | }, | |
188 | ], | |
189 | [ | |
190 | 't101asponly.jpg', | |
191 | { | |
192 | i_xres => 50, | |
193 | i_yres => 100, | |
194 | i_aspect_only => 1, | |
195 | }, | |
196 | { | |
197 | i_xres => 50, | |
198 | i_yres => 100, | |
199 | i_aspect_only => 1, | |
200 | jpeg_density_unit => 0, | |
201 | }, | |
202 | ], | |
203 | [ | |
204 | 't101com.jpg', | |
205 | { | |
206 | jpeg_comment => 'test comment' | |
207 | }, | |
208 | ], | |
209 | ); | |
210 | ||
211 | print "# test density tags\n"; | |
212 | # I don't care about the content | |
213 | my $base_im = Imager->new(xsize => 10, ysize => 10); | |
214 | for my $test (@density_tests) { | |
215 | my ($filename, $out_tags, $expect_tags) = @$test; | |
216 | $expect_tags ||= $out_tags; | |
217 | ||
218 | my $work = $base_im->copy; | |
219 | for my $key (keys %$out_tags) { | |
220 | $work->addtag(name => $key, value => $out_tags->{$key}); | |
221 | } | |
222 | ||
223 | ok($work->write(file=>"testout/$filename", type=>'jpeg'), | |
224 | "save $filename"); | |
225 | ||
226 | my $check = Imager->new; | |
227 | ok($check->read(file=> "testout/$filename"), | |
228 | "read $filename"); | |
229 | ||
230 | my %tags; | |
231 | for my $key (keys %$expect_tags) { | |
232 | $tags{$key} = $check->tags(name=>$key); | |
233 | } | |
234 | is_deeply($expect_tags, \%tags, "check tags for $filename"); | |
235 | } | |
236 | } | |
41cdb347 TC |
237 | |
238 | { # Issue # 17981 | |
239 | # the test image has a zero-length user_comment field | |
240 | # the code would originally attempt to convert '\0' to ' ' | |
241 | # for the first 8 bytes, even if the string was less than | |
242 | # 8 bytes long | |
243 | my $im = Imager->new; | |
244 | ok($im->read(file => 'testimg/209_yonge.jpg', type=>'jpeg'), | |
245 | "test read of image with invalid exif_user_comment"); | |
246 | is($im->tags(name=>'exif_user_comment'), '', | |
247 | "check exif_user_comment set correctly"); | |
248 | } | |
24ae6325 TC |
249 | |
250 | { # test parseiptc handling no IPTC data correctly | |
251 | my $saw_warn; | |
252 | local $SIG{__WARN__} = | |
253 | sub { | |
254 | ++$saw_warn; | |
255 | print "# @_\n"; | |
256 | }; | |
257 | my $im = Imager->new; | |
258 | ok($im->read(file => 'testout/t101.jpg', type=>'jpeg'), | |
259 | "read jpeg with no IPTC data"); | |
260 | ok(!defined $im->{IPTCRAW}, "no iptc data"); | |
261 | my %iptc = $im->parseiptc; | |
262 | ok(!$saw_warn, "should be no warnings"); | |
263 | } | |
0389bf49 TC |
264 | |
265 | { # Issue # 18397 | |
266 | # attempting to write a 4 channel image to a bufchain would | |
267 | # cause a seg fault. | |
268 | # it should fail still | |
6e4af7d4 TC |
269 | # overridden by # 29876 |
270 | # give 4/2 channel images a background color when saving to JPEG | |
271 | my $im = Imager->new(xsize => 16, ysize => 16, channels => 4); | |
272 | $im->box(filled => 1, xmin => 8, color => '#FFE0C0'); | |
0389bf49 | 273 | my $data; |
6e4af7d4 TC |
274 | ok($im->write(data => \$data, type => 'jpeg'), |
275 | "should write with a black background"); | |
276 | my $imread = Imager->new; | |
277 | ok($imread->read(data => $data, type => 'jpeg'), 'read it back'); | |
278 | is_color_close3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, 4, | |
279 | "check it's black"); | |
280 | is_color_close3($imread->getpixel('x' => 15, 'y' => 9), 255, 224, 192, 4, | |
281 | "check filled area filled"); | |
282 | ||
283 | # write with a red background | |
284 | $data = ''; | |
285 | ok($im->write(data => \$data, type => 'jpeg', i_background => '#FF0000'), | |
286 | "write with red background"); | |
287 | ok($imread->read(data => $data, type => 'jpeg'), "read it back"); | |
288 | is_color_close3($imread->getpixel('x' => 0, 'y' => 0), 255, 0, 0, 4, | |
289 | "check it's red"); | |
290 | is_color_close3($imread->getpixel('x' => 15, 'y' => 9), 255, 224, 192, 4, | |
291 | "check filled area filled"); | |
0389bf49 | 292 | } |
0e457de9 TC |
293 | SKIP: |
294 | { # Issue # 18496 | |
295 | # If a jpeg with EXIF data containing an (invalid) IFD entry with a | |
296 | # type of zero is read then Imager crashes with a Floating point | |
297 | # exception | |
298 | # testimg/zerojpeg.jpg was manually modified from exiftest.jpg to | |
299 | # reproduce the problem. | |
300 | Imager::i_exif_enabled() | |
301 | or skip("no exif support", 1); | |
302 | my $im = Imager->new; | |
303 | ok($im->read(file=>'testimg/zerotype.jpg'), "shouldn't crash"); | |
304 | } | |
9792933e TC |
305 | |
306 | SKIP: | |
307 | { # code coverage - make sure wiol_skip_input_data is called | |
308 | open BASEDATA, "< testimg/exiftest.jpg" | |
309 | or skip "can't open base data", 1; | |
310 | binmode BASEDATA; | |
311 | my $data = do { local $/; <BASEDATA> }; | |
312 | close BASEDATA; | |
313 | ||
314 | substr($data, 3, 1) eq "\xE1" | |
315 | or skip "base data isn't as expected", 1; | |
316 | # inserting a lot of marker data here means we take the branch in | |
317 | # wiol_skip_input_data that refills the buffer | |
318 | my $marker = "\xFF\xE9"; # APP9 marker | |
319 | $marker .= pack("n", 8192) . "x" x 8190; | |
320 | $marker x= 10; # make it take up a lot of space | |
321 | substr($data, 2, 0) = $marker; | |
322 | my $im = Imager->new; | |
323 | ok($im->read(data => $data), "read with a skip of data"); | |
324 | } | |
325 | ||
326 | SKIP: | |
327 | { # code coverage - take the branch that provides a fake EOI | |
328 | open BASEDATA, "< testimg/exiftest.jpg" | |
329 | or skip "can't open base data", 1; | |
330 | binmode BASEDATA; | |
331 | my $data = do { local $/; <BASEDATA> }; | |
332 | close BASEDATA; | |
333 | substr($data, -1000) = ''; | |
334 | ||
335 | my $im = Imager->new; | |
336 | ok($im->read(data => $data), "read with image data truncated"); | |
337 | } | |
338 | ||
339 | { # code coverage - make sure wiol_empty_output_buffer is called | |
340 | my $im = Imager->new(xsize => 1000, ysize => 1000); | |
341 | for my $x (0 .. 999) { | |
342 | $im->line(x1 => $x, y1 => 0, x2 => $x, y2 => 999, | |
343 | color => Imager::Color->new(rand 256, rand 256, rand 256)); | |
344 | } | |
345 | my $data; | |
346 | ok($im->write(data => \$data, type=>'jpeg', jpegquality => 100), | |
347 | "write big file to ensure wiol_empty_output_buffer is called"); | |
348 | ||
349 | # code coverage - write failure path in wiol_empty_output_buffer | |
350 | ok(!$im->write(callback => sub { return }, | |
351 | type => 'jpeg', jpegquality => 100), | |
352 | "fail to write") | |
353 | and print "# ", $im->errstr, "\n"; | |
354 | } | |
355 | ||
356 | { # code coverage - virtual image branch in i_writejpeg_wiol() | |
357 | my $im = $imoo->copy; | |
358 | my $immask = $im->masked; | |
359 | ok($immask, "made a virtual image (via masked)"); | |
360 | ok($immask->virtual, "check it's virtual"); | |
361 | my $mask_data; | |
362 | ok($immask->write(data => \$mask_data, type => 'jpeg'), | |
363 | "write masked version"); | |
364 | my $base_data; | |
365 | ok($im->write(data => \$base_data, type=>'jpeg'), | |
366 | "write normal version"); | |
367 | is($base_data, $mask_data, "check the data written matches"); | |
368 | } | |
369 | ||
370 | SKIP: | |
371 | { # code coverage - IPTC data | |
372 | # this is dummy data | |
373 | my $iptc = "\x04\x04" . | |
374 | "\034\002x My Caption" | |
375 | . "\034\002P Tony Cook" | |
376 | . "\034\002i Dummy Headline!" | |
377 | . "\034\002n No Credit Given"; | |
378 | ||
379 | my $app13 = "\xFF\xED" . pack("n", 2 + length $iptc) . $iptc; | |
380 | ||
381 | open BASEDATA, "< testimg/exiftest.jpg" | |
382 | or skip "can't open base data", 1; | |
383 | binmode BASEDATA; | |
384 | my $data = do { local $/; <BASEDATA> }; | |
385 | close BASEDATA; | |
386 | substr($data, 2, 0) = $app13; | |
387 | ||
388 | my $im = Imager->new; | |
389 | ok($im->read(data => $data), "read with app13 data"); | |
390 | my %iptc = $im->parseiptc; | |
391 | is($iptc{caption}, 'My Caption', 'check iptc caption'); | |
392 | is($iptc{photogr}, 'Tony Cook', 'check iptc photogr'); | |
393 | is($iptc{headln}, 'Dummy Headline!', 'check iptc headln'); | |
394 | is($iptc{credit}, 'No Credit Given', 'check iptc credit'); | |
395 | } | |
02ea5e47 TC |
396 | |
397 | { # handling of CMYK jpeg | |
398 | # http://rt.cpan.org/Ticket/Display.html?id=20416 | |
399 | my $im = Imager->new; | |
400 | ok($im->read(file => 'testimg/scmyk.jpg'), 'read a CMYK jpeg'); | |
401 | is($im->getchannels, 3, "check channel count"); | |
402 | my $col = $im->getpixel(x => 0, 'y' => 0); | |
403 | ok($col, "got the 'black' pixel"); | |
404 | # this is jpeg, so we can't compare colors exactly | |
405 | # older versions returned this pixel at a light color, but | |
406 | # it's black in the image | |
407 | my ($r, $g, $b) = $col->rgba; | |
408 | cmp_ok($r, '<', 10, 'black - red low'); | |
409 | cmp_ok($g, '<', 10, 'black - green low'); | |
410 | cmp_ok($b, '<', 10, 'black - blue low'); | |
411 | $col = $im->getpixel(x => 15, 'y' => 0); | |
412 | ok($col, "got the dark blue"); | |
413 | ($r, $g, $b) = $col->rgba; | |
414 | cmp_ok($r, '<', 10, 'dark blue - red low'); | |
415 | cmp_ok($g, '<', 10, 'dark blue - green low'); | |
416 | cmp_ok($b, '>', 110, 'dark blue - blue middle (bottom)'); | |
417 | cmp_ok($b, '<', 130, 'dark blue - blue middle (top)'); | |
418 | $col = $im->getpixel(x => 0, 'y' => 15); | |
419 | ok($col, "got the red"); | |
420 | ($r, $g, $b) = $col->rgba; | |
421 | cmp_ok($r, '>', 245, 'red - red high'); | |
422 | cmp_ok($g, '<', 10, 'red - green low'); | |
423 | cmp_ok($b, '<', 10, 'red - blue low'); | |
424 | } | |
f245645a TC |
425 | |
426 | { | |
427 | ok(grep($_ eq 'jpeg', Imager->read_types), "check jpeg in read types"); | |
428 | ok(grep($_ eq 'jpeg', Imager->write_types), "check jpeg in write types"); | |
429 | } | |
f873cb01 TC |
430 | } |
431 |