]> git.imager.perl.org - imager.git/blob - t/t01introvert.t
9f682ce1f41292680be00be93ddfe98146d37c99
[imager.git] / t / t01introvert.t
1 #!perl -w
2 # t/t01introvert.t - tests internals of image formats
3 # to make sure we get expected values
4
5 use strict;
6 use Test::More tests => 433;
7
8 BEGIN { use_ok(Imager => qw(:handy :all)) }
9
10 use Imager::Test qw(image_bounds_checks is_color3 is_color4 is_fcolor4 color_cmp mask_tests is_fcolor3);
11
12 -d "testout" or mkdir "testout";
13
14 Imager->open_log(log => "testout/t01introvert.log");
15
16 my $im_g = Imager::ImgRaw::new(100, 101, 1);
17
18 my $red = NC(255, 0, 0);
19 my $green = NC(0, 255, 0);
20 my $blue = NC(0, 0, 255);
21
22 use Imager::Color::Float;
23 my $f_black = Imager::Color::Float->new(0, 0, 0);
24 my $f_red = Imager::Color::Float->new(1.0, 0, 0);
25 my $f_green = Imager::Color::Float->new(0, 1.0, 0);
26 my $f_blue = Imager::Color::Float->new(0, 0, 1.0);
27
28 is(Imager::i_img_getchannels($im_g), 1, "1 channel image channel count");
29 ok(Imager::i_img_getmask($im_g) & 1, "1 channel image mask");
30 ok(!Imager::i_img_virtual($im_g), "1 channel image not virtual");
31 is(Imager::i_img_bits($im_g), 8, "1 channel image has 8 bits/sample");
32 is(Imager::i_img_type($im_g), 0, "1 channel image is direct");
33 is(Imager::i_img_get_width($im_g), 100, "100 pixels wide");
34 is(Imager::i_img_get_height($im_g), 101, "101 pixels high");
35
36 my @ginfo = Imager::i_img_info($im_g);
37 is($ginfo[0], 100, "1 channel image width");
38 is($ginfo[1], 101, "1 channel image height");
39
40 undef $im_g; # can we check for release after this somehow?
41
42 my $im_rgb = Imager::ImgRaw::new(100, 101, 3);
43
44 is(Imager::i_img_getchannels($im_rgb), 3, "3 channel image channel count");
45 is((Imager::i_img_getmask($im_rgb) & 7), 7, "3 channel image mask");
46 is(Imager::i_img_bits($im_rgb), 8, "3 channel image has 8 bits/sample");
47 is(Imager::i_img_type($im_rgb), 0, "3 channel image is direct");
48
49 undef $im_rgb;
50
51 my $im_pal = Imager::i_img_pal_new(100, 101, 3, 256);
52
53 ok($im_pal, "make paletted image");
54 is(Imager::i_img_getchannels($im_pal), 3, "pal img channel count");
55 is(Imager::i_img_bits($im_pal), 8, "pal img bits");
56 is(Imager::i_img_type($im_pal), 1, "pal img is paletted");
57
58 my $red_idx = check_add($im_pal, $red, 0);
59 my $green_idx = check_add($im_pal, $green, 1);
60 my $blue_idx = check_add($im_pal, $blue, 2);
61
62 # basic writing of palette indicies
63 # fill with red
64 is(Imager::i_ppal($im_pal, 0, 0, ($red_idx) x 100), 100, 
65    "write red 100 times");
66 # and blue
67 is(Imager::i_ppal($im_pal, 50, 0, ($blue_idx) x 50), 50,
68    "write blue 50 times");
69
70 # make sure we get it back
71 my @pals = Imager::i_gpal($im_pal, 0, 100, 0);
72 ok(!grep($_ != $red_idx, @pals[0..49]), "check for red");
73 ok(!grep($_ != $blue_idx, @pals[50..99]), "check for blue");
74 is(Imager::i_gpal($im_pal, 0, 100, 0), "\0" x 50 . "\2" x 50, 
75    "gpal in scalar context");
76 my @samp = Imager::i_gsamp($im_pal, 0, 100, 0, [ 0, 1, 2 ]);
77 is(@samp, 300, "gsamp count in list context");
78 my @samp_exp = ((255, 0, 0) x 50, (0, 0, 255) x 50);
79 is_deeply(\@samp, \@samp_exp, "gsamp list deep compare");
80 my $samp = Imager::i_gsamp($im_pal, 0, 100, 0, [ 0, 1, 2 ]);
81 is(length($samp), 300, "gsamp scalar length");
82 is($samp, "\xFF\0\0" x 50 . "\0\0\xFF" x 50, "gsamp scalar bytes");
83
84 # reading indicies as colors
85 my $c_red = Imager::i_get_pixel($im_pal, 0, 0);
86 ok($c_red, "got the red pixel");
87 is_color3($c_red, 255, 0, 0, "and it's red");
88 my $c_blue = Imager::i_get_pixel($im_pal, 50, 0);
89 ok($c_blue, "got the blue pixel");
90 is_color3($c_blue, 0, 0, 255, "and it's blue");
91
92 # drawing with colors
93 ok(Imager::i_ppix($im_pal, 0, 0, $green) == 0, "draw with color in palette");
94 # that was in the palette, should still be paletted
95 is(Imager::i_img_type($im_pal), 1, "image still paletted");
96
97 my $c_green = Imager::i_get_pixel($im_pal, 0, 0);
98 ok($c_green, "got green pixel");
99 is_color3($c_green, 0, 255, 0, "and it's green");
100
101 is(Imager::i_colorcount($im_pal), 3, "still 3 colors in palette");
102 is(Imager::i_findcolor($im_pal, $green), 1, "and green is the second");
103
104 my $black = NC(0, 0, 0);
105 # this should convert the image to RGB
106 ok(Imager::i_ppix($im_pal, 1, 0, $black) == 0, "draw with black (not in palette)");
107 is(Imager::i_img_type($im_pal), 0, "pal img shouldn't be paletted now");
108
109 {
110   my %quant =
111     (
112      colors => [$red, $green, $blue, $black],
113      make_colors => 'none',
114     );
115   my $im_pal2 = Imager::i_img_to_pal($im_pal, \%quant);
116   ok($im_pal2, "got an image from quantizing");
117   is(@{$quant{colors}}, 4, "quant has the right number of colours");
118   is(Imager::i_colorcount($im_pal2), 4, "and so does the image");
119   my @colors = Imager::i_getcolors($im_pal2, 0, 4);
120   my ($first) = Imager::i_getcolors($im_pal2, 0);
121   my @first = $colors[0]->rgba;
122   is_color3($first, $first[0], $first[1], $first[2],
123            "check first color is first for multiple or single fetch");
124   is_color3($colors[0], 255, 0, 0, "still red");
125   is_color3($colors[1], 0, 255, 0, "still green");
126   is_color3($colors[2], 0, 0, 255, "still blue");
127   is_color3($colors[3], 0, 0, 0, "still black");
128   is_deeply([ unpack("C", Imager::i_gsamp($im_pal2, 0, 100, 0, [ 0, 1, 2 ])) ],
129             [ unpack("C", "\0\xFF\0\0\0\0"."\xFF\0\0" x 48 . "\0\0\xFF" x 50) ],
130             "colors are still correct");
131 }
132
133 # test the OO interfaces
134 my $impal2 = Imager->new(type=>'pseudo', xsize=>200, ysize=>201);
135 ok($impal2, "make paletted via OO")
136   or diag(Imager->errstr);
137 is($impal2->getchannels, 3, "check channels");
138 is($impal2->bits, 8, "check bits");
139 is($impal2->type, 'paletted', "check type");
140 is($impal2->getwidth, 200, "check width");
141 is($impal2->getheight, 201, "check height");
142
143 {
144   my $red_idx = $impal2->addcolors(colors=>[$red]);
145   ok($red_idx, "add red to OO");
146   is(0+$red_idx, 0, "and it's expected index for red");
147   my $blue_idx = $impal2->addcolors(colors=>[$blue, $green]);
148   ok($blue_idx, "add blue/green via OO");
149   is($blue_idx, 1, "and it's expected index for blue");
150   my $green_idx = $blue_idx + 1;
151   my $c = $impal2->getcolors(start=>$green_idx);
152   is_color3($c, 0, 255, 0, "found green where expected");
153   my @cols = $impal2->getcolors;
154   is(@cols, 3, "got 3 colors");
155   my @exp = ( $red, $blue, $green );
156   my $good = 1;
157   for my $i (0..2) {
158     if (color_cmp($cols[$i], $exp[$i])) {
159       $good = 0;
160       last;
161     }
162   }
163   ok($good, "all colors in palette as expected");
164   is($impal2->colorcount, 3, "and colorcount returns 3");
165   is($impal2->maxcolors, 256, "maxcolors as expected");
166   is($impal2->findcolor(color=>$blue), 1, "findcolors found blue");
167   ok($impal2->setcolors(start=>0, colors=>[ $blue, $red ]),
168      "we can setcolors");
169
170   # make an rgb version
171   my $imrgb2 = $impal2->to_rgb8()
172     or diag($impal2->errstr);
173   is($imrgb2->type, 'direct', "converted is direct");
174
175   # and back again, specifying the palette
176   my @colors = ( $red, $blue, $green );
177   my $impal3 = $imrgb2->to_paletted(colors=>\@colors,
178                                     make_colors=>'none',
179                                     translate=>'closest');
180   ok($impal3, "got a paletted image from conversion");
181   dump_colors(@colors);
182   print "# in image\n";
183   dump_colors($impal3->getcolors);
184   is($impal3->colorcount, 3, "new image has expected color table size");
185   is($impal3->type, 'paletted', "and is paletted");
186 }
187
188 { # to_rgb on incomplete image
189   my $im = Imager->new;
190   ok($im, "make empty image");
191   ok(!$im->to_rgb8, "convert to rgb8");
192   is($im->errstr, "empty input image", "check message");
193 }
194
195 { # basic checks, 8-bit direct images
196   my $im = Imager->new(xsize => 2, ysize => 3);
197   ok($im, 'create 8-bit direct image');
198   is($im->bits, 8, '8 bits');
199   ok(!$im->virtual, 'not virtual');
200   is($im->type, 'direct', 'direct image');
201   ok(!$im->is_bilevel, 'not mono');
202 }
203
204 ok(!Imager->new(xsize=>0, ysize=>1), "fail to create 0 height image");
205 cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
206        "0 height error message check");
207 ok(!Imager->new(xsize=>1, ysize=>0), "fail to create 0 width image");
208 cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
209        "0 width error message check");
210 ok(!Imager->new(xsize=>-1, ysize=>1), "fail to create -ve height image");
211 cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
212        "-ve width error message check");
213 ok(!Imager->new(xsize=>1, ysize=>-1), "fail to create -ve width image");
214 cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
215        "-ve height error message check");
216 ok(!Imager->new(xsize=>-1, ysize=>-1), "fail to create -ve width/height image");
217 cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
218        "-ve width/height error message check");
219
220 ok(!Imager->new(xsize=>1, ysize=>1, channels=>0),
221    "fail to create a zero channel image");
222 cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
223        "out of range channel message check");
224 ok(!Imager->new(xsize=>1, ysize=>1, channels=>5),
225    "fail to create a five channel image");
226 cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
227        "out of range channel message check");
228
229 {
230   # https://rt.cpan.org/Ticket/Display.html?id=8213
231   # check for handling of memory allocation of very large images
232   # only test this on 32-bit machines - on a 64-bit machine it may
233   # result in trying to allocate 4Gb of memory, which is unfriendly at
234   # least and may result in running out of memory, causing a different
235   # type of exit
236  SKIP:
237   {
238     use Config;
239     skip("don't want to allocate 4Gb", 8) unless $Config{ptrsize} == 4;
240
241     my $uint_range = 256 ** $Config{intsize};
242     print "# range $uint_range\n";
243     my $dim1 = int(sqrt($uint_range))+1;
244     
245     my $im_b = Imager->new(xsize=>$dim1, ysize=>$dim1, channels=>1);
246     is($im_b, undef, "integer overflow check - 1 channel");
247     
248     $im_b = Imager->new(xisze=>$dim1, ysize=>1, channels=>1);
249     ok($im_b, "but same width ok");
250     $im_b = Imager->new(xisze=>1, ysize=>$dim1, channels=>1);
251     ok($im_b, "but same height ok");
252     cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
253            "check the error message");
254
255     # do a similar test with a 3 channel image, so we're sure we catch
256     # the same case where the third dimension causes the overflow
257     my $dim3 = int(sqrt($uint_range / 3))+1;
258     
259     $im_b = Imager->new(xsize=>$dim3, ysize=>$dim3, channels=>3);
260     is($im_b, undef, "integer overflow check - 3 channel");
261     
262     $im_b = Imager->new(xisze=>$dim3, ysize=>1, channels=>3);
263     ok($im_b, "but same width ok");
264     $im_b = Imager->new(xisze=>1, ysize=>$dim3, channels=>3);
265     ok($im_b, "but same height ok");
266
267     cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
268            "check the error message");
269   }
270 }
271
272 { # http://rt.cpan.org/NoAuth/Bug.html?id=9672
273   my $warning;
274   local $SIG{__WARN__} = 
275     sub { 
276       $warning = "@_";
277       my $printed = $warning;
278       $printed =~ s/\n$//;
279       $printed =~ s/\n/\n\#/g; 
280       print "# ",$printed, "\n";
281     };
282   my $img = Imager->new(xsize=>10, ysize=>10);
283   $img->to_rgb8(); # doesn't really matter what the source is
284   cmp_ok($warning, '=~', 'void', "correct warning");
285   cmp_ok($warning, '=~', 't01introvert\\.t', "correct file");
286 }
287
288 { # http://rt.cpan.org/NoAuth/Bug.html?id=11860
289   my $im = Imager->new(xsize=>2, ysize=>2);
290   $im->setpixel(x=>0, 'y'=>0, color=>$red);
291   $im->setpixel(x=>1, 'y'=>0, color=>$blue);
292
293   my @row = Imager::i_glin($im->{IMG}, 0, 2, 0);
294   is(@row, 2, "got 2 pixels from i_glin");
295   is_color3($row[0], 255, 0, 0, "red first");
296   is_color3($row[1], 0, 0, 255, "then blue");
297 }
298
299 { # general tag tests
300   
301   # we don't care much about the image itself
302   my $im = Imager::ImgRaw::new(10, 10, 1);
303
304   ok(Imager::i_tags_addn($im, 'alpha', 0, 101), "i_tags_addn(...alpha, 0, 101)");
305   ok(Imager::i_tags_addn($im, undef, 99, 102), "i_tags_addn(...undef, 99, 102)");
306   is(Imager::i_tags_count($im), 2, "should have 2 tags");
307   ok(Imager::i_tags_addn($im, undef, 99, 103), "i_tags_addn(...undef, 99, 103)");
308   is(Imager::i_tags_count($im), 3, "should have 3 tags, despite the dupe");
309   is(Imager::i_tags_find($im, 'alpha', 0), '0 but true', "find alpha");
310   is(Imager::i_tags_findn($im, 99, 0), 1, "find 99");
311   is(Imager::i_tags_findn($im, 99, 2), 2, "find 99 again");
312   is(Imager::i_tags_get($im, 0), 101, "check first");
313   is(Imager::i_tags_get($im, 1), 102, "check second");
314   is(Imager::i_tags_get($im, 2), 103, "check third");
315
316   ok(Imager::i_tags_add($im, 'beta', 0, "hello", 0), 
317      "add string with string key");
318   ok(Imager::i_tags_add($im, 'gamma', 0, "goodbye", 0),
319      "add another one");
320   ok(Imager::i_tags_add($im, undef, 199, "aloha", 0),
321      "add one keyed by number");
322   is(Imager::i_tags_find($im, 'beta', 0), 3, "find beta");
323   is(Imager::i_tags_find($im, 'gamma', 0), 4, "find gamma");
324   is(Imager::i_tags_findn($im, 199, 0), 5, "find 199");
325   ok(Imager::i_tags_delete($im, 2), "delete");
326   is(Imager::i_tags_find($im, 'beta', 0), 2, 'find beta after deletion');
327   ok(Imager::i_tags_delbyname($im, 'beta'), 'delete beta by name');
328   is(Imager::i_tags_find($im, 'beta', 0), undef, 'beta not there now');
329   is(Imager::i_tags_get_string($im, "gamma"), "goodbye", 
330      'i_tags_get_string() on a string');
331   is(Imager::i_tags_get_string($im, 99), 102, 
332      'i_tags_get_string() on a number entry');
333   ok(Imager::i_tags_delbycode($im, 99), 'delete by code');
334   is(Imager::i_tags_findn($im, 99, 0), undef, '99 not there now');
335   is(Imager::i_tags_count($im), 3, 'final count of 3');
336 }
337
338
339   print "# low-level scan line function tests\n";
340   my $im = Imager::ImgRaw::new(10, 10, 4);
341   Imager::i_ppix($im, 5, 0, $red);
342
343   # i_glin/i_glinf
344   my @colors = Imager::i_glin($im, 0, 10, 0);
345   is_deeply([ (0) x 20, (255, 0, 0, 255), (0) x 16 ], 
346             [ map $_->rgba, @colors ],
347             "i_glin - list context");
348   my $colors = Imager::i_glin($im, 0, 10, 0);
349   is("00" x 20 . "FF0000FF" . "00" x 16, 
350      uc unpack("H*", $colors), "i_glin - scalar context");
351   my @fcolors = Imager::i_glinf($im, 0, 10, 0);
352   is_deeply([ (0.0) x 20, (1.0, 0, 0, 1.0) , (0) x 16 ],
353             [ map $_->rgba, @fcolors ],
354             "i_glinf - list context");
355   my $fcolors = Imager::i_glinf($im, 0, 10, 0);
356   is_deeply([ (0.0) x 20, (1.0, 0, 0, 1.0) , (0) x 16 ],
357             [ unpack "d*", $fcolors ],
358             "i_glinf - scalar context");
359
360   # i_plin/i_plinf
361   my @plin_colors = (($black) x 4, $red, $blue, ($black) x 4);
362   is(Imager::i_plin($im, 0, 1, @plin_colors),
363      10, "i_plin - pass in a list");
364   # make sure we get it back
365   is_deeply([ map [ $_->rgba ], @plin_colors ],
366             [ map [ $_->rgba ], Imager::i_glin($im, 0, 10, 1) ],
367             "check i_plin wrote to the image");
368   my @scalar_plin = 
369     (
370      (0,0,0,0) x 4, 
371      (0, 255, 0, 255),
372      (0, 0, 255, 255), 
373      (0, 0, 0, 0) x 4,
374     );
375   is(Imager::i_plin($im, 0, 2, pack("C*", @scalar_plin)),
376      10, "i_plin - pass in a scalar");
377   is_deeply(\@scalar_plin,
378             [ map $_->rgba , Imager::i_glin($im, 0, 10, 2) ],
379             "check i_plin scalar wrote to the image");
380
381   my @plinf_colors = # Note: only 9 pixels
382     ( 
383      ($f_blue) x 4, 
384      $f_red, 
385      ($f_black) x 3, 
386      $f_black
387     );
388   is(Imager::i_plinf($im, 0, 3, @plinf_colors), 9,
389      "i_plinf - list");
390   is_deeply([ map $_->rgba, Imager::i_glinf($im, 0, 9, 3) ],
391             [ map $_->rgba, @plinf_colors ],
392             "check colors were written");
393   my @scalar_plinf =
394     (
395      ( 1.0, 1.0,   0, 1.0 ) x 3,
396      (   0, 1.0, 1.0, 1.0 ) x 2,
397      (   0,   0,   0,   0 ),
398      ( 1.0,   0, 1.0, 1.0 ),
399     );
400   is(Imager::i_plinf($im, 2, 4, pack("d*", @scalar_plinf)),
401      7, "i_plinf - scalar");
402   is_deeply(\@scalar_plinf,
403             [ map $_->rgba, Imager::i_glinf($im, 2, 9, 4) ],
404             "check colors were written");
405
406   is_deeply([ Imager::i_gsamp($im, 0, 10, 0, [ 0, 3 ]) ],
407             [ (0, 0) x 5, (255, 255), (0, 0) x 4 ],
408             "i_gsamp list context");
409   is("0000" x 5 . "FFFF" . "0000" x 4,
410      uc unpack("H*", Imager::i_gsamp($im, 0, 10, 0, [ 0, 3 ])),
411      "i_gsamp scalar context");
412   is_deeply([ Imager::i_gsampf($im, 2, 9, 4, [ 0, 2, 3 ]) ],
413             [ (1.0, 0, 1.0) x 3, (0, 1.0, 1.0) x 2, (0, 0, 0),
414               (1.0, 1.0, 1.0) ], "i_gsampf - list context");
415   is_deeply([ unpack("d*", Imager::i_gsampf($im, 2, 9, 4, [ 0, 2, 3 ])) ],
416             [ (1.0, 0, 1.0) x 3, (0, 1.0, 1.0) x 2, (0, 0, 0),
417               (1.0, 1.0, 1.0) ], "i_gsampf - scalar context");
418   print "# end low-level scan-line function tests\n";
419 }
420
421 my $psamp_outside_error = "Image position outside of image";
422 { # psamp
423   print "# psamp\n";
424   my $imraw = Imager::ImgRaw::new(10, 20, 3);
425   {
426     is(Imager::i_psamp($imraw, 0, 2, undef, [ 255, 128, 64 ]), 3,
427        "i_psamp def channels, 3 samples");
428     is_color3(Imager::i_get_pixel($imraw, 0, 2), 255, 128, 64,
429               "check color written");
430     Imager::i_img_setmask($imraw, 5);
431     is(Imager::i_psamp($imraw, 1, 3, undef, [ 64, 128, 192 ]), 3,
432        "i_psamp def channels, 3 samples, masked");
433     is_color3(Imager::i_get_pixel($imraw, 1, 3), 64, 0, 192,
434               "check color written");
435     is(Imager::i_psamp($imraw, 1, 7, [ 0, 1, 2 ], [ 64, 128, 192 ]), 3,
436        "i_psamp channels listed, 3 samples, masked");
437     is_color3(Imager::i_get_pixel($imraw, 1, 7), 64, 0, 192,
438               "check color written");
439     Imager::i_img_setmask($imraw, ~0);
440     is(Imager::i_psamp($imraw, 2, 4, [ 0, 1 ], [ 255, 128, 64, 32 ]), 4,
441        "i_psamp channels [0, 1], 4 samples");
442     is_color3(Imager::i_get_pixel($imraw, 2, 4), 255, 128, 0,
443               "check first color written");
444     is_color3(Imager::i_get_pixel($imraw, 3, 4), 64, 32, 0,
445               "check second color written");
446     is(Imager::i_psamp($imraw, 0, 5, [ 0, 1, 2 ], [ (128, 63, 32) x 10 ]), 30,
447        "write a full row");
448     is_deeply([ Imager::i_gsamp($imraw, 0, 10, 5, [ 0, 1, 2 ]) ],
449               [ (128, 63, 32) x 10 ],
450               "check full row");
451     is(Imager::i_psamp($imraw, 8, 8, [ 0, 1, 2 ],
452                        [ 255, 128, 32, 64, 32, 16, 32, 16, 8 ]),
453        6, "i_psamp channels [0, 1, 2], 9 samples, but room for 6");
454     is(Imager::i_psamp($imraw, 4, 6, undef, [ 0 .. 18 ], 1), 18,
455        "psamp with offset");
456     is_deeply([ Imager::i_gsamp($imraw, 0, 10, 6, undef) ],
457               [ (0) x 12, 1 .. 18 ],
458               "check result");
459     is(Imager::i_psamp($imraw, 4, 11, undef, [ 0 .. 18 ], 1, 3), 9,
460        "psamp with offset and width");
461     is_deeply([ Imager::i_gsamp($imraw, 0, 10, 11, undef) ],
462               [ (0) x 12, 1 .. 9, (0) x 9 ],
463               "check result");
464   }
465   { # errors we catch
466     is(Imager::i_psamp($imraw, 6, 8, [ 0, 1, 3 ], [ 255, 128, 32 ]),
467        undef, "i_psamp channels [0, 1, 3], 3 samples (invalid channel number)");
468     is(_get_error(), "No channel 3 in this image",
469        "check error message");
470     is(Imager::i_psamp($imraw, 6, 8, [ 0, 1, -1 ], [ 255, 128, 32 ]),
471        undef, "i_psamp channels [0, 1, -1], 3 samples (invalid channel number)");
472     is(_get_error(), "No channel -1 in this image",
473        "check error message");
474     is(Imager::i_psamp($imraw, 0, -1, undef, [ 0, 0, 0 ]), undef,
475        "negative y");
476     is(_get_error(), $psamp_outside_error,
477        "check error message");
478     is(Imager::i_psamp($imraw, 0, 20, undef, [ 0, 0, 0 ]), undef,
479        "y overflow");
480     is(_get_error(), $psamp_outside_error,
481        "check error message");
482     is(Imager::i_psamp($imraw, -1, 0, undef, [ 0, 0, 0 ]), undef,
483        "negative x");
484     is(_get_error(), $psamp_outside_error,
485        "check error message");
486     is(Imager::i_psamp($imraw, 10, 0, undef, [ 0, 0, 0 ]), undef,
487        "x overflow");
488     is(_get_error(), $psamp_outside_error,
489        "check error message");
490   }
491   { # test the im_sample_list typemap
492     ok(!eval { Imager::i_psamp($imraw, 9, 9, [ 0 ], undef); 1 },
493        "pass undef as the sample list");
494     like($@, qr/data must be a scalar or an arrayref/,
495          "check message");
496     ok(!eval { Imager::i_psamp($imraw, 9, 9, [ 0 ], { a => 1 }); 1 },
497        "hashref as the sample list");
498     like($@, qr/data must be a scalar or an arrayref/,
499          "check message");
500     ok(!eval { Imager::i_psamp($imraw, 9, 9, [ 0 ], []); 1 },
501        "empty sample list");
502     like($@, qr/i_psamp: no samples provided in data/,
503          "check message");
504     ok(!eval { Imager::i_psamp($imraw, 9, 9, [ 0 ], ""); 1 },
505        "empty scalar sample list");
506     like($@, qr/i_psamp: no samples provided in data/,
507          "check message");
508
509     # not the typemap
510     is(Imager::i_psamp($imraw, 0, 8, undef, [ (0) x 3 ], -1), undef,
511        "negative offset");
512     is(_get_error(), "offset must be non-negative",
513        "check message");
514
515     is(Imager::i_psamp($imraw, 0, 8, undef, [ (0) x 3 ], 4), undef,
516        "too high offset");
517     is(_get_error(), "offset greater than number of samples supplied",
518        "check message");
519   }
520   print "# end psamp tests\n";
521 }
522
523 { # psampf
524   print "# psampf\n";
525   my $imraw = Imager::ImgRaw::new(10, 20, 3);
526   {
527     is(Imager::i_psampf($imraw, 0, 2, undef, [ 1, 0.5, 0.25 ]), 3,
528        "i_psampf def channels, 3 samples");
529     is_color3(Imager::i_get_pixel($imraw, 0, 2), 255, 128, 64,
530               "check color written");
531     Imager::i_img_setmask($imraw, 5);
532     is(Imager::i_psampf($imraw, 1, 3, undef, [ 0.25, 0.5, 0.75 ]), 3,
533        "i_psampf def channels, 3 samples, masked");
534     is_color3(Imager::i_get_pixel($imraw, 1, 3), 64, 0, 191,
535               "check color written");
536     is(Imager::i_psampf($imraw, 1, 7, [ 0, 1, 2 ], [ 0.25, 0.5, 0.75 ]), 3,
537        "i_psampf channels listed, 3 samples, masked");
538     is_color3(Imager::i_get_pixel($imraw, 1, 7), 64, 0, 191,
539               "check color written");
540     Imager::i_img_setmask($imraw, ~0);
541     is(Imager::i_psampf($imraw, 2, 4, [ 0, 1 ], [ 1, 0.5, 0.25, 0.125 ]), 4,
542        "i_psampf channels [0, 1], 4 samples");
543     is_color3(Imager::i_get_pixel($imraw, 2, 4), 255, 128, 0,
544               "check first color written");
545     is_color3(Imager::i_get_pixel($imraw, 3, 4), 64, 32, 0,
546               "check second color written");
547     is(Imager::i_psampf($imraw, 0, 5, [ 0, 1, 2 ], [ (0.5, 0.25, 0.125) x 10 ]), 30,
548        "write a full row");
549     is_deeply([ Imager::i_gsamp($imraw, 0, 10, 5, [ 0, 1, 2 ]) ],
550               [ (128, 64, 32) x 10 ],
551               "check full row");
552     is(Imager::i_psampf($imraw, 8, 8, [ 0, 1, 2 ],
553                         [ 1.0, 0.5, 0.125, 0.25, 0.125, 0.0625, 0.125, 0, 1 ]),
554        6, "i_psampf channels [0, 1, 2], 9 samples, but room for 6");
555     is(Imager::i_psampf($imraw, 4, 6, undef, [ map $_/254.9, 0 .. 18 ], 1), 18,
556        "psampf with offset");
557     is_deeply([ Imager::i_gsamp($imraw, 0, 10, 6, undef) ],
558               [ (0) x 12, 1 .. 18 ],
559               "check result");
560     is(Imager::i_psampf($imraw, 4, 11, undef, [ map $_/254.9, 0 .. 18 ], 1, 3), 9,
561        "psampf with offset and width");
562     is_deeply([ Imager::i_gsamp($imraw, 0, 10, 11, undef) ],
563               [ (0) x 12, 1 .. 9, (0) x 9 ],
564               "check result");
565   }
566   { # errors we catch
567     is(Imager::i_psampf($imraw, 6, 8, [ 0, 1, 3 ], [ 1, 0.5, 0.125 ]),
568        undef, "i_psampf channels [0, 1, 3], 3 samples (invalid channel number)");
569     is(_get_error(), "No channel 3 in this image",
570        "check error message");
571     is(Imager::i_psampf($imraw, 6, 8, [ 0, 1, -1 ], [ 1, 0.5, 0.125 ]),
572        undef, "i_psampf channels [0, 1, -1], 3 samples (invalid channel number)");
573     is(_get_error(), "No channel -1 in this image",
574        "check error message");
575     is(Imager::i_psampf($imraw, 0, -1, undef, [ 0, 0, 0 ]), undef,
576        "negative y");
577     is(_get_error(), $psamp_outside_error,
578        "check error message");
579     is(Imager::i_psampf($imraw, 0, 20, undef, [ 0, 0, 0 ]), undef,
580        "y overflow");
581     is(_get_error(), $psamp_outside_error,
582        "check error message");
583     is(Imager::i_psampf($imraw, -1, 0, undef, [ 0, 0, 0 ]), undef,
584        "negative x");
585     is(_get_error(), $psamp_outside_error,
586        "check error message");
587     is(Imager::i_psampf($imraw, 10, 0, undef, [ 0, 0, 0 ]), undef,
588        "x overflow");
589     is(_get_error(), $psamp_outside_error,
590        "check error message");
591   }
592   { # test the im_fsample_list typemap
593     ok(!eval { Imager::i_psampf($imraw, 9, 9, [ 0 ], undef); 1 },
594        "pass undef as the sample list");
595     like($@, qr/data must be a scalar or an arrayref/,
596          "check message");
597     ok(!eval { Imager::i_psampf($imraw, 9, 9, [ 0 ], { a => 1 }); 1 },
598        "hashref as the sample list");
599     like($@, qr/data must be a scalar or an arrayref/,
600          "check message");
601     ok(!eval { Imager::i_psampf($imraw, 9, 9, [ 0 ], []); 1 },
602        "empty sample list");
603     like($@, qr/i_psampf: no samples provided in data/,
604          "check message");
605     ok(!eval { Imager::i_psampf($imraw, 9, 9, [ 0 ], ""); 1 },
606        "empty scalar sample list");
607     like($@, qr/i_psampf: no samples provided in data/,
608          "check message");
609
610     # not the typemap
611     is(Imager::i_psampf($imraw, 0, 8, undef, [ (0) x 3 ], -1), undef,
612        "negative offset");
613     is(_get_error(), "offset must be non-negative",
614        "check message");
615
616     is(Imager::i_psampf($imraw, 0, 8, undef, [ (0) x 3 ], 4), undef,
617        "too high offset");
618     is(_get_error(), "offset greater than number of samples supplied",
619        "check message");
620   }
621   print "# end psampf tests\n";
622 }
623
624 {
625   print "# OO level scanline function tests\n";
626   my $im = Imager->new(xsize=>10, ysize=>10, channels=>4);
627   $im->setpixel(color=>$red, 'x'=>5, 'y'=>0);
628   ok(!$im->getscanline(), "getscanline() - supply nothing, get nothing");
629   is($im->errstr, "missing y parameter", "check message");
630   is_deeply([ map [ $_->rgba ], $im->getscanline('y'=>0) ],
631             [ ([ 0,0,0,0]) x 5, [ 255, 0, 0, 255 ], ([ 0,0,0,0]) x 4 ],
632             "getscanline, list context, default x, width");
633   is_deeply([ map [ $_->rgba ], $im->getscanline('y'=>0, 'x'=>3) ],
634             [ ([0,0,0,0]) x 2, [ 255, 0, 0, 255 ], ([0,0,0,0]) x 4 ],
635             "getscanline, list context, default width");
636   is_deeply([ map [ $_->rgba ], $im->getscanline('y'=>0, 'x'=>4, width=>4) ],
637             [ [0,0,0,0], [ 255, 0, 0, 255 ], ([0,0,0,0]) x 2 ],
638             "getscanline, list context, no defaults");
639   is(uc unpack("H*",  $im->getscanline('y'=>0)),
640      "00000000" x 5 .  "FF0000FF" . "00000000" x 4,
641      "getscanline, scalar context, default x, width");
642   is_deeply([ map [ $_->rgba ], 
643               $im->getscanline('y'=>0, 'x'=>4, width=>4, type=>'float') ],
644             [ [0,0,0,0], [ 1.0, 0, 0, 1.0 ], ([0,0,0,0]) x 2 ],
645             "getscanline float, list context, no defaults");
646   is_deeply([ unpack "d*",
647               $im->getscanline('y'=>0, 'x'=>4, width=>4, type=>'float') ],
648             [ (0,0,0,0), ( 1.0, 0, 0, 1.0 ), (0,0,0,0) x 2 ],
649             "getscanline float, scalar context, no defaults");
650
651   ok(!$im->getscanline('y'=>0, type=>'invalid'),
652      "check invalid type checking");
653   like($im->errstr, qr/invalid type parameter/, 
654        "check message for invalid type");
655
656   my @plin_colors = (($black) x 4, $red, $blue, ($green) x 4);
657   is($im->setscanline('y'=>1, pixels=>\@plin_colors), 10,
658      "setscanline - arrayref, default x");
659   is_deeply([ map [ $_->rgba ], @plin_colors ],
660             [ map [ $_->rgba ], $im->getscanline('y'=>1) ],
661             "check colors were written");
662
663   my @plin_colors2 = ( $green, $red, $blue, $red );
664   is($im->setscanline('y'=>2, 'x'=>3, pixels=>\@plin_colors2), 4,
665      "setscanline - arrayref");
666
667   # using map instead of x here due to a bug in some versions of Test::More
668   # fixed in the latest Test::More
669   is_deeply([ ( map [ 0,0,0,0 ], 1..3), (map [ $_->rgba ], @plin_colors2),
670               ( map [ 0,0,0,0 ], 1..3) ],
671             [ map [ $_->rgba ], $im->getscanline('y'=>2) ],
672             "check write to middle of line");
673   
674   my $raw_colors = pack "H*", "FF00FFFF"."FF0000FF"."FFFFFFFF";
675   is($im->setscanline('y'=>3, 'x'=>2, pixels=>$raw_colors), 3,
676      "setscanline - scalar, default raw type")
677     or print "# ",$im->errstr,"\n";
678   is(uc unpack("H*", $im->getscanline('y'=>3, 'x'=>1, 'width'=>5)),
679      "00000000".uc(unpack "H*", $raw_colors)."00000000",
680      "check write");
681
682   # float colors
683   my @fcolors = ( $f_red, $f_blue, $f_black, $f_green );
684   is($im->setscanline('y'=>4, 'x'=>3, pixels=>\@fcolors), 4,
685      "setscanline - float arrayref");
686   is_deeply([ map [ $_->rgba ], @fcolors ],
687             [ map [ $_->rgba ], $im->getscanline('y'=>4, 'x'=>3, width=>4, type=>'float') ],
688             "check write");
689   # packed
690   my $packed_fcolors = pack "d*", map $_->rgba, @fcolors;
691   is($im->setscanline('y'=>5, 'x'=>4, pixels=>$packed_fcolors, type=>'float'), 4,
692      "setscanline - float scalar");
693   is_deeply([ map [ $_->rgba ], @fcolors ],
694             [ map [ $_->rgba ], $im->getscanline('y'=>5, 'x'=>4, width=>4, type=>'float') ],
695             "check write");
696
697   # get samples
698   is_deeply([ $im->getsamples('y'=>1, channels=>[ 0 ]) ],
699             [ map +($_->rgba)[0], @plin_colors ],
700             "get channel 0, list context, default x, width");
701   is_deeply([ unpack "C*", $im->getsamples('y'=>1, channels=>[0, 2]) ],
702             [ map { ($_->rgba)[0, 2] } @plin_colors ],
703             "get channel 0, 1, scalar context");
704   is_deeply([ $im->getsamples('y'=>4, 'x'=>3, width=>4, type=>'float',
705                               channels=>[1,3]) ],
706             [ map { ($_->rgba)[1,3] } @fcolors ],
707             "get channels 1,3, list context, float samples");
708   is_deeply([ unpack "d*", 
709               $im->getsamples('y'=>4, 'x'=>3, width=>4,
710                               type=>'float', channels=>[3,2,1,0]) ],
711             [ map { ($_->rgba)[3,2,1,0] } @fcolors ],
712             "get channels 3..0 as scalar, float samples");
713   
714   print "# end OO level scanline function tests\n";
715 }
716
717 { # RT 74882
718   # for the non-gsamp_bits case with a target parameter it was
719   # treating the target parameter as a hashref
720   {
721     my $im = Imager->new(xsize => 10, ysize => 10);
722     my $c1 = NC(0, 63, 255);
723     my $c2 = NC(255, 128, 255);
724     is($im->setscanline(y => 1, pixels => [ ( $c1, $c2 ) x 5 ]),
725        10, "set some test data")
726       or diag "setscanline: ", $im->errstr;
727     my @target;
728     is($im->getsamples(y => 1, x => 1, target => \@target, width => 3),
729        9, "getsamples to target");
730     is_deeply(\@target, [ 255, 128, 255, 0, 63, 255, 255, 128, 255 ],
731               "check result");
732   }
733   {
734     my $im = Imager->new(xsize => 10, ysize => 10, bits => "double");
735     my $c1 = NCF(0, 0.25, 1.0);
736     my $c2 = NCF(1.0, 0.5, 1.0);
737     is($im->setscanline(y => 1, pixels => [ ( $c1, $c2 ) x 5 ]),
738        10, "set some test data")
739       or diag "setscanline: ", $im->errstr;
740     my @target;
741     is($im->getsamples(y => 1, x => 1, target => \@target, width => 3, type => "float"),
742        9, "getsamples to target");
743     is_deeply(\@target, [ 1.0, 0.5, 1.0, 0, 0.25, 1.0, 1.0, 0.5, 1.0 ],
744               "check result");
745   }
746 }
747
748 { # to avoid confusion, i_glin/i_glinf modified to return 0 in unused
749   # channels at the perl level
750   my $im = Imager->new(xsize => 4, ysize => 4, channels => 2);
751   my $fill = Imager::Color->new(128, 255, 0, 0);
752   ok($im->box(filled => 1, color => $fill), 'fill it up');
753   my $data = $im->getscanline('y' => 0);
754   is(unpack("H*", $data), "80ff000080ff000080ff000080ff0000",
755      "check we get zeros");
756   my @colors = $im->getscanline('y' => 0);
757   is_color4($colors[0], 128, 255, 0, 0, "check object interface[0]");
758   is_color4($colors[1], 128, 255, 0, 0, "check object interface[1]");
759   is_color4($colors[2], 128, 255, 0, 0, "check object interface[2]");
760   is_color4($colors[3], 128, 255, 0, 0, "check object interface[3]");
761   
762   my $dataf = $im->getscanline('y' => 0, type => 'float');
763   # the extra pack/unpack is to force double precision rather than long
764   # double, otherwise the test fails
765   is_deeply([ unpack("d*", $dataf) ],
766             [ unpack("d*", pack("d*", ( 128.0 / 255.0, 1.0, 0, 0, ) x 4)) ],
767             "check we get zeroes (double)");
768   my @fcolors = $im->getscanline('y' => 0, type => 'float');
769   is_fcolor4($fcolors[0], 128.0/255.0, 1.0, 0, 0, "check object interface[0]");
770   is_fcolor4($fcolors[1], 128.0/255.0, 1.0, 0, 0, "check object interface[1]");
771   is_fcolor4($fcolors[2], 128.0/255.0, 1.0, 0, 0, "check object interface[2]");
772   is_fcolor4($fcolors[3], 128.0/255.0, 1.0, 0, 0, "check object interface[3]");
773 }
774
775 { # check the channel mask function
776   
777   my $im = Imager->new(xsize => 10, ysize=>10, bits=>8);
778
779   mask_tests($im, 0.005);
780 }
781
782 { # check bounds checking
783   my $im = Imager->new(xsize => 10, ysize => 10);
784
785   image_bounds_checks($im);
786 }
787
788 { # setsamples() interface to psamp()
789   my $im = Imager->new(xsize => 10, ysize => 10);
790   is($im->setsamples(y => 1, x => 2, data => [ 1 .. 6 ]), 6,
791      "simple put (array), default channels");
792   is_deeply([ $im->getsamples(y => 1, x => 0) ],
793             [ (0) x 6, 1 .. 6, (0) x 18 ], "check they were stored");
794   is($im->setsamples(y => 3, x => 3, data => pack("C*", 2 .. 10 )), 9,
795      "simple put (scalar), default channels")
796     or diag $im->errstr;
797   is_deeply([ $im->getsamples(y => 3, x => 0) ],
798             [ (0) x 9, 2 .. 10, (0) x 12 ], "check they were stored");
799   is($im->setsamples(y => 4, x => 4, data => [ map $_ / 254.5, 1 .. 6 ], type => 'float'),
800      6, "simple put (float array), default channels");
801   is_deeply([ $im->getsamples(y => 4, x => 0) ],
802             [ (0) x 12, 1 .. 6, (0) x 12 ], "check they were stored");
803
804   is($im->setsamples(y => 5, x => 3, data => pack("d*", map $_ / 254.5, 1 .. 6), type => 'float'),
805      6, "simple put (float scalar), default channels");
806   is_deeply([ $im->getsamples(y => 5, x => 0) ],
807             [ (0) x 9, 1 .. 6, (0) x 15 ], "check they were stored");
808
809   is($im->setsamples(y => 7, x => 3, data => [ 0 .. 18 ], offset => 1), 18,
810      "setsamples offset");
811   is_deeply([ $im->getsamples(y => 7) ],
812             [ (0) x 9, 1 .. 18, (0) x 3 ],
813             "check result");
814
815   is($im->setsamples(y => 8, x => 3, data => [ map $_ / 254.9, 0 .. 18 ],
816                      offset => 1, type => 'float'),
817      18, "setsamples offset (float)");
818   is_deeply([ $im->getsamples(y => 8) ],
819             [ (0) x 9, 1 .. 18, (0) x 3 ],
820             "check result");
821
822   is_deeply([ $im->setsamples(y => 6, x => 10, data => [ (0) x 3 ]) ],
823             [], "check out of range result (8bit)");
824   is($im->errstr, $psamp_outside_error, "check error message");
825
826   is_deeply([ $im->setsamples(y => 6, x => 10, data => [ (0) x 3 ], type => "float") ],
827             [], "check out of range result (float)");
828   is($im->errstr, $psamp_outside_error, "check error message");
829
830   is_deeply([ $im->setsamples(y => 6, x => 2, channels => [0, 1, 3 ],
831                               data => [ (0) x 3 ]) ],
832             [], "check bad channels (8bit)");
833   is($im->errstr, "No channel 3 in this image",
834      "check error message");
835   
836   is_deeply([ $im->setsamples(y => 6, x => 2, channels => [0, 1, 3 ], 
837                               data => [ (0) x 3 ], type => "float") ],
838             [], "check bad channels (float)");
839   is($im->errstr, "No channel 3 in this image",
840      "check error message");
841
842   is($im->setsamples(y => 5, data => [ (0) x 3 ], type => "bad"),
843      undef, "setsamples with bad type");
844   is($im->errstr, "setsamples: type parameter invalid",
845      "check error message");
846   is($im->setsamples(y => 5),
847      undef, "setsamples with no data");
848   is($im->errstr, "setsamples: data parameter missing",
849      "check error message");
850
851   is($im->setsamples(y => 5, data => undef),
852      undef, "setsamples with undef data");
853   is($im->errstr, "setsamples: data parameter not defined",
854      "check error message");
855
856   my $imempty = Imager->new;
857   is($imempty->setsamples(y => 0, data => [ (0) x 3 ]), undef,
858      "setsamples to empty image");
859   is($imempty->errstr, "setsamples: empty input image",
860      "check error message");
861 }
862
863 { # getpixel parameters
864   my $im = Imager->new(xsize => 10, ysize => 10);
865   $im->box(filled => 1, xmax => 4, color => NC(255, 0, 0));
866   $im->box(filled => 1, xmin => 5, ymax => 4, color => NC(0, 255, 255));
867   $im->box(filled => 1, xmin => 5, ymin => 5, color => NC(255, 0, 255));
868   { # error handling
869     my $empty = Imager->new;
870     ok(!$empty->getpixel(x => 0, y => 0), "getpixel empty image");
871     is($empty->errstr, "getpixel: empty input image", "check message");
872
873     ok(!$im->getpixel(y => 0), "missing x");
874     is($im->errstr, "getpixel: missing x or y parameter", "check message");
875
876     $im->_set_error("something different");
877     ok(!$im->getpixel(x => 0), "missing y");
878     is($im->errstr, "getpixel: missing x or y parameter", "check message");
879
880     ok(!$im->getpixel(x => [], y => 0), "empty x array ref");
881     is($im->errstr, "getpixel: x is a reference to an empty array",
882        "check message");
883
884     ok(!$im->getpixel(x => 0, y => []), "empty y array ref");
885     is($im->errstr, "getpixel: y is a reference to an empty array",
886        "check message");
887
888     ok(!$im->getpixel(x => 0, y => 0, type => "bad"), "bad type (scalar path)");
889     is($im->errstr, "getpixel: type must be '8bit' or 'float'",
890       "check message");
891
892     $im->_set_error("something different");
893     ok(!$im->getpixel(x => [ 0 ], y => [ 0 ], type => "bad"),
894        "bad type (array path)");
895     is($im->errstr, "getpixel: type must be '8bit' or 'float'",
896       "check message");
897   }
898
899   # simple calls
900   is_color3($im->getpixel(x => 1, y => 0), 255, 0, 0,
901             "getpixel(1, 0)");
902   is_color3($im->getpixel(x => 8, y => 1), 0, 255, 255,
903             "getpixel(8, 1)");
904   is_color3($im->getpixel(x => 8, y => 7), 255, 0, 255,
905             "getpixel(8, 7)");
906
907   {
908     # simple arrayrefs
909     my @colors = $im->getpixel(x => [ 0, 8, 7 ], y => [ 0, 7, 3 ]);
910     is(@colors, 3, "getpixel 2 3 element array refs");
911     is_color3($colors[0], 255, 0, 0, "check first color");
912     is_color3($colors[1], 255, 0, 255, "check second color");
913     is_color3($colors[2], 0, 255, 255, "check third color");
914   }
915   
916   # array and scalar
917   {
918     my @colors = $im->getpixel(x => 5, y => [ 4, 5, 9 ]);
919     is(@colors, 3, "getpixel x scalar, y arrayref of 3");
920     is_color3($colors[0], 0, 255, 255, "check first color");
921     is_color3($colors[1], 255, 0, 255, "check second color");
922     is_color3($colors[2], 255, 0, 255, "check third color");
923   }
924
925   {
926     my @colors = $im->getpixel(x => [ 0, 4, 5 ], y => 2);
927     is(@colors, 3, "getpixel y scalar, x arrayref of 3");
928     is_color3($colors[0], 255, 0, 0, "check first color");
929     is_color3($colors[1], 255, 0, 0, "check second color");
930     is_color3($colors[2], 0, 255, 255, "check third color");
931   }
932
933   { # float
934     is_fcolor3($im->getpixel(x => 1, y => 0, type => 'float'),
935                1.0, 0, 0, "getpixel(1,0) float");
936     is_fcolor3($im->getpixel(x => 8, y => 1, type => 'float'),
937                0, 1.0, 1.0, "getpixel(8,1) float");
938     is_fcolor3($im->getpixel(x => 8, y => 7, type => 'float'),
939                1.0, 0, 1.0, "getpixel(8,7) float");
940
941     my @colors = $im->getpixel(x => [ 0, 8, 7 ], y => [ 0, 7, 3 ], type => 'float');
942     is(@colors, 3, "getpixel 2 3 element array refs (float)");
943     is_fcolor3($colors[0], 1, 0, 0, "check first color");
944     is_fcolor3($colors[1], 1, 0, 1, "check second color");
945     is_fcolor3($colors[2], 0, 1, 1, "check third color");
946   }
947
948   { # out of bounds
949     my @colors = $im->getpixel(x => [ 0, -1, 5, 10 ], y => 0);
950     is(@colors, 4, "should be 4 entries")
951       or diag $im->errstr;
952     is_color3($colors[0], 255, 0, 0, "first red");
953     is($colors[1], undef, "second undef");
954     is_color3($colors[2], 0, 255, 255, "third cyan");
955     is($colors[3], undef, "fourth undef");
956   }
957
958   { # out of bounds
959     my @colors = $im->getpixel(x => [ 0, -1, 5, 10 ], y => 0, type => "float");
960     is(@colors, 4, "should be 4 entries")
961       or diag $im->errstr;
962     is_fcolor3($colors[0], 1.0, 0, 0, "first red");
963     is($colors[1], undef, "second undef");
964     is_fcolor3($colors[2], 0, 1.0, 1.0, "third cyan");
965     is($colors[3], undef, "fourth undef");
966   }
967 }
968
969 { # setpixel
970   my $im = Imager->new(xsize => 10, ysize => 10);
971   { # errors
972     my $empty = Imager->new;
973     ok(!$empty->setpixel(x => 0, y => 0, color => $red),
974        "setpixel on empty image");
975     is($empty->errstr, "setpixel: empty input image", "check message");
976
977     ok(!$im->setpixel(y => 0, color => $red), "missing x");
978     is($im->errstr, "setpixel: missing x or y parameter", "check message");
979
980     $im->_set_error("something different");
981     ok(!$im->setpixel(x => 0, color => $red), "missing y");
982     is($im->errstr, "setpixel: missing x or y parameter", "check message");
983
984     ok(!$im->setpixel(x => [], y => 0, color => $red), "empty x array ref");
985     is($im->errstr, "setpixel: x is a reference to an empty array",
986        "check message");
987
988     ok(!$im->setpixel(x => 0, y => [], color => $red), "empty y array ref");
989     is($im->errstr, "setpixel: y is a reference to an empty array",
990        "check message");
991
992     ok(!$im->setpixel(x => 0, y => 0, color => "not really a color"),
993        "color not a color");
994     is($im->errstr, "setpixel: No color named not really a color found",
995        "check message");
996   }
997
998   # simple set
999   is($im->setpixel(x => 0, y => 0, color => $red), $im,
1000      "simple setpixel")
1001     or diag "simple set float: ", $im->errstr;
1002   is_color3($im->getpixel(x => 0, y => 0), 255, 0, 0, "check stored pixel");
1003
1004   is($im->setpixel(x => 1, y => 2, color => $f_red), $im,
1005      "simple setpixel (float)")
1006     or diag "simple set float: ", $im->errstr;
1007   is_color3($im->getpixel(x => 1, y => 2), 255, 0, 0, "check stored pixel");
1008
1009   is($im->setpixel(x => -1, y => 0, color => $red), undef,
1010      "simple setpixel outside of image");
1011   is($im->setpixel(x => 0, y => -1, color => $f_red), undef,
1012      "simple setpixel (float) outside of image");
1013
1014   # simple arrayrefs
1015   is($im->setpixel( x => [ 0, 8, 7 ], y => [ 0, 7, 3 ], color => $blue),
1016      3, "setpixel with 3 element array refs");
1017   my @colors = $im->getpixel(x => [ 8, 7, 0 ], y => [ 7, 3, 0 ]);
1018   is_color3($colors[0], 0, 0, 255, "check first color");
1019   is_color3($colors[1], 0, 0, 255, "check second color");
1020   is_color3($colors[2], 0, 0, 255, "check third color");
1021
1022   # array and scalar
1023   {
1024     is($im->setpixel(x => 5, y => [ 4, 5, 9 ], color => $green), 3,
1025        "setpixel with x scalar, y arrayref of 3");
1026     my @colors = $im->getpixel(x => [ 5, 5, 5 ], y => [ 4, 5, 9 ]);
1027     is_color3($colors[0], 0, 255, 0, "check first color");
1028     is_color3($colors[1], 0, 255, 0, "check second color");
1029     is_color3($colors[2], 0, 255, 0, "check third color");
1030   }
1031
1032   {
1033     is($im->setpixel(x => [ 0, 4, 5 ], y => 2, color => $blue), 3,
1034        "setpixel with y scalar, x arrayref of 3");
1035     my @colors = $im->getpixel(x => [ 0, 4, 5 ], y => [ 2, 2, 2 ]);
1036     is_color3($colors[0], 0, 0, 255, "check first color");
1037     is_color3($colors[1], 0, 0, 255, "check second color");
1038     is_color3($colors[2], 0, 0, 255, "check third color");
1039   }
1040
1041   {
1042     is($im->setpixel(x => [ 0, -1, 10, 5, 0 ], y => [ 0, 1, 2, 3, 1 ], color => $blue), 3,
1043        "set array with two bad locations")
1044       or diag "set array bad locations: ", $im->errstr;
1045     my @colors = $im->getpixel(x => [ 0, 5, 0 ], y => [ 0, 3, 1 ]);
1046     is_color3($colors[0], 0, 0, 255, "check first color");
1047     is_color3($colors[1], 0, 0, 255, "check second color");
1048     is_color3($colors[2], 0, 0, 255, "check third color");
1049   }
1050   {
1051     is($im->setpixel(x => [ 0, -1, 10, 5, 0 ], y => [ 0, 1, 2, 3, 1 ], color => $f_green), 3,
1052        "set array with two bad locations (float)")
1053       or diag "set array bad locations (float): ", $im->errstr;
1054     my @colors = $im->getpixel(x => [ 0, 5, 0 ], y => [ 0, 3, 1 ]);
1055     is_color3($colors[0], 0, 255, 0, "check first color");
1056     is_color3($colors[1], 0, 255, 0, "check second color");
1057     is_color3($colors[2], 0, 255, 0, "check third color");
1058   }
1059   { # default color
1060     is($im->setpixel(x => 0, y => 9), $im, "setpixel() default color")
1061       or diag "setpixel default color: ", $im->errstr;
1062     is_color3($im->getpixel(x => 0, y => 9), 255, 255, 255,
1063               "check color set");
1064   }
1065 }
1066
1067 Imager->close_log();
1068
1069 unless ($ENV{IMAGER_KEEP_FILES}) {
1070   unlink "testout/t01introvert.log";
1071 }
1072
1073 sub check_add {
1074   my ($im, $color, $expected) = @_;
1075   my $index = Imager::i_addcolors($im, $color);
1076   ok($index, "got index");
1077   print "# $index\n";
1078   is(0+$index, $expected, "index matched expected");
1079   my ($new) = Imager::i_getcolors($im, $index);
1080   ok($new, "got the color");
1081   ok(color_cmp($new, $color) == 0, "color matched what was added");
1082
1083   $index;
1084 }
1085
1086 # sub array_ncmp {
1087 #   my ($a1, $a2) = @_;
1088 #   my $len = @$a1 < @$a2 ? @$a1 : @$a2;
1089 #   for my $i (0..$len-1) {
1090 #     my $diff = $a1->[$i] <=> $a2->[$i] 
1091 #       and return $diff;
1092 #   }
1093 #   return @$a1 <=> @$a2;
1094 # }
1095
1096 sub dump_colors {
1097   for my $col (@_) {
1098     print "# ", map(sprintf("%02X", $_), ($col->rgba)[0..2]),"\n";
1099   }
1100 }
1101
1102 sub _get_error {
1103   my @errors = Imager::i_errors();
1104   return join(": ", map $_->[0], @errors);
1105 }