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