]> git.imager.perl.org - imager.git/blobdiff - t/t01introvert.t
compare unpacked data to avoid unintelligble cpan testers output
[imager.git] / t / t01introvert.t
index 3276926b4d65889a6069dc12d5c5e77a22c27050..7f6d511295d8134b55df21a1b0d66b9d4ba897c2 100644 (file)
@@ -3,14 +3,15 @@
 # to make sure we get expected values
 
 use strict;
-use lib 't';
-use Test::More tests=>196;
+use Test::More tests => 431;
 
 BEGIN { use_ok(Imager => qw(:handy :all)) }
 
-require "t/testtools.pl";
+use Imager::Test qw(image_bounds_checks is_color3 is_color4 is_fcolor4 color_cmp mask_tests is_fcolor3);
 
-init_log("testout/t01introvert.log",1);
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t01introvert.log");
 
 my $im_g = Imager::ImgRaw::new(100, 101, 1);
 
@@ -29,6 +30,8 @@ ok(Imager::i_img_getmask($im_g) & 1, "1 channel image mask");
 ok(!Imager::i_img_virtual($im_g), "1 channel image not virtual");
 is(Imager::i_img_bits($im_g), 8, "1 channel image has 8 bits/sample");
 is(Imager::i_img_type($im_g), 0, "1 channel image is direct");
+is(Imager::i_img_get_width($im_g), 100, "100 pixels wide");
+is(Imager::i_img_get_height($im_g), 101, "101 pixels high");
 
 my @ginfo = Imager::i_img_info($im_g);
 is($ginfo[0], 100, "1 channel image width");
@@ -70,21 +73,21 @@ ok(!grep($_ != $red_idx, @pals[0..49]), "check for red");
 ok(!grep($_ != $blue_idx, @pals[50..99]), "check for blue");
 is(Imager::i_gpal($im_pal, 0, 100, 0), "\0" x 50 . "\2" x 50, 
    "gpal in scalar context");
-my @samp = Imager::i_gsamp($im_pal, 0, 100, 0, 0, 1, 2);
+my @samp = Imager::i_gsamp($im_pal, 0, 100, 0, [ 0, 1, 2 ]);
 is(@samp, 300, "gsamp count in list context");
 my @samp_exp = ((255, 0, 0) x 50, (0, 0, 255) x 50);
 is_deeply(\@samp, \@samp_exp, "gsamp list deep compare");
-my $samp = Imager::i_gsamp($im_pal, 0, 100, 0, 0, 1, 2);
+my $samp = Imager::i_gsamp($im_pal, 0, 100, 0, [ 0, 1, 2 ]);
 is(length($samp), 300, "gsamp scalar length");
 is($samp, "\xFF\0\0" x 50 . "\0\0\xFF" x 50, "gsamp scalar bytes");
 
 # reading indicies as colors
 my $c_red = Imager::i_get_pixel($im_pal, 0, 0);
 ok($c_red, "got the red pixel");
-ok(color_cmp($red, $c_red) == 0, "and it's red");
+is_color3($c_red, 255, 0, 0, "and it's red");
 my $c_blue = Imager::i_get_pixel($im_pal, 50, 0);
 ok($c_blue, "got the blue pixel");
-ok(color_cmp($blue, $c_blue) == 0, "and it's blue");
+is_color3($c_blue, 0, 0, 255, "and it's blue");
 
 # drawing with colors
 ok(Imager::i_ppix($im_pal, 0, 0, $green) == 0, "draw with color in palette");
@@ -93,7 +96,7 @@ is(Imager::i_img_type($im_pal), 1, "image still paletted");
 
 my $c_green = Imager::i_get_pixel($im_pal, 0, 0);
 ok($c_green, "got green pixel");
-ok(color_cmp($green, $c_green) == 0, "and it's green");
+is_color3($c_green, 0, 255, 0, "and it's green");
 
 is(Imager::i_colorcount($im_pal), 3, "still 3 colors in palette");
 is(Imager::i_findcolor($im_pal, $green), 1, "and green is the second");
@@ -103,24 +106,39 @@ my $black = NC(0, 0, 0);
 ok(Imager::i_ppix($im_pal, 1, 0, $black) == 0, "draw with black (not in palette)");
 is(Imager::i_img_type($im_pal), 0, "pal img shouldn't be paletted now");
 
-my %quant =
-  (
-   colors => [$red, $green, $blue, $black],
-   makemap => 'none',
-  );
-my $im_pal2 = Imager::i_img_to_pal($im_pal, \%quant);
-ok($im_pal2, "got an image from quantizing");
-is(@{$quant{colors}}, 4, "has the right number of colours");
-is(Imager::i_gsamp($im_pal2, 0, 100, 0, 0, 1, 2),
-  "\0\xFF\0\0\0\0"."\xFF\0\0" x 48 . "\0\0\xFF" x 50,
-   "colors are still correct");
+{
+  my %quant =
+    (
+     colors => [$red, $green, $blue, $black],
+     make_colors => 'none',
+    );
+  my $im_pal2 = Imager::i_img_to_pal($im_pal, \%quant);
+  ok($im_pal2, "got an image from quantizing");
+  is(@{$quant{colors}}, 4, "quant has the right number of colours");
+  is(Imager::i_colorcount($im_pal2), 4, "and so does the image");
+  my @colors = Imager::i_getcolors($im_pal2, 0, 4);
+  my ($first) = Imager::i_getcolors($im_pal2, 0);
+  my @first = $colors[0]->rgba;
+  is_color3($first, $first[0], $first[1], $first[2],
+          "check first color is first for multiple or single fetch");
+  is_color3($colors[0], 255, 0, 0, "still red");
+  is_color3($colors[1], 0, 255, 0, "still green");
+  is_color3($colors[2], 0, 0, 255, "still blue");
+  is_color3($colors[3], 0, 0, 0, "still black");
+  is(unpack("C", Imager::i_gsamp($im_pal2, 0, 100, 0, [ 0, 1, 2 ])),
+     unpack("C", "\0\xFF\0\0\0\0"."\xFF\0\0" x 48 . "\0\0\xFF" x 50),
+     "colors are still correct");
+}
 
 # test the OO interfaces
 my $impal2 = Imager->new(type=>'pseudo', xsize=>200, ysize=>201);
-ok($impal2, "make paletted via OO");
+ok($impal2, "make paletted via OO")
+  or diag(Imager->errstr);
 is($impal2->getchannels, 3, "check channels");
 is($impal2->bits, 8, "check bits");
 is($impal2->type, 'paletted', "check type");
+is($impal2->getwidth, 200, "check width");
+is($impal2->getheight, 201, "check height");
 
 {
   my $red_idx = $impal2->addcolors(colors=>[$red]);
@@ -131,7 +149,7 @@ is($impal2->type, 'paletted', "check type");
   is($blue_idx, 1, "and it's expected index for blue");
   my $green_idx = $blue_idx + 1;
   my $c = $impal2->getcolors(start=>$green_idx);
-  ok(color_cmp($green, $c) == 0, "found green where expected");
+  is_color3($c, 0, 255, 0, "found green where expected");
   my @cols = $impal2->getcolors;
   is(@cols, 3, "got 3 colors");
   my @exp = ( $red, $blue, $green );
@@ -150,7 +168,8 @@ is($impal2->type, 'paletted', "check type");
      "we can setcolors");
 
   # make an rgb version
-  my $imrgb2 = $impal2->to_rgb8();
+  my $imrgb2 = $impal2->to_rgb8()
+    or diag($impal2->errstr);
   is($imrgb2->type, 'direct', "converted is direct");
 
   # and back again, specifying the palette
@@ -166,6 +185,22 @@ is($impal2->type, 'paletted', "check type");
   is($impal3->type, 'paletted', "and is paletted");
 }
 
+{ # to_rgb on incomplete image
+  my $im = Imager->new;
+  ok($im, "make empty image");
+  ok(!$im->to_rgb8, "convert to rgb8");
+  is($im->errstr, "empty input image", "check message");
+}
+
+{ # basic checks, 8-bit direct images
+  my $im = Imager->new(xsize => 2, ysize => 3);
+  ok($im, 'create 8-bit direct image');
+  is($im->bits, 8, '8 bits');
+  ok(!$im->virtual, 'not virtual');
+  is($im->type, 'direct', 'direct image');
+  ok(!$im->is_bilevel, 'not mono');
+}
+
 ok(!Imager->new(xsize=>0, ysize=>1), "fail to create 0 height image");
 cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
        "0 height error message check");
@@ -201,7 +236,7 @@ cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
  SKIP:
   {
     use Config;
-    skip("don't want to allocate 4Gb", 8) unless $Config{intsize} == 4;
+    skip("don't want to allocate 4Gb", 8) unless $Config{ptrsize} == 4;
 
     my $uint_range = 256 ** $Config{intsize};
     print "# range $uint_range\n";
@@ -257,8 +292,8 @@ cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
 
   my @row = Imager::i_glin($im->{IMG}, 0, 2, 0);
   is(@row, 2, "got 2 pixels from i_glin");
-  ok(color_cmp($row[0], $red) == 0, "red first");
-  ok(color_cmp($row[1], $blue) == 0, "then blue");
+  is_color3($row[0], 255, 0, 0, "red first");
+  is_color3($row[1], 0, 0, 255, "then blue");
 }
 
 { # general tag tests
@@ -368,21 +403,224 @@ cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
            [ map $_->rgba, Imager::i_glinf($im, 2, 9, 4) ],
            "check colors were written");
 
-  is_deeply([ Imager::i_gsamp($im, 0, 10, 0, (0, 3)) ],
+  is_deeply([ Imager::i_gsamp($im, 0, 10, 0, [ 0, 3 ]) ],
            [ (0, 0) x 5, (255, 255), (0, 0) x 4 ],
            "i_gsamp list context");
   is("0000" x 5 . "FFFF" . "0000" x 4,
-     uc unpack("H*", Imager::i_gsamp($im, 0, 10, 0, (0, 3))),
+     uc unpack("H*", Imager::i_gsamp($im, 0, 10, 0, [ 0, 3 ])),
      "i_gsamp scalar context");
-  is_deeply([ Imager::i_gsampf($im, 2, 9, 4, 0, 2, 3) ],
+  is_deeply([ Imager::i_gsampf($im, 2, 9, 4, [ 0, 2, 3 ]) ],
            [ (1.0, 0, 1.0) x 3, (0, 1.0, 1.0) x 2, (0, 0, 0),
              (1.0, 1.0, 1.0) ], "i_gsampf - list context");
-  is_deeply([ unpack("d*", Imager::i_gsampf($im, 2, 9, 4, 0, 2, 3)) ],
+  is_deeply([ unpack("d*", Imager::i_gsampf($im, 2, 9, 4, [ 0, 2, 3 ])) ],
            [ (1.0, 0, 1.0) x 3, (0, 1.0, 1.0) x 2, (0, 0, 0),
               (1.0, 1.0, 1.0) ], "i_gsampf - scalar context");
   print "# end low-level scan-line function tests\n";
 }
 
+my $psamp_outside_error = "Image position outside of image";
+{ # psamp
+  print "# psamp\n";
+  my $imraw = Imager::ImgRaw::new(10, 20, 3);
+  {
+    is(Imager::i_psamp($imraw, 0, 2, undef, [ 255, 128, 64 ]), 3,
+       "i_psamp def channels, 3 samples");
+    is_color3(Imager::i_get_pixel($imraw, 0, 2), 255, 128, 64,
+             "check color written");
+    Imager::i_img_setmask($imraw, 5);
+    is(Imager::i_psamp($imraw, 1, 3, undef, [ 64, 128, 192 ]), 3,
+       "i_psamp def channels, 3 samples, masked");
+    is_color3(Imager::i_get_pixel($imraw, 1, 3), 64, 0, 192,
+             "check color written");
+    is(Imager::i_psamp($imraw, 1, 7, [ 0, 1, 2 ], [ 64, 128, 192 ]), 3,
+       "i_psamp channels listed, 3 samples, masked");
+    is_color3(Imager::i_get_pixel($imraw, 1, 7), 64, 0, 192,
+             "check color written");
+    Imager::i_img_setmask($imraw, ~0);
+    is(Imager::i_psamp($imraw, 2, 4, [ 0, 1 ], [ 255, 128, 64, 32 ]), 4,
+       "i_psamp channels [0, 1], 4 samples");
+    is_color3(Imager::i_get_pixel($imraw, 2, 4), 255, 128, 0,
+             "check first color written");
+    is_color3(Imager::i_get_pixel($imraw, 3, 4), 64, 32, 0,
+             "check second color written");
+    is(Imager::i_psamp($imraw, 0, 5, [ 0, 1, 2 ], [ (128, 63, 32) x 10 ]), 30,
+       "write a full row");
+    is_deeply([ Imager::i_gsamp($imraw, 0, 10, 5, [ 0, 1, 2 ]) ],
+             [ (128, 63, 32) x 10 ],
+             "check full row");
+    is(Imager::i_psamp($imraw, 8, 8, [ 0, 1, 2 ],
+                      [ 255, 128, 32, 64, 32, 16, 32, 16, 8 ]),
+       6, "i_psamp channels [0, 1, 2], 9 samples, but room for 6");
+    is(Imager::i_psamp($imraw, 4, 6, undef, [ 0 .. 18 ], 1), 18,
+       "psamp with offset");
+    is_deeply([ Imager::i_gsamp($imraw, 0, 10, 6, undef) ],
+             [ (0) x 12, 1 .. 18 ],
+             "check result");
+    is(Imager::i_psamp($imraw, 4, 11, undef, [ 0 .. 18 ], 1, 3), 9,
+       "psamp with offset and width");
+    is_deeply([ Imager::i_gsamp($imraw, 0, 10, 11, undef) ],
+             [ (0) x 12, 1 .. 9, (0) x 9 ],
+             "check result");
+  }
+  { # errors we catch
+    is(Imager::i_psamp($imraw, 6, 8, [ 0, 1, 3 ], [ 255, 128, 32 ]),
+       undef, "i_psamp channels [0, 1, 3], 3 samples (invalid channel number)");
+    is(_get_error(), "No channel 3 in this image",
+       "check error message");
+    is(Imager::i_psamp($imraw, 6, 8, [ 0, 1, -1 ], [ 255, 128, 32 ]),
+       undef, "i_psamp channels [0, 1, -1], 3 samples (invalid channel number)");
+    is(_get_error(), "No channel -1 in this image",
+       "check error message");
+    is(Imager::i_psamp($imraw, 0, -1, undef, [ 0, 0, 0 ]), undef,
+       "negative y");
+    is(_get_error(), $psamp_outside_error,
+       "check error message");
+    is(Imager::i_psamp($imraw, 0, 20, undef, [ 0, 0, 0 ]), undef,
+       "y overflow");
+    is(_get_error(), $psamp_outside_error,
+       "check error message");
+    is(Imager::i_psamp($imraw, -1, 0, undef, [ 0, 0, 0 ]), undef,
+       "negative x");
+    is(_get_error(), $psamp_outside_error,
+       "check error message");
+    is(Imager::i_psamp($imraw, 10, 0, undef, [ 0, 0, 0 ]), undef,
+       "x overflow");
+    is(_get_error(), $psamp_outside_error,
+       "check error message");
+  }
+  { # test the im_sample_list typemap
+    ok(!eval { Imager::i_psamp($imraw, 9, 9, [ 0 ], undef); 1 },
+       "pass undef as the sample list");
+    like($@, qr/data must be a scalar or an arrayref/,
+        "check message");
+    ok(!eval { Imager::i_psamp($imraw, 9, 9, [ 0 ], { a => 1 }); 1 },
+       "hashref as the sample list");
+    like($@, qr/data must be a scalar or an arrayref/,
+        "check message");
+    ok(!eval { Imager::i_psamp($imraw, 9, 9, [ 0 ], []); 1 },
+       "empty sample list");
+    like($@, qr/i_psamp: no samples provided in data/,
+        "check message");
+    ok(!eval { Imager::i_psamp($imraw, 9, 9, [ 0 ], ""); 1 },
+       "empty scalar sample list");
+    like($@, qr/i_psamp: no samples provided in data/,
+        "check message");
+
+    # not the typemap
+    is(Imager::i_psamp($imraw, 0, 8, undef, [ (0) x 3 ], -1), undef,
+       "negative offset");
+    is(_get_error(), "offset must be non-negative",
+       "check message");
+
+    is(Imager::i_psamp($imraw, 0, 8, undef, [ (0) x 3 ], 4), undef,
+       "too high offset");
+    is(_get_error(), "offset greater than number of samples supplied",
+       "check message");
+  }
+  print "# end psamp tests\n";
+}
+
+{ # psampf
+  print "# psampf\n";
+  my $imraw = Imager::ImgRaw::new(10, 20, 3);
+  {
+    is(Imager::i_psampf($imraw, 0, 2, undef, [ 1, 0.5, 0.25 ]), 3,
+       "i_psampf def channels, 3 samples");
+    is_color3(Imager::i_get_pixel($imraw, 0, 2), 255, 127, 63,
+             "check color written");
+    Imager::i_img_setmask($imraw, 5);
+    is(Imager::i_psampf($imraw, 1, 3, undef, [ 0.25, 0.5, 0.75 ]), 3,
+       "i_psampf def channels, 3 samples, masked");
+    is_color3(Imager::i_get_pixel($imraw, 1, 3), 63, 0, 191,
+             "check color written");
+    is(Imager::i_psampf($imraw, 1, 7, [ 0, 1, 2 ], [ 0.25, 0.5, 0.75 ]), 3,
+       "i_psampf channels listed, 3 samples, masked");
+    is_color3(Imager::i_get_pixel($imraw, 1, 7), 63, 0, 191,
+             "check color written");
+    Imager::i_img_setmask($imraw, ~0);
+    is(Imager::i_psampf($imraw, 2, 4, [ 0, 1 ], [ 1, 0.5, 0.25, 0.125 ]), 4,
+       "i_psampf channels [0, 1], 4 samples");
+    is_color3(Imager::i_get_pixel($imraw, 2, 4), 255, 127, 0,
+             "check first color written");
+    is_color3(Imager::i_get_pixel($imraw, 3, 4), 63, 31, 0,
+             "check second color written");
+    is(Imager::i_psampf($imraw, 0, 5, [ 0, 1, 2 ], [ (0.5, 0.25, 0.125) x 10 ]), 30,
+       "write a full row");
+    is_deeply([ Imager::i_gsamp($imraw, 0, 10, 5, [ 0, 1, 2 ]) ],
+             [ (127, 63, 31) x 10 ],
+             "check full row");
+    is(Imager::i_psampf($imraw, 8, 8, [ 0, 1, 2 ],
+                       [ 1.0, 0.5, 0.125, 0.25, 0.125, 0.0625, 0.125, 0, 1 ]),
+       6, "i_psampf channels [0, 1, 2], 9 samples, but room for 6");
+    is(Imager::i_psampf($imraw, 4, 6, undef, [ map $_/254.9, 0 .. 18 ], 1), 18,
+       "psampf with offset");
+    is_deeply([ Imager::i_gsamp($imraw, 0, 10, 6, undef) ],
+             [ (0) x 12, 1 .. 18 ],
+             "check result");
+    is(Imager::i_psampf($imraw, 4, 11, undef, [ map $_/254.9, 0 .. 18 ], 1, 3), 9,
+       "psampf with offset and width");
+    is_deeply([ Imager::i_gsamp($imraw, 0, 10, 11, undef) ],
+             [ (0) x 12, 1 .. 9, (0) x 9 ],
+             "check result");
+  }
+  { # errors we catch
+    is(Imager::i_psampf($imraw, 6, 8, [ 0, 1, 3 ], [ 1, 0.5, 0.125 ]),
+       undef, "i_psampf channels [0, 1, 3], 3 samples (invalid channel number)");
+    is(_get_error(), "No channel 3 in this image",
+       "check error message");
+    is(Imager::i_psampf($imraw, 6, 8, [ 0, 1, -1 ], [ 1, 0.5, 0.125 ]),
+       undef, "i_psampf channels [0, 1, -1], 3 samples (invalid channel number)");
+    is(_get_error(), "No channel -1 in this image",
+       "check error message");
+    is(Imager::i_psampf($imraw, 0, -1, undef, [ 0, 0, 0 ]), undef,
+       "negative y");
+    is(_get_error(), $psamp_outside_error,
+       "check error message");
+    is(Imager::i_psampf($imraw, 0, 20, undef, [ 0, 0, 0 ]), undef,
+       "y overflow");
+    is(_get_error(), $psamp_outside_error,
+       "check error message");
+    is(Imager::i_psampf($imraw, -1, 0, undef, [ 0, 0, 0 ]), undef,
+       "negative x");
+    is(_get_error(), $psamp_outside_error,
+       "check error message");
+    is(Imager::i_psampf($imraw, 10, 0, undef, [ 0, 0, 0 ]), undef,
+       "x overflow");
+    is(_get_error(), $psamp_outside_error,
+       "check error message");
+  }
+  { # test the im_fsample_list typemap
+    ok(!eval { Imager::i_psampf($imraw, 9, 9, [ 0 ], undef); 1 },
+       "pass undef as the sample list");
+    like($@, qr/data must be a scalar or an arrayref/,
+        "check message");
+    ok(!eval { Imager::i_psampf($imraw, 9, 9, [ 0 ], { a => 1 }); 1 },
+       "hashref as the sample list");
+    like($@, qr/data must be a scalar or an arrayref/,
+        "check message");
+    ok(!eval { Imager::i_psampf($imraw, 9, 9, [ 0 ], []); 1 },
+       "empty sample list");
+    like($@, qr/i_psampf: no samples provided in data/,
+        "check message");
+    ok(!eval { Imager::i_psampf($imraw, 9, 9, [ 0 ], ""); 1 },
+       "empty scalar sample list");
+    like($@, qr/i_psampf: no samples provided in data/,
+        "check message");
+
+    # not the typemap
+    is(Imager::i_psampf($imraw, 0, 8, undef, [ (0) x 3 ], -1), undef,
+       "negative offset");
+    is(_get_error(), "offset must be non-negative",
+       "check message");
+
+    is(Imager::i_psampf($imraw, 0, 8, undef, [ (0) x 3 ], 4), undef,
+       "too high offset");
+    is(_get_error(), "offset greater than number of samples supplied",
+       "check message");
+  }
+  print "# end psampf tests\n";
+}
+
 {
   print "# OO level scanline function tests\n";
   my $im = Imager->new(xsize=>10, ysize=>10, channels=>4);
@@ -425,8 +663,11 @@ cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
   my @plin_colors2 = ( $green, $red, $blue, $red );
   is($im->setscanline('y'=>2, 'x'=>3, pixels=>\@plin_colors2), 4,
      "setscanline - arrayref");
-  is_deeply([ ([ 0,0,0,0 ]) x 3, (map [ $_->rgba ], @plin_colors2),
-             ([ 0,0,0,0 ]) x 3 ],
+
+  # using map instead of x here due to a bug in some versions of Test::More
+  # fixed in the latest Test::More
+  is_deeply([ ( map [ 0,0,0,0 ], 1..3), (map [ $_->rgba ], @plin_colors2),
+             ( map [ 0,0,0,0 ], 1..3) ],
            [ map [ $_->rgba ], $im->getscanline('y'=>2) ],
            "check write to middle of line");
   
@@ -473,6 +714,64 @@ cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
   print "# end OO level scanline function tests\n";
 }
 
+{ # RT 74882
+  # for the non-gsamp_bits case with a target parameter it was
+  # treating the target parameter as a hashref
+  {
+    my $im = Imager->new(xsize => 10, ysize => 10);
+    my $c1 = NC(0, 63, 255);
+    my $c2 = NC(255, 128, 255);
+    is($im->setscanline(y => 1, pixels => [ ( $c1, $c2 ) x 5 ]),
+       10, "set some test data")
+      or diag "setscanline: ", $im->errstr;
+    my @target;
+    is($im->getsamples(y => 1, x => 1, target => \@target, width => 3),
+       9, "getsamples to target");
+    is_deeply(\@target, [ 255, 128, 255, 0, 63, 255, 255, 128, 255 ],
+             "check result");
+  }
+  {
+    my $im = Imager->new(xsize => 10, ysize => 10, bits => "double");
+    my $c1 = NCF(0, 0.25, 1.0);
+    my $c2 = NCF(1.0, 0.5, 1.0);
+    is($im->setscanline(y => 1, pixels => [ ( $c1, $c2 ) x 5 ]),
+       10, "set some test data")
+      or diag "setscanline: ", $im->errstr;
+    my @target;
+    is($im->getsamples(y => 1, x => 1, target => \@target, width => 3, type => "float"),
+       9, "getsamples to target");
+    is_deeply(\@target, [ 1.0, 0.5, 1.0, 0, 0.25, 1.0, 1.0, 0.5, 1.0 ],
+             "check result");
+  }
+}
+
+{ # to avoid confusion, i_glin/i_glinf modified to return 0 in unused
+  # channels at the perl level
+  my $im = Imager->new(xsize => 4, ysize => 4, channels => 2);
+  my $fill = Imager::Color->new(128, 255, 0, 0);
+  ok($im->box(filled => 1, color => $fill), 'fill it up');
+  my $data = $im->getscanline('y' => 0);
+  is(unpack("H*", $data), "80ff000080ff000080ff000080ff0000",
+     "check we get zeros");
+  my @colors = $im->getscanline('y' => 0);
+  is_color4($colors[0], 128, 255, 0, 0, "check object interface[0]");
+  is_color4($colors[1], 128, 255, 0, 0, "check object interface[1]");
+  is_color4($colors[2], 128, 255, 0, 0, "check object interface[2]");
+  is_color4($colors[3], 128, 255, 0, 0, "check object interface[3]");
+  
+  my $dataf = $im->getscanline('y' => 0, type => 'float');
+  # the extra pack/unpack is to force double precision rather than long
+  # double, otherwise the test fails
+  is_deeply([ unpack("d*", $dataf) ],
+           [ unpack("d*", pack("d*", ( 128.0 / 255.0, 1.0, 0, 0, ) x 4)) ],
+           "check we get zeroes (double)");
+  my @fcolors = $im->getscanline('y' => 0, type => 'float');
+  is_fcolor4($fcolors[0], 128.0/255.0, 1.0, 0, 0, "check object interface[0]");
+  is_fcolor4($fcolors[1], 128.0/255.0, 1.0, 0, 0, "check object interface[1]");
+  is_fcolor4($fcolors[2], 128.0/255.0, 1.0, 0, 0, "check object interface[2]");
+  is_fcolor4($fcolors[3], 128.0/255.0, 1.0, 0, 0, "check object interface[3]");
+}
+
 { # check the channel mask function
   
   my $im = Imager->new(xsize => 10, ysize=>10, bits=>8);
@@ -480,6 +779,292 @@ cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
   mask_tests($im, 0.005);
 }
 
+{ # check bounds checking
+  my $im = Imager->new(xsize => 10, ysize => 10);
+
+  image_bounds_checks($im);
+}
+
+{ # setsamples() interface to psamp()
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  is($im->setsamples(y => 1, x => 2, data => [ 1 .. 6 ]), 6,
+     "simple put (array), default channels");
+  is_deeply([ $im->getsamples(y => 1, x => 0) ],
+           [ (0) x 6, 1 .. 6, (0) x 18 ], "check they were stored");
+  is($im->setsamples(y => 3, x => 3, data => pack("C*", 2 .. 10 )), 9,
+     "simple put (scalar), default channels")
+    or diag $im->errstr;
+  is_deeply([ $im->getsamples(y => 3, x => 0) ],
+           [ (0) x 9, 2 .. 10, (0) x 12 ], "check they were stored");
+  is($im->setsamples(y => 4, x => 4, data => [ map $_ / 254.5, 1 .. 6 ], type => 'float'),
+     6, "simple put (float array), default channels");
+  is_deeply([ $im->getsamples(y => 4, x => 0) ],
+           [ (0) x 12, 1 .. 6, (0) x 12 ], "check they were stored");
+
+  is($im->setsamples(y => 5, x => 3, data => pack("d*", map $_ / 254.5, 1 .. 6), type => 'float'),
+     6, "simple put (float scalar), default channels");
+  is_deeply([ $im->getsamples(y => 5, x => 0) ],
+           [ (0) x 9, 1 .. 6, (0) x 15 ], "check they were stored");
+
+  is($im->setsamples(y => 7, x => 3, data => [ 0 .. 18 ], offset => 1), 18,
+     "setsamples offset");
+  is_deeply([ $im->getsamples(y => 7) ],
+           [ (0) x 9, 1 .. 18, (0) x 3 ],
+           "check result");
+
+  is($im->setsamples(y => 8, x => 3, data => [ map $_ / 254.9, 0 .. 18 ],
+                    offset => 1, type => 'float'),
+     18, "setsamples offset (float)");
+  is_deeply([ $im->getsamples(y => 8) ],
+           [ (0) x 9, 1 .. 18, (0) x 3 ],
+           "check result");
+
+  is_deeply([ $im->setsamples(y => 6, x => 10, data => [ (0) x 3 ]) ],
+           [], "check out of range result (8bit)");
+  is($im->errstr, $psamp_outside_error, "check error message");
+
+  is_deeply([ $im->setsamples(y => 6, x => 10, data => [ (0) x 3 ], type => "float") ],
+           [], "check out of range result (float)");
+  is($im->errstr, $psamp_outside_error, "check error message");
+
+  is_deeply([ $im->setsamples(y => 6, x => 2, channels => [0, 1, 3 ],
+                             data => [ (0) x 3 ]) ],
+           [], "check bad channels (8bit)");
+  is($im->errstr, "No channel 3 in this image",
+     "check error message");
+  
+  is_deeply([ $im->setsamples(y => 6, x => 2, channels => [0, 1, 3 ], 
+                             data => [ (0) x 3 ], type => "float") ],
+           [], "check bad channels (float)");
+  is($im->errstr, "No channel 3 in this image",
+     "check error message");
+
+  is($im->setsamples(y => 5, data => [ (0) x 3 ], type => "bad"),
+     undef, "setsamples with bad type");
+  is($im->errstr, "setsamples: type parameter invalid",
+     "check error message");
+  is($im->setsamples(y => 5),
+     undef, "setsamples with no data");
+  is($im->errstr, "setsamples: data parameter missing",
+     "check error message");
+
+  my $imempty = Imager->new;
+  is($imempty->setsamples(y => 0, data => [ (0) x 3 ]), undef,
+     "setsamples to empty image");
+  is($imempty->errstr, "setsamples: empty input image",
+     "check error message");
+}
+
+{ # getpixel parameters
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  $im->box(filled => 1, xmax => 4, color => NC(255, 0, 0));
+  $im->box(filled => 1, xmin => 5, ymax => 4, color => NC(0, 255, 255));
+  $im->box(filled => 1, xmin => 5, ymin => 5, color => NC(255, 0, 255));
+  { # error handling
+    my $empty = Imager->new;
+    ok(!$empty->getpixel(x => 0, y => 0), "getpixel empty image");
+    is($empty->errstr, "getpixel: empty input image", "check message");
+
+    ok(!$im->getpixel(y => 0), "missing x");
+    is($im->errstr, "getpixel: missing x or y parameter", "check message");
+
+    $im->_set_error("something different");
+    ok(!$im->getpixel(x => 0), "missing y");
+    is($im->errstr, "getpixel: missing x or y parameter", "check message");
+
+    ok(!$im->getpixel(x => [], y => 0), "empty x array ref");
+    is($im->errstr, "getpixel: x is a reference to an empty array",
+       "check message");
+
+    ok(!$im->getpixel(x => 0, y => []), "empty y array ref");
+    is($im->errstr, "getpixel: y is a reference to an empty array",
+       "check message");
+
+    ok(!$im->getpixel(x => 0, y => 0, type => "bad"), "bad type (scalar path)");
+    is($im->errstr, "getpixel: type must be '8bit' or 'float'",
+      "check message");
+
+    $im->_set_error("something different");
+    ok(!$im->getpixel(x => [ 0 ], y => [ 0 ], type => "bad"),
+       "bad type (array path)");
+    is($im->errstr, "getpixel: type must be '8bit' or 'float'",
+      "check message");
+  }
+
+  # simple calls
+  is_color3($im->getpixel(x => 1, y => 0), 255, 0, 0,
+           "getpixel(1, 0)");
+  is_color3($im->getpixel(x => 8, y => 1), 0, 255, 255,
+           "getpixel(8, 1)");
+  is_color3($im->getpixel(x => 8, y => 7), 255, 0, 255,
+           "getpixel(8, 7)");
+
+  {
+    # simple arrayrefs
+    my @colors = $im->getpixel(x => [ 0, 8, 7 ], y => [ 0, 7, 3 ]);
+    is(@colors, 3, "getpixel 2 3 element array refs");
+    is_color3($colors[0], 255, 0, 0, "check first color");
+    is_color3($colors[1], 255, 0, 255, "check second color");
+    is_color3($colors[2], 0, 255, 255, "check third color");
+  }
+  
+  # array and scalar
+  {
+    my @colors = $im->getpixel(x => 5, y => [ 4, 5, 9 ]);
+    is(@colors, 3, "getpixel x scalar, y arrayref of 3");
+    is_color3($colors[0], 0, 255, 255, "check first color");
+    is_color3($colors[1], 255, 0, 255, "check second color");
+    is_color3($colors[2], 255, 0, 255, "check third color");
+  }
+
+  {
+    my @colors = $im->getpixel(x => [ 0, 4, 5 ], y => 2);
+    is(@colors, 3, "getpixel y scalar, x arrayref of 3");
+    is_color3($colors[0], 255, 0, 0, "check first color");
+    is_color3($colors[1], 255, 0, 0, "check second color");
+    is_color3($colors[2], 0, 255, 255, "check third color");
+  }
+
+  { # float
+    is_fcolor3($im->getpixel(x => 1, y => 0, type => 'float'),
+              1.0, 0, 0, "getpixel(1,0) float");
+    is_fcolor3($im->getpixel(x => 8, y => 1, type => 'float'),
+              0, 1.0, 1.0, "getpixel(8,1) float");
+    is_fcolor3($im->getpixel(x => 8, y => 7, type => 'float'),
+              1.0, 0, 1.0, "getpixel(8,7) float");
+
+    my @colors = $im->getpixel(x => [ 0, 8, 7 ], y => [ 0, 7, 3 ], type => 'float');
+    is(@colors, 3, "getpixel 2 3 element array refs (float)");
+    is_fcolor3($colors[0], 1, 0, 0, "check first color");
+    is_fcolor3($colors[1], 1, 0, 1, "check second color");
+    is_fcolor3($colors[2], 0, 1, 1, "check third color");
+  }
+
+  { # out of bounds
+    my @colors = $im->getpixel(x => [ 0, -1, 5, 10 ], y => 0);
+    is(@colors, 4, "should be 4 entries")
+      or diag $im->errstr;
+    is_color3($colors[0], 255, 0, 0, "first red");
+    is($colors[1], undef, "second undef");
+    is_color3($colors[2], 0, 255, 255, "third cyan");
+    is($colors[3], undef, "fourth undef");
+  }
+
+  { # out of bounds
+    my @colors = $im->getpixel(x => [ 0, -1, 5, 10 ], y => 0, type => "float");
+    is(@colors, 4, "should be 4 entries")
+      or diag $im->errstr;
+    is_fcolor3($colors[0], 1.0, 0, 0, "first red");
+    is($colors[1], undef, "second undef");
+    is_fcolor3($colors[2], 0, 1.0, 1.0, "third cyan");
+    is($colors[3], undef, "fourth undef");
+  }
+}
+
+{ # setpixel
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  { # errors
+    my $empty = Imager->new;
+    ok(!$empty->setpixel(x => 0, y => 0, color => $red),
+       "setpixel on empty image");
+    is($empty->errstr, "setpixel: empty input image", "check message");
+
+    ok(!$im->setpixel(y => 0, color => $red), "missing x");
+    is($im->errstr, "setpixel: missing x or y parameter", "check message");
+
+    $im->_set_error("something different");
+    ok(!$im->setpixel(x => 0, color => $red), "missing y");
+    is($im->errstr, "setpixel: missing x or y parameter", "check message");
+
+    ok(!$im->setpixel(x => [], y => 0, color => $red), "empty x array ref");
+    is($im->errstr, "setpixel: x is a reference to an empty array",
+       "check message");
+
+    ok(!$im->setpixel(x => 0, y => [], color => $red), "empty y array ref");
+    is($im->errstr, "setpixel: y is a reference to an empty array",
+       "check message");
+
+    ok(!$im->setpixel(x => 0, y => 0, color => "not really a color"),
+       "color not a color");
+    is($im->errstr, "setpixel: No color named not really a color found",
+       "check message");
+  }
+
+  # simple set
+  is($im->setpixel(x => 0, y => 0, color => $red), $im,
+     "simple setpixel")
+    or diag "simple set float: ", $im->errstr;
+  is_color3($im->getpixel(x => 0, y => 0), 255, 0, 0, "check stored pixel");
+
+  is($im->setpixel(x => 1, y => 2, color => $f_red), $im,
+     "simple setpixel (float)")
+    or diag "simple set float: ", $im->errstr;
+  is_color3($im->getpixel(x => 1, y => 2), 255, 0, 0, "check stored pixel");
+
+  is($im->setpixel(x => -1, y => 0, color => $red), undef,
+     "simple setpixel outside of image");
+  is($im->setpixel(x => 0, y => -1, color => $f_red), undef,
+     "simple setpixel (float) outside of image");
+
+  # simple arrayrefs
+  is($im->setpixel( x => [ 0, 8, 7 ], y => [ 0, 7, 3 ], color => $blue),
+     3, "setpixel with 3 element array refs");
+  my @colors = $im->getpixel(x => [ 8, 7, 0 ], y => [ 7, 3, 0 ]);
+  is_color3($colors[0], 0, 0, 255, "check first color");
+  is_color3($colors[1], 0, 0, 255, "check second color");
+  is_color3($colors[2], 0, 0, 255, "check third color");
+
+  # array and scalar
+  {
+    is($im->setpixel(x => 5, y => [ 4, 5, 9 ], color => $green), 3,
+       "setpixel with x scalar, y arrayref of 3");
+    my @colors = $im->getpixel(x => [ 5, 5, 5 ], y => [ 4, 5, 9 ]);
+    is_color3($colors[0], 0, 255, 0, "check first color");
+    is_color3($colors[1], 0, 255, 0, "check second color");
+    is_color3($colors[2], 0, 255, 0, "check third color");
+  }
+
+  {
+    is($im->setpixel(x => [ 0, 4, 5 ], y => 2, color => $blue), 3,
+       "setpixel with y scalar, x arrayref of 3");
+    my @colors = $im->getpixel(x => [ 0, 4, 5 ], y => [ 2, 2, 2 ]);
+    is_color3($colors[0], 0, 0, 255, "check first color");
+    is_color3($colors[1], 0, 0, 255, "check second color");
+    is_color3($colors[2], 0, 0, 255, "check third color");
+  }
+
+  {
+    is($im->setpixel(x => [ 0, -1, 10, 5, 0 ], y => [ 0, 1, 2, 3, 1 ], color => $blue), 3,
+       "set array with two bad locations")
+      or diag "set array bad locations: ", $im->errstr;
+    my @colors = $im->getpixel(x => [ 0, 5, 0 ], y => [ 0, 3, 1 ]);
+    is_color3($colors[0], 0, 0, 255, "check first color");
+    is_color3($colors[1], 0, 0, 255, "check second color");
+    is_color3($colors[2], 0, 0, 255, "check third color");
+  }
+  {
+    is($im->setpixel(x => [ 0, -1, 10, 5, 0 ], y => [ 0, 1, 2, 3, 1 ], color => $f_green), 3,
+       "set array with two bad locations (float)")
+      or diag "set array bad locations (float): ", $im->errstr;
+    my @colors = $im->getpixel(x => [ 0, 5, 0 ], y => [ 0, 3, 1 ]);
+    is_color3($colors[0], 0, 255, 0, "check first color");
+    is_color3($colors[1], 0, 255, 0, "check second color");
+    is_color3($colors[2], 0, 255, 0, "check third color");
+  }
+  { # default color
+    is($im->setpixel(x => 0, y => 9), $im, "setpixel() default color")
+      or diag "setpixel default color: ", $im->errstr;
+    is_color3($im->getpixel(x => 0, y => 9), 255, 255, 255,
+             "check color set");
+  }
+}
+
+Imager->close_log();
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t01introvert.log";
+}
+
 sub check_add {
   my ($im, $color, $expected) = @_;
   my $index = Imager::i_addcolors($im, $color);
@@ -508,3 +1093,8 @@ sub dump_colors {
     print "# ", map(sprintf("%02X", $_), ($col->rgba)[0..2]),"\n";
   }
 }
+
+sub _get_error {
+  my @errors = Imager::i_errors();
+  return join(": ", map $_->[0], @errors);
+}