]> git.imager.perl.org - imager.git/commitdiff
[rt #84596] rearrange Imager's test files
authorTony Cook <tony@develop-help.com>
Thu, 2 May 2013 23:56:50 +0000 (09:56 +1000)
committerTony Cook <tony@develop-help.com>
Thu, 2 May 2013 23:56:50 +0000 (09:56 +1000)
The old one-level, 2 digit test file arrangement made it difficult to
add new test files, with the effect that I have some test files that
test things that should be separate (eg. the filters test).

This is mostly a simple rename pass so far,

141 files changed:
MANIFEST
MANIFEST.SKIP
Makefile.PL
t/000-load.t [new file with mode: 0644]
t/100-base/010-introvert.t [new file with mode: 0644]
t/100-base/020-color.t [new file with mode: 0644]
t/100-base/030-countc.t [new file with mode: 0644]
t/100-base/800-tr18561.t [new file with mode: 0644]
t/100-base/801-tr18561b.t [new file with mode: 0644]
t/150-type/020-sixteen.t [new file with mode: 0644]
t/150-type/030-double.t [new file with mode: 0644]
t/150-type/040-palette.t [new file with mode: 0644]
t/150-type/100-masked.t [new file with mode: 0644]
t/200-file/010-iolayer.t [new file with mode: 0644]
t/200-file/100-files.t [new file with mode: 0644]
t/200-file/200-nojpeg.t [new file with mode: 0644]
t/200-file/210-nopng.t [new file with mode: 0644]
t/200-file/220-nogif.t [new file with mode: 0644]
t/200-file/230-notiff.t [new file with mode: 0644]
t/200-file/300-raw.t [new file with mode: 0644]
t/200-file/310-pnm.t [new file with mode: 0644]
t/200-file/320-bmp.t [new file with mode: 0644]
t/200-file/330-tga.t [new file with mode: 0644]
t/200-file/400-basic.t [new file with mode: 0644]
t/250-draw/010-draw.t [new file with mode: 0644]
t/250-draw/020-flood.t [new file with mode: 0644]
t/250-draw/030-paste.t [new file with mode: 0644]
t/250-draw/040-rubthru.t [new file with mode: 0644]
t/250-draw/050-polyaa.t [new file with mode: 0644]
t/250-draw/100-fill.t [new file with mode: 0644]
t/250-draw/200-compose.t [new file with mode: 0644]
t/300-transform/010-scale.t [new file with mode: 0644]
t/300-transform/020-combine.t [new file with mode: 0644]
t/300-transform/030-copyflip.t [new file with mode: 0644]
t/300-transform/040-crop.t [new file with mode: 0644]
t/300-transform/050-convert.t [new file with mode: 0644]
t/300-transform/060-map.t [new file with mode: 0644]
t/300-transform/500-trans.t [new file with mode: 0644]
t/300-transform/600-trans2.t [new file with mode: 0644]
t/300-transform/610-postfix.t [new file with mode: 0644]
t/300-transform/620-infix.t [new file with mode: 0644]
t/300-transform/630-assem.t [new file with mode: 0644]
t/350-font/010-font.t [new file with mode: 0644]
t/350-font/020-tt.t [new file with mode: 0644]
t/350-font/030-ttoo.t [new file with mode: 0644]
t/350-font/040-ttstd.t [new file with mode: 0644]
t/350-font/100-texttools.t [new file with mode: 0644]
t/400-filter/010-filters.t [new file with mode: 0644]
t/450-api/100-inline.t [new file with mode: 0644]
t/450-api/110-inlinectx.t [new file with mode: 0644]
t/850-thread/010-base.t [new file with mode: 0644]
t/850-thread/100-error.t [new file with mode: 0644]
t/850-thread/110-log.t [new file with mode: 0644]
t/900-util/010-test.t [new file with mode: 0644]
t/900-util/020-error.t [new file with mode: 0644]
t/900-util/030-log.t [new file with mode: 0644]
t/900-util/040-limit.t [new file with mode: 0644]
t/900-util/050-matrix.t [new file with mode: 0644]
t/900-util/060-extutil.t [new file with mode: 0644]
t/900-util/060-hlines.t [new file with mode: 0644]
t/950-kwalitee/010-pod.t [new file with mode: 0644]
t/950-kwalitee/020-samples.t [new file with mode: 0644]
t/950-kwalitee/030-podcover.t [new file with mode: 0644]
t/950-kwalitee/040-strict.t [new file with mode: 0644]
t/950-kwalitee/050-meta.t [new file with mode: 0644]
t/t00basic.t [deleted file]
t/t01introvert.t [deleted file]
t/t020masked.t [deleted file]
t/t021sixteen.t [deleted file]
t/t022double.t [deleted file]
t/t023palette.t [deleted file]
t/t03test.t [deleted file]
t/t05error.t [deleted file]
t/t07iolayer.t [deleted file]
t/t080log.t [deleted file]
t/t081error.t [deleted file]
t/t082limit.t [deleted file]
t/t1000files.t [deleted file]
t/t101nojpeg.t [deleted file]
t/t102nopng.t [deleted file]
t/t103raw.t [deleted file]
t/t104ppm.t [deleted file]
t/t105nogif.t [deleted file]
t/t106notiff.t [deleted file]
t/t107bmp.t [deleted file]
t/t108tga.t [deleted file]
t/t15color.t [deleted file]
t/t16matrix.t [deleted file]
t/t20fill.t [deleted file]
t/t21draw.t [deleted file]
t/t22flood.t [deleted file]
t/t31font.t [deleted file]
t/t35ttfont.t [deleted file]
t/t36oofont.t [deleted file]
t/t37std.t [deleted file]
t/t40scale.t [deleted file]
t/t50basicoo.t [deleted file]
t/t55trans.t [deleted file]
t/t56postfix.t [deleted file]
t/t57infix.t [deleted file]
t/t58trans2.t [deleted file]
t/t59assem.t [deleted file]
t/t61filters.t [deleted file]
t/t62compose.t [deleted file]
t/t63combine.t [deleted file]
t/t64copyflip.t [deleted file]
t/t65crop.t [deleted file]
t/t66paste.t [deleted file]
t/t67convert.t [deleted file]
t/t68map.t [deleted file]
t/t69rubthru.t [deleted file]
t/t75polyaa.t [deleted file]
t/t80texttools.t [deleted file]
t/t81hlines.t [deleted file]
t/t82inline.t [deleted file]
t/t83extutil.t [deleted file]
t/t84inlinectx.t [deleted file]
t/t90cc.t [deleted file]
t/t91pod.t [deleted file]
t/t92samples.t [deleted file]
t/t93podcover.t [deleted file]
t/t94kwalitee.t [deleted file]
t/t95log.t [deleted file]
t/t98meta.t [deleted file]
t/t99thread.t [deleted file]
t/tr18561.t [deleted file]
t/tr18561b.t [deleted file]
t/x107bmp.t [deleted file]
t/x11rubthru.t [deleted file]
t/x20spell.t [deleted file]
t/x30podlinkcheck.t [deleted file]
t/x40checklib.t [deleted file]
t/x90cmpversion.t [deleted file]
t/x91manifest.t [deleted file]
xt/x107bmp.t [new file with mode: 0644]
xt/x11rubthru.t [new file with mode: 0644]
xt/x20spell.t [new file with mode: 0644]
xt/x30podlinkcheck.t [new file with mode: 0644]
xt/x40checklib.t [new file with mode: 0644]
xt/x90cmpversion.t [new file with mode: 0644]
xt/x91manifest.t [new file with mode: 0644]

index 55a9466d77640df85da8a51cf50bef785b702265..25d87eaf03c8138f053c29e7280e63559f98fa44 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -311,71 +311,71 @@ SGI/testimg/verb6.rgb
 spot.perl                      For making an ordered dither matrix from a spot function
 stackmach.c
 stackmach.h
 spot.perl                      For making an ordered dither matrix from a spot function
 stackmach.c
 stackmach.h
+t/000-load.t                   Test Imager modules can be loaded
+t/100-base/010-introvert.t     Test image inspection
+t/100-base/020-color.t         Test Imager::Color
+t/100-base/030-countc.t                Test getcolorcount() etc
+t/100-base/800-tr18561.t       Regression test for RT #18561
+t/100-base/801-tr18561b.t      Regression test for RT #18561
+t/150-type/020-sixteen.t       Test 16-bit/sample images
+t/150-type/030-double.t                Test double/sample images
+t/150-type/040-palette.t       Test paletted images
+t/150-type/100-masked.t                Test masked images
+t/200-file/010-iolayer.t       Test Imager I/O layer objects
+t/200-file/100-files.t         Format independent file tests
+t/200-file/200-nojpeg.t                Test handling when jpeg not available
+t/200-file/210-nopng.t         Test handling when png not available
+t/200-file/220-nogif.t         Test handling when gif not available
+t/200-file/230-notiff.t                Test handling when tiff not available
+t/200-file/300-raw.t           Test raw file handling
+t/200-file/310-pnm.t           Test PNM file handling
+t/200-file/320-bmp.t           Test BMP file handling
+t/200-file/330-tga.t           Test TGA file handling
+t/200-file/400-basic.t         Test basic operations across file formats
+t/250-draw/010-draw.t          Basic drawing tests
+t/250-draw/020-flood.t         Flood fill tests
+t/250-draw/030-paste.t         Test the paste() method
+t/250-draw/040-rubthru.t       Test the rubthrough() method
+t/250-draw/050-polyaa.t                polygon()
+t/250-draw/100-fill.t          fills
+t/250-draw/200-compose.t       compose()
+t/300-transform/010-scale.t    scale(), scaleX() and scaleY()
+t/300-transform/020-combine.t  Test combine() method
+t/300-transform/030-copyflip.t Test copy, flip, rotate, matrix_transform
+t/300-transform/040-crop.t
+t/300-transform/050-convert.t
+t/300-transform/060-map.t
+t/300-transform/500-trans.t    transform()
+t/300-transform/600-trans2.t   transform2() using RPN
+t/300-transform/610-postfix.t  more transform2() using RPN
+t/300-transform/620-infix.t    transform2() using infix
+t/300-transform/630-assem.t    transform2() using assembler
+t/350-font/010-font.t          General font interface tests
+t/350-font/020-tt.t            low level FT1 tests
+t/350-font/030-ttoo.t          OO level FT1 tests
+t/350-font/040-ttstd.t         Standard font tests for TT
+t/350-font/100-texttools.t     Test text wrapping
+t/400-filter/010-filters.t     Consolidated filter tests (needs to split)
+t/450-api/100-inline.t         Inline::C integration and API
+t/450-api/110-inlinectx.t      context APIs
+t/850-thread/010-base.t                Test wrt to perl threads
+t/850-thread/100-error.t       error stack handling with threads
+t/850-thread/110-log.t         log handling with threads
+t/900-util/010-test.t          Test Imager::Test
+t/900-util/020-error.t         Error stack
+t/900-util/030-log.t           log
+t/900-util/040-limit.t         file size limits
+t/900-util/050-matrix.t                Imager::Matrix2d
+t/900-util/060-extutil.t       Imager::ExtUtils
+t/900-util/060-hlines.t                hlines.c internal API
+t/950-kwalitee/010-pod.t       Test POD with Test::Pod
+t/950-kwalitee/020-samples.t   Check samples are in samples/README
+t/950-kwalitee/030-podcover.t  POD Coverage tests
+t/950-kwalitee/040-strict.t    Check use strict is usede
+t/950-kwalitee/050-meta.t      Check META.yml is valid
 t/GoodTestFont.pm              A dummy (hardly implemented) font driver.
 t/Pod/Coverage/Imager.pm
 t/GoodTestFont.pm              A dummy (hardly implemented) font driver.
 t/Pod/Coverage/Imager.pm
-t/t00basic.t
-t/t01introvert.t
-t/t020masked.t
-t/t021sixteen.t
-t/t022double.t                 Test double/sample images
-t/t023palette.t                        Test paletted images
-t/t03test.t                    Test Imager::Test
-t/t05error.t
-t/t07iolayer.t
-t/t080log.t
-t/t081error.t
-t/t082limit.t
-t/t1000files.t                 Format independent file tests
 t/t1000lib/Imager/File/BAD.pm  Test failing to load a file handler
 t/t1000lib/Imager/File/BAD.pm  Test failing to load a file handler
-t/t101nojpeg.t                 Test handling when jpeg not available
-t/t102nopng.t                  Test handling when png not available
-t/t103raw.t
-t/t104ppm.t
-t/t105nogif.t                  Test handling when gif not available
-t/t106notiff.t                 Test handling when tiff not available
-t/t107bmp.t
-t/t108tga.t
-t/t15color.t
-t/t16matrix.t                  Tests Imager::Matrix2d
-t/t20fill.t                    Tests fills
-t/t21draw.t                    Basic drawing tests
-t/t22flood.t                   Flood fill tests
-t/t31font.t                    General font interface tests
-t/t35ttfont.t
-t/t36oofont.t
-t/t37std.t                     Standard font tests for TT
-t/t40scale.t
-t/t50basicoo.t
-t/t55trans.t
-t/t56postfix.t
-t/t57infix.t
-t/t58trans2.t
-t/t59assem.t
-t/t61filters.t
-t/t62compose.t
-t/t63combine.t                 Test combine() method
-t/t64copyflip.t                        Test copy, flip, rotate, matrix_transform
-t/t65crop.t
-t/t66paste.t
-t/t67convert.t
-t/t68map.t
-t/t69rubthru.t
-t/t75polyaa.t
-t/t80texttools.t               Test text wrapping
-t/t81hlines.t                  Test hlines.c
-t/t82inline.t                  Test Inline::C integration
-t/t83extutil.t                 Test Imager::ExtUtils
-t/t84inlinectx.t
-t/t90cc.t
-t/t91pod.t                     Test POD with Test::Pod
-t/t92samples.t
-t/t93podcover.t                        POD Coverage tests
-t/t94kwalitee.t                        Various "kwalitee" tests
-t/t95log.t
-t/t98meta.t
-t/t99thread.t                  Test wrt to perl threads
-t/tr18561.t                    Regression tests
-t/tr18561b.t
 T1/fontfiles/dcr10.afm
 T1/fontfiles/dcr10.pfb
 T1/fontfiles/ExistenceTest.afm please edit ExistenceTest.sfd in CVS
 T1/fontfiles/dcr10.afm
 T1/fontfiles/dcr10.pfb
 T1/fontfiles/ExistenceTest.afm please edit ExistenceTest.sfd in CVS
@@ -514,7 +514,7 @@ W32/lib/Imager/Font/Win32.pm
 W32/Makefile.PL
 W32/README
 W32/t/t10win32.t               Tests Win32 GDI font support
 W32/Makefile.PL
 W32/README
 W32/t/t10win32.t               Tests Win32 GDI font support
-W32/t/t90std.t                         Standard font tests for W32
+W32/t/t90std.t                 Standard font tests for W32
 W32/W32.pm
 W32/W32.xs
 W32/win32.c                    Implements font support through Win32 GDI
 W32/W32.pm
 W32/W32.xs
 W32/win32.c                    Implements font support through Win32 GDI
index b1d5a89807d70782cfd1a062cecac43cb2e0808d..96d847e0a7240ab92225f72f9884c6228952175b 100644 (file)
@@ -10,6 +10,9 @@
 # unshipped test images
 ^xtestimg/
 
 # unshipped test images
 ^xtestimg/
 
+# unshipped tests
+^xt/
+
 # base for some other images
 ^testimg/pbm_base\.pgm$
 
 # base for some other images
 ^testimg/pbm_base\.pgm$
 
@@ -37,7 +40,6 @@
 ^fileformatdocs/
 ^extraimages/
 ^fontfiles/.*\.sfd$
 ^fileformatdocs/
 ^extraimages/
 ^fontfiles/.*\.sfd$
-^t/x.*\.t$
 ^imcover.perl$
 
 # might distribute one day
 ^imcover.perl$
 
 # might distribute one day
@@ -99,6 +101,9 @@ Makefile\.old$
 # generated if we build them in their own directory
 ^(PNG|TIFF|FT2|W32|GIF)/blib/
 
 # generated if we build them in their own directory
 ^(PNG|TIFF|FT2|W32|GIF)/blib/
 
+# not shipped with Imager itself
+^(PNG|TIFF|FT2|W32|GIF)/MANIFEST
+
 # generated from .im files
 ^combine\.c$
 ^compose\.c$
 # generated from .im files
 ^combine\.c$
 ^compose\.c$
@@ -134,4 +139,7 @@ Makefile\.old$
 
 # sub-module build junk
 \.bak$
 
 # sub-module build junk
 \.bak$
-MYMETA.json
\ No newline at end of file
+MYMETA.json
+
+# dist dir
+^Imager-[01]\.[0-9]+/
\ No newline at end of file
index 39a0278fe4346990d003f7933d5011c135bbd16a..35d80babd04a61f54a89f0decaaf52f1b58d66cf 100644 (file)
@@ -201,6 +201,11 @@ if ($trace_context) {
   $CFLAGS .= " -DIMAGER_TRACE_CONTEXT";
 }
 
   $CFLAGS .= " -DIMAGER_TRACE_CONTEXT";
 }
 
+my $tests = 't/*.t t/*/*.t';
+if (-d "xt" && scalar(() = glob("xt/*.t"))) {
+  $tests .= " xt/*.t";
+}
+
 my %opts=
   (
    'NAME'         => 'Imager',
 my %opts=
   (
    'NAME'         => 'Imager',
@@ -218,6 +223,7 @@ my %opts=
     'XSLoader'    => 0,
    },
    TYPEMAPS       => \@typemaps,
     'XSLoader'    => 0,
    },
    TYPEMAPS       => \@typemaps,
+   test =>        { TESTS => $tests },
   );
 
 if ($coverage) {
   );
 
 if ($coverage) {
diff --git a/t/000-load.t b/t/000-load.t
new file mode 100644 (file)
index 0000000..883af43
--- /dev/null
@@ -0,0 +1,19 @@
+#!perl -w
+use strict;
+use Test::More tests => 15;
+
+use_ok('Imager');
+use_ok('Imager::Font');
+use_ok('Imager::Color');
+use_ok('Imager::Color::Float');
+use_ok('Imager::Color::Table');
+use_ok('Imager::Matrix2d');
+use_ok('Imager::ExtUtils');
+use_ok('Imager::Expr');
+use_ok('Imager::Expr::Assem');
+use_ok('Imager::Font::BBox');
+use_ok('Imager::Font::Wrap');
+use_ok('Imager::Fountain');
+use_ok('Imager::Regops');
+use_ok('Imager::Test');
+use_ok('Imager::Transform');
diff --git a/t/100-base/010-introvert.t b/t/100-base/010-introvert.t
new file mode 100644 (file)
index 0000000..d040eaf
--- /dev/null
@@ -0,0 +1,1160 @@
+#!perl -w
+# t/t01introvert.t - tests internals of image formats
+# to make sure we get expected values
+
+use strict;
+use Test::More tests => 466;
+
+BEGIN { use_ok(Imager => qw(:handy :all)) }
+
+use Imager::Test qw(image_bounds_checks is_color3 is_color4 is_fcolor4 color_cmp mask_tests is_fcolor3);
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t01introvert.log");
+
+my $im_g = Imager::ImgRaw::new(100, 101, 1);
+
+my $red = NC(255, 0, 0);
+my $green = NC(0, 255, 0);
+my $blue = NC(0, 0, 255);
+
+use Imager::Color::Float;
+my $f_black = Imager::Color::Float->new(0, 0, 0);
+my $f_red = Imager::Color::Float->new(1.0, 0, 0);
+my $f_green = Imager::Color::Float->new(0, 1.0, 0);
+my $f_blue = Imager::Color::Float->new(0, 0, 1.0);
+
+is(Imager::i_img_getchannels($im_g), 1, "1 channel image channel count");
+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");
+is($ginfo[1], 101, "1 channel image height");
+
+undef $im_g; # can we check for release after this somehow?
+
+my $im_rgb = Imager::ImgRaw::new(100, 101, 3);
+
+is(Imager::i_img_getchannels($im_rgb), 3, "3 channel image channel count");
+is((Imager::i_img_getmask($im_rgb) & 7), 7, "3 channel image mask");
+is(Imager::i_img_bits($im_rgb), 8, "3 channel image has 8 bits/sample");
+is(Imager::i_img_type($im_rgb), 0, "3 channel image is direct");
+
+undef $im_rgb;
+
+my $im_pal = Imager::i_img_pal_new(100, 101, 3, 256);
+
+ok($im_pal, "make paletted image");
+is(Imager::i_img_getchannels($im_pal), 3, "pal img channel count");
+is(Imager::i_img_bits($im_pal), 8, "pal img bits");
+is(Imager::i_img_type($im_pal), 1, "pal img is paletted");
+
+my $red_idx = check_add($im_pal, $red, 0);
+my $green_idx = check_add($im_pal, $green, 1);
+my $blue_idx = check_add($im_pal, $blue, 2);
+
+# basic writing of palette indicies
+# fill with red
+is(Imager::i_ppal($im_pal, 0, 0, ($red_idx) x 100), 100, 
+   "write red 100 times");
+# and blue
+is(Imager::i_ppal($im_pal, 50, 0, ($blue_idx) x 50), 50,
+   "write blue 50 times");
+
+# make sure we get it back
+my @pals = Imager::i_gpal($im_pal, 0, 100, 0);
+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 ]);
+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 ]);
+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");
+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");
+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");
+# that was in the palette, should still be paletted
+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");
+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");
+
+my $black = NC(0, 0, 0);
+# this should convert the image to RGB
+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],
+     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");
+  my @samples = Imager::i_gsamp($im_pal2, 0, 100, 0, [ 0, 1, 2 ]);
+  my @expect = unpack("C*", "\0\xFF\0\0\0\0"."\xFF\0\0" x 48 . "\0\0\xFF" x 50);
+  my $match_list = is_deeply(\@samples, \@expect, "colors are still correct");
+  my $samples = Imager::i_gsamp($im_pal2, 0, 100, 0, [ 0, 1, 2 ]);
+  my $match_scalar = is_deeply([ unpack("C*", $samples) ],
+                              \@expect, "colors are still correct (scalar)");
+  unless ($match_list && $match_scalar) {
+    # this has been failing on a particular smoker, provide more
+    # diagnostic information
+    print STDERR "Pallete:\n";
+    print STDERR "  $_: ", join(",", $colors[$_]->rgba), "\n" for 0..$#colors;
+    print STDERR "Samples (list): ", join(",", @samples), "\n";
+    print STDERR "Samples (scalar): ", join(",", unpack("C*", $samples)), "\n";
+    print STDERR "Indexes: ", join(",", Imager::i_gpal($im_pal2, 0, 100, 0)), "\n";
+  }
+}
+
+# test the OO interfaces
+my $impal2 = Imager->new(type=>'pseudo', xsize=>200, ysize=>201);
+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]);
+  ok($red_idx, "add red to OO");
+  is(0+$red_idx, 0, "and it's expected index for red");
+  my $blue_idx = $impal2->addcolors(colors=>[$blue, $green]);
+  ok($blue_idx, "add blue/green via OO");
+  is($blue_idx, 1, "and it's expected index for blue");
+  my $green_idx = $blue_idx + 1;
+  my $c = $impal2->getcolors(start=>$green_idx);
+  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 );
+  my $good = 1;
+  for my $i (0..2) {
+    if (color_cmp($cols[$i], $exp[$i])) {
+      $good = 0;
+      last;
+    }
+  }
+  ok($good, "all colors in palette as expected");
+  is($impal2->colorcount, 3, "and colorcount returns 3");
+  is($impal2->maxcolors, 256, "maxcolors as expected");
+  is($impal2->findcolor(color=>$blue), 1, "findcolors found blue");
+  ok($impal2->setcolors(start=>0, colors=>[ $blue, $red ]),
+     "we can setcolors");
+
+  # make an rgb version
+  my $imrgb2 = $impal2->to_rgb8()
+    or diag($impal2->errstr);
+  is($imrgb2->type, 'direct', "converted is direct");
+
+  # and back again, specifying the palette
+  my @colors = ( $red, $blue, $green );
+  my $impal3 = $imrgb2->to_paletted(colors=>\@colors,
+                                    make_colors=>'none',
+                                    translate=>'closest');
+  ok($impal3, "got a paletted image from conversion");
+  dump_colors(@colors);
+  print "# in image\n";
+  dump_colors($impal3->getcolors);
+  is($impal3->colorcount, 3, "new image has expected color table size");
+  is($impal3->type, 'paletted', "and is paletted");
+}
+
+{
+  my $im = Imager->new;
+  ok($im, "make empty image");
+  ok(!$im->to_rgb8, "convert to rgb8");
+  is($im->errstr, "to_rgb8: empty input image", "check message");
+  is($im->bits, undef, "can't call bits on an empty image");
+  is($im->errstr, "bits: empty input image", "check message");
+  is($im->type, undef, "can't call type on an empty image");
+  is($im->errstr, "type: empty input image", "check message");
+  is($im->virtual, undef, "can't call virtual on an empty image");
+  is($im->errstr, "virtual: empty input image", "check message");
+  is($im->is_bilevel, undef, "can't call virtual on an empty image");
+  is($im->errstr, "is_bilevel: empty input image", "check message");
+  ok(!$im->getscanline(y => 0), "can't call getscanline on an empty image");
+  is($im->errstr, "getscanline: empty input image", "check message");
+  ok(!$im->setscanline(y => 0, pixels => [ $red, $blue ]),
+     "can't call setscanline on an empty image");
+  is($im->errstr, "setscanline: empty input image", "check message");
+  ok(!$im->getsamples(y => 0), "can't call getsamples on an empty image");
+  is($im->errstr, "getsamples: empty input image", "check message");
+  is($im->getwidth, undef, "can't get width of empty image");
+  is($im->errstr, "getwidth: empty input image", "check message");
+  is($im->getheight, undef, "can't get height of empty image");
+  is($im->errstr, "getheight: empty input image", "check message");
+  is($im->getchannels, undef, "can't get channels of empty image");
+  is($im->errstr, "getchannels: empty input image", "check message");
+  is($im->getmask, undef, "can't get mask of empty image");
+  is($im->errstr, "getmask: empty input image", "check message");
+  is($im->setmask, undef, "can't set mask of empty image");
+  is($im->errstr, "setmask: 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");
+ok(!Imager->new(xsize=>1, ysize=>0), "fail to create 0 width image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "0 width error message check");
+ok(!Imager->new(xsize=>-1, ysize=>1), "fail to create -ve height image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "-ve width error message check");
+ok(!Imager->new(xsize=>1, ysize=>-1), "fail to create -ve width image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "-ve height error message check");
+ok(!Imager->new(xsize=>-1, ysize=>-1), "fail to create -ve width/height image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "-ve width/height error message check");
+
+ok(!Imager->new(xsize=>1, ysize=>1, channels=>0),
+   "fail to create a zero channel image");
+cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
+       "out of range channel message check");
+ok(!Imager->new(xsize=>1, ysize=>1, channels=>5),
+   "fail to create a five channel image");
+cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
+       "out of range channel message check");
+
+{
+  # https://rt.cpan.org/Ticket/Display.html?id=8213
+  # check for handling of memory allocation of very large images
+  # only test this on 32-bit machines - on a 64-bit machine it may
+  # result in trying to allocate 4Gb of memory, which is unfriendly at
+  # least and may result in running out of memory, causing a different
+  # type of exit
+ SKIP:
+  {
+    use Config;
+    skip("don't want to allocate 4Gb", 8) unless $Config{ptrsize} == 4;
+
+    my $uint_range = 256 ** $Config{intsize};
+    print "# range $uint_range\n";
+    my $dim1 = int(sqrt($uint_range))+1;
+    
+    my $im_b = Imager->new(xsize=>$dim1, ysize=>$dim1, channels=>1);
+    is($im_b, undef, "integer overflow check - 1 channel");
+    
+    $im_b = Imager->new(xisze=>$dim1, ysize=>1, channels=>1);
+    ok($im_b, "but same width ok");
+    $im_b = Imager->new(xisze=>1, ysize=>$dim1, channels=>1);
+    ok($im_b, "but same height ok");
+    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
+           "check the error message");
+
+    # do a similar test with a 3 channel image, so we're sure we catch
+    # the same case where the third dimension causes the overflow
+    my $dim3 = int(sqrt($uint_range / 3))+1;
+    
+    $im_b = Imager->new(xsize=>$dim3, ysize=>$dim3, channels=>3);
+    is($im_b, undef, "integer overflow check - 3 channel");
+    
+    $im_b = Imager->new(xisze=>$dim3, ysize=>1, channels=>3);
+    ok($im_b, "but same width ok");
+    $im_b = Imager->new(xisze=>1, ysize=>$dim3, channels=>3);
+    ok($im_b, "but same height ok");
+
+    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
+           "check the error message");
+  }
+}
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=9672
+  my $warning;
+  local $SIG{__WARN__} = 
+    sub { 
+      $warning = "@_";
+      my $printed = $warning;
+      $printed =~ s/\n$//;
+      $printed =~ s/\n/\n\#/g; 
+      print "# ",$printed, "\n";
+    };
+  my $img = Imager->new(xsize=>10, ysize=>10);
+  $img->to_rgb8(); # doesn't really matter what the source is
+  cmp_ok($warning, '=~', 'void', "correct warning");
+  cmp_ok($warning, '=~', 'introvert\\.t', "correct file");
+}
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=11860
+  my $im = Imager->new(xsize=>2, ysize=>2);
+  $im->setpixel(x=>0, 'y'=>0, color=>$red);
+  $im->setpixel(x=>1, 'y'=>0, color=>$blue);
+
+  my @row = Imager::i_glin($im->{IMG}, 0, 2, 0);
+  is(@row, 2, "got 2 pixels from i_glin");
+  is_color3($row[0], 255, 0, 0, "red first");
+  is_color3($row[1], 0, 0, 255, "then blue");
+}
+
+{ # general tag tests
+  
+  # we don't care much about the image itself
+  my $im = Imager::ImgRaw::new(10, 10, 1);
+
+  ok(Imager::i_tags_addn($im, 'alpha', 0, 101), "i_tags_addn(...alpha, 0, 101)");
+  ok(Imager::i_tags_addn($im, undef, 99, 102), "i_tags_addn(...undef, 99, 102)");
+  is(Imager::i_tags_count($im), 2, "should have 2 tags");
+  ok(Imager::i_tags_addn($im, undef, 99, 103), "i_tags_addn(...undef, 99, 103)");
+  is(Imager::i_tags_count($im), 3, "should have 3 tags, despite the dupe");
+  is(Imager::i_tags_find($im, 'alpha', 0), '0 but true', "find alpha");
+  is(Imager::i_tags_findn($im, 99, 0), 1, "find 99");
+  is(Imager::i_tags_findn($im, 99, 2), 2, "find 99 again");
+  is(Imager::i_tags_get($im, 0), 101, "check first");
+  is(Imager::i_tags_get($im, 1), 102, "check second");
+  is(Imager::i_tags_get($im, 2), 103, "check third");
+
+  ok(Imager::i_tags_add($im, 'beta', 0, "hello", 0), 
+     "add string with string key");
+  ok(Imager::i_tags_add($im, 'gamma', 0, "goodbye", 0),
+     "add another one");
+  ok(Imager::i_tags_add($im, undef, 199, "aloha", 0),
+     "add one keyed by number");
+  is(Imager::i_tags_find($im, 'beta', 0), 3, "find beta");
+  is(Imager::i_tags_find($im, 'gamma', 0), 4, "find gamma");
+  is(Imager::i_tags_findn($im, 199, 0), 5, "find 199");
+  ok(Imager::i_tags_delete($im, 2), "delete");
+  is(Imager::i_tags_find($im, 'beta', 0), 2, 'find beta after deletion');
+  ok(Imager::i_tags_delbyname($im, 'beta'), 'delete beta by name');
+  is(Imager::i_tags_find($im, 'beta', 0), undef, 'beta not there now');
+  is(Imager::i_tags_get_string($im, "gamma"), "goodbye", 
+     'i_tags_get_string() on a string');
+  is(Imager::i_tags_get_string($im, 99), 102, 
+     'i_tags_get_string() on a number entry');
+  ok(Imager::i_tags_delbycode($im, 99), 'delete by code');
+  is(Imager::i_tags_findn($im, 99, 0), undef, '99 not there now');
+  is(Imager::i_tags_count($im), 3, 'final count of 3');
+}
+
+{ 
+  print "# low-level scan line function tests\n";
+  my $im = Imager::ImgRaw::new(10, 10, 4);
+  Imager::i_ppix($im, 5, 0, $red);
+
+  # i_glin/i_glinf
+  my @colors = Imager::i_glin($im, 0, 10, 0);
+  is_deeply([ (0) x 20, (255, 0, 0, 255), (0) x 16 ], 
+           [ map $_->rgba, @colors ],
+           "i_glin - list context");
+  my $colors = Imager::i_glin($im, 0, 10, 0);
+  is("00" x 20 . "FF0000FF" . "00" x 16, 
+     uc unpack("H*", $colors), "i_glin - scalar context");
+  my @fcolors = Imager::i_glinf($im, 0, 10, 0);
+  is_deeply([ (0.0) x 20, (1.0, 0, 0, 1.0) , (0) x 16 ],
+           [ map $_->rgba, @fcolors ],
+           "i_glinf - list context");
+  my $fcolors = Imager::i_glinf($im, 0, 10, 0);
+  is_deeply([ (0.0) x 20, (1.0, 0, 0, 1.0) , (0) x 16 ],
+           [ unpack "d*", $fcolors ],
+           "i_glinf - scalar context");
+
+  # i_plin/i_plinf
+  my @plin_colors = (($black) x 4, $red, $blue, ($black) x 4);
+  is(Imager::i_plin($im, 0, 1, @plin_colors),
+     10, "i_plin - pass in a list");
+  # make sure we get it back
+  is_deeply([ map [ $_->rgba ], @plin_colors ],
+           [ map [ $_->rgba ], Imager::i_glin($im, 0, 10, 1) ],
+           "check i_plin wrote to the image");
+  my @scalar_plin = 
+    (
+     (0,0,0,0) x 4, 
+     (0, 255, 0, 255),
+     (0, 0, 255, 255), 
+     (0, 0, 0, 0) x 4,
+    );
+  is(Imager::i_plin($im, 0, 2, pack("C*", @scalar_plin)),
+     10, "i_plin - pass in a scalar");
+  is_deeply(\@scalar_plin,
+           [ map $_->rgba , Imager::i_glin($im, 0, 10, 2) ],
+           "check i_plin scalar wrote to the image");
+
+  my @plinf_colors = # Note: only 9 pixels
+    ( 
+     ($f_blue) x 4, 
+     $f_red, 
+     ($f_black) x 3, 
+     $f_black
+    );
+  is(Imager::i_plinf($im, 0, 3, @plinf_colors), 9,
+     "i_plinf - list");
+  is_deeply([ map $_->rgba, Imager::i_glinf($im, 0, 9, 3) ],
+           [ map $_->rgba, @plinf_colors ],
+           "check colors were written");
+  my @scalar_plinf =
+    (
+     ( 1.0, 1.0,   0, 1.0 ) x 3,
+     (   0, 1.0, 1.0, 1.0 ) x 2,
+     (   0,   0,   0,   0 ),
+     ( 1.0,   0, 1.0, 1.0 ),
+    );
+  is(Imager::i_plinf($im, 2, 4, pack("d*", @scalar_plinf)),
+     7, "i_plinf - scalar");
+  is_deeply(\@scalar_plinf,
+           [ map $_->rgba, Imager::i_glinf($im, 2, 9, 4) ],
+           "check colors were written");
+
+  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 ])),
+     "i_gsamp scalar context");
+  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 ])) ],
+           [ (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, 128, 64,
+             "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), 64, 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), 64, 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, 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_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 ]) ],
+             [ (128, 64, 32) 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);
+  $im->setpixel(color=>$red, 'x'=>5, 'y'=>0);
+  ok(!$im->getscanline(), "getscanline() - supply nothing, get nothing");
+  is($im->errstr, "missing y parameter", "check message");
+  is_deeply([ map [ $_->rgba ], $im->getscanline('y'=>0) ],
+           [ ([ 0,0,0,0]) x 5, [ 255, 0, 0, 255 ], ([ 0,0,0,0]) x 4 ],
+           "getscanline, list context, default x, width");
+  is_deeply([ map [ $_->rgba ], $im->getscanline('y'=>0, 'x'=>3) ],
+           [ ([0,0,0,0]) x 2, [ 255, 0, 0, 255 ], ([0,0,0,0]) x 4 ],
+           "getscanline, list context, default width");
+  is_deeply([ map [ $_->rgba ], $im->getscanline('y'=>0, 'x'=>4, width=>4) ],
+           [ [0,0,0,0], [ 255, 0, 0, 255 ], ([0,0,0,0]) x 2 ],
+           "getscanline, list context, no defaults");
+  is(uc unpack("H*",  $im->getscanline('y'=>0)),
+     "00000000" x 5 .  "FF0000FF" . "00000000" x 4,
+     "getscanline, scalar context, default x, width");
+  is_deeply([ map [ $_->rgba ], 
+             $im->getscanline('y'=>0, 'x'=>4, width=>4, type=>'float') ],
+           [ [0,0,0,0], [ 1.0, 0, 0, 1.0 ], ([0,0,0,0]) x 2 ],
+           "getscanline float, list context, no defaults");
+  is_deeply([ unpack "d*",
+             $im->getscanline('y'=>0, 'x'=>4, width=>4, type=>'float') ],
+           [ (0,0,0,0), ( 1.0, 0, 0, 1.0 ), (0,0,0,0) x 2 ],
+           "getscanline float, scalar context, no defaults");
+
+  ok(!$im->getscanline('y'=>0, type=>'invalid'),
+     "check invalid type checking");
+  like($im->errstr, qr/invalid type parameter/, 
+       "check message for invalid type");
+
+  my @plin_colors = (($black) x 4, $red, $blue, ($green) x 4);
+  is($im->setscanline('y'=>1, pixels=>\@plin_colors), 10,
+     "setscanline - arrayref, default x");
+  is_deeply([ map [ $_->rgba ], @plin_colors ],
+           [ map [ $_->rgba ], $im->getscanline('y'=>1) ],
+           "check colors were written");
+
+  my @plin_colors2 = ( $green, $red, $blue, $red );
+  is($im->setscanline('y'=>2, 'x'=>3, pixels=>\@plin_colors2), 4,
+     "setscanline - arrayref");
+
+  # 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");
+  
+  my $raw_colors = pack "H*", "FF00FFFF"."FF0000FF"."FFFFFFFF";
+  is($im->setscanline('y'=>3, 'x'=>2, pixels=>$raw_colors), 3,
+     "setscanline - scalar, default raw type")
+    or print "# ",$im->errstr,"\n";
+  is(uc unpack("H*", $im->getscanline('y'=>3, 'x'=>1, 'width'=>5)),
+     "00000000".uc(unpack "H*", $raw_colors)."00000000",
+     "check write");
+
+  # float colors
+  my @fcolors = ( $f_red, $f_blue, $f_black, $f_green );
+  is($im->setscanline('y'=>4, 'x'=>3, pixels=>\@fcolors), 4,
+     "setscanline - float arrayref");
+  is_deeply([ map [ $_->rgba ], @fcolors ],
+           [ map [ $_->rgba ], $im->getscanline('y'=>4, 'x'=>3, width=>4, type=>'float') ],
+           "check write");
+  # packed
+  my $packed_fcolors = pack "d*", map $_->rgba, @fcolors;
+  is($im->setscanline('y'=>5, 'x'=>4, pixels=>$packed_fcolors, type=>'float'), 4,
+     "setscanline - float scalar");
+  is_deeply([ map [ $_->rgba ], @fcolors ],
+           [ map [ $_->rgba ], $im->getscanline('y'=>5, 'x'=>4, width=>4, type=>'float') ],
+           "check write");
+
+  # get samples
+  is_deeply([ $im->getsamples('y'=>1, channels=>[ 0 ]) ],
+           [ map +($_->rgba)[0], @plin_colors ],
+           "get channel 0, list context, default x, width");
+  is_deeply([ unpack "C*", $im->getsamples('y'=>1, channels=>[0, 2]) ],
+           [ map { ($_->rgba)[0, 2] } @plin_colors ],
+           "get channel 0, 1, scalar context");
+  is_deeply([ $im->getsamples('y'=>4, 'x'=>3, width=>4, type=>'float',
+                             channels=>[1,3]) ],
+           [ map { ($_->rgba)[1,3] } @fcolors ],
+           "get channels 1,3, list context, float samples");
+  is_deeply([ unpack "d*", 
+             $im->getsamples('y'=>4, 'x'=>3, width=>4,
+                             type=>'float', channels=>[3,2,1,0]) ],
+           [ map { ($_->rgba)[3,2,1,0] } @fcolors ],
+           "get channels 3..0 as scalar, float samples");
+  
+  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);
+
+  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");
+
+  is($im->setsamples(y => 5, data => undef),
+     undef, "setsamples with undef data");
+  is($im->errstr, "setsamples: data parameter not defined",
+     "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");
+  }
+}
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->addtag(name => "foo", value => 1),
+     "can't addtag on an empty image");
+  is($empty->errstr, "addtag: empty input image",
+    "check error message");
+  ok(!$empty->settag(name => "foo", value => 1),
+     "can't settag on an empty image");
+  is($empty->errstr, "settag: empty input image",
+    "check error message");
+  ok(!$empty->deltag(name => "foo"), "can't deltag on an empty image");
+  is($empty->errstr, "deltag: empty input image",
+     "check error message");
+  ok(!$empty->tags(name => "foo"), "can't tags on an empty image");
+  is($empty->errstr, "tags: empty input image",
+     "check error message");
+}
+
+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);
+  ok($index, "got index");
+  print "# $index\n";
+  is(0+$index, $expected, "index matched expected");
+  my ($new) = Imager::i_getcolors($im, $index);
+  ok($new, "got the color");
+  ok(color_cmp($new, $color) == 0, "color matched what was added");
+
+  $index;
+}
+
+# sub array_ncmp {
+#   my ($a1, $a2) = @_;
+#   my $len = @$a1 < @$a2 ? @$a1 : @$a2;
+#   for my $i (0..$len-1) {
+#     my $diff = $a1->[$i] <=> $a2->[$i] 
+#       and return $diff;
+#   }
+#   return @$a1 <=> @$a2;
+# }
+
+sub dump_colors {
+  for my $col (@_) {
+    print "# ", map(sprintf("%02X", $_), ($col->rgba)[0..2]),"\n";
+  }
+}
+
+sub _get_error {
+  my @errors = Imager::i_errors();
+  return join(": ", map $_->[0], @errors);
+}
diff --git a/t/100-base/020-color.t b/t/100-base/020-color.t
new file mode 100644 (file)
index 0000000..edcb923
--- /dev/null
@@ -0,0 +1,241 @@
+#!perl -w
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.pl'
+
+######################### We start with some black magic to print on failure.
+
+# Change 1..1 below to 1..last_test_to_print .
+# (It may become useful if the test is moved to ./t subdirectory.)
+
+use Test::More tests => 70;
+
+use Imager;
+use Imager::Test qw(is_fcolor4);
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t15color.log");
+
+my $c1 = Imager::Color->new(100, 150, 200, 250);
+ok(test_col($c1, 100, 150, 200, 250), 'simple 4-arg');
+my $c2 = Imager::Color->new(100, 150, 200);
+ok(test_col($c2, 100, 150, 200, 255), 'simple 3-arg');
+my $c3 = Imager::Color->new("#6496C8");
+ok(test_col($c3, 100, 150, 200, 255), 'web color');
+# crashes in Imager-0.38pre8 and earlier
+my @foo;
+for (1..1000) {
+  push(@foo, Imager::Color->new("#FFFFFF"));
+}
+my $fail;
+for (@foo) {
+  Imager::Color::set_internal($_, 128, 128, 128, 128) == $_ or ++$fail;
+  Imager::Color::set_internal($_, 128, 128, 128, 128) == $_ or ++$fail;
+  test_col($_, 128, 128, 128, 128) or ++$fail;
+}
+ok(!$fail, 'consitency check');
+
+# test the new OO methods
+color_ok('r g b',, 100, 150, 200, 255, Imager::Color->new(r=>100, g=>150, b=>200));
+color_ok('red green blue', 101, 151, 201, 255, 
+         Imager::Color->new(red=>101, green=>151, blue=>201));
+color_ok('grey', 102, 255, 255, 255, Imager::Color->new(grey=>102));
+color_ok('gray', 103, 255, 255, 255, Imager::Color->new(gray=>103));
+SKIP:
+{
+  skip "no X rgb.txt found", 1 
+    unless grep -r, Imager::Color::_test_x_palettes();
+  color_ok('xname', 0, 0, 255, 255, Imager::Color->new(xname=>'blue'));
+}
+color_ok('gimp', 255, 250, 250, 255, 
+         Imager::Color->new(gimp=>'snow', palette=>'testimg/test_gimp_pal'));
+color_ok('h s v', 255, 255, 255, 255, Imager::Color->new(h=>0, 's'=>0, 'v'=>1.0));
+color_ok('h s v again', 255, 0, 0, 255, Imager::Color->new(h=>0, 's'=>1, v=>1));
+color_ok('web 6 digit', 128, 129, 130, 255, Imager::Color->new(web=>'#808182'));
+color_ok('web 3 digit', 0x11, 0x22, 0x33, 255, Imager::Color->new(web=>'#123'));
+color_ok('rgb arrayref', 255, 150, 121, 255, Imager::Color->new(rgb=>[ 255, 150, 121 ]));
+color_ok('rgba arrayref', 255, 150, 121, 128, 
+         Imager::Color->new(rgba=>[ 255, 150, 121, 128 ]));
+color_ok('hsv arrayref', 255, 0, 0, 255, Imager::Color->new(hsv=>[ 0, 1, 1 ]));
+color_ok('channel0-3', 129, 130, 131, 134, 
+         Imager::Color->new(channel0=>129, channel1=>130, channel2=>131,
+                            channel3=>134));
+color_ok('c0-3', 129, 130, 131, 134, 
+         Imager::Color->new(c0=>129, c1=>130, c2=>131, c3=>134));
+color_ok('channels arrayref', 200, 201, 203, 204, 
+         Imager::Color->new(channels=>[ 200, 201, 203, 204 ]));
+color_ok('name', 255, 250, 250, 255, 
+         Imager::Color->new(name=>'snow', palette=>'testimg/test_gimp_pal'));
+
+# test the internal HSV <=> RGB conversions
+# these values were generated using the GIMP
+# all but hue is 0..360, saturation and value from 0 to 1
+# rgb from 0 to 255
+my @hsv_vs_rgb =
+  (
+   { hsv => [ 0, 0.2, 0.1 ], rgb=> [ 25, 20, 20 ] },
+   { hsv => [ 0, 0.5, 1.0 ], rgb => [ 255, 127, 127 ] },
+   { hsv => [ 100, 0.5, 1.0 ], rgb => [ 170, 255, 127 ] },
+   { hsv => [ 100, 1.0, 1.0 ], rgb=> [ 85, 255, 0 ] },
+   { hsv => [ 335, 0.5, 0.5 ], rgb=> [127, 63, 90 ] },
+  );
+
+use Imager::Color::Float;
+my $test_num = 23;
+my $index = 0;
+for my $entry (@hsv_vs_rgb) {
+  print "# color index $index\n";
+  my $hsv = $entry->{hsv};
+  my $rgb = $entry->{rgb};
+  my $fhsvo = Imager::Color::Float->new($hsv->[0]/360.0, $hsv->[1], $hsv->[2]);
+  my $fc = Imager::Color::Float::i_hsv_to_rgb($fhsvo);
+  fcolor_close_enough("i_hsv_to_rgbf $index", $rgb->[0]/255, $rgb->[1]/255, 
+                      $rgb->[2]/255, $fc);
+  my $fc2 = Imager::Color::Float::i_rgb_to_hsv($fc);
+  fcolor_close_enough("i_rgbf_to_hsv $index", $hsv->[0]/360.0, $hsv->[1], $hsv->[2], 
+                      $fc2);
+
+  my $hsvo = Imager::Color->new($hsv->[0]*255/360.0, $hsv->[1] * 255, 
+                                $hsv->[2] * 255);
+  my $c = Imager::Color::i_hsv_to_rgb($hsvo);
+  color_close_enough("i_hsv_to_rgb $index", @$rgb, $c);
+  my $c2 = Imager::Color::i_rgb_to_hsv($c);
+  color_close_enough_hsv("i_rgb_to_hsv $index", $hsv->[0]*255/360.0, $hsv->[1] * 255, 
+                     $hsv->[2] * 255, $c2);
+  ++$index;
+}
+
+# check the built-ins table
+color_ok('builtin black', 0, 0, 0, 255, 
+       Imager::Color->new(builtin=>'black'));
+
+{
+  my $c1 = Imager::Color->new(255, 255, 255, 0);
+  my $c2 = Imager::Color->new(255, 255, 255, 255);
+  ok(!$c1->equals(other=>$c2), "not equal no ignore alpha");
+  ok(scalar($c1->equals(other=>$c2, ignore_alpha=>1)), 
+      "equal with ignore alpha");
+  ok($c1->equals(other=>$c1), "equal to itself");
+}
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=13143
+  # Imager::Color->new(color_name) warning if HOME environment variable not set
+  local $ENV{HOME};
+  my @warnings;
+  local $SIG{__WARN__} = sub { push @warnings, "@_" };
+
+  # presumably no-one will name a color like this.
+  my $c1 = Imager::Color->new(gimp=>"ABCDEFGHIJKLMNOP");
+  is(@warnings, 0, "Should be no warnings")
+    or do { print "# $_" for @warnings };
+}
+
+{
+  # float color from hex triple
+  my $f3white = Imager::Color::Float->new("#FFFFFF");
+  is_fcolor4($f3white, 1.0, 1.0, 1.0, 1.0, "check color #FFFFFF");
+  my $f3black = Imager::Color::Float->new("#000000");
+  is_fcolor4($f3black, 0, 0, 0, 1.0, "check color #000000");
+  my $f3grey = Imager::Color::Float->new("#808080");
+  is_fcolor4($f3grey, 0x80/0xff, 0x80/0xff, 0x80/0xff, 1.0, "check color #808080");
+
+  my $f4white = Imager::Color::Float->new("#FFFFFF80");
+  is_fcolor4($f4white, 1.0, 1.0, 1.0, 0x80/0xff, "check color #FFFFFF80");
+}
+
+{
+  # fail to make a color
+  ok(!Imager::Color::Float->new("-unknown-"), "try to make float color -unknown-");
+}
+
+{
+  # set after creation
+  my $c = Imager::Color::Float->new(0, 0, 0);
+  is_fcolor4($c, 0, 0, 0, 1.0, "check simple init of float color");
+  ok($c->set(1.0, 0.5, 0.25, 1.0), "set() the color");
+  is_fcolor4($c, 1.0, 0.5, 0.25, 1.0, "check after set");
+
+  ok(!$c->set("-unknown-"), "set to unknown");
+}
+
+{
+  # test ->hsv
+  my $c = Imager::Color->new(255, 0, 0);
+  my($h,$s,$v) = $c->hsv;
+  is($h,0,'red hue');
+  is($s,1,'red saturation');
+  is($v,1,'red value');
+
+  $c = Imager::Color->new(0, 255, 0);
+  ($h,$s,$v) = $c->hsv;
+  is($h,120,'green hue');
+  is($s,1,'green saturation');
+  is($v,1,'green value');
+
+  $c = Imager::Color->new(0, 0, 255);
+  ($h,$s,$v) = $c->hsv;
+  is($h,240,'blue hue');
+  is($s,1,'blue saturation');
+  is($v,1,'blue value');
+
+  $c = Imager::Color->new(255, 255, 255);
+  ($h,$s,$v) = $c->hsv;
+  is($h,0,'white hue');
+  is($s,0,'white saturation');
+  is($v,1,'white value');
+
+  $c = Imager::Color->new(0, 0, 0);
+  ($h,$s,$v) = $c->hsv;
+  is($h,0,'black hue');
+  is($s,0,'black saturation');
+  is($v,0,'black value');
+}
+
+sub test_col {
+  my ($c, $r, $g, $b, $a) = @_;
+  unless ($c) {
+    print "# $Imager::ERRSTR\n";
+    return 0;
+  }
+  my ($cr, $cg, $cb, $ca) = $c->rgba;
+  return $r == $cr && $g == $cg && $b == $cb && $a == $ca;
+}
+
+sub color_close_enough {
+  my ($name, $r, $g, $b, $c) = @_;
+
+  my ($cr, $cg, $cb) = $c->rgba;
+  ok(abs($cr-$r) <= 5 && abs($cg-$g) <= 5 && abs($cb-$b) <= 5,
+    "$name - ($cr, $cg, $cb) <=> ($r, $g, $b)");
+}
+
+sub color_close_enough_hsv {
+  my ($name, $h, $s, $v, $c) = @_;
+
+  my ($ch, $cs, $cv) = $c->rgba;
+  if ($ch < 5 && $h > 250) {
+    $ch += 255;
+  }
+  elsif ($ch > 250 && $h < 5) {
+    $h += 255;
+  }
+  ok(abs($ch-$h) <= 5 && abs($cs-$s) <= 5 && abs($cv-$v) <= 5,
+    "$name - ($ch, $cs, $cv) <=> ($h, $s, $v)");
+}
+
+sub fcolor_close_enough {
+  my ($name, $r, $g, $b, $c) = @_;
+
+  my ($cr, $cg, $cb) = $c->rgba;
+  ok(abs($cr-$r) <= 0.01 && abs($cg-$g) <= 0.01 && abs($cb-$b) <= 0.01,
+    "$name - ($cr, $cg, $cb) <=> ($r, $g, $b)");
+}
+
+sub color_ok {
+  my ($name, $r, $g, $b, $a, $c) = @_;
+
+  unless (ok(test_col($c, $r, $g, $b, $a), $name)) {
+    print "# ($r,$g,$b,$a) != (".join(",", $c ? $c->rgba: ()).")\n";
+  }
+}
+
diff --git a/t/100-base/030-countc.t b/t/100-base/030-countc.t
new file mode 100644 (file)
index 0000000..c38453e
--- /dev/null
@@ -0,0 +1,84 @@
+#!perl -w
+use strict;
+use Test::More tests => 22;
+
+use Imager;
+
+-d "testout" or mkdir "testout";
+
+Imager::init('log'=>'testout/t90cc.log');
+
+{
+  my $img=Imager->new();
+  ok($img->open(file=>'testimg/scale.ppm'), 'load test image')
+    or print "failed: ",$img->{ERRSTR},"\n";
+  
+  ok(defined($img->getcolorcount(maxcolors=>10000)), 'check color count is small enough');
+  print "# color count: ".$img->getcolorcount()."\n";
+  is($img->getcolorcount(), 86, 'expected number of colors');
+  is($img->getcolorcount(maxcolors => 50), undef, 'check overflow handling');
+}
+
+{
+  my $black = Imager::Color->new(0, 0, 0);
+  my $blue  = Imager::Color->new(0, 0, 255);
+  my $red   = Imager::Color->new(255, 0, 0);
+  
+  my $im    = Imager->new(xsize=>50, ysize=>50);
+  
+  my $count = $im->getcolorcount();
+  is ($count, 1, "getcolorcount is 1");
+  my @colour_usage = $im->getcolorusage();
+  is_deeply (\@colour_usage, [2500], "2500 are in black");
+  
+  $im->box(filled=>1, color=>$blue, xmin=>25);
+  
+  $count = $im->getcolorcount();
+  is ($count, 2, "getcolorcount is 2");
+  @colour_usage = $im->getcolorusage();
+  is_deeply(\@colour_usage, [1250, 1250] , "1250, 1250: Black and blue");
+  
+  $im->box(filled=>1, color=>$red, ymin=>25);
+  
+  $count = $im->getcolorcount();
+  is ($count, 3, "getcolorcount is 3");
+  @colour_usage = $im->getcolorusage();
+  is_deeply(\@colour_usage, [625, 625, 1250] , 
+           "625, 625, 1250: Black blue and red");
+  @colour_usage = $im->getcolorusage(maxcolors => 2);
+  is(@colour_usage, 0, 'test overflow check');
+  
+  my $colour_usage = $im->getcolorusagehash();
+  my $red_pack = pack("CCC", 255, 0, 0);
+  my $blue_pack = pack("CCC", 0, 0, 255);
+  my $black_pack = pack("CCC", 0, 0, 0);
+  is_deeply( $colour_usage, 
+            { $black_pack => 625, $blue_pack => 625, $red_pack => 1250 },
+            "625, 625, 1250: Black blue and red (hash)");
+  is($im->getcolorusagehash(maxcolors => 2), undef,
+     'test overflow check');
+
+  # test with a greyscale image
+  my $im_g = $im->convert(preset => 'grey');
+  # since the grey preset scales each source channel differently
+  # each of the original colors will be converted to different colors
+  is($im_g->getcolorcount, 3, '3 colors (grey)');
+  is_deeply([ $im_g->getcolorusage ], [ 625, 625, 1250 ], 
+           'color counts (grey)');
+  is_deeply({ "\x00" => 625, "\x12" => 625, "\x38" => 1250 },
+           $im_g->getcolorusagehash,
+           'color usage hash (grey)');
+}
+
+{
+  my $empty = Imager->new;
+  is($empty->getcolorcount, undef, "can't getcolorcount an empty image");
+  is($empty->errstr, "getcolorcount: empty input image",
+     "check error message");
+  is($empty->getcolorusagehash, undef, "can't getcolorusagehash an empty image");
+  is($empty->errstr, "getcolorusagehash: empty input image",
+     "check error message");
+  is($empty->getcolorusage, undef, "can't getcolorusage an empty image");
+  is($empty->errstr, "getcolorusage: empty input image",
+     "check error message");
+}
diff --git a/t/100-base/800-tr18561.t b/t/100-base/800-tr18561.t
new file mode 100644 (file)
index 0000000..fb7264f
--- /dev/null
@@ -0,0 +1,21 @@
+#!perl -w
+# regression test for RT issue 18561
+# 
+use strict;
+use Test::More tests => 1;
+eval {
+  use Imager;
+  
+  my $i = Imager->new(
+          xsize => 50,
+         ysize => 50,
+  );
+  
+  $i->setpixel(
+       x => 10,
+       y => 10,
+       color => [0, 0, 0],
+  );
+};
+ok(!$@, "shouldn't crash")
+  or print "# $@\n";
diff --git a/t/100-base/801-tr18561b.t b/t/100-base/801-tr18561b.t
new file mode 100644 (file)
index 0000000..6b93389
--- /dev/null
@@ -0,0 +1,17 @@
+#!perl -w
+# variant on the code that produces 18561
+# the old _color() code could return floating colors in some cases
+# but in most cases the caller couldn't handle it
+use strict;
+use Test::More tests => 1;
+eval {
+  use Imager;
+  use Imager::Color::Float; # prevent the actual 18561 crash
+  my $i = Imager->new(
+         xsize => 50,
+         ysize => 50,
+  );
+  $i->line(x1 => 0, y1 => 0, x2 => 99, y2=>99, color => [ 0, 0, 0 ]);
+};
+ok(!$@, "shouldn't crash")
+  or print "# $@\n";
diff --git a/t/150-type/020-sixteen.t b/t/150-type/020-sixteen.t
new file mode 100644 (file)
index 0000000..a1054e5
--- /dev/null
@@ -0,0 +1,369 @@
+#!perl -w
+use strict;
+use Test::More tests => 155;
+
+BEGIN { use_ok(Imager=>qw(:all :handy)) }
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t021sixteen.log");
+
+use Imager::Color::Float;
+use Imager::Test qw(test_image is_image image_bounds_checks test_colorf_gpix
+                    test_colorf_glin mask_tests is_color3);
+
+my $im_g = Imager::i_img_16_new(100, 101, 1);
+
+is(Imager::i_img_getchannels($im_g), 1, "1 channel image channel count");
+ok(Imager::i_img_getmask($im_g) & 1, "1 channel image mask");
+ok(!Imager::i_img_virtual($im_g), "shouldn't be marked virtual");
+is(Imager::i_img_bits($im_g), 16, "1 channel image has bits == 16");
+is(Imager::i_img_type($im_g), 0, "1 channel image isn't direct");
+
+my @ginfo = i_img_info($im_g);
+is($ginfo[0], 100, "1 channel image width");
+is($ginfo[1], 101, "1 channel image height");
+
+undef $im_g;
+
+my $im_rgb = Imager::i_img_16_new(100, 101, 3);
+
+is(Imager::i_img_getchannels($im_rgb), 3, "3 channel image channel count");
+ok((Imager::i_img_getmask($im_rgb) & 7) == 7, "3 channel image mask");
+is(Imager::i_img_bits($im_rgb), 16, "3 channel image bits");
+is(Imager::i_img_type($im_rgb), 0, "3 channel image type");
+
+my $redf = NCF(1, 0, 0);
+my $greenf = NCF(0, 1, 0);
+my $bluef = NCF(0, 0, 1);
+
+# fill with red
+for my $y (0..101) {
+  Imager::i_plinf($im_rgb, 0, $y, ($redf) x 100);
+}
+pass("fill with red");
+# basic sanity
+test_colorf_gpix($im_rgb, 0,  0,   $redf, 0, "top-left");
+test_colorf_gpix($im_rgb, 99, 0,   $redf, 0, "top-right");
+test_colorf_gpix($im_rgb, 0,  100, $redf, 0, "bottom left");
+test_colorf_gpix($im_rgb, 99, 100, $redf, 0, "bottom right");
+test_colorf_glin($im_rgb, 0,  0,   [ ($redf) x 100 ], "first line");
+test_colorf_glin($im_rgb, 0,  100, [ ($redf) x 100 ], "last line");
+
+Imager::i_plinf($im_rgb, 20, 1, ($greenf) x 60);
+test_colorf_glin($im_rgb, 0, 1, 
+                 [ ($redf) x 20, ($greenf) x 60, ($redf) x 20 ],
+               "added some green in the middle");
+{
+  my @samples;
+  is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 16, \@samples, 0, [ 0 .. 2 ]), 12, 
+     "i_gsamp_bits all channels - count")
+    or print "# ", Imager->_error_as_msg(), "\n";
+  is_deeply(\@samples, [ 65535, 0, 0,   65535, 0, 0,
+                        0, 65535, 0,   0, 65535, 0 ],
+           "check samples retrieved");
+  @samples = ();
+  is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 16, \@samples, 0, [ 0, 2 ]), 8, 
+     "i_gsamp_bits some channels - count")
+    or print "# ", Imager->_error_as_msg(), "\n";
+  is_deeply(\@samples, [ 65535, 0,   65535, 0,
+                        0, 0,       0, 0     ],
+           "check samples retrieved");
+  # fail gsamp
+  is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 16, \@samples, 0, [ 0, 3 ]), undef,
+     "i_gsamp_bits fail bad channel");
+  is(Imager->_error_as_msg(), 'No channel 3 in this image', 'check message');
+
+  is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 17, \@samples, 0, [ 0, 2 ]), 8, 
+     "i_gsamp_bits succeed high bits");
+  is($samples[0], 131071, "check correct with high bits");
+
+  # write some samples back
+  my @wr_samples = 
+    ( 
+     0, 0, 65535,
+     65535, 0, 0,  
+     0, 65535, 0,  
+     65535, 65535, 0 
+    );
+  is(Imager::i_psamp_bits($im_rgb, 18, 2, 16, [ 0 .. 2 ], \@wr_samples),
+     12, "write 16-bit samples")
+    or print "# ", Imager->_error_as_msg(), "\n";
+  @samples = ();
+  is(Imager::i_gsamp_bits($im_rgb, 18, 22, 2, 16, \@samples, 0, [ 0 .. 2 ]), 12, 
+     "read them back")
+    or print "# ", Imager->_error_as_msg(), "\n";
+  is_deeply(\@samples, \@wr_samples, "check they match");
+  my $c = Imager::i_get_pixel($im_rgb, 18, 2);
+  is_color3($c, 0, 0, 255, "check it write to the right places");
+}
+
+# basic OO tests
+my $oo16img = Imager->new(xsize=>200, ysize=>201, bits=>16);
+ok($oo16img, "make a 16-bit oo image");
+is($oo16img->bits,  16, "test bits");
+isnt($oo16img->is_bilevel, "should not be considered mono");
+# make sure of error handling
+ok(!Imager->new(xsize=>0, ysize=>1, bits=>16),
+    "fail to create a 0 pixel wide image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct error message");
+
+ok(!Imager->new(xsize=>1, ysize=>0, bits=>16),
+    "fail to create a 0 pixel high image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct error message");
+
+ok(!Imager->new(xsize=>-1, ysize=>1, bits=>16),
+    "fail to create a negative width image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct error message");
+
+ok(!Imager->new(xsize=>1, ysize=>-1, bits=>16),
+    "fail to create a negative height image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct error message");
+
+ok(!Imager->new(xsize=>-1, ysize=>-1, bits=>16),
+    "fail to create a negative width/height image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct error message");
+
+ok(!Imager->new(xsize=>1, ysize=>1, bits=>16, channels=>0),
+    "fail to create a zero channel image");
+cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
+       "and correct error message");
+ok(!Imager->new(xsize=>1, ysize=>1, bits=>16, channels=>5),
+    "fail to create a five channel image");
+cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
+       "and correct error message");
+
+{
+  # https://rt.cpan.org/Ticket/Display.html?id=8213
+  # check for handling of memory allocation of very large images
+  # only test this on 32-bit machines - on a 64-bit machine it may
+  # result in trying to allocate 4Gb of memory, which is unfriendly at
+  # least and may result in running out of memory, causing a different
+  # type of exit
+ SKIP: {
+    use Config;
+    $Config{ptrsize} == 4
+      or skip("don't want to allocate 4Gb", 10);
+    my $uint_range = 256 ** $Config{intsize};
+    print "# range $uint_range\n";
+    my $dim1 = int(sqrt($uint_range/2))+1;
+    
+    my $im_b = Imager->new(xsize=>$dim1, ysize=>$dim1, channels=>1, bits=>16);
+    is($im_b, undef, "integer overflow check - 1 channel");
+    
+    $im_b = Imager->new(xisze=>$dim1, ysize=>1, channels=>1, bits=>16);
+    ok($im_b, "but same width ok");
+    $im_b = Imager->new(xisze=>1, ysize=>$dim1, channels=>1, bits=>16);
+    ok($im_b, "but same height ok");
+    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
+           "check the error message");
+
+    # do a similar test with a 3 channel image, so we're sure we catch
+    # the same case where the third dimension causes the overflow
+    my $dim3 = int(sqrt($uint_range / 3 / 2))+1;
+    
+    $im_b = Imager->new(xsize=>$dim3, ysize=>$dim3, channels=>3, bits=>16);
+    is($im_b, undef, "integer overflow check - 3 channel");
+    
+    $im_b = Imager->new(xisze=>$dim3, ysize=>1, channels=>3, bits=>16);
+    ok($im_b, "but same width ok");
+    $im_b = Imager->new(xisze=>1, ysize=>$dim3, channels=>3, bits=>16);
+    ok($im_b, "but same height ok");
+
+    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
+           "check the error message");
+
+    # check we can allocate a scanline, unlike double images the scanline
+    # in the image itself is smaller than a line of i_fcolor
+    # divide by 2 to get to int range, by 2 for 2 bytes/pixel, by 3 to 
+    # fit the image allocation in, but for the floats to overflow
+    my $dim4 = $uint_range / 2 / 2 / 3;
+    my $im_o = Imager->new(xsize=>$dim4, ysize=>1, channels=>1, bits=>16);
+    is($im_o, undef, "integer overflow check - scanline");
+    cmp_ok(Imager->errstr, '=~',
+           qr/integer overflow calculating scanline allocation/,
+           "check error message");
+  }
+}
+
+{ # check the channel mask function
+  
+  my $im = Imager->new(xsize => 10, ysize=>10, bits=>16);
+
+  mask_tests($im, 1.0/65535);
+}
+
+{ # convert to rgb16
+  my $im = test_image();
+  my $im16 = $im->to_rgb16;
+  print "# check conversion to 16 bit\n";
+  is($im16->bits, 16, "check bits");
+  is_image($im, $im16, "check image data matches");
+}
+
+{ # empty image handling
+  my $im = Imager->new;
+  ok($im, "make empty image");
+  ok(!$im->to_rgb16, "convert empty image to 16-bit");
+  is($im->errstr, "to_rgb16: empty input image", "check message");
+}
+
+{ # bounds checks
+  my $im = Imager->new(xsize => 10, ysize => 10, bits => 16);
+  image_bounds_checks($im);
+}
+
+{
+  my $im = Imager->new(xsize => 10, ysize => 10, bits => 16, channels => 3);
+  my @wr_samples = map int(rand 65536), 1..30;
+  is($im->setsamples('y' => 1, data => \@wr_samples, type => '16bit'),
+     30, "write 16-bit to OO image")
+    or print "# ", $im->errstr, "\n";
+  my @samples;
+  is($im->getsamples(y => 1, target => \@samples, type => '16bit'),
+     30, "read 16-bit from OO image")
+    or print "# ", $im->errstr, "\n";
+  is_deeply(\@wr_samples, \@samples, "check it matches");
+}
+
+my $psamp_outside_error = "Image position outside of image";
+{ # psamp
+  print "# psamp\n";
+  my $imraw = Imager::i_img_16_new(10, 10, 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");
+  }
+  { # 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, 10, 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");
+  }
+  print "# end psamp tests\n";
+}
+
+{ # psampf
+  print "# psampf\n";
+  my $imraw = Imager::i_img_16_new(10, 10, 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, 64,
+             "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), 64, 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), 64, 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), 64, 32, 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, 64, 32) 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");
+  }
+  { # 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, 10, 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");
+  }
+  print "# end psampf tests\n";
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t021sixteen.log";
+}
+
+sub _get_error {
+  my @errors = Imager::i_errors();
+  return join(": ", map $_->[0], @errors);
+}
+
diff --git a/t/150-type/030-double.t b/t/150-type/030-double.t
new file mode 100644 (file)
index 0000000..6a2f757
--- /dev/null
@@ -0,0 +1,298 @@
+#!perl -w
+use strict;
+use Test::More tests => 136;
+BEGIN { use_ok(Imager => qw(:all :handy)) }
+
+use Imager::Test qw(test_image is_image is_color3);
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t022double.log");
+
+use Imager::Test qw(image_bounds_checks test_colorf_gpix test_colorf_glin mask_tests);
+
+use Imager::Color::Float;
+
+my $im_g = Imager::i_img_double_new(100, 101, 1);
+
+ok(Imager::i_img_getchannels($im_g) == 1, 
+   "1 channel image channel count mismatch");
+ok(Imager::i_img_getmask($im_g) & 1, "1 channel image bad mask");
+ok(Imager::i_img_virtual($im_g) == 0, 
+  "1 channel image thinks it is virtual");
+my $double_bits = length(pack("d", 1)) * 8;
+print "# $double_bits double bits\n";
+ok(Imager::i_img_bits($im_g) == $double_bits, 
+   "1 channel image has bits != $double_bits");
+ok(Imager::i_img_type($im_g) == 0, "1 channel image isn't direct");
+
+my @ginfo = i_img_info($im_g);
+ok($ginfo[0] == 100, "1 channel image width incorrect");
+ok($ginfo[1] == 101, "1 channel image height incorrect");
+
+undef $im_g;
+
+my $im_rgb = Imager::i_img_double_new(100, 101, 3);
+
+ok(Imager::i_img_getchannels($im_rgb) == 3,
+   "3 channel image channel count mismatch");
+ok((Imager::i_img_getmask($im_rgb) & 7) == 7, "3 channel image bad mask");
+ok(Imager::i_img_bits($im_rgb) == $double_bits,
+  "3 channel image has bits != $double_bits");
+ok(Imager::i_img_type($im_rgb) == 0, "3 channel image isn't direct");
+
+my $redf = NCF(1, 0, 0);
+my $greenf = NCF(0, 1, 0);
+my $bluef = NCF(0, 0, 1);
+
+# fill with red
+for my $y (0..101) {
+  Imager::i_plinf($im_rgb, 0, $y, ($redf) x 100);
+}
+
+# basic sanity
+test_colorf_gpix($im_rgb, 0,  0,   $redf);
+test_colorf_gpix($im_rgb, 99, 0,   $redf);
+test_colorf_gpix($im_rgb, 0,  100, $redf);
+test_colorf_gpix($im_rgb, 99, 100, $redf);
+test_colorf_glin($im_rgb, 0,  0,   [ ($redf) x 100 ], 'sanity glin @0');
+test_colorf_glin($im_rgb, 0,  100, [ ($redf) x 100 ], 'sanity glin @100');
+
+Imager::i_plinf($im_rgb, 20, 1, ($greenf) x 60);
+test_colorf_glin($im_rgb, 0, 1, 
+                 [ ($redf) x 20, ($greenf) x 60, ($redf) x 20 ],
+                'check after write');
+
+# basic OO tests
+my $ooimg = Imager->new(xsize=>200, ysize=>201, bits=>'double');
+ok($ooimg, "couldn't make double image");
+is($ooimg->bits, 'double', "oo didn't give double image");
+ok(!$ooimg->is_bilevel, 'not monochrome');
+
+# check that the image is copied correctly
+my $oocopy = $ooimg->copy;
+is($oocopy->bits, 'double', "oo copy didn't give double image");
+
+ok(!Imager->new(xsize=>0, ysize=>1, bits=>'double'),
+    "fail making 0 width image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct message");
+ok(!Imager->new(xsize=>1, ysize=>0, bits=>'double'),
+    "fail making 0 height image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct message");
+ok(!Imager->new(xsize=>-1, ysize=>1, bits=>'double'),
+    "fail making -ve width image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct message");
+ok(!Imager->new(xsize=>1, ysize=>-1, bits=>'double'),
+    "fail making -ve height image");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct message");
+ok(!Imager->new(xsize=>1, ysize=>1, bits=>'double', channels=>0),
+    "fail making 0 channel image");
+cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
+       "and correct message");
+ok(!Imager->new(xsize=>1, ysize=>1, bits=>'double', channels=>5),
+    "fail making 5 channel image");
+cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
+       "and correct message");
+
+{
+  # https://rt.cpan.org/Ticket/Display.html?id=8213
+  # check for handling of memory allocation of very large images
+  # only test this on 32-bit machines - on a 64-bit machine it may
+  # result in trying to allocate 4Gb of memory, which is unfriendly at
+  # least and may result in running out of memory, causing a different
+  # type of exit
+  use Config;
+  SKIP: 
+  {
+    $Config{ptrsize} == 4
+      or skip "don't want to allocate 4Gb", 8;
+    my $uint_range = 256 ** $Config{intsize};
+    my $dbl_size = $Config{doublesize} || 8;
+    my $dim1 = int(sqrt($uint_range/$dbl_size))+1;
+    
+    my $im_b = Imager->new(xsize=>$dim1, ysize=>$dim1, channels=>1, bits=>'double');
+    is($im_b, undef, "integer overflow check - 1 channel");
+    
+    $im_b = Imager->new(xisze=>$dim1, ysize=>1, channels=>1, bits=>'double');
+    ok($im_b, "but same width ok");
+    $im_b = Imager->new(xisze=>1, ysize=>$dim1, channels=>1, bits=>'double');
+    ok($im_b, "but same height ok");
+    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
+           "check the error message");
+
+    # do a similar test with a 3 channel image, so we're sure we catch
+    # the same case where the third dimension causes the overflow
+    my $dim3 = int(sqrt($uint_range / 3 / $dbl_size))+1;
+    
+    $im_b = Imager->new(xsize=>$dim3, ysize=>$dim3, channels=>3, bits=>'double');
+    is($im_b, undef, "integer overflow check - 3 channel");
+    
+    $im_b = Imager->new(xsize=>$dim3, ysize=>1, channels=>3, bits=>'double');
+    ok($im_b, "but same width ok");
+    $im_b = Imager->new(xsize=>1, ysize=>$dim3, channels=>3, bits=>'double');
+    ok($im_b, "but same height ok");
+
+    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
+           "check the error message");
+  }
+}
+
+{ # check the channel mask function
+  
+  my $im = Imager->new(xsize => 10, ysize=>10, bits=>'double');
+
+  mask_tests($im);
+}
+
+{ # bounds checking
+  my $im = Imager->new(xsize => 10, ysize=>10, bits=>'double');
+  image_bounds_checks($im);
+}
+
+
+{ # convert to rgb double
+  my $im = test_image();
+  my $imdb = $im->to_rgb_double;
+  print "# check conversion to double\n";
+  is($imdb->bits, "double", "check bits");
+  is_image($im, $imdb, "check image data matches");
+}
+
+{ # empty image handling
+  my $im = Imager->new;
+  ok($im, "make empty image");
+  ok(!$im->to_rgb_double, "convert empty image to double");
+  is($im->errstr, "to_rgb_double: empty input image", "check message");
+}
+
+my $psamp_outside_error = "Image position outside of image";
+{ # psamp
+  print "# psamp\n";
+  my $imraw = Imager::i_img_double_new(10, 10, 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");
+  }
+  { # 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, 10, 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");
+  }
+  print "# end psamp tests\n";
+}
+
+{ # psampf
+  print "# psampf\n";
+  my $imraw = Imager::i_img_double_new(10, 10, 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, 128, 64,
+             "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), 64, 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), 64, 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, 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_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 ]) ],
+             [ (128, 64, 32) 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");
+  }
+  { # 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, 10, 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");
+  }
+  print "# end psampf tests\n";
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t022double.log";
+}
+
+sub _get_error {
+  my @errors = Imager::i_errors();
+  return join(": ", map $_->[0], @errors);
+}
diff --git a/t/150-type/040-palette.t b/t/150-type/040-palette.t
new file mode 100644 (file)
index 0000000..a5976b7
--- /dev/null
@@ -0,0 +1,679 @@
+#!perl -w
+# some of this is tested in t01introvert.t too
+use strict;
+use Test::More tests => 226;
+BEGIN { use_ok("Imager", ':handy'); }
+
+use Imager::Test qw(image_bounds_checks test_image is_color3 isnt_image is_color4 is_fcolor3);
+
+Imager->open_log(log => "testout/t023palette.log");
+
+sub isbin($$$);
+
+my $img = Imager->new(xsize=>50, ysize=>50, type=>'paletted');
+
+ok($img, "paletted image created");
+
+is($img->type, 'paletted', "got a paletted image");
+
+my $black = Imager::Color->new(0,0,0);
+my $red = Imager::Color->new(255,0,0);
+my $green = Imager::Color->new(0,255,0);
+my $blue = Imager::Color->new(0,0,255);
+
+my $white = Imager::Color->new(255,255,255);
+
+# add some color
+my $blacki = $img->addcolors(colors=>[ $black, $red, $green, $blue ]);
+
+print "# blacki $blacki\n";
+ok(defined $blacki && $blacki == 0, "we got the first color");
+
+is($img->colorcount(), 4, "should have 4 colors");
+is($img->maxcolors, 256, "maxcolors always 256");
+
+my ($redi, $greeni, $bluei) = 1..3;
+
+my @all = $img->getcolors;
+ok(@all == 4, "all colors is 4");
+coloreq($all[0], $black, "first black");
+coloreq($all[1], $red, "then red");
+coloreq($all[2], $green, "then green");
+coloreq($all[3], $blue, "and finally blue");
+
+# keep this as an assignment, checking for scalar context
+# we don't want the last color, otherwise if the behaviour changes to
+# get all up to the last (count defaulting to size-index) we'd get a
+# false positive
+my $one_color = $img->getcolors(start=>$redi);
+ok($one_color->isa('Imager::Color'), "check scalar context");
+coloreq($one_color, $red, "and that it's what we want");
+
+# make sure we can find colors
+ok(!defined($img->findcolor(color=>$white)), 
+    "shouldn't be able to find white");
+ok($img->findcolor(color=>$black) == $blacki, "find black");
+ok($img->findcolor(color=>$red) == $redi, "find red");
+ok($img->findcolor(color=>$green) == $greeni, "find green");
+ok($img->findcolor(color=>$blue) == $bluei, "find blue");
+
+# various failure tests for setcolors
+ok(!defined($img->setcolors(start=>-1, colors=>[$white])),
+    "expect failure: low index");
+ok(!defined($img->setcolors(start=>1, colors=>[])),
+    "expect failure: no colors");
+ok(!defined($img->setcolors(start=>5, colors=>[$white])),
+    "expect failure: high index");
+
+# set the green index to white
+ok($img->setcolors(start => $greeni, colors => [$white]),
+    "set a color");
+# and check it
+coloreq(scalar($img->getcolors(start=>$greeni)), $white,
+       "make sure it was set");
+ok($img->findcolor(color=>$white) == $greeni, "and that we can find it");
+ok(!defined($img->findcolor(color=>$green)), "and can't find the old color");
+
+# write a few colors
+ok(scalar($img->setcolors(start=>$redi, colors=>[ $green, $red])),
+          "save multiple");
+coloreq(scalar($img->getcolors(start=>$redi)), $green, "first of multiple");
+coloreq(scalar($img->getcolors(start=>$greeni)), $red, "second of multiple");
+
+# put it back
+$img->setcolors(start=>$red, colors=>[$red, $green]);
+
+# draw on the image, make sure it stays paletted when it should
+ok($img->box(color=>$red, filled=>1), "fill with red");
+is($img->type, 'paletted', "paletted after fill");
+ok($img->box(color=>$green, filled=>1, xmin=>10, ymin=>10,
+             xmax=>40, ymax=>40), "green box");
+is($img->type, 'paletted', 'still paletted after box');
+# an AA line will almost certainly convert the image to RGB, don't use
+# an AA line here
+ok($img->line(color=>$blue, x1=>10, y1=>10, x2=>40, y2=>40),
+    "draw a line");
+is($img->type, 'paletted', 'still paletted after line');
+
+# draw with white - should convert to direct
+ok($img->box(color=>$white, filled=>1, xmin=>20, ymin=>20, 
+             xmax=>30, ymax=>30), "white box");
+is($img->type, 'direct', "now it should be direct");
+
+# various attempted to make a paletted image from our now direct image
+my $palimg = $img->to_paletted;
+ok($palimg, "we got an image");
+# they should be the same pixel for pixel
+ok(Imager::i_img_diff($img->{IMG}, $palimg->{IMG}) == 0, "same pixels");
+
+# strange case: no color picking, and no colors
+# this was causing a segmentation fault
+$palimg = $img->to_paletted(colors=>[ ], make_colors=>'none');
+ok(!defined $palimg, "to paletted with an empty palette is an error");
+print "# ",$img->errstr,"\n";
+ok(scalar($img->errstr =~ /no colors available for translation/),
+    "and got the correct msg");
+
+ok(!Imager->new(xsize=>1, ysize=>-1, type=>'paletted'), 
+    "fail on -ve height");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct error message");
+ok(!Imager->new(xsize=>-1, ysize=>1, type=>'paletted'), 
+    "fail on -ve width");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct error message");
+ok(!Imager->new(xsize=>-1, ysize=>-1, type=>'paletted'), 
+    "fail on -ve width/height");
+cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
+       "and correct error message");
+
+ok(!Imager->new(xsize=>1, ysize=>1, type=>'paletted', channels=>0),
+    "fail on 0 channels");
+cmp_ok(Imager->errstr, '=~', qr/Channels must be positive and <= 4/,
+       "and correct error message");
+ok(!Imager->new(xsize=>1, ysize=>1, type=>'paletted', channels=>5),
+    "fail on 5 channels");
+cmp_ok(Imager->errstr, '=~', qr/Channels must be positive and <= 4/,
+       "and correct error message");
+
+{
+  # https://rt.cpan.org/Ticket/Display.html?id=8213
+  # check for handling of memory allocation of very large images
+  # only test this on 32-bit machines - on a 64-bit machine it may
+  # result in trying to allocate 4Gb of memory, which is unfriendly at
+  # least and may result in running out of memory, causing a different
+  # type of exit
+  use Config;
+ SKIP:
+  {
+    skip("don't want to allocate 4Gb", 10)
+      unless $Config{ptrsize} == 4;
+
+    my $uint_range = 256 ** $Config{intsize};
+    my $dim1 = int(sqrt($uint_range))+1;
+    
+    my $im_b = Imager->new(xsize=>$dim1, ysize=>$dim1, channels=>1, type=>'paletted');
+    is($im_b, undef, "integer overflow check - 1 channel");
+    
+    $im_b = Imager->new(xisze=>$dim1, ysize=>1, channels=>1, type=>'paletted');
+    ok($im_b, "but same width ok");
+    $im_b = Imager->new(xisze=>1, ysize=>$dim1, channels=>1, type=>'paletted');
+    ok($im_b, "but same height ok");
+    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
+           "check the error message");
+
+    # do a similar test with a 3 channel image, so we're sure we catch
+    # the same case where the third dimension causes the overflow
+    # for paletted images the third dimension can't cause an overflow
+    # but make sure we didn't anything too dumb in the checks
+    my $dim3 = $dim1;
+    
+    $im_b = Imager->new(xsize=>$dim3, ysize=>$dim3, channels=>3, type=>'paletted');
+    is($im_b, undef, "integer overflow check - 3 channel");
+    
+    $im_b = Imager->new(xsize=>$dim3, ysize=>1, channels=>3, type=>'paletted');
+    ok($im_b, "but same width ok");
+    $im_b = Imager->new(xsize=>1, ysize=>$dim3, channels=>3, type=>'paletted');
+    ok($im_b, "but same height ok");
+
+    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
+           "check the error message");
+
+    # test the scanline allocation check
+    # divide by 2 to get int range, by 3 so that the image (one byte/pixel)
+    # doesn't integer overflow, but the scanline of i_color (4/pixel) does
+    my $dim4 = $uint_range / 3;
+    my $im_o = Imager->new(xsize=>$dim4, ysize=>1, channels=>3, type=>'paletted');
+    is($im_o, undef, "integer overflow check - scanline size");
+    cmp_ok(Imager->errstr, '=~', 
+           qr/integer overflow calculating scanline allocation/,
+           "check error message");
+  }
+}
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=9672
+  my $warning;
+  local $SIG{__WARN__} = 
+    sub { 
+      $warning = "@_";
+      my $printed = $warning;
+      $printed =~ s/\n$//;
+      $printed =~ s/\n/\n\#/g; 
+      print "# ",$printed, "\n";
+    };
+  my $img = Imager->new(xsize=>10, ysize=>10);
+  $img->to_paletted();
+  cmp_ok($warning, '=~', 'void', "correct warning");
+  cmp_ok($warning, '=~', 'palette\\.t', "correct file");
+}
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=12676
+  # setcolors() has a fencepost error
+  my $img = Imager->new(xsize=>10, ysize=>10, type=>'paletted');
+
+  is($img->addcolors(colors=>[ $black, $red ]), "0 but true",
+     "add test colors");
+  ok($img->setcolors(start=>1, colors=>[ $green ]), "set the last color");
+  ok(!$img->setcolors(start=>2, colors=>[ $black ]), 
+     "set after the last color");
+}
+
+{ # https://rt.cpan.org/Ticket/Display.html?id=20056
+  # added named color support to addcolor/setcolor
+  my $img = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
+  is($img->addcolors(colors => [ qw/000000 FF0000/ ]), "0 but true",
+     "add colors as strings instead of objects");
+  my @colors = $img->getcolors;
+  iscolor($colors[0], $black, "check first color");
+  iscolor($colors[1], $red, "check second color");
+  ok($img->setcolors(colors => [ qw/00FF00 0000FF/ ]),
+     "setcolors as strings instead of objects");
+  @colors = $img->getcolors;
+  iscolor($colors[0], $green, "check first color");
+  iscolor($colors[1], $blue, "check second color");
+
+  # make sure we handle bad colors correctly
+  is($img->colorcount, 2, "start from a known state");
+  is($img->addcolors(colors => [ 'XXFGXFXGXFX' ]), undef,
+     "fail to add unknown color");
+  is($img->errstr, 'No color named XXFGXFXGXFX found', 'check error message');
+  is($img->setcolors(colors => [ 'XXFGXFXGXFXZ' ]), undef,
+     "fail to set to unknown color");
+  is($img->errstr, 'No color named XXFGXFXGXFXZ found', 'check error message');
+}
+
+{ # https://rt.cpan.org/Ticket/Display.html?id=20338
+  # OO interface to i_glin/i_plin
+  my $im = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
+  is($im->addcolors(colors => [ "#000", "#F00", "#0F0", "#00F" ]), "0 but true",
+     "add some test colors")
+    or print "# ", $im->errstr, "\n";
+  # set a pixel to check
+  $im->setpixel(x => 1, 'y' => 0, color => "#0F0");
+  is_deeply([ $im->getscanline('y' => 0, type=>'index') ],
+            [ 0, 2, (0) x 8 ], "getscanline index in list context");
+  isbin($im->getscanline('y' => 0, type=>'index'),
+        "\x00\x02" . "\x00" x 8,
+        "getscanline index in scalar context");
+  is($im->setscanline('y' => 0, pixels => [ 1, 2, 0, 3 ], type => 'index'),
+     4, "setscanline with list");
+  is($im->setscanline('y' => 0, x => 4, pixels => pack("C*", 3, 2, 1, 0, 3),
+                      type => 'index'),
+     5, "setscanline with pv");
+  is_deeply([ $im->getscanline(type => 'index', 'y' => 0) ],
+            [ 1, 2, 0, 3, 3, 2, 1, 0, 3, 0 ],
+            "check values set");
+  eval { # should croak on OOR index
+    $im->setscanline('y' => 1, pixels => [ 255 ], type=>'index');
+  };
+  ok($@, "croak on setscanline() to invalid index");
+  eval { # same again with pv
+    $im->setscanline('y' => 1, pixels => "\xFF", type => 'index');
+  };
+  ok($@, "croak on setscanline() with pv to invalid index");
+}
+
+{
+  print "# make_colors => mono\n";
+  # test mono make_colors
+  my $imrgb = Imager->new(xsize => 10, ysize => 10);
+  $imrgb->setpixel(x => 0, 'y' => 0, color => '#FFF');
+  $imrgb->setpixel(x => 1, 'y' => 0, color => '#FF0');
+  $imrgb->setpixel(x => 2, 'y' => 0, color => '#000');
+  my $mono = $imrgb->to_paletted(make_colors => 'mono',
+                                  translate => 'closest');
+  is($mono->type, 'paletted', "check we get right image type");
+  is($mono->colorcount, 2, "only 2 colors");
+  my ($is_mono, $ziw) = $mono->is_bilevel;
+  ok($is_mono, "check monochrome check true");
+  is($ziw, 0, "check ziw false");
+  my @colors = $mono->getcolors;
+  iscolor($colors[0], $black, "check first entry");
+  iscolor($colors[1], $white, "check second entry");
+  my @pixels = $mono->getscanline(x => 0, 'y' => 0, width => 3, type=>'index');
+  is($pixels[0], 1, "check white pixel");
+  is($pixels[1], 1, "check yellow pixel");
+  is($pixels[2], 0, "check black pixel");
+}
+
+{ # check for the various mono images we accept
+  my $mono_8_bw_3 = Imager->new(xsize => 2, ysize => 2, channels => 3, 
+                             type => 'paletted');
+  ok($mono_8_bw_3->addcolors(colors => [ qw/000000 FFFFFF/ ]), 
+     "mono8bw3 - add colors");
+  ok($mono_8_bw_3->is_bilevel, "it's mono");
+  is(($mono_8_bw_3->is_bilevel)[1], 0, 'zero not white');
+  
+  my $mono_8_wb_3 = Imager->new(xsize => 2, ysize => 2, channels => 3, 
+                             type => 'paletted');
+  ok($mono_8_wb_3->addcolors(colors => [ qw/FFFFFF 000000/ ]), 
+     "mono8wb3 - add colors");
+  ok($mono_8_wb_3->is_bilevel, "it's mono");
+  is(($mono_8_wb_3->is_bilevel)[1], 1, 'zero is white');
+  
+  my $mono_8_bw_1 = Imager->new(xsize => 2, ysize => 2, channels => 1, 
+                             type => 'paletted');
+  ok($mono_8_bw_1->addcolors(colors => [ qw/000000 FFFFFF/ ]), 
+     "mono8bw - add colors");
+  ok($mono_8_bw_1->is_bilevel, "it's mono");
+  is(($mono_8_bw_1->is_bilevel)[1], 0, 'zero not white');
+  
+  my $mono_8_wb_1 = Imager->new(xsize => 2, ysize => 2, channels => 1, 
+                             type => 'paletted');
+  ok($mono_8_wb_1->addcolors(colors => [ qw/FFFFFF 000000/ ]), 
+     "mono8wb - add colors");
+  ok($mono_8_wb_1->is_bilevel, "it's mono");
+  is(($mono_8_wb_1->is_bilevel)[1], 1, 'zero is white');
+}
+
+{ # check bounds checking
+  my $im = Imager->new(xsize => 10, ysize => 10, type=>'paletted');
+  ok($im->addcolors(colors => [ $black ]), "add color of pixel bounds check writes");
+
+  image_bounds_checks($im);
+}
+
+{ # test colors array returns colors
+  my $data;
+  my $im = test_image();
+  my @colors;
+  my $imp = $im->to_paletted(colors => \@colors, 
+                            make_colors => 'webmap', 
+                            translate => 'closest');
+  ok($imp, "made paletted");
+  is(@colors, 216, "should be 216 colors in the webmap");
+  is_color3($colors[0], 0, 0, 0, "first should be 000000");
+  is_color3($colors[1], 0, 0, 0x33, "second should be 000033");
+  is_color3($colors[8], 0, 0x33, 0x66, "9th should be 003366");
+}
+
+{ # RT 68508
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  $im->box(filled => 1, color => Imager::Color->new(255, 0, 0));
+  my $palim = $im->to_paletted(make_colors => "mono", translate => "errdiff");
+  ok($palim, "convert to mono with error diffusion");
+  my $blank = Imager->new(xsize => 10, ysize => 10);
+  isnt_image($palim, $blank, "make sure paletted isn't all black");
+}
+
+{ # check validation of palette entries
+  my $im = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
+  $im->addcolors(colors => [ $black, $red ]);
+  {
+    my $no_croak = eval {
+      $im->setscanline(y => 0, type => 'index', pixels => [ 0, 1 ]);
+      1;
+    };
+    ok($no_croak, "valid values don't croak");
+  }
+  {
+    my $no_croak = eval {
+      $im->setscanline(y => 0, type => 'index', pixels => pack("C*", 0, 1));
+      1;
+    };
+    ok($no_croak, "valid values don't croak (packed)");
+  }
+  {
+    my $no_croak = eval {
+      $im->setscanline(y => 0, type => 'index', pixels => [ 2, 255 ]);
+      1;
+    };
+    ok(!$no_croak, "invalid values do croak");
+  }
+  {
+    my $no_croak = eval {
+      $im->setscanline(y => 0, type => 'index', pixels => pack("C*", 2, 255));
+      1;
+    };
+    ok(!$no_croak, "invalid values do croak (packed)");
+  }
+}
+
+{
+  my $im = Imager->new(xsize => 1, ysize => 1);
+  my $im_bad = Imager->new;
+  {
+    my @map = Imager->make_palette({});
+    ok(!@map, "make_palette should fail with no images");
+    is(Imager->errstr, "make_palette: supply at least one image",
+       "check error message");
+  }
+  {
+    my @map = Imager->make_palette({}, $im, $im_bad, $im);
+    ok(!@map, "make_palette should fail with an empty image");
+    is(Imager->errstr, "make_palette: image 2 is empty",
+       "check error message");
+  }
+  {
+    my @map = Imager->make_palette({ make_colors => "mono" }, $im);
+    is(@map, 2, "mono should make 2 color palette")
+      or skip("unexpected color count", 2);
+    is_color4($map[0], 0, 0, 0, 255, "check map[0]");
+    is_color4($map[1], 255, 255, 255, 255, "check map[1]");
+  }
+  {
+    my @map = Imager->make_palette({ make_colors => "gray4" }, $im);
+    is(@map, 4, "gray4 should make 4 color palette")
+      or skip("unexpected color count", 4);
+    is_color4($map[0], 0, 0, 0, 255, "check map[0]");
+    is_color4($map[1], 85, 85, 85, 255, "check map[1]");
+    is_color4($map[2], 170, 170, 170, 255, "check map[2]");
+    is_color4($map[3], 255, 255, 255, 255, "check map[3]");
+  }
+  {
+    my @map = Imager->make_palette({ make_colors => "gray16" }, $im);
+    is(@map, 16, "gray16 should make 16 color palette")
+      or skip("unexpected color count", 4);
+    is_color4($map[0], 0, 0, 0, 255, "check map[0]");
+    is_color4($map[1], 17, 17, 17, 255, "check map[1]");
+    is_color4($map[2], 34, 34, 34, 255, "check map[2]");
+    is_color4($map[15], 255, 255, 255, 255, "check map[15]");
+  }
+  {
+    my @map = Imager->make_palette({ make_colors => "gray" }, $im);
+    is(@map, 256, "gray16 should make 256 color palette")
+      or skip("unexpected color count", 4);
+    is_color4($map[0], 0, 0, 0, 255, "check map[0]");
+    is_color4($map[1], 1, 1, 1, 255, "check map[1]");
+    is_color4($map[33], 33, 33, 33, 255, "check map[2]");
+    is_color4($map[255], 255, 255, 255, 255, "check map[15]");
+  }
+}
+
+my $psamp_outside_error = "Image position outside of image";
+{ # psamp
+  print "# psamp\n";
+  my $imraw = Imager::i_img_pal_new(10, 10, 3, 255);
+  my @colors =
+    (
+     NC(0, 0, 0), NC(255, 128, 64), NC(64, 128, 192),
+     NC(64, 0, 192), NC(255, 128, 0), NC(64, 32, 0),
+     NC(128, 63, 32), NC(255, 128, 32), NC(64, 32, 16),
+    );
+  is(Imager::i_addcolors($imraw, @colors), "0 but true",
+     "add colors needed for testing");
+  {
+    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");
+  }
+  { # 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 message");
+    is(Imager::i_psamp($imraw, 0, 10, undef, [ 0, 0, 0 ]), undef,
+       "y overflow");
+    is(_get_error(), $psamp_outside_error, "check message");
+    is(Imager::i_psamp($imraw, -1, 0, undef, [ 0, 0, 0 ]), undef,
+       "negative x");
+    is(_get_error(), $psamp_outside_error, "check message");
+    is(Imager::i_psamp($imraw, 10, 0, undef, [ 0, 0, 0 ]), undef,
+       "x overflow");
+    is(_get_error(), $psamp_outside_error, "check message");
+  }
+  ok(Imager::i_img_type($imraw), "still paletted");
+  print "# end psamp tests\n";
+}
+
+{ # psampf
+  print "# psampf\n";
+  my $imraw = Imager::i_img_pal_new(10, 10, 3, 255);
+  my @colors =
+    (
+     NC(0, 0, 0), NC(255, 128, 64), NC(64, 128, 192),
+     NC(64, 0, 191), NC(255, 128, 0), NC(64, 32, 0),
+     NC(128, 64, 32), NC(255, 128, 32), NC(64, 32, 16),
+    );
+  is(Imager::i_addcolors($imraw, @colors), "0 but true",
+     "add colors needed for testing");
+  {
+    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, 128, 64,
+             "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), 64, 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), 64, 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, 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_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 ]) ],
+             [ (128, 64, 32) 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");
+  }
+  { # 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 message");
+    is(Imager::i_psampf($imraw, 0, 10, undef, [ 0, 0, 0 ]), undef,
+       "y overflow");
+    is(_get_error(), $psamp_outside_error, "check message");
+    is(Imager::i_psampf($imraw, -1, 0, undef, [ 0, 0, 0 ]), undef,
+       "negative x");
+    is(_get_error(), $psamp_outside_error, "check message");
+    is(Imager::i_psampf($imraw, 10, 0, undef, [ 0, 0, 0 ]), undef,
+       "x overflow");
+    is(_get_error(), $psamp_outside_error, "check message");
+  }
+  ok(Imager::i_img_type($imraw), "still paletted");
+  print "# end psampf tests\n";
+}
+
+{ # 75258 - gpixf() broken for paletted images
+  my $im = Imager->new(xsize => 10, ysize => 10, type => "paletted");
+  ok($im, "make a test image");
+  my @colors = ( $black, $red, $green, $blue );
+  is($im->addcolors(colors => \@colors), "0 but true",
+     "add some colors");
+  $im->setpixel(x => 0, y => 0, color => $red);
+  $im->setpixel(x => 1, y => 0, color => $green);
+  $im->setpixel(x => 2, y => 0, color => $blue);
+  is_fcolor3($im->getpixel(x => 0, y => 0, type => "float"),
+            1.0, 0, 0, "get a pixel in float form, make sure it's red");
+  is_fcolor3($im->getpixel(x => 1, y => 0, type => "float"),
+            0, 1.0, 0, "get a pixel in float form, make sure it's green");
+  is_fcolor3($im->getpixel(x => 2, y => 0, type => "float"),
+            0, 0, 1.0, "get a pixel in float form, make sure it's blue");
+}
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->to_paletted, "can't convert an empty image");
+  is($empty->errstr, "to_paletted: empty input image",
+    "check error message");
+
+  is($empty->addcolors(colors => [ $black ]), -1,
+     "can't addcolors() to an empty image");
+  is($empty->errstr, "addcolors: empty input image",
+     "check error message");
+
+  ok(!$empty->setcolors(colors => [ $black ]),
+     "can't setcolors() to an empty image");
+  is($empty->errstr, "setcolors: empty input image",
+     "check error message");
+
+  ok(!$empty->getcolors(),
+     "can't getcolors() from an empty image");
+  is($empty->errstr, "getcolors: empty input image",
+     "check error message");
+
+  is($empty->colorcount, -1, "can't colorcount() an empty image");
+  is($empty->errstr, "colorcount: empty input image",
+     "check error message");
+
+  is($empty->maxcolors, -1, "can't maxcolors() an empty image");
+  is($empty->errstr, "maxcolors: empty input image",
+     "check error message");
+
+  is($empty->findcolor(color => $blue), undef,
+     "can't findcolor an empty image");
+  is($empty->errstr, "findcolor: empty input image",
+     "check error message");
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t023palette.log"
+}
+
+sub iscolor {
+  my ($c1, $c2, $msg) = @_;
+
+  my $builder = Test::Builder->new;
+  my @c1 = $c1->rgba;
+  my @c2 = $c2->rgba;
+  if (!$builder->ok($c1[0] == $c2[0] && $c1[1] == $c2[1] && $c1[2] == $c2[2],
+                    $msg)) {
+    $builder->diag(<<DIAG);
+      got color: [ @c1 ]
+ expected color: [ @c2 ]
+DIAG
+  }
+}
+
+sub isbin ($$$) {
+  my ($got, $expected, $msg) = @_;
+
+  my $builder = Test::Builder->new;
+  if (!$builder->ok($got eq $expected, $msg)) {
+    (my $got_dec = $got) =~ s/([^ -~])/sprintf("\\x%02x", ord $1)/ge;
+    (my $exp_dec = $expected)  =~ s/([^ -~])/sprintf("\\x%02x", ord $1)/ge;
+    $builder->diag(<<DIAG);
+      got: "$got_dec"
+ expected: "$exp_dec"
+DIAG
+  }
+}
+
+sub coloreq {
+  my ($left, $right, $comment) = @_;
+
+  my ($rl, $gl, $bl, $al) = $left->rgba;
+  my ($rr, $gr, $br, $ar) = $right->rgba;
+
+  print "# comparing color($rl,$gl,$bl,$al) with ($rr,$gr,$br,$ar)\n";
+  ok($rl == $rr && $gl == $gr && $bl == $br && $al == $ar,
+      $comment);
+}
+
+sub _get_error {
+  my @errors = Imager::i_errors();
+  return join(": ", map $_->[0], @errors);
+}
diff --git a/t/150-type/100-masked.t b/t/150-type/100-masked.t
new file mode 100644 (file)
index 0000000..95d5f1a
--- /dev/null
@@ -0,0 +1,706 @@
+#!perl -w
+use strict;
+use Test::More tests => 244;
+use Imager qw(:all :handy);
+use Imager::Test qw(is_color3 is_fcolor3);
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t020masked.log");
+
+my $base_rgb = Imager::ImgRaw::new(100, 100, 3);
+# put something in there
+my $black = NC(0, 0, 0);
+my $red = NC(255, 0, 0);
+my $green = NC(0, 255, 0);
+my $blue = NC(0, 0, 255);
+my $white = NC(255, 255, 255);
+my $grey = NC(128, 128, 128);
+use Imager::Color::Float;
+my $redf = Imager::Color::Float->new(1, 0, 0);
+my $greenf = Imager::Color::Float->new(0, 1, 0);
+my $bluef = Imager::Color::Float->new(0, 0, 1);
+my $greyf = Imager::Color::Float->new(0.5, 0.5, 0.5);
+my @cols = ($red, $green, $blue);
+for my $y (0..99) {
+  Imager::i_plin($base_rgb, 0, $y, ($cols[$y % 3] ) x 100);
+}
+
+# first a simple subset image
+my $s_rgb = Imager::i_img_masked_new($base_rgb, undef, 25, 25, 50, 50);
+
+is(Imager::i_img_getchannels($s_rgb), 3,
+   "1 channel image channel count match");
+ok(Imager::i_img_getmask($s_rgb) & 1,
+   "1 channel image mask");
+ok(Imager::i_img_virtual($s_rgb),
+   "1 channel image thinks it isn't virtual");
+is(Imager::i_img_bits($s_rgb), 8,
+   "1 channel image has bits == 8");
+is(Imager::i_img_type($s_rgb), 0, # direct
+   "1 channel image is direct");
+
+my @ginfo = i_img_info($s_rgb);
+is($ginfo[0], 50, "check width");
+is($ginfo[1], 50, "check height");
+
+# sample some pixels through the subset
+my $c = Imager::i_get_pixel($s_rgb, 0, 0);
+is_color3($c, 0, 255, 0, "check (0,0)");
+$c = Imager::i_get_pixel($s_rgb, 49, 49);
+# (25+49)%3 = 2
+is_color3($c, 0, 0, 255, "check (49,49)");
+
+# try writing to it
+for my $y (0..49) {
+  Imager::i_plin($s_rgb, 0, $y, ($cols[$y % 3]) x 50);
+}
+pass("managed to write to it");
+# and checking the target image
+$c = Imager::i_get_pixel($base_rgb, 25, 25);
+is_color3($c, 255, 0, 0, "check (25,25)");
+$c = Imager::i_get_pixel($base_rgb, 29, 29);
+is_color3($c, 0, 255, 0, "check (29,29)");
+
+undef $s_rgb;
+
+# a basic background
+for my $y (0..99) {
+  Imager::i_plin($base_rgb, 0, $y, ($red ) x 100);
+}
+my $mask = Imager::ImgRaw::new(50, 50, 1);
+# some venetian blinds
+for my $y (4..20) {
+  Imager::i_plin($mask, 5, $y*2, ($white) x 40);
+}
+# with a strip down the middle
+for my $y (0..49) {
+  Imager::i_plin($mask, 20, $y, ($white) x 8);
+}
+my $m_rgb = Imager::i_img_masked_new($base_rgb, $mask, 25, 25, 50, 50);
+ok($m_rgb, "make masked with mask");
+for my $y (0..49) {
+  Imager::i_plin($m_rgb, 0, $y, ($green) x 50);
+}
+my @color_tests =
+  (
+   [ 25+0,  25+0,  $red ],
+   [ 25+19, 25+0,  $red ],
+   [ 25+20, 25+0,  $green ],
+   [ 25+27, 25+0,  $green ],
+   [ 25+28, 25+0,  $red ],
+   [ 25+49, 25+0,  $red ],
+   [ 25+19, 25+7,  $red ],
+   [ 25+19, 25+8,  $green ],
+   [ 25+19, 25+9,  $red ],
+   [ 25+0,  25+8,  $red ],
+   [ 25+4,  25+8,  $red ],
+   [ 25+5,  25+8,  $green ],
+   [ 25+44, 25+8,  $green ],
+   [ 25+45, 25+8,  $red ],
+   [ 25+49, 25+49, $red ],
+  );
+my $test_num = 15;
+for my $test (@color_tests) {
+  my ($x, $y, $testc) = @$test;
+  my ($r, $g, $b) = $testc->rgba;
+  my $c = Imager::i_get_pixel($base_rgb, $x, $y);
+  is_color3($c, $r, $g, $b, "at ($x, $y)");
+}
+
+{
+  # tests for the OO versions, fairly simple, since the basic functionality
+  # is covered by the low-level interface tests
+   
+  my $base = Imager->new(xsize=>100, ysize=>100);
+  ok($base, "make base OO image");
+  $base->box(color=>$blue, filled=>1); # fill it all
+  my $mask = Imager->new(xsize=>80, ysize=>80, channels=>1);
+  $mask->box(color=>$white, filled=>1, xmin=>5, xmax=>75, ymin=>5, ymax=>75);
+  my $m_img = $base->masked(mask=>$mask, left=>5, top=>5);
+  ok($m_img, "make masked OO image");
+  is($m_img->getwidth, 80, "check width");
+  $m_img->box(color=>$green, filled=>1);
+  my $c = $m_img->getpixel(x=>0, y=>0);
+  is_color3($c, 0, 0, 255, "check (0,0)");
+  $c = $m_img->getpixel(x => 5, y => 5);
+  is_color3($c, 0, 255, 0, "check (5,5)");
+
+  # older versions destroyed the Imager::ImgRaw object manually in 
+  # Imager::DESTROY rather than letting Imager::ImgRaw::DESTROY 
+  # destroy the object
+  # so we test here by destroying the base and mask objects and trying 
+  # to draw to the masked wrapper
+  # you may need to test with ElectricFence to trigger the problem
+  undef $mask;
+  undef $base;
+  $m_img->box(color=>$blue, filled=>1);
+  pass("didn't crash unreffing base or mask for masked image");
+}
+
+# 35.7% cover on maskimg.c up to here
+
+{ # error handling:
+  my $base = Imager->new(xsize => 100, ysize => 100);
+  ok($base, "make base");
+  { #  make masked image subset outside of the base image
+    my $masked = $base->masked(left => 100);
+    ok (!$masked, "fail to make empty masked");
+    is($base->errstr, "subset outside of target image", "check message");
+  }
+}
+
+{ # size limiting
+  my $base = Imager->new(xsize => 10, ysize => 10);
+  ok($base, "make base for size limit tests");
+  {
+    my $masked = $base->masked(left => 5, right => 15);
+    ok($masked, "make masked");
+    is($masked->getwidth, 5, "check width truncated");
+  }
+  {
+    my $masked = $base->masked(top => 5, bottom => 15);
+    ok($masked, "make masked");
+    is($masked->getheight, 5, "check height truncated");
+  }
+}
+# 36.7% up to here
+
+$mask = Imager->new(xsize => 80, ysize => 80, channels => 1);
+$mask->box(filled => 1, color => $white, xmax => 39, ymax => 39);
+$mask->box(fill => { hatch => "check1x1" }, ymin => 40, xmax => 39);
+
+{
+  my $base = Imager->new(xsize => 100, ysize => 100, bits => "double");
+  ok($base, "base for single pixel tests");
+  is($base->type, "direct", "check type");
+  my $masked = $base->masked(mask => $mask, left => 1, top => 2);
+  my $limited = $base->masked(left => 1, top => 2);
+
+  is($masked->type, "direct", "check masked is same type as base");
+  is($limited->type, "direct", "check limited is same type as base");
+
+  {
+    # single pixel writes, masked
+    {
+      ok($masked->setpixel(x => 1, y => 3, color => $green),
+        "set (1,3) in masked (2, 5) in based");
+      my $c = $base->getpixel(x => 2, y => 5);
+      is_color3($c, 0, 255, 0, "check it wrote through");
+      ok($masked->setpixel(x => 45, y => 2, color => $red),
+        "set (45,2) in masked (46,4) in base (no mask)");
+    $c = $base->getpixel(x => 46, y => 4);
+      is_color3($c, 0, 0, 0, "shouldn't have written through");
+    }
+    {
+      ok($masked->setpixel(x => 2, y => 3, color => $redf),
+        "write float red to (2,3) base(3,5)");
+      my $c = $base->getpixel(x => 3, y => 5);
+      is_color3($c, 255, 0, 0, "check it wrote through");
+      ok($masked->setpixel(x => 45, y => 3, color => $greenf),
+        "set float (45,3) in masked (46,5) in base (no mask)");
+      $c = $base->getpixel(x => 46, y => 5);
+      is_color3($c, 0, 0, 0, "check it didn't write");
+    }
+    {
+      # write out of range should fail
+      ok(!$masked->setpixel(x => 80, y => 0, color => $green),
+        "write 8-bit color out of range");
+      ok(!$masked->setpixel(x => 0, y => 80, color => $greenf),
+        "write float color out of range");
+    }
+  }
+
+  # 46.9
+
+  {
+    print "# plin coverage\n";
+    {
+      $base->box(filled => 1, color => $black);
+      # plin masked
+      # simple path
+      is($masked->setscanline(x => 76, y => 1, pixels => [ ($red, $green) x 3 ]),
+        4, "try to write 6 pixels, but only write 4");
+      is_deeply([ $base->getsamples(x => 77, y => 3, width => 4) ],
+               [ ( 0 ) x 12 ],
+               "check not written through");
+      # !simple path
+      is($masked->setscanline(x => 4, y => 2, pixels => [ ($red, $green, $blue, $grey) x (72/4) ]),
+        72, "write many pixels (masked)");
+      is_deeply([ $base->getsamples(x => 5, y => 4, width => 72) ],
+               [ ( (255, 0, 0), (0, 255, 0), (0, 0, 255), (128, 128, 128)) x 9,
+                 ( 0, 0, 0 ) x 36 ],
+               "check written through to base");
+      
+      # simple path, due to number of transitions
+      is($masked->setscanline(x => 0, y => 40, pixels => [ ($red, $green, $blue, $grey) x 5 ]),
+        20, "try to write 20 pixels, with alternating write through");
+      is_deeply([ $base->getsamples(x => 1, y => 42, width => 20) ],
+               [ ( (0, 0, 0), (0,255,0), (0,0,0), (128,128,128) ) x 5 ],
+               "check correct pixels written through");
+    }
+    
+    {
+      $base->box(filled => 1, color => $black);
+      # plin, non-masked path
+      is($limited->setscanline(x => 4, y => 2, pixels => [ ($red, $green, $blue, $grey) x (72/4) ]),
+        72, "write many pixels (limited)");
+      is_deeply([ $base->getsamples(x => 5, y => 4, width => 72) ],
+               [ ( (255, 0, 0), (0, 255, 0), (0, 0, 255), (128, 128, 128)) x 18 ],
+               "check written through to based");
+    }
+    
+    {
+      # draw outside fails
+      is($masked->setscanline(x => 80, y => 2, pixels => [ $red, $green ]),
+        0, "check writing no pixels");
+    }
+  }
+
+  {
+    print "# plinf coverage\n";
+    {
+      $base->box(filled => 1, color => $black);
+      # plinf masked
+      # simple path
+      is($masked->setscanline(x => 76, y => 1, pixels => [ ($redf, $greenf) x 3 ]),
+        4, "try to write 6 pixels, but only write 4");
+      is_deeply([ $base->getsamples(x => 77, y => 3, width => 4, type => "float") ],
+               [ ( 0 ) x 12 ],
+               "check not written through");
+      # !simple path
+      is($masked->setscanline(x => 4, y => 2, pixels => [ ($redf, $greenf, $bluef, $greyf) x (72/4) ]),
+        72, "write many pixels (masked)");
+      is_deeply([ $base->getsamples(x => 5, y => 4, width => 72, type => "float") ],
+               [ ( (1, 0, 0), (0, 1, 0), (0, 0, 1), (0.5, 0.5, 0.5)) x 9,
+                 ( 0, 0, 0 ) x 36 ],
+               "check written through to base");
+      
+      # simple path, due to number of transitions
+      is($masked->setscanline(x => 0, y => 40, pixels => [ ($redf, $greenf, $bluef, $greyf) x 5 ]),
+        20, "try to write 20 pixels, with alternating write through");
+      is_deeply([ $base->getsamples(x => 1, y => 42, width => 20, type => "float") ],
+               [ ( (0, 0, 0), (0,1,0), (0,0,0), (0.5,0.5,0.5) ) x 5 ],
+               "check correct pixels written through");
+    }
+    
+    {
+      $base->box(filled => 1, color => $black);
+      # plinf, non-masked path
+      is($limited->setscanline(x => 4, y => 2, pixels => [ ($redf, $greenf, $bluef, $greyf) x (72/4) ]),
+        72, "write many pixels (limited)");
+      is_deeply([ $base->getsamples(x => 5, y => 4, width => 72, type => "float") ],
+               [ ( (1, 0, 0), (0, 1, 0), (0, 0, 1), (0.5, 0.5, 0.5)) x 18 ],
+               "check written through to based");
+    }
+    
+    {
+      # draw outside fails
+      is($masked->setscanline(x => 80, y => 2, pixels => [ $redf, $greenf ]),
+        0, "check writing no pixels");
+    }
+  }
+  # 71.4%
+  {
+    {
+      print "# gpix\n";
+      # gpix
+      $base->box(filled => 1, color => $black);
+      ok($base->setpixel(x => 4, y => 10, color => $red),
+        "set base(4,10) to red");
+      is_fcolor3($masked->getpixel(x => 3, y => 8),
+                255, 0, 0, "check pixel written");
+
+      # out of range
+      is($masked->getpixel(x => -1, y => 1),
+        undef, "check failure to left");
+      is($masked->getpixel(x => 0, y => -1),
+        undef, "check failure to top");
+      is($masked->getpixel(x => 80, y => 1),
+        undef, "check failure to right");
+      is($masked->getpixel(x => 0, y => 80),
+        undef, "check failure to bottom");
+    }
+    {
+      print "# gpixf\n";
+      # gpixf
+      $base->box(filled => 1, color => $black);
+      ok($base->setpixel(x => 4, y => 10, color => $redf),
+        "set base(4,10) to red");
+      is_fcolor3($masked->getpixel(x => 3, y => 8, type => "float"),
+                1.0, 0, 0, 0, "check pixel written");
+
+      # out of range
+      is($masked->getpixel(x => -1, y => 1, type => "float"),
+        undef, "check failure to left");
+      is($masked->getpixel(x => 0, y => -1, type => "float"),
+        undef, "check failure to top");
+      is($masked->getpixel(x => 80, y => 1, type => "float"),
+        undef, "check failure to right");
+      is($masked->getpixel(x => 0, y => 80, type => "float"),
+        undef, "check failure to bottom");
+    }
+  }
+  # 74.5
+  {
+    {
+      print "# glin\n";
+      $base->box(filled => 1, color => $black);
+      is($base->setscanline(x => 31, y => 3, 
+                           pixels => [ ( $red, $green) x 10 ]),
+        20, "write 20 pixels to base image");
+      my @colors = $masked->
+       getscanline(x => 30, y => 1, width => 20);
+      is(@colors, 20, "check we got right number of colors");
+      is_color3($colors[0], 255, 0, 0, "check first pixel");
+      is_color3($colors[19], 0, 255, 0, "check last pixel");
+
+      @colors = $masked->getscanline(x => 76, y => 2, width => 10);
+      is(@colors, 4, "read line from right edge");
+      is_color3($colors[0], 0, 0, 0, "check pixel");
+
+      is_deeply([ $masked->getscanline(x => -1, y => 0, width => 1) ],
+        [], "fail read left of image");
+      is_deeply([ $masked->getscanline(x => 0, y => -1, width => 1) ],
+        [], "fail read top of image");
+      is_deeply([$masked->getscanline(x => 80, y => 0, width => 1)],
+        [], "fail read right of image");
+      is_deeply([$masked->getscanline(x => 0, y => 80, width => 1)],
+        [], "fail read bottom of image");
+    }
+    {
+      print "# glinf\n";
+      $base->box(filled => 1, color => $black);
+      is($base->setscanline(x => 31, y => 3, 
+                           pixels => [ ( $redf, $greenf) x 10 ]),
+        20, "write 20 pixels to base image");
+      my @colors = $masked->
+       getscanline(x => 30, y => 1, width => 20, type => "float");
+      is(@colors, 20, "check we got right number of colors");
+      is_fcolor3($colors[0], 1.0, 0, 0, 0, "check first pixel");
+      is_fcolor3($colors[19], 0, 1.0, 0, 0, "check last pixel");
+
+      @colors = $masked->
+       getscanline(x => 76, y => 2, width => 10, type => "float");
+      is(@colors, 4, "read line from right edge");
+      is_fcolor3($colors[0], 0, 0, 0, 0, "check pixel");
+
+      is_deeply([ $masked->getscanline(x => -1, y => 0, width => 1, type => "float") ],
+        [], "fail read left of image");
+      is_deeply([ $masked->getscanline(x => 0, y => -1, width => 1, type => "float") ],
+        [], "fail read top of image");
+      is_deeply([$masked->getscanline(x => 80, y => 0, width => 1, type => "float")],
+        [], "fail read right of image");
+      is_deeply([$masked->getscanline(x => 0, y => 80, width => 1, type => "float")],
+        [], "fail read bottom of image");
+    }
+  }
+  # 81.6%
+  {
+    {
+      print "# gsamp\n";
+      $base->box(filled => 1, color => $black);
+      is($base->setscanline(x => 31, y => 3, 
+                           pixels => [ ( $red, $green) x 10 ]),
+        20, "write 20 pixels to base image");
+      my @samps = $masked->
+       getsamples(x => 30, y => 1, width => 20);
+      is(@samps, 60, "check we got right number of samples");
+      is_deeply(\@samps,
+               [ (255, 0, 0, 0, 255, 0) x 10 ],
+               "check it");
+
+      @samps = $masked->
+       getsamples(x => 76, y => 2, width => 10);
+      is(@samps, 12, "read line from right edge");
+      is_deeply(\@samps, [ (0, 0, 0) x 4], "check result");
+
+      is_deeply([ $masked->getsamples(x => -1, y => 0, width => 1) ],
+        [], "fail read left of image");
+      is_deeply([ $masked->getsamples(x => 0, y => -1, width => 1) ],
+        [], "fail read top of image");
+      is_deeply([$masked->getsamples(x => 80, y => 0, width => 1)],
+        [], "fail read right of image");
+      is_deeply([$masked->getsamples(x => 0, y => 80, width => 1)],
+        [], "fail read bottom of image");
+    }
+    {
+      print "# gsampf\n";
+      $base->box(filled => 1, color => $black);
+      is($base->setscanline(x => 31, y => 3, 
+                           pixels => [ ( $redf, $greenf) x 10 ]),
+        20, "write 20 pixels to base image");
+      my @samps = $masked->
+       getsamples(x => 30, y => 1, width => 20, type => "float");
+      is(@samps, 60, "check we got right number of samples");
+      is_deeply(\@samps,
+               [ (1.0, 0, 0, 0, 1.0, 0) x 10 ],
+               "check it");
+
+      @samps = $masked->
+       getsamples(x => 76, y => 2, width => 10, type => "float");
+      is(@samps, 12, "read line from right edge");
+      is_deeply(\@samps, [ (0, 0, 0) x 4], "check result");
+
+      is_deeply([ $masked->getsamples(x => -1, y => 0, width => 1, type => "float") ],
+        [], "fail read left of image");
+      is_deeply([ $masked->getsamples(x => 0, y => -1, width => 1, type => "float") ],
+        [], "fail read top of image");
+      is_deeply([$masked->getsamples(x => 80, y => 0, width => 1, type => "float")],
+        [], "fail read right of image");
+      is_deeply([$masked->getsamples(x => 0, y => 80, width => 1, type => "float")],
+        [], "fail read bottom of image");
+    }
+  }
+  # 86.2%
+}
+
+{
+  my $base = Imager->new(xsize => 100, ysize => 100, type => "paletted");
+  ok($base, "make paletted base");
+  is($base->type, "paletted", "check we got paletted");
+  is($base->addcolors(colors => [ $black, $red, $green, $blue ]),
+     "0 but true",
+     "add some colors to base");
+  my $masked = $base->masked(mask => $mask, left => 1, top => 2);
+  my $limited = $base->masked(left => 1, top => 2);
+
+  is($masked->type, "paletted", "check masked is same type as base");
+  is($limited->type, "paletted", "check limited is same type as base");
+
+  {
+    # make sure addcolors forwarded
+    is($masked->addcolors(colors => [ $grey ]), 4,
+       "test addcolors forwarded");
+    my @colors = $masked->getcolors();
+    is(@colors, 5, "check getcolors forwarded");
+    is_color3($colors[1], 255, 0, 0, "check color from palette");
+  }
+
+  my ($blacki, $redi, $greeni, $bluei, $greyi) = 0 .. 4;
+
+  { # gpal
+    print "# gpal\n";
+    $base->box(filled => 1, color => $black);
+    is($base->setscanline(x => 0, y => 5, type => "index",
+                         pixels => [ ( $redi, $greeni, $bluei, $greyi) x 25 ]),
+       100, "write some pixels to base");
+    my @indexes = $masked->getscanline(y => 3, type => "index", width => "81");
+    is(@indexes, 80, "got 80 indexes");
+    is_deeply(\@indexes,
+             [ ( $greeni, $bluei, $greyi, $redi) x 20 ],
+             "check values");
+
+    is_deeply([ $masked->getscanline(x => -1, y => 3, type => "index") ],
+             [], "fail read left of image");
+  }
+  # 89.8%
+
+  { # ppal, unmasked
+    print "# ppal\n";
+    $base->box(filled => 1, color => $black);
+    is($limited->setscanline(x => 1, y => 1, type => "index",
+                            pixels => [ ( $redi, $greeni, $bluei) x 3 ]),
+       9, "ppal limited");
+    is_deeply([ $base->getscanline(x => 2, y => 3, type => "index", 
+                                  width => 9) ],
+             [ ( $redi, $greeni, $bluei) x 3 ],
+             "check set in base");
+  }
+  { # ppal, masked
+    $base->box(filled => 1, color => $black);
+    is($masked->setscanline(x => 1, y => 2, type => "index",
+                           pixels => [ ( $redi, $greeni, $bluei, $greyi) x 12 ]),
+       48, "ppal masked");
+    is_deeply([ $base->getscanline(x => 0, y => 4, type => "index") ],
+             [ 0, 0,
+               ( $redi, $greeni, $bluei, $greyi ) x 9,
+               $redi, $greeni, $bluei, ( 0 ) x 59 ],
+             "check written");
+  }
+  {
+    # ppal, errors
+    is($masked->setscanline(x => -1, y => 0, type => "index",
+                           pixels => [ $redi, $bluei ]),
+       0, "fail to write ppal");
+
+    is($masked->setscanline(x => 78, y => 0, type => "index",
+                          pixels => [ $redi, $bluei, $greeni, $greyi ]),
+       2, "write over right side");
+  }
+}
+
+my $full_mask = Imager->new(xsize => 10, ysize => 10, channels => 1);
+$full_mask->box(filled => 1, color => NC(255, 0, 0));
+
+# no mask and mask with full coverage should behave the same
+my $psamp_outside_error = "Image position outside of image";
+for my $masked (0, 1){ # psamp
+  print "# psamp masked: $masked\n";
+  my $imback = Imager::ImgRaw::new(20, 20, 3);
+  my $mask;
+  if ($masked) {
+    $mask = $full_mask->{IMG};
+  }
+  my $imraw = Imager::i_img_masked_new($imback, $mask, 3, 4, 10, 10);
+  {
+    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");
+  }
+  { # 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, 10, 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");
+  }
+  print "# end psamp tests\n";
+}
+
+for my $masked (0, 1) { # psampf
+  print "# psampf\n";
+  my $imback = Imager::ImgRaw::new(20, 20, 3);
+  my $mask;
+  if ($masked) {
+    $mask = $full_mask->{IMG};
+  }
+  my $imraw = Imager::i_img_masked_new($imback, $mask, 3, 4, 10, 10);
+  {
+    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, 128, 64,
+             "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), 64, 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), 64, 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, 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_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 ]) ],
+             [ (128, 64, 32) 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");
+  }
+  { # 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, 10, 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");
+  }
+  print "# end psampf tests\n";
+}
+
+{
+  my $sub_mask = $full_mask->copy;
+  $sub_mask->box(filled => 1, color => NC(0,0,0), xmin => 4, xmax => 6);
+  my $base = Imager::ImgRaw::new(20, 20, 3);
+  my $masked = Imager::i_img_masked_new($base, $sub_mask->{IMG}, 3, 4, 10, 10);
+
+  is(Imager::i_psamp($masked, 0, 2, undef, [ ( 0, 127, 255) x 10 ]), 30,
+     "psamp() to masked image");
+  is_deeply([ Imager::i_gsamp($base, 0, 20, 6, undef) ],
+           [ ( 0, 0, 0 ) x 3, # left of mask
+             ( 0, 127, 255 ) x 4, # masked area
+             ( 0, 0, 0 ) x 3, # unmasked area
+             ( 0, 127, 255 ) x 3, # masked area
+             ( 0, 0, 0 ) x 7 ], # right of mask
+           "check values written");
+  is(Imager::i_psampf($masked, 0, 2, undef, [ ( 0, 0.5, 1.0) x 10 ]), 30,
+     "psampf() to masked image");
+  is_deeply([ Imager::i_gsamp($base, 0, 20, 6, undef) ],
+           [ ( 0, 0, 0 ) x 3, # left of mask
+             ( 0, 128, 255 ) x 4, # masked area
+             ( 0, 0, 0 ) x 3, # unmasked area
+             ( 0, 128, 255 ) x 3, # masked area
+             ( 0, 0, 0 ) x 7 ], # right of mask
+           "check values written");
+}
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->masked, "fail to make a masked image from an empty");
+  is($empty->errstr, "masked: empty input image",
+    "check error message");
+}
+
+Imager->close_log();
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t020masked.log";
+}
+
+sub _get_error {
+  my @errors = Imager::i_errors();
+  return join(": ", map $_->[0], @errors);
+}
diff --git a/t/200-file/010-iolayer.t b/t/200-file/010-iolayer.t
new file mode 100644 (file)
index 0000000..76388a9
--- /dev/null
@@ -0,0 +1,987 @@
+#!perl -w
+use strict;
+use Test::More tests => 274;
+use Imager::Test qw(is_image);
+# for SEEK_SET etc, Fcntl doesn't provide these in 5.005_03
+use IO::Seekable;
+use Config;
+
+BEGIN { use_ok(Imager => ':all') };
+
+-d "testout" or mkdir "testout";
+
+$| = 1;
+
+Imager->open_log(log => "testout/t07iolayer.log");
+
+undef($/);
+# start by testing io buffer
+
+my $data="P2\n2 2\n255\n 255 0\n0 255\n";
+my $IO = Imager::io_new_buffer($data);
+my $im = Imager::i_readpnm_wiol($IO, -1);
+
+ok($im, "read from data io");
+
+open(FH, ">testout/t07.ppm") or die $!;
+binmode(FH);
+my $fd = fileno(FH);
+my $IO2 = Imager::io_new_fd( $fd );
+Imager::i_writeppm_wiol($im, $IO2);
+close(FH);
+undef($im);
+
+open(FH, "<testimg/penguin-base.ppm");
+binmode(FH);
+$data = <FH>;
+close(FH);
+my $IO3 = Imager::IO->new_buffer($data);
+#undef($data);
+$im = Imager::i_readpnm_wiol($IO3, -1);
+
+ok($im, "read from buffer, for compare");
+undef $IO3;
+
+open(FH, "<testimg/penguin-base.ppm") or die $!;
+binmode(FH);
+$fd = fileno(FH);
+my $IO4 = Imager::IO->new_fd( $fd );
+my $im2 = Imager::i_readpnm_wiol($IO4, -1);
+close(FH);
+undef($IO4);
+
+ok($im2, "read from file, for compare");
+
+is(i_img_diff($im, $im2), 0, "compare images");
+undef($im2);
+
+my $IO5 = Imager::io_new_bufchain();
+Imager::i_writeppm_wiol($im, $IO5);
+my $data2 = Imager::io_slurp($IO5);
+undef($IO5);
+
+ok($data2, "check we got data from bufchain");
+
+my $IO6 = Imager::io_new_buffer($data2);
+my $im3 = Imager::i_readpnm_wiol($IO6, -1);
+
+is(Imager::i_img_diff($im, $im3), 0, "read from buffer");
+
+my $work = $data;
+my $pos = 0;
+sub io_reader {
+  my ($size, $maxread) = @_;
+  my $out = substr($work, $pos, $maxread);
+  $pos += length $out;
+  $out;
+}
+sub io_reader2 {
+  my ($size, $maxread) = @_;
+  my $out = substr($work, $pos, $maxread);
+  $pos += length $out;
+  $out;
+}
+my $IO7 = Imager::IO->new_cb(undef, \&io_reader, undef, undef);
+ok($IO7, "making readcb object");
+my $im4 = Imager::i_readpnm_wiol($IO7, -1);
+ok($im4, "read from cb");
+ok(Imager::i_img_diff($im, $im4) == 0, "read from cb image match");
+
+$pos = 0;
+$IO7 = Imager::io_new_cb(undef, \&io_reader2, undef, undef);
+ok($IO7, "making short readcb object");
+my $im5 = Imager::i_readpnm_wiol($IO7, -1);
+ok($im4, "read from cb2");
+is(Imager::i_img_diff($im, $im5), 0, "read from cb2 image match");
+
+sub io_writer {
+  my ($what) = @_;
+  substr($work, $pos, $pos+length $what) = $what;
+  $pos += length $what;
+
+  1;
+}
+
+my $did_close;
+sub io_close {
+  ++$did_close;
+}
+
+my $IO8 = Imager::io_new_cb(\&io_writer, undef, undef, \&io_close);
+ok($IO8, "making writecb object");
+$pos = 0;
+$work = '';
+ok(Imager::i_writeppm_wiol($im, $IO8), "write to cb");
+# I originally compared this to $data, but that doesn't include the
+# Imager header
+is($work, $data2, "write image match");
+ok($did_close, "did close");
+
+# with a short buffer, no closer
+my $IO9 = Imager::io_new_cb(\&io_writer, undef, undef, undef, 1);
+ok($IO9, "making short writecb object");
+$pos = 0;
+$work = '';
+ok(Imager::i_writeppm_wiol($im, $IO9), "write to short cb");
+is($work, $data2, "short write image match");
+
+{
+  my $buf_data = "Test data";
+  my $io9 = Imager::io_new_buffer($buf_data);
+  is(ref $io9, "Imager::IO", "check class");
+  my $work;
+  is($io9->raw_read($work, 4), 4, "read 4 from buffer object");
+  is($work, "Test", "check data read");
+  is($io9->raw_read($work, 5), 5, "read the rest");
+  is($work, " data", "check data read");
+  is($io9->raw_seek(5, SEEK_SET), 5, "seek");
+  is($io9->raw_read($work, 5), 4, "short read");
+  is($work, "data", "check data read");
+  is($io9->raw_seek(-1, SEEK_CUR), 8, "seek relative");
+  is($io9->raw_seek(-5, SEEK_END), 4, "seek relative to end");
+  is($io9->raw_seek(-10, SEEK_CUR), -1, "seek failure");
+  undef $io9;
+}
+{
+  my $io = Imager::IO->new_bufchain();
+  is(ref $io, "Imager::IO", "check class");
+  is($io->raw_write("testdata"), 8, "check write");
+  is($io->raw_seek(-8, SEEK_CUR), 0, "seek relative");
+  my $work;
+  is($io->raw_read($work, 8), 8, "check read");
+  is($work, "testdata", "check data read");
+  is($io->raw_seek(-3, SEEK_END), 5, "seek end relative");
+  is($io->raw_read($work, 5), 3, "short read");
+  is($work, "ata", "check read data");
+  is($io->raw_seek(4, SEEK_SET), 4, "absolute seek to write some");
+  is($io->raw_write("testdata"), 8, "write");
+  is($io->raw_seek(0, SEEK_CUR), 12, "check size");
+  $io->raw_close();
+  
+  # grab the data
+  my $data = Imager::io_slurp($io);
+  is($data, "testtestdata", "check we have the right data");
+}
+
+{ # callback failure checks
+  my $fail_io = Imager::io_new_cb(\&fail_write, \&fail_read, \&fail_seek, undef, 1);
+  # scalar context
+  my $buffer;
+  my $read_result = $fail_io->raw_read($buffer, 10);
+  is($read_result, undef, "read failure undef in scalar context");
+  my @read_result = $fail_io->raw_read($buffer, 10);
+  is(@read_result, 0, "empty list in list context");
+  $read_result = $fail_io->raw_read2(10);
+  is($read_result, undef, "raw_read2 failure (scalar)");
+  @read_result = $fail_io->raw_read2(10);
+  is(@read_result, 0, "raw_read2 failure (list)");
+
+  my $write_result = $fail_io->raw_write("test");
+  is($write_result, -1, "failed write");
+
+  my $seek_result = $fail_io->raw_seek(-1, SEEK_SET);
+  is($seek_result, -1, "failed seek");
+}
+
+{ # callback success checks
+  my $good_io = Imager::io_new_cb(\&good_write, \&good_read, \&good_seek, undef, 1);
+  # scalar context
+  my $buffer;
+  my $read_result = $good_io->raw_read($buffer, 10);
+  is($read_result, 8, "read success (scalar)");
+  is($buffer, "testdata", "check data");
+  my @read_result = $good_io->raw_read($buffer, 10);
+  is_deeply(\@read_result, [ 8 ], "read success (list)");
+  is($buffer, "testdata", "check data");
+  $read_result = $good_io->raw_read2(10);
+  is($read_result, "testdata", "read2 success (scalar)");
+  @read_result = $good_io->raw_read2(10);
+  is_deeply(\@read_result, [ "testdata" ], "read2 success (list)");
+}
+
+{ # end of file
+  my $eof_io = Imager::io_new_cb(undef, \&eof_read, undef, undef, 1);
+  my $buffer;
+  my $read_result = $eof_io->raw_read($buffer, 10);
+  is($read_result, 0, "read eof (scalar)");
+  is($buffer, '', "check data");
+  my @read_result = $eof_io->raw_read($buffer, 10);
+  is_deeply(\@read_result, [ 0 ], "read eof (list)");
+  is($buffer, '', "check data");
+}
+
+{ # no callbacks
+  my $none_io = Imager::io_new_cb(undef, undef, undef, undef, 0);
+  is($none_io->raw_write("test"), -1, "write with no writecb should fail");
+  my $buffer;
+  is($none_io->raw_read($buffer, 10), undef, "read with no readcb should fail");
+  is($none_io->raw_seek(0, SEEK_SET), -1, "seek with no seekcb should fail");
+}
+
+SKIP:
+{ # make sure we croak when trying to write a string with characters over 0xff
+  # the write callback shouldn't get called
+  skip("no native UTF8 support in this version of perl", 2)
+    unless $] >= 5.006;
+  my $io = Imager::io_new_cb(\&good_write, undef, undef, 1);
+  my $data = chr(0x100);
+  is(ord $data, 0x100, "make sure we got what we expected");
+  my $result = 
+    eval {
+      $io->raw_write($data);
+      1;
+    };
+  ok(!$result, "should have croaked")
+    and print "# $@\n";
+}
+
+{ # 0.52 left some debug code in a path that wasn't tested, make sure
+  # that path is tested
+  # http://rt.cpan.org/Ticket/Display.html?id=20705
+  my $io = Imager::io_new_cb
+    (
+     sub { 
+       print "# write $_[0]\n";
+       1 
+     }, 
+     sub { 
+       print "# read $_[0], $_[1]\n";
+       "x" x $_[1]
+     }, 
+     sub { print "# seek\n"; 0 }, 
+     sub { print "# close\n"; 1 });
+  my $buffer;
+  is($io->raw_read($buffer, 10), 10, "read 10");
+  is($buffer, "xxxxxxxxxx", "read value");
+  ok($io->raw_write("foo"), "write");
+  is($io->raw_close, 0, "close");
+}
+
+SKIP:
+{ # fd_seek write failure
+  -c "/dev/full"
+    or skip("No /dev/full", 3);
+  open my $fh, "> /dev/full"
+    or skip("Can't open /dev/full: $!", 3);
+  my $io = Imager::io_new_fd(fileno($fh));
+  ok($io, "make fd io for /dev/full");
+  Imager::i_clear_error();
+  is($io->raw_write("test"), -1, "fail to write");
+  my $msg = Imager->_error_as_msg;
+  like($msg, qr/^write\(\) failure: /, "check error message");
+  print "# $msg\n";
+
+  # /dev/full succeeds on seek on Linux
+
+  undef $io;
+}
+
+SKIP:
+{ # fd_seek seek failure
+  my $seekfail = "testout/t07seekfail.dat";
+  open my $fh, "> $seekfail"
+    or skip("Can't open $seekfail: $!", 3);
+  my $io = Imager::io_new_fd(fileno($fh));
+  ok($io, "make fd io for $seekfail");
+
+  Imager::i_clear_error();
+  is($io->raw_seek(-1, SEEK_SET), -1, "shouldn't be able to seek to -1");
+  my $msg = Imager->_error_as_msg;
+  like($msg, qr/^lseek\(\) failure: /, "check error message");
+  print "# $msg\n";
+
+  undef $io;
+  close $fh;
+  unlink $seekfail;
+}
+
+SKIP:
+{ # fd_seek read failure
+  open my $fh, "> testout/t07writeonly.txt"
+    or skip("Can't open testout/t07writeonly.txt: $!", 3);
+  my $io = Imager::io_new_fd(fileno($fh));
+  ok($io, "make fd io for write-only");
+
+  Imager::i_clear_error();
+  my $buf;
+  is($io->raw_read($buf, 10), undef,
+     "file open for write shouldn't be readable");
+  my $msg = Imager->_error_as_msg;
+  like($msg, qr/^read\(\) failure: /, "check error message");
+  print "# $msg\n";
+
+  undef $io;
+}
+
+SKIP:
+{ # fd_seek eof
+  open my $fh, "> testout/t07readeof.txt"
+    or skip("Can't open testout/t07readeof.txt: $!", 5);
+  binmode $fh;
+  print $fh "test";
+  close $fh;
+  open my $fhr, "< testout/t07readeof.txt",
+    or skip("Can't open testout/t07readeof.txt: $!", 5);
+  my $io = Imager::io_new_fd(fileno($fhr));
+  ok($io, "make fd io for read eof");
+
+  Imager::i_clear_error();
+  my $buf;
+  is($io->raw_read($buf, 10), 4,
+     "10 byte read on 4 byte file should return 4");
+  my $msg = Imager->_error_as_msg;
+  is($msg, "", "should be no error message")
+    or print STDERR "# read(4) message is: $msg\n";
+
+  Imager::i_clear_error();
+  $buf = '';
+  is($io->raw_read($buf, 10), 0,
+     "10 byte read at end of 4 byte file should return 0 (eof)");
+
+  $msg = Imager->_error_as_msg;
+  is($msg, "", "should be no error message")
+    or print STDERR "# read(4), eof message is: $msg\n";
+
+  undef $io;
+}
+
+{ # buffered I/O
+  my $data="P2\n2 2\n255\n 255 0\n0 255\n";
+  my $io = Imager::io_new_buffer($data);
+
+  my $c = $io->getc();
+
+  is($c, ord "P", "getc");
+  my $peekc = $io->peekc();
+
+  is($peekc, ord "2", "peekc");
+
+  my $peekn = $io->peekn(2);
+  is($peekn, "2\n", "peekn");
+
+  $c = $io->getc();
+  is($c, ord "2", "getc after peekc/peekn");
+
+  is($io->seek(0, SEEK_SET), "0", "seek");
+  is($io->getc, ord "P", "check we got back to the start");
+}
+
+{ # test closecb result is propagated
+  my $success_cb = sub { 1 };
+  my $failure_cb = sub { 0 };
+
+  {
+    my $io = Imager::io_new_cb(undef, $success_cb, undef, $success_cb);
+    is($io->close(), 0, "test successful close");
+  }
+  {
+    my $io = Imager::io_new_cb(undef, $success_cb, undef, $failure_cb);
+    is($io->close(), -1, "test failed close");
+  }
+}
+
+{ # buffered coverage/function tests
+  # some data to play with
+  my $base = pack "C*", map rand(26) + ord("a"), 0 .. 20_001;
+
+  { # buffered accessors
+    my $io = Imager::io_new_buffer($base);
+    ok($io->set_buffered(0), "set unbuffered");
+    ok(!$io->is_buffered, "verify unbuffered");
+    ok($io->set_buffered(1), "set buffered");
+    ok($io->is_buffered, "verify buffered");
+  }
+
+  { # initial i_io_read(), buffered
+    my $pos = 0;
+    my $ops = "";
+    my $work = $base;
+    my $read = sub {
+      my ($size) = @_;
+
+      my $req_size = $size;
+
+      if ($pos + $size > length $work) {
+       $size = length($work) - $pos;
+      }
+
+      my $result = substr($work, $pos, $size);
+      $pos += $size;
+      $ops .= "R$req_size>$size;";
+
+      print "# read $req_size>$size\n";
+
+      return $result;
+    };
+    my $write = sub {
+      my ($data) = @_;
+
+      substr($work, $pos, length($data), $data);
+
+      return 1;
+    };
+    {
+      my $io = Imager::io_new_cb(undef, $read, undef, undef);
+      my $buf;
+      is($io->read($buf, 1000), 1000, "read initial 1000");
+      is($buf, substr($base, 0, 1000), "check data read");
+      is($ops, "R8192>8192;", "check read op happened to buffer size");
+
+      undef $buf;
+      is($io->read($buf, 1001), 1001, "read another 1001");
+      is($buf, substr($base, 1000, 1001), "check data read");
+      is($ops, "R8192>8192;", "should be no further reads");
+
+      undef $buf;
+      is($io->read($buf, 40_000), length($base) - 2001,
+        "read the rest in one chunk");
+      is($buf, substr($base, 2001), "check the data read");
+      my $buffer_left = 8192 - 2001;
+      my $after_buffer = length($base) - 8192;
+      is($ops, "R8192>8192;R".(40_000 - $buffer_left).">$after_buffer;R21999>0;",
+        "check we tried to read the remainder");
+    }
+    {
+      # read after write errors
+      my $io = Imager::io_new_cb($write, $read, undef, undef);
+      is($io->write("test"), 4, "write 4 bytes, io in write mode");
+      is($io->read2(10), undef, "read should fail");
+      is($io->peekn(10), undef, "peekn should fail");
+      is($io->getc(), -1, "getc should fail");
+      is($io->peekc(), -1, "peekc should fail");
+    }
+  }
+
+  {
+    my $io = Imager::io_new_buffer($base);
+    print "# buffer fill check\n";
+    ok($io, "make memory io");
+    my $buf;
+    is($io->read($buf, 4096), 4096, "read 4k");
+    is($buf, substr($base, 0, 4096), "check data is correct");
+
+    # peek a bit
+    undef $buf;
+    is($io->peekn(5120), substr($base, 4096, 5120),
+       "peekn() 5120, which should exceed the buffer, and only read the left overs");
+  }
+
+  { # initial peekn
+    my $io = Imager::io_new_buffer($base);
+    is($io->peekn(10), substr($base, 0, 10),
+       "make sure initial peekn() is sane");
+    is($io->read2(10), substr($base, 0, 10),
+       "and that reading 10 gets the expected data");
+  }
+
+  { # oversize peekn
+    my $io = Imager::io_new_buffer($base);
+    is($io->peekn(10_000), substr($base, 0, 8192),
+       "peekn() larger than buffer should return buffer-size bytes");
+  }
+
+  { # small peekn then large peekn with a small I/O back end
+    # this might happen when reading from a socket
+    my $work = $base;
+    my $pos = 0;
+    my $ops = '';
+    my $reader = sub {
+      my ($size) = @_;
+
+      my $req_size = $size;
+      # do small reads, to trigger a possible bug
+      if ($size > 10) {
+       $size = 10;
+      }
+
+      if ($pos + $size > length $work) {
+       $size = length($work) - $pos;
+      }
+
+      my $result = substr($work, $pos, $size);
+      $pos += $size;
+      $ops .= "R$req_size>$size;";
+
+      print "# read $req_size>$size\n";
+
+      return $result;
+    };
+    my $io = Imager::io_new_cb(undef, $reader, undef, undef);
+    ok($io, "small reader io");
+    is($io->peekn(25), substr($base, 0, 25), "peek 25");
+    is($ops, "R8192>10;R8182>10;R8172>10;",
+       "check we got the raw calls expected");
+    is($io->peekn(65), substr($base, 0, 65), "peek 65");
+    is($ops, "R8192>10;R8182>10;R8172>10;R8162>10;R8152>10;R8142>10;R8132>10;",
+       "check we got the raw calls expected");
+  }
+  for my $buffered (1, 0) { # peekn followed by errors
+    my $buffered_desc = $buffered ? "buffered" : "unbuffered";
+    my $read = 0;
+    my $base = "abcdef";
+    my $pos = 0;
+    my $reader = sub {
+      my $size = shift;
+      my $req_size = $size;
+      if ($pos + $size > length $base) {
+       $size = length($base) - $pos;
+      }
+      # error instead of eof
+      if ($size == 0) {
+       print "# read $req_size>error\n";
+       return;
+      }
+      my $result = substr($base, $pos, $size);
+      $pos += $size;
+
+      print "# read $req_size>$size\n";
+
+      return $result;
+    };
+    my $io = Imager::io_new_cb(undef, $reader, undef, undef);
+    ok($io, "make $buffered_desc cb with error after 6 bytes");
+    is($io->peekn(5), "abcde",
+       "peekn until just before error ($buffered_desc)");
+    is($io->peekn(6), "abcdef", "peekn until error ($buffered_desc)");
+    is($io->peekn(7), "abcdef", "peekn past error ($buffered_desc)");
+    ok(!$io->error,
+       "should be no error indicator, since data buffered ($buffered_desc)");
+    ok(!$io->eof,
+       "should be no eof indicator, since data buffered ($buffered_desc)");
+
+    # consume it
+    is($io->read2(6), "abcdef", "consume the buffer ($buffered_desc)");
+    is($io->peekn(10), undef,
+       "peekn should get an error indicator ($buffered_desc)");
+    ok($io->error, "should be an error state ($buffered_desc)");
+    ok(!$io->eof, "but not eof ($buffered_desc)");
+  }
+  { # peekn on an empty file
+    my $io = Imager::io_new_buffer("");
+    is($io->peekn(10), "", "peekn on empty source");
+    ok($io->eof, "should be in eof state");
+    ok(!$io->error, "but not error");
+  }
+  { # peekn on error source
+    my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef);
+    is($io->peekn(10), undef, "peekn on empty source");
+    ok($io->error, "should be in error state");
+    ok(!$io->eof, "but not eof");
+  }
+  { # peekn on short source
+    my $io = Imager::io_new_buffer("abcdef");
+    is($io->peekn(4), "abcd", "peekn 4 on 6 byte source");
+    is($io->peekn(10), "abcdef", "followed by peekn 10 on 6 byte source");
+    is($io->peekn(10), "abcdef", "and again, now eof is set");
+  }
+  { # peekn(0)
+    Imager::i_clear_error();
+    my $io = Imager::io_new_buffer("abcdef");
+    is($io->peekn(0), undef, "peekn 0 on 6 byte source");
+    my $msg = Imager->_error_as_msg;
+    is($msg, "peekn size must be positive");
+  }
+  { # getc through a whole file (buffered)
+    my $io = Imager::io_new_buffer($base);
+    my $out = '';
+    while ((my $c = $io->getc()) != -1) {
+      $out .= chr($c);
+    }
+    is($out, $base, "getc should return the file byte by byte (buffered)");
+    is($io->getc, -1, "another getc after eof should fail too");
+    ok($io->eof, "should be marked eof");
+    ok(!$io->error, "shouldn't be marked in error");
+  }
+  { # getc through a whole file (unbuffered)
+    my $io = Imager::io_new_buffer($base);
+    $io->set_buffered(0);
+    my $out = '';
+    while ((my $c = $io->getc()) != -1) {
+      $out .= chr($c);
+    }
+    is($out, $base, "getc should return the file byte by byte (unbuffered)");
+    is($io->getc, -1, "another getc after eof should fail too");
+    ok($io->eof, "should be marked eof");
+    ok(!$io->error, "shouldn't be marked in error");
+  }
+  { # buffered getc with an error
+    my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef);
+    is($io->getc, -1, "buffered getc error");
+    ok($io->error, "io marked in error");
+    ok(!$io->eof, "but not eof");
+  }
+  { # unbuffered getc with an error
+    my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef);
+    $io->set_buffered(0);
+    is($io->getc, -1, "unbuffered getc error");
+    ok($io->error, "io marked in error");
+    ok(!$io->eof, "but not eof");
+  }
+  { # initial peekc - buffered
+    my $io = Imager::io_new_buffer($base);
+    my $c = $io->peekc;
+    is($c, ord($base), "buffered peekc matches");
+    is($io->peekc, $c, "duplicate peekc matchess");
+  }
+  { # initial peekc - unbuffered
+    my $io = Imager::io_new_buffer($base);
+    $io->set_buffered(0);
+    my $c = $io->peekc;
+    is($c, ord($base), "unbuffered peekc matches");
+    is($io->peekc, $c, "duplicate peekc matchess");
+  }
+  { # initial peekc eof - buffered
+    my $io = Imager::io_new_cb(undef, sub { "" }, undef, undef);
+    my $c = $io->peekc;
+    is($c, -1, "buffered eof peekc is -1");
+    is($io->peekc, $c, "duplicate matches");
+    ok($io->eof, "io marked eof");
+    ok(!$io->error, "but not error");
+  }
+  { # initial peekc eof - unbuffered
+    my $io = Imager::io_new_cb(undef, sub { "" }, undef, undef);
+    $io->set_buffered(0);
+    my $c = $io->peekc;
+    is($c, -1, "buffered eof peekc is -1");
+    is($io->peekc, $c, "duplicate matches");
+    ok($io->eof, "io marked eof");
+    ok(!$io->error, "but not error");
+  }
+  { # initial peekc error - buffered
+    my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef);
+    my $c = $io->peekc;
+    is($c, -1, "buffered error peekc is -1");
+    is($io->peekc, $c, "duplicate matches");
+    ok($io->error, "io marked error");
+    ok(!$io->eof, "but not eof");
+  }
+  { # initial peekc error - unbuffered
+    my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef);
+    $io->set_buffered(0);
+    my $c = $io->peekc;
+    is($c, -1, "unbuffered error peekc is -1");
+    is($io->peekc, $c, "duplicate matches");
+    ok($io->error, "io marked error");
+    ok(!$io->eof, "but not eof");
+  }
+  { # initial putc
+    my $io = Imager::io_new_bufchain();
+    is($io->putc(ord "A"), ord "A", "initial putc buffered");
+    is($io->close, 0, "close it");
+    is(Imager::io_slurp($io), "A", "check it was written");
+  }
+  { # initial putc - unbuffered
+    my $io = Imager::io_new_bufchain();
+    $io->set_buffered(0);
+    is($io->putc(ord "A"), ord "A", "initial putc unbuffered");
+    is($io->close, 0, "close it");
+    is(Imager::io_slurp($io), "A", "check it was written");
+  }
+  { # putc unbuffered with error
+    my $io = Imager::io_new_cb(undef, undef, undef, undef);
+    $io->set_buffered(0);
+    is($io->putc(ord "A"), -1, "initial putc unbuffered error");
+    ok($io->error, "io in error");
+    is($io->putc(ord "B"), -1, "still in error");
+  }
+  { # writes while in read state
+    my $io = Imager::io_new_cb(sub { 1 }, sub { return "AA" }, undef, undef);
+    is($io->getc, ord "A", "read to setup read buffer");
+    is($io->putc(ord "B"), -1, "putc should fail");
+    is($io->write("test"), -1, "write should fail");
+  }
+  { # buffered putc error handling
+    # tests the check for error state in the buffered putc code
+    my $io = Imager::io_new_cb(undef, undef, undef, undef);
+    $io->putc(ord "A");
+    ok(!$io->flush, "flush should fail");
+    ok($io->error, "should be in error state");
+    is($io->putc(ord "B"), -1, "check for error");
+  }
+  { # buffered putc flush error handling
+    # test handling of flush failure and of the error state resulting
+    # from that
+    my $io = Imager::io_new_cb(undef, undef, undef, undef);
+    my $i = 0;
+    while (++$i < 100_000 && $io->putc(ord "A") == ord "A") {
+      # until we have to flush and fail doing do
+    }
+    is($i, 8193, "should have failed on 8193rd byte");
+    ok($io->error, "should be in error state");
+    is($io->putc(ord "B"), -1, "next putc should fail");
+  }
+  { # buffered write flush error handling
+    # test handling of flush failure and of the error state resulting
+    # from that
+    my $io = Imager::io_new_cb(undef, undef, undef, undef);
+    my $i = 0;
+    while (++$i < 100_000 && $io->write("A") == 1) {
+      # until we have to flush and fail doing do
+    }
+    is($i, 8193, "should have failed on 8193rd byte");
+    ok($io->error, "should be in error state");
+    is($io->write("B"), -1, "next write should fail");
+  }
+  { # buffered read error
+    my $io = Imager::io_new_cb(undef, undef, undef, undef);
+    is($io->read2(10), undef, "initial read returning error");
+    ok($io->error, "should be in error state");
+  }
+  { # unbuffered read error
+    my $io = Imager::io_new_cb(undef, undef, undef, undef);
+    $io->set_buffered(0);
+    is($io->read2(10), undef, "initial read returning error");
+    ok($io->error, "should be in error state");
+  }
+  { # unbuffered write error
+    my $count = 0;
+    my $io = Imager::io_new_cb(sub { return $count++; }, undef, undef, undef);
+    $io->set_buffered(0);
+    is($io->write("A"), -1, "unbuffered write failure");
+    ok($io->error, "should be in error state");
+    is($io->write("BC"), -1, "should still fail");
+  }
+  { # buffered write + large write
+    my $io = Imager::io_new_bufchain();
+    is($io->write(substr($base, 0, 4096)), 4096,
+       "should be buffered");
+    is($io->write(substr($base, 4096)), length($base) - 4096,
+       "large write, should fill buffer and fall back to direct write");
+    is($io->close, 0, "close it");
+    is(Imager::io_slurp($io), $base, "make sure the data is correct");
+  }
+  { # initial large write with failure
+    # tests error handling for the case where we bypass the buffer
+    # when the write is too large to fit
+    my $io = Imager::io_new_cb(undef, undef, undef, undef);
+    ok($io->flush, "flush with nothing buffered should succeed");
+    is($io->write($base), -1, "large write failure");
+    ok($io->error, "should be in error state");
+    is($io->close, -1, "should fail to close");
+  }
+  { # write that causes a flush then fills the buffer a bit
+    my $io = Imager::io_new_bufchain();
+    is($io->write(substr($base, 0, 6000)), 6000, "fill the buffer a bit");
+    is($io->write(substr($base, 6000, 4000)), 4000,
+       "cause it to flush and then fill some more");
+    is($io->write(substr($base, 10000)), length($base)-10000,
+       "write out the rest of our test data");
+    is($io->close, 0, "close the stream");
+    is(Imager::io_slurp($io), $base, "make sure the data is right");
+  }
+  { # failure on flush on close
+    my $io = Imager::io_new_cb(undef, undef, undef, undef);
+    is($io->putc(ord "A"), ord "A", "something in the buffer");
+    ok(!$io->error, "should be no error yet");
+    is($io->close, -1, "close should failure due to flush error");
+  }
+  { # seek failure
+    my $io = Imager::io_new_cb(undef, undef, undef, undef);
+    is($io->seek(0, SEEK_SET), -1, "seek failure");
+  }
+  { # read a little and seek
+    my $io = Imager::io_new_buffer($base);
+    is($io->getc, ord $base, "read one");
+    is($io->getc, ord substr($base, 1, 1), "read another");
+    is($io->seek(-1, SEEK_CUR), 1, "seek relative back to origin+1");
+    is($io->getc, ord substr($base, 1, 1), "read another again");
+  }
+  { # seek with failing flush
+    my $io = Imager::io_new_cb(undef, undef, undef, undef);
+    is($io->putc(ord "A"), ord "A", "write one");
+    ok(!$io->error, "not in error mode (yet)");
+    is($io->seek(0, SEEK_SET), -1, "seek failure due to flush");
+    ok($io->error, "in error mode");
+  }
+  { # gets()
+    my $data = "test1\ntest2\ntest3";
+    my $io = Imager::io_new_buffer($data);
+    is($io->gets(6), "test1\n", "gets(6)");
+    is($io->gets(5), "test2", "gets(5) (short for the line)");
+    is($io->gets(10), "\n", "gets(10) the rest of the line (the newline)");
+    is($io->gets(), "test3", "gets(default) unterminated line");
+  }
+  { # more gets()
+    my $data = "test1\ntest2\ntest3";
+    my $io = Imager::io_new_buffer($data);
+    is($io->gets(6, ord("1")), "test1", "gets(6) (line terminator 1)");
+    is($io->gets(6, ord("2")), "\ntest2", "gets(6) (line terminator 2)");
+    is($io->gets(6, ord("3")), "\ntest3", "gets(6) (line terminator 3)");
+    is($io->getc, -1, "should be eof");
+  }
+}
+
+{ # based on discussion on IRC, user was attempting to write a TIFF
+  # image file with only a write callback, but TIFF requires seek and
+  # read callbacks when writing.
+  # https://rt.cpan.org/Ticket/Display.html?id=76782
+  my $cb = Imager::io_new_cb(undef, undef, undef, undef);
+  {
+    Imager::i_clear_error();
+    my $data;
+    is($cb->read($data, 10), undef, "default read callback should fail");
+    is(Imager->_error_as_msg(), "read callback called but no readcb supplied",
+       "check error message");
+  }
+  {
+    Imager::i_clear_error();
+    is($cb->raw_write("abc"), -1, "default write callback should fail");
+    is(Imager->_error_as_msg(), "write callback called but no writecb supplied",
+       "check error message");
+  }
+  {
+    Imager::i_clear_error();
+    is($cb->seek(0, 0), -1, "default seek callback should fail");
+    is(Imager->_error_as_msg(), "seek callback called but no seekcb supplied",
+       "check error message");
+  }
+}
+
+SKIP:
+{
+  $Config{useperlio}
+    or skip "PerlIO::scalar requires perlio", 13;
+
+  my $foo;
+  open my $fh, "+<", \$foo;
+  my $io = Imager::IO->_new_perlio($fh);
+  ok($io, "perlio: make a I/O object for a perl scalar fh");
+  is($io->write("test"), 4, "perlio: check we can write");
+  is($io->seek(2, SEEK_SET), 2, "perlio: check we can seek");
+  is($io->write("more"), 4, "perlio: write some more");
+  is($io->seek(0, SEEK_SET), 0, "perlio: seek back to start");
+  my $data;
+  is($io->read($data, 10), 6, "perlio: read everything back");
+  is($data, "temore", "perlio: check we read back what we wrote");
+  is($io->close, 0, "perlio: close it");
+  is($foo, "temore", "perlio: check it got to the scalar properly");
+
+  my $io2 = Imager::IO->new_fh($fh);
+  ok($io2, "new_fh() can make an I/O layer object from a scalar fh");
+  close $fh;
+
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  $foo = "";
+  open my $fh2, ">", \$foo;
+  ok($im->write(fh => $fh2, type => "pnm"), "can write image to scalar fh")
+    or print "# ", $im->errstr, "\n";
+
+  close $fh2;
+  open my $fh3, "<", \$foo;
+  my $im2 = Imager->new(fh => $fh3);
+  ok($im2, "read image from a scalar fh");
+  is_image($im, $im2, "check they match");
+}
+
+{
+  tie *FOO, "IO::Tied";
+  my $io = Imager::IO->new_fh(\*FOO);
+  ok($io, "tied: make a I/O object for a tied fh");
+  is($io->write("test"), 4, "tied: check we can write");
+  is($io->seek(2, SEEK_SET), 2, "tied: check we can seek");
+  is($io->write("more"), 4, "tied: write some more");
+  is($io->seek(0, SEEK_SET), 0, "tied: seek back to start");
+  my $data;
+  is($io->read($data, 10), 6, "tied: read everything back");
+  is($data, "temore", "tied: check we read back what we wrote");
+  is($io->close, 0, "tied: close it");
+  is(tied(*FOO)->[0], "temore", "tied: check it got to the output properly");
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t07.ppm", "testout/t07iolayer.log";
+}
+
+sub eof_read {
+  my ($max_len) = @_;
+
+  return '';
+}
+
+sub good_read {
+  my ($max_len) = @_;
+
+  my $data = "testdata";
+  length $data <= $max_len or substr($data, $max_len) = '';
+
+  print "# good_read ($max_len) => $data\n";
+
+  return $data;
+}
+
+sub fail_write {
+  return;
+}
+
+sub fail_read {
+  return;
+}
+
+sub fail_seek {
+  return -1;
+}
+
+package IO::Tied;
+use base 'Tie::Handle';
+use IO::Seekable;
+
+sub TIEHANDLE {
+  return bless [ "", 0 ];
+}
+
+sub PRINT {
+  for my $entry (@_[1 .. $#_]) {
+    substr($_[0][0], $_[0][1], length $entry, $entry);
+    $_[0][1] += length $entry;
+  }
+
+  return 1;
+}
+
+sub SEEK {
+  my ($self, $offset, $whence) = @_;
+
+  my $newpos;
+  if ($whence == SEEK_SET) {
+    $newpos = $offset;
+  }
+  elsif ($whence == SEEK_CUR) {
+    $newpos = $self->[1] + $offset;
+  }
+  elsif ($whence == SEEK_END) {
+    $newpos = length($self->[0]) + $newpos;
+  }
+  else {
+    return -1;
+  }
+
+  if ($newpos < 0) {
+    return 0;
+  }
+
+  $self->[1] = $newpos;
+
+  return 1;
+}
+
+sub TELL {
+  return $_[0][1];
+}
+
+sub READ {
+  my $self = shift;
+  my $outlen = $_[1];
+  my $offset = @_ > 2 ? $_[2] : 0;
+  if ($self->[1] + $outlen > length $self->[0]) {
+    $outlen = length($self->[0]) - $self->[1];
+    $outlen <= 0
+      and return "";
+  }
+  defined $_[0] or $_[0] = "";
+  substr($_[0], $offset, $outlen) = substr($self->[0], $self->[1], $outlen);
+  $self->[1] += $outlen;
+
+  return $outlen;
+}
diff --git a/t/200-file/100-files.t b/t/200-file/100-files.t
new file mode 100644 (file)
index 0000000..f937019
--- /dev/null
@@ -0,0 +1,354 @@
+#!perl -w
+
+# This file is for testing file functionality that is independent of
+# the file format
+
+use strict;
+use Test::More tests => 89;
+use Imager;
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t1000files.log");
+
+SKIP:
+{
+  # Test that i_test_format_probe() doesn't pollute stdout
+
+  # Initally I tried to write this test using open to redirect files,
+  # but there was a buffering problem that made it so the data wasn't
+  # being written to the output file.  This external perl call avoids
+  # that problem
+
+  my $test_script = 'testout/t1000files_probe.pl';
+
+  # build a temp test script to use
+  ok(open(SCRIPT, "> $test_script"), "open test script")
+    or skip("no test script $test_script: $!", 2);
+  print SCRIPT <<'PERL';
+#!perl
+use Imager;
+use strict;
+my $file = shift or die "No file supplied";
+open FH, "< $file" or die "Cannot open file: $!";
+binmode FH;
+my $io = Imager::io_new_fd(fileno(FH));
+Imager::i_test_format_probe($io, -1);
+PERL
+  close SCRIPT;
+  my $perl = $^X;
+  $perl = qq/"$perl"/ if $perl =~ / /;
+  
+  print "# script: $test_script\n";
+  my $cmd = "$perl -Mblib $test_script t/200-file/100-files.t";
+  print "# command: $cmd\n";
+
+  my $out = `$cmd`;
+  is($?, 0, "command successful");
+  is($out, '', "output should be empty");
+}
+
+# test the file limit functions
+# by default the limits are zero (unlimited)
+print "# image file limits\n";
+is_deeply([ Imager->get_file_limits() ], [0, 0, 0x40000000 ],
+         "check defaults");
+ok(Imager->set_file_limits(width=>100), "set only width");
+is_deeply([ Imager->get_file_limits() ], [100, 0, 0x40000000 ],
+         "check width set");
+ok(Imager->set_file_limits(height=>150, bytes=>10000),
+   "set height and bytes");
+is_deeply([ Imager->get_file_limits() ], [ 100, 150, 10000 ],
+         "check all values now set");
+ok(Imager->check_file_limits(width => 100, height => 30),
+   "check 100 x 30 (def channels, sample_size) ok")
+  or diag(Imager->errstr);
+ok(Imager->check_file_limits(width => 100, height => 100, channels => 1),
+   "check 100 x 100 x 1 (def sample_size) ok")
+  or diag(Imager->errstr);
+ok(Imager->check_file_limits(width => 100, height => 100, channels => 1),
+   "check 100 x 100 x 1 (def sample_size) ok")
+  or diag(Imager->errstr);
+ok(!Imager->check_file_limits(width => 100, height => 100, channels => 1, sample_size => "float"),
+   "check 100 x 100 x 1 x float should fail");
+ok(!Imager->check_file_limits(width => 100, height => 100, channels => 0),
+   "0 channels should fail");
+is(Imager->errstr, "file size limit - channels 0 out of range",
+   "check error message");
+ok(!Imager->check_file_limits(width => 0, height => 100),
+   "0 width should fail");
+is(Imager->errstr, "file size limit - image width of 0 is not positive",
+   "check error message");
+ok(!Imager->check_file_limits(width => 100, height => 0),
+   "0 height should fail");
+is(Imager->errstr, "file size limit - image height of 0 is not positive",
+   "check error message");
+ok(!Imager->check_file_limits(width => 10, height => 10, sample_size => 0),
+   "0 sample_size should fail");
+is(Imager->errstr, "file size limit - sample_size 0 out of range",
+   "check error message");
+ok(!Imager->check_file_limits(width => 10, height => 10, sample_size => 1000),
+   "1000 sample_size should fail");
+is(Imager->errstr, "file size limit - sample_size 1000 out of range",
+   "check error message");
+ok(Imager->set_file_limits(reset=>1, height => 99),
+   "set height and reset");
+is_deeply([ Imager->get_file_limits() ], [ 0, 99, 0x40000000 ],
+         "check only height is set");
+ok(Imager->set_file_limits(reset=>1),
+   "just reset");
+is_deeply([ Imager->get_file_limits() ], [ 0, 0, 0x40000000 ],
+         "check all are reset");
+
+# bad parameters
+is_deeply([ Imager->check_file_limits() ], [],
+         "missing size paramaters");
+is(Imager->errstr, "check_file_limits: width must be defined",
+   "check message");
+is_deeply([ Imager->check_file_limits(width => 100.5) ], [],
+         "non-integer parameter");
+is(Imager->errstr, "check_file_limits: width must be a positive integer",
+   "check message");
+
+# test error handling for loading file handers
+{
+  # first, no module at all
+  {
+    my $data = "abc";
+    ok(!Imager->new(data => $data, filetype => "unknown"),
+       "try to read an unknown file type");
+   like(Imager->errstr, qr(^format 'unknown' not supported - formats .* - Can't locate Imager/File/UNKNOWN.pm or Imager/File/UNKNOWNReader.pm$),
+       "check error message");
+  }
+  {
+    my $data;
+    my $im = Imager->new(xsize => 10, ysize => 10);
+    ok(!$im->write(data => \$data, type => "unknown"),
+       "try to write an unknown file type");
+   like($im->errstr, qr(^format 'unknown' not supported - formats .* - Can't locate Imager/File/UNKNOWN.pm or Imager/File/UNKNOWNWriter.pm$),
+       "check error message");
+  }
+  push @INC, "t/t1000lib";
+  {
+    my $data = "abc";
+    ok(!Imager->new(data => $data, filetype => "bad"),
+       "try to read an bad (other load failure) file type");
+   like(Imager->errstr, qr(^format 'bad' not supported - formats .* available for reading - This module fails to load loading Imager/File/BAD.pm$),
+       "check error message");
+  }
+  {
+    my $data;
+    my $im = Imager->new(xsize => 10, ysize => 10);
+    ok(!$im->write(data => \$data, type => "bad"),
+       "try to write an bad file type");
+   like($im->errstr, qr(^format 'bad' not supported - formats .* available for writing - This module fails to load loading Imager/File/BAD.pm$),
+       "check error message");
+  }
+}
+
+{ # test empty image handling for write()/write_multi()
+  my $empty = Imager->new;
+  my $data;
+  ok(!$empty->write(data => \$data, type => "pnm"),
+     "fail to write an empty image");
+  is($empty->errstr, "write: empty input image", "check error message");
+  my $good = Imager->new(xsize => 1, ysize => 1);
+  ok(!Imager->write_multi({ data => \$data, type => "pnm" }, $good, $empty),
+     "fail to write_multi an empty image");
+  is(Imager->errstr, "write_multi: empty input image (image 2)");
+}
+
+# check file type probe
+probe_ok("49492A41", undef, "not quite tiff");
+probe_ok("4D4D0041", undef, "not quite tiff");
+probe_ok("49492A00", "tiff", "tiff intel");
+probe_ok("4D4D002A", "tiff", "tiff motorola");
+probe_ok("474946383961", "gif", "gif 89");
+probe_ok("474946383761", "gif", "gif 87");
+probe_ok(<<TGA, "tga", "TGA");
+00 00 0A 00 00 00 00 00 00 00 00 00 96 00 96 00
+18 20 FF 00 00 00 95 00 00 00 FF 00 00 00 95 00
+00 00 FF 00 00 00 95 00 00 00 FF 00 00 00 95 00
+00 00 FF 00 00 00 95 00 00 00 FF 00 00 00 95 00
+TGA
+
+probe_ok(<<TGA, "tga", "TGA 32-bit");
+00 00 0A 00 00 00 00 00 00 00 00 00 0A 00 0A 00
+20 08 84 00 00 00 00 84 FF FF FF FF 84 00 00 00
+00 84 FF FF FF FF 84 00 00 00 00 84 FF FF FF FF
+TGA
+
+probe_ok(<<ICO, "ico", "Windows Icon");
+00 00 01 00 02 00 20 20 10 00 00 00 00 00 E8 02
+00 00 26 00 00 00 20 20 00 00 00 00 00 00 A8 08
+00 00 0E 03 00 00 28 00 00 00 20 00 00 00 40 00
+ICO
+
+probe_ok(<<ICO, "cur", "Windows Cursor");
+00 00 02 00 02 00 20 20 10 00 00 00 00 00 E8 02
+00 00 26 00 00 00 20 20 00 00 00 00 00 00 A8 08
+00 00 0E 03 00 00 28 00 00 00 20 00 00 00 40 00
+ICO
+
+probe_ok(<<SGI, "sgi", "SGI RGB");
+01 DA 01 01 00 03 00 96 00 96 00 03 00 00 00 00 
+00 00 00 FF 00 00 00 00 6E 6F 20 6E 61 6D 65 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+SGI
+
+probe_ok(<<ILBM, "ilbm", "ILBM");
+46 4F 52 4D 00 00 60 7A 49 4C 42 4D 42 4D 48 44
+00 00 00 14 00 96 00 96 00 00 00 00 18 00 01 80
+00 00 0A 0A 00 96 00 96 42 4F 44 59 00 00 60 51
+ILBM
+
+probe_ok(<<XPM, "xpm", "XPM");
+2F 2A 20 58 50 4D 20 2A 2F 0A 73 74 61 74 69 63
+20 63 68 61 72 20 2A 6E 6F 6E 61 6D 65 5B 5D 20
+3D 20 7B 0A 2F 2A 20 77 69 64 74 68 20 68 65 69
+XPM
+
+probe_ok(<<PCX, "pcx", 'PCX');
+0A 05 01 08 00 00 00 00 95 00 95 00 96 00 96 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+PCX
+
+probe_ok(<<FITS, "fits", "FITS");
+53 49 4D 50 4C 45 20 20 3D 20 20 20 20 20 20 20 
+20 20 20 20 20 20 20 20 20 20 20 20 20 54 20 20 
+20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
+FITS
+
+probe_ok(<<PSD, "psd", "Photoshop");
+38 42 50 53 00 01 00 00 00 00 00 00 00 06 00 00
+00 3C 00 00 00 96 00 08 00 03 00 00 00 00 00 00
+0B E6 38 42 49 4D 03 ED 00 00 00 00 00 10 00 90
+PSD
+
+probe_ok(<<EPS, "eps", "Encapsulated Postscript");
+25 21 50 53 2D 41 64 6F 62 65 2D 32 2E 30 20 45
+50 53 46 2D 32 2E 30 0A 25 25 43 72 65 61 74 6F
+72 3A 20 70 6E 6D 74 6F 70 73 0A 25 25 54 69 74
+EPS
+
+probe_ok(<<UTAH, "utah", "Utah RLE");
+52 CC 00 00 00 00 0A 00 0A 00 0A 03 08 00 08 00 
+2F 00 48 49 53 54 4F 52 59 3D 70 6E 6D 74 6F 72 
+6C 65 20 6F 6E 20 54 68 75 20 4D 61 79 20 31 31 
+20 31 36 3A 33 35 3A 34 33 20 32 30 30 36 0A 09 
+UTAH
+
+probe_ok(<<XWD, "xwd", "X Window Dump");
+00 00 00 69 00 00 00 07 00 00 00 02 00 00 00 18
+00 00 01 E4 00 00 01 3C 00 00 00 00 00 00 00 00
+00 00 00 20 00 00 00 00 00 00 00 20 00 00 00 20
+00 00 07 90 00 00 00 04 00 FF 00 00 00 00 FF 00
+XWD
+
+probe_ok(<<GZIP, "gzip", "gzip compressed");
+1F 8B 08 08 C2 81 BD 44 02 03 49 6D 61 67 65 72
+2D 30 2E 35 31 5F 30 33 2E 74 61 72 00 EC 5B 09
+40 53 C7 BA 9E 24 AC 01 D9 44 04 44 08 8B B2 8A
+C9 C9 42 92 56 41 50 20 A0 02 41 41 01 17 48 80
+GZIP
+
+probe_ok(<<BZIP2, "bzip2", "bzip2 compressed");
+42 5A 68 39 31 41 59 26 53 59 0F D8 8C 09 00 03
+28 FF FF FF FF FB 7F FB 77 FF EF BF 6B 7F BE FF
+FF DF EE C8 0F FF F3 FF FF FF FC FF FB B1 FF FB
+F4 07 DF D0 03 B8 03 60 31 82 05 2A 6A 06 83 20
+BZIP2
+
+probe_ok(<<WEBP, "webp", "Google WEBP");
+52 49 46 46 2C 99 00 00 57 45 42 50 56 50 38 20
+20 99 00 00 70 7A 02 9D 01 2A E0 01 80 02 00 87
+08 85 85 88 85 84 88 88 83 AF E2 F7 64 1F 98 55
+1B 6A 70 F5 8A 45 09 95 0C 09 7E 25 D9 2E 46 44
+07 84 FB 01 FD 2C 8A 2F 97 CC ED DB 50 0F 11 3B
+WEBP
+
+probe_ok(<<JPEG2K, "jp2", "JPEG 2000");
+00 00 00 0C 6A 50 20 20 0D 0A 87 0A 00 00 00 14
+66 74 79 70 6A 70 32 20 00 00 00 00 6A 70 32 20
+00 00 00 2D 6A 70 32 68 00 00 00 16 69 68 64 72
+00 00 02 80 00 00 01 E0 00 03 07 07 00 00 00 00
+00 0F 63 6F 6C 72 01 00 00 00 00 00 10 00 00 00
+00 6A 70 32 63 FF 4F FF 51 00 2F 00 00 00 00 01
+JPEG2K
+
+{ # RT 72475
+  # check error messages from read/read_multi
+  my $data = "nothing useful";
+  my @mult_data = Imager->read_multi(data => $data);
+  is(@mult_data, 0, "read_multi with non-image input data should fail");
+  is(Imager->errstr,
+     "type parameter missing and it couldn't be determined from the file contents",
+     "check the error message");
+
+  my @mult_file = Imager->read_multi(file => "t/200-file/100-files.t");
+  is(@mult_file, 0, "read_multi with non-image filename should fail");
+  is(Imager->errstr,
+     "type parameter missing and it couldn't be determined from the file contents or file name",
+     "check the error message");
+
+  my $im = Imager->new;
+  ok(!$im->read(data => $data), "read from non-image data should fail");
+  is($im->errstr,
+     "type parameter missing and it couldn't be determined from the file contents",
+     "check the error message");
+
+  ok(!$im->read(file => "t/200-file/100-files.t"),
+     "read from non-image file should fail");
+  is($im->errstr,
+     "type parameter missing and it couldn't be determined from the file contents or file name",
+     "check the error message");
+}
+
+{
+  # test def_guess_type
+  my @tests =
+    (
+     pnm => "pnm",
+     GIF => "gif",
+     tif => "tiff",
+     TIFF => "tiff",
+     JPG => "jpeg",
+     rle => "utah",
+     bmp => "bmp",
+     dib => "bmp",
+     rgb => "sgi",
+     BW => "sgi",
+     TGA => "tga",
+     CUR => "cur",
+     ico => "ico",
+     ILBM => "ilbm",
+     pcx => "pcx",
+     psd => "psd",
+    );
+
+  while (my ($ext, $expect) = splice(@tests, 0, 2)) {
+    my $filename = "foo.$ext";
+    is(Imager::def_guess_type($filename), $expect,
+       "type for $filename should be $expect");
+  }
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t1000files.log";
+}
+
+sub probe_ok {
+  my ($packed, $exp_type, $name) = @_;
+
+  my $builder = Test::Builder->new;
+  $packed =~ tr/ \r\n//d; # remove whitespace used for layout
+  my $data = pack("H*", $packed);
+
+  my $io = Imager::io_new_buffer($data);
+  my $result = Imager::i_test_format_probe($io, -1);
+
+  return $builder->is_eq($result, $exp_type, $name)
+}
diff --git a/t/200-file/200-nojpeg.t b/t/200-file/200-nojpeg.t
new file mode 100644 (file)
index 0000000..582d5bf
--- /dev/null
@@ -0,0 +1,28 @@
+#!perl -w
+use strict;
+use Test::More;
+use Imager qw(:all);
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t101jpeg.log");
+
+$Imager::formats{"jpeg"}
+  and plan skip_all => "have jpeg support - this tests the lack of it";
+
+plan tests => 6;
+
+my $im = Imager->new;
+ok(!$im->read(file=>"testimg/base.jpg"), "should fail to read jpeg");
+cmp_ok($im->errstr, '=~', qr/format 'jpeg' not supported/, "check no jpeg message");
+$im = Imager->new(xsize=>2, ysize=>2);
+ok(!$im->write(file=>"testout/nojpeg.jpg"), "should fail to write jpeg");
+cmp_ok($im->errstr, '=~', qr/format 'jpeg' not supported/, "check no jpeg message");
+ok(!grep($_ eq 'jpeg', Imager->read_types), "check jpeg not in read types");
+ok(!grep($_ eq 'jpeg', Imager->write_types), "check jpeg not in write types");
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t101jpeg.log";
+}
diff --git a/t/200-file/210-nopng.t b/t/200-file/210-nopng.t
new file mode 100644 (file)
index 0000000..3509582
--- /dev/null
@@ -0,0 +1,19 @@
+#!perl -w
+use strict;
+use Imager qw(:all);
+use Test::More;
+
+$Imager::formats{"png"}
+  and plan skip_all => "png available, and this tests the lack of it";
+
+plan tests => 6;
+
+my $im = Imager->new;
+ok(!$im->read(file=>"testimg/test.png"), "should fail to read png");
+cmp_ok($im->errstr, '=~', "format 'png' not supported", "check no png message");
+$im = Imager->new(xsize=>2, ysize=>2);
+ok(!$im->write(file=>"testout/nopng.png"), "should fail to write png");
+cmp_ok($im->errstr, '=~', "format 'png' not supported", "check no png message");
+ok(!grep($_ eq 'png', Imager->read_types), "check png not in read types");
+ok(!grep($_ eq 'png', Imager->write_types), "check png not in write types");
+
diff --git a/t/200-file/220-nogif.t b/t/200-file/220-nogif.t
new file mode 100644 (file)
index 0000000..bf053e8
--- /dev/null
@@ -0,0 +1,35 @@
+#!perl -w
+use strict;
+$|=1;
+use Test::More;
+use Imager qw(:all);
+
+$Imager::formats{"gif"}
+  and plan skip_all => "gif support available and this tests the lack of it";
+
+plan tests => 12;
+
+my $im = Imager->new;
+ok(!$im->read(file=>"GIF/testimg/scale.gif"), "should fail to read gif");
+cmp_ok($im->errstr, '=~', "format 'gif' not supported",
+       "check no gif message");
+ok(!Imager->read_multi(file=>"GIF/testimg/scale.gif"), 
+   "should fail to read multi gif");
+cmp_ok($im->errstr, '=~', "format 'gif' not supported",
+       "check no gif message");
+
+$im = Imager->new(xsize=>2, ysize=>2);
+
+ok(!$im->write(file=>"testout/nogif.gif"), "should fail to write gif");
+ok(!-e "testout/nogif.gif", "shouldn't create the file");
+cmp_ok($im->errstr, '=~', "format 'gif' not supported",
+       "check no gif message");
+
+ok(!Imager->write_multi({file => "testout/nogif.gif"}, $im, $im),
+   "should fail to write multi gif");
+ok(!-e "testout/nogif.gif", "shouldn't create the file");
+cmp_ok($im->errstr, '=~', "format 'gif' not supported",
+       "check no gif message");
+
+ok(!grep($_ eq 'gif', Imager->read_types), "check gif not in read types");
+ok(!grep($_ eq 'gif', Imager->write_types), "check gif not in write types");
diff --git a/t/200-file/230-notiff.t b/t/200-file/230-notiff.t
new file mode 100644 (file)
index 0000000..0854640
--- /dev/null
@@ -0,0 +1,36 @@
+#!perl -w
+use strict;
+use Test::More;
+use Imager qw(:all);
+
+$Imager::formats{"tiff"}
+  and plan skip_all => "tiff support available - this tests the lack of it";
+
+plan tests => 12;
+
+my $im = Imager->new;
+
+ok(!$im->read(file=>"TIFF/testimg/comp4.tif"), "should fail to read tif");
+cmp_ok($im->errstr, '=~', "format 'tiff' not supported",
+       "check no tiff message");
+
+ok(!$im->read_multi(file => "TIFF/testimg/comp4.tif"),
+   "should fail to read multi tiff");
+cmp_ok($im->errstr, '=~', "format 'tiff' not supported",
+       "check no tiff message");
+
+$im = Imager->new(xsize=>2, ysize=>2);
+
+ok(!$im->write(file=>"testout/notiff.tif"), "should fail to write tiff");
+cmp_ok($im->errstr, '=~', "format 'tiff' not supported",
+       "check no tiff message");
+ok(!-e "testout/notiff.tif", "file shouldn't be created");
+
+ok(!Imager->write_multi({file=>"testout/notiff.tif"}, $im, $im),
+   "should fail to write multi tiff");
+cmp_ok($im->errstr, '=~', "format 'tiff' not supported",
+       "check no tiff message");
+ok(!-e "testout/notiff.tif", "file shouldn't be created");
+
+ok(!grep($_ eq 'tiff', Imager->read_types), "check tiff not in read types");
+ok(!grep($_ eq 'tiff', Imager->write_types), "check tiff not in write types");
diff --git a/t/200-file/300-raw.t b/t/200-file/300-raw.t
new file mode 100644 (file)
index 0000000..52ab850
--- /dev/null
@@ -0,0 +1,407 @@
+#!perl -w
+use strict;
+use Test::More tests => 53;
+use Imager qw(:all);
+use Imager::Test qw/is_color3 is_color4 test_image test_image_mono/;
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t103raw.log");
+
+$| = 1;
+
+my $green=i_color_new(0,255,0,255);
+my $blue=i_color_new(0,0,255,255);
+my $red=i_color_new(255,0,0,255);
+
+my $img=Imager::ImgRaw::new(150,150,3);
+my $cmpimg=Imager::ImgRaw::new(150,150,3);
+
+i_box_filled($img,70,25,130,125,$green);
+i_box_filled($img,20,25,80,125,$blue);
+i_arc($img,75,75,30,0,361,$red);
+i_conv($img,[0.1, 0.2, 0.4, 0.2, 0.1]);
+
+my $timg = Imager::ImgRaw::new(20, 20, 4);
+my $trans = i_color_new(255, 0, 0, 127);
+i_box_filled($timg, 0, 0, 20, 20, $green);
+i_box_filled($timg, 2, 2, 18, 18, $trans);
+
+open(FH,">testout/t103.raw") || die "Cannot open testout/t103.raw for writing\n";
+binmode(FH);
+my $IO = Imager::io_new_fd( fileno(FH) );
+ok(i_writeraw_wiol($img, $IO), "write raw low") or
+  print "# Cannot write testout/t103.raw\n";
+close(FH);
+
+open(FH,"testout/t103.raw") || die "Cannot open testout/t103.raw\n";
+binmode(FH);
+$IO = Imager::io_new_fd( fileno(FH) );
+$cmpimg = i_readraw_wiol($IO, 150, 150, 3, 3, 0);
+ok($cmpimg, "read raw low")
+  or print "# Cannot read testout/t103.raw\n";
+close(FH);
+
+print "# raw average mean square pixel difference: ",sqrt(i_img_diff($img,$cmpimg))/150*150,"\n";
+
+# I could have kept the raw images for these tests in binary files in
+# testimg/, but I think keeping them as hex encoded data in here makes
+# it simpler to add more if necessary
+# Later we may change this to read from a scalar instead
+save_data('testout/t103_base.raw');
+save_data('testout/t103_3to4.raw');
+save_data('testout/t103_line_int.raw');
+save_data('testout/t103_img_int.raw');
+
+# load the base image
+open FH, "testout/t103_base.raw" 
+  or die "Cannot open testout/t103_base.raw: $!";
+binmode FH;
+$IO = Imager::io_new_fd( fileno(FH) );
+
+my $baseimg = i_readraw_wiol( $IO, 4, 4, 3, 3, 0);
+ok($baseimg, "read base raw image")
+  or die "Cannot read base raw image";
+close FH;
+
+# the actual read tests
+# each read_test() call does 2 tests:
+#  - check if the read succeeds
+#  - check if it matches $baseimg
+read_test('testout/t103_3to4.raw', 4, 4, 4, 3, 0, $baseimg);
+read_test('testout/t103_line_int.raw', 4, 4, 3, 3, 1, $baseimg);
+# intrl==2 is documented in raw.c but doesn't seem to be implemented
+#read_test('testout/t103_img_int.raw', 4, 4, 3, 3, 2, $baseimg, 7);
+
+# paletted images
+SKIP:
+{
+  my $palim = Imager::i_img_pal_new(20, 20, 3, 256);
+  ok($palim, "make paletted image")
+    or skip("couldn't make paletted image", 2);
+  my $redindex = Imager::i_addcolors($palim, $red);
+  my $blueindex = Imager::i_addcolors($palim, $blue);
+  for my $y (0..9) {
+    Imager::i_ppal($palim, 0, $y, ($redindex) x 20);
+  }
+  for my $y (10..19) {
+    Imager::i_ppal($palim, 0, $y, ($blueindex) x 20);
+  }
+  open FH, "> testout/t103_pal.raw"
+    or die "Cannot create testout/t103_pal.raw: $!";
+  binmode FH;
+  $IO = Imager::io_new_fd(fileno(FH));
+  ok(i_writeraw_wiol($palim, $IO), "write low paletted");
+  close FH;
+  
+  open FH, "testout/t103_pal.raw"
+    or die "Cannot open testout/t103_pal.raw: $!";
+  binmode FH;
+  my $data = do { local $/; <FH> };
+  is($data, "\x0" x 200 . "\x1" x 200, "compare paletted data written");
+  close FH;
+}
+
+# 16-bit image
+# we don't have 16-bit reads yet
+SKIP:
+{
+  my $img16 = Imager::i_img_16_new(150, 150, 3);
+  ok($img16, "make 16-bit/sample image")
+    or skip("couldn't make 16 bit/sample image", 1);
+  i_box_filled($img16,70,25,130,125,$green);
+  i_box_filled($img16,20,25,80,125,$blue);
+  i_arc($img16,75,75,30,0,361,$red);
+  i_conv($img16,[0.1, 0.2, 0.4, 0.2, 0.1]);
+  
+  open FH, "> testout/t103_16.raw" 
+    or die "Cannot create testout/t103_16.raw: $!";
+  binmode FH;
+  $IO = Imager::io_new_fd(fileno(FH));
+  ok(i_writeraw_wiol($img16, $IO), "write low 16 bit image");
+  close FH;
+}
+
+# try a simple virtual image
+SKIP:
+{
+  my $maskimg = Imager::i_img_masked_new($img, undef, 0, 0, 150, 150);
+  ok($maskimg, "make masked image")
+    or skip("couldn't make masked image", 3);
+
+  open FH, "> testout/t103_virt.raw" 
+    or die "Cannot create testout/t103_virt.raw: $!";
+  binmode FH;
+  $IO = Imager::io_new_fd(fileno(FH));
+  ok(i_writeraw_wiol($maskimg, $IO), "write virtual raw");
+  close FH;
+
+  open FH, "testout/t103_virt.raw"
+    or die "Cannot open testout/t103_virt.raw: $!";
+  binmode FH;
+  $IO = Imager::io_new_fd(fileno(FH));
+  my $cmpimgmask = i_readraw_wiol($IO, 150, 150, 3, 3, 0);
+  ok($cmpimgmask, "read result of masked write");
+  my $diff = i_img_diff($maskimg, $cmpimgmask);
+  print "# difference for virtual image $diff\n";
+  is($diff, 0, "compare masked to read");
+
+  # check that i_format is set correctly
+  my $index = Imager::i_tags_find($cmpimgmask, 'i_format', 0);
+  if ($index) {
+    my $value = Imager::i_tags_get($cmpimgmask, $index);
+    is($value, 'raw', "check i_format value");
+  }
+  else {
+    fail("couldn't find i_format tag");
+  }
+}
+
+{ # error handling checks
+  # should get an error writing to a open for read file
+  # make a empty file
+  open RAW, "> testout/t103_empty.raw"
+    or die "Cannot create testout/t103_empty.raw: $!";
+  close RAW;
+  open RAW, "< testout/t103_empty.raw"
+    or die "Cannot open testout/t103_empty.raw: $!";
+  my $im = Imager->new(xsize => 50, ysize=>50);
+  ok(!$im->write(fh => \*RAW, type => 'raw', buffered => 0),
+     "write to open for read handle");
+  cmp_ok($im->errstr, '=~', '^Could not write to file: write\(\) failure', 
+        "check error message");
+  close RAW;
+
+  # should get an error reading an empty file
+  ok(!$im->read(file => 'testout/t103_empty.raw', xsize => 50, ysize=>50, type=>'raw', interleave => 1),
+     'read an empty file');
+  is($im->errstr, 'premature end of file', "check message");
+ SKIP:
+  {
+    # see 862083f7e40bc2a9e3b94aedce56c1336e7bdb25 in perl5 git
+    $] >= 5.010
+      or skip "5.8.x and earlier don't treat a read on a WRONLY file as an error", 2;
+    open RAW, "> testout/t103_empty.raw"
+      or die "Cannot create testout/t103_empty.raw: $!";
+    ok(!$im->read(fh => \*RAW, , xsize => 50, ysize=>50, type=>'raw', interleave => 1),
+       'read a file open for write');
+    cmp_ok($im->errstr, '=~', '^error reading file: read\(\) failure', "check message");
+  }
+}
+
+
+{
+  ok(grep($_ eq 'raw', Imager->read_types), "check raw in read types");
+  ok(grep($_ eq 'raw', Imager->write_types), "check raw in write types");
+}
+
+
+{ # OO no interleave warning
+  my $im = Imager->new;
+  my $msg;
+  local $SIG{__WARN__} = sub { $msg = "@_" };
+  ok($im->read(file => "testout/t103_line_int.raw", xsize => 4, ysize => 4,
+              type => "raw"),
+     "read without interleave parameter")
+    or print "# ", $im->errstr, "\n";
+  ok($msg, "should have warned");
+  like($msg, qr/interleave/, "check warning is ok");
+  # check we got the right value
+  is_color3($im->getpixel(x => 0, y => 0), 0x00, 0x11, 0x22,
+           "check the image was read correctly");
+
+  # check no warning if either is supplied
+  $im = Imager->new;
+  undef $msg;
+  ok($im->read(file => "testout/t103_base.raw", xsize => 4, ysize => 4, type => "raw", interleave => 0), 
+     "read with interleave 0");
+  is($msg, undef, "no warning");
+  is_color3($im->getpixel(x => 0, y => 0), 0x00, 0x11, 0x22,
+           "check read non-interleave");
+
+  $im = Imager->new;
+  undef $msg;
+  ok($im->read(file => "testout/t103_base.raw", xsize => 4, ysize => 4, type => "raw", raw_interleave => 0), 
+     "read with raw_interleave 0");
+  is($msg, undef, "no warning");
+  is_color3($im->getpixel(x => 1, y => 0), 0x01, 0x12, 0x23,
+           "check read non-interleave");
+
+  # make sure set to 1 is sane
+  $im = Imager->new;
+  undef $msg;
+  ok($im->read(file => "testout/t103_line_int.raw", xsize => 4, ysize => 4, type => "raw", raw_interleave => 1), 
+     "read with raw_interleave 1");
+  is($msg, undef, "no warning");
+  is_color3($im->getpixel(x => 2, y => 0), 0x02, 0x13, 0x24,
+           "check read interleave = 1");
+}
+
+{ # invalid interleave error handling
+  my $im = Imager->new;
+  ok(!$im->read(file => "testout/t103_base.raw", raw_interleave => 2, type => "raw", xsize => 4, ysize => 4),
+     "invalid interleave");
+  is($im->errstr, "raw_interleave must be 0 or 1", "check message");
+}
+
+{ # store/data channel behaviour
+  my $im = Imager->new;
+  ok($im->read(file => "testout/t103_3to4.raw", xsize => 4, ysize => 4, 
+              raw_datachannels => 4, raw_interleave => 0, type => "raw"),
+     "read 4 channel file as 3 channels")
+    or print "# ", $im->errstr, "\n";
+  is_color3($im->getpixel(x => 2, y => 1), 0x12, 0x23, 0x34,
+           "check read correctly");
+}
+
+{ # should fail to read with storechannels > 4
+  my $im = Imager->new;
+  ok(!$im->read(file => "testout/t103_line_int.raw", type => "raw",
+               raw_interleave => 1, xsize => 4, ysize => 4,
+               raw_storechannels => 5),
+     "read with large storechannels");
+  is($im->errstr, "raw_storechannels must be between 1 and 4", 
+     "check error message");
+}
+
+{ # should zero spare channels if storechannels > datachannels
+  my $im = Imager->new;
+  ok($im->read(file => "testout/t103_base.raw", type => "raw",
+               raw_interleave => 0, xsize => 4, ysize => 4,
+               raw_storechannels => 4),
+     "read with storechannels > datachannels");
+  is($im->getchannels, 4, "should have 4 channels");
+  is_color4($im->getpixel(x => 2, y => 1), 0x12, 0x23, 0x34, 0x00,
+           "check last channel zeroed");
+}
+
+{
+  my @ims = ( basic => test_image(), mono => test_image_mono() );
+  push @ims, masked => test_image()->masked();
+
+  my $fail_close = sub {
+    Imager::i_push_error(0, "synthetic close failure");
+    return 0;
+  };
+
+  while (my ($type, $im) = splice(@ims, 0, 2)) {
+    my $io = Imager::io_new_cb(sub { 1 }, undef, undef, $fail_close);
+    ok(!$im->write(io => $io, type => "raw"),
+       "write $type image with a failing close handler");
+    like($im->errstr, qr/synthetic close failure/,
+        "check error message");
+  }
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t103raw.log";
+  unlink(qw(testout/t103_base.raw testout/t103_3to4.raw
+           testout/t103_line_int.raw testout/t103_img_int.raw))
+}
+
+sub read_test {
+  my ($in, $xsize, $ysize, $data, $store, $intrl, $base) = @_;
+  open FH, $in or die "Cannot open $in: $!";
+  binmode FH;
+  my $IO = Imager::io_new_fd( fileno(FH) );
+
+  my $img = i_readraw_wiol($IO, $xsize, $ysize, $data, $store, $intrl);
+ SKIP:
+  {
+    ok($img, "read_test $in read")
+      or skip("couldn't read $in", 1);
+    is(i_img_diff($img, $baseimg), 0, "read_test $in compare");
+  }
+}
+
+sub save_data {
+  my $outname = shift;
+  my $data = load_data();
+  open FH, "> $outname" or die "Cannot create $outname: $!";
+  binmode FH;
+  print FH $data;
+  close FH;
+}
+
+sub load_data {
+  my $hex = '';
+  while (<DATA>) {
+    next if /^#/;
+    last if /^EOF/;
+    chomp;
+    $hex .= $_;
+  }
+  $hex =~ tr/ //d;
+  my $result = pack("H*", $hex);
+  #print unpack("H*", $result),"\n";
+  return $result;
+}
+
+# FIXME: may need tests for 1,2,4 channel images
+
+__DATA__
+# we keep some packed raw images here
+# we decode this in the code, ignoring lines starting with #, a subfile
+# ends with EOF, data is HEX encoded (spaces ignored)
+
+# basic 3 channel version of the image
+001122 011223 021324 031425
+102132 112233 122334 132435
+203142 213243 223344 233445
+304152 314253 324354 334455
+EOF
+
+# test image for reading a 4 channel image into a 3 channel image
+# 4 x 4 pixels
+00112233 01122334 02132435 03142536
+10213243 11223344 12233445 13243546
+20314253 21324354 22334455 23344556
+30415263 31425364 32435465 33445566
+EOF
+
+# test image for line based interlacing
+# 4 x 4 pixels
+# first line
+00 01 02 03
+11 12 13 14
+22 23 24 25
+
+# second line
+10 11 12 13
+21 22 23 24
+32 33 34 35
+
+# third line
+20 21 22 23
+31 32 33 34
+42 43 44 45
+
+# fourth line
+30 31 32 33
+41 42 43 44
+52 53 54 55
+
+EOF
+
+# test image for image based interlacing
+# first channel
+00 01 02 03
+10 11 12 13
+20 21 22 23
+30 31 32 33
+
+# second channel
+11 12 13 14
+21 22 23 24
+31 32 33 34
+41 42 43 44
+
+# third channel
+22 23 24 25
+32 33 34 35
+42 43 44 45
+52 53 54 55
+
+EOF
diff --git a/t/200-file/310-pnm.t b/t/200-file/310-pnm.t
new file mode 100644 (file)
index 0000000..d299b3a
--- /dev/null
@@ -0,0 +1,661 @@
+#!perl -w
+use Imager ':all';
+use Test::More tests => 205;
+use strict;
+use Imager::Test qw(test_image_raw test_image_16 is_color3 is_color1 is_image test_image_named);
+
+$| = 1;
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t104ppm.log");
+
+my $green = i_color_new(0,255,0,255);
+my $blue  = i_color_new(0,0,255,255);
+my $red   = i_color_new(255,0,0,255);
+
+my @files;
+
+my $img    = test_image_raw();
+
+my $fh = openimage(">testout/t104.ppm");
+push @files, "t104.ppm";
+my $IO = Imager::io_new_fd(fileno($fh));
+ok(i_writeppm_wiol($img, $IO), "write pnm low")
+  or die "Cannot write testout/t104.ppm\n";
+close($fh);
+
+$IO = Imager::io_new_bufchain();
+ok(i_writeppm_wiol($img, $IO), "write to bufchain")
+  or die "Cannot write to bufchain";
+my $data = Imager::io_slurp($IO);
+
+$fh = openimage("testout/t104.ppm");
+$IO = Imager::io_new_fd( fileno($fh) );
+my $cmpimg = i_readpnm_wiol($IO,-1);
+ok($cmpimg, "read image we wrote")
+  or die "Cannot read testout/t104.ppm\n";
+close($fh);
+
+is(i_img_diff($img, $cmpimg), 0, "compare written and read images");
+
+my $rdata = slurp("testout/t104.ppm");
+is($data, $rdata, "check data read from file and bufchain data");
+
+# build a grayscale image
+my $gimg = Imager::ImgRaw::new(150, 150, 1);
+my $gray = i_color_new(128, 0, 0, 255);
+my $dgray = i_color_new(64, 0, 0, 255);
+my $white = i_color_new(255, 0, 0, 255);
+i_box_filled($gimg, 20, 20, 130, 130, $gray);
+i_box_filled($gimg, 40, 40, 110, 110, $dgray);
+i_arc($gimg, 75, 75, 30, 0, 361, $white);
+
+push @files, "t104_gray.pgm";
+open FH, "> testout/t104_gray.pgm" or die "Cannot create testout/t104_gray.pgm: $!\n";
+binmode FH;
+$IO = Imager::io_new_fd(fileno(FH));
+ok(i_writeppm_wiol($gimg, $IO), "write grayscale");
+close FH;
+
+open FH, "< testout/t104_gray.pgm" or die "Cannot open testout/t104_gray.pgm: $!\n";
+binmode FH;
+$IO = Imager::io_new_fd(fileno(FH));
+my $gcmpimg = i_readpnm_wiol($IO, -1);
+ok($gcmpimg, "read grayscale");
+is(i_img_diff($gimg, $gcmpimg), 0, 
+   "compare written and read greyscale images");
+
+my $ooim = Imager->new;
+ok($ooim->read(file=>"testimg/simple.pbm"), "read simple pbm, via OO")
+  or print "# ", $ooim->errstr, "\n";
+
+check_gray(Imager::i_get_pixel($ooim->{IMG}, 0, 0), 0);
+check_gray(Imager::i_get_pixel($ooim->{IMG}, 0, 1), 255);
+check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 0), 255);
+check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 1), 0);
+is($ooim->type, 'paletted', "check pbm read as paletted");
+is($ooim->tags(name=>'pnm_type'), 1, "check pnm_type tag");
+
+{
+  # https://rt.cpan.org/Ticket/Display.html?id=7465
+  # the pnm reader ignores the maxval that it reads from the pnm file
+  my $maxval = Imager->new;
+  ok($maxval->read(file=>"testimg/maxval.ppm"),
+     "read testimg/maxval.ppm");
+  
+  # this image contains three pixels, with each sample from 0 to 63
+  # the pixels are (63, 63, 63), (32, 32, 32) and (31, 31, 0)
+  
+  # check basic parameters
+  is($maxval->getchannels, 3, "channel count");
+  is($maxval->getwidth, 3, "width");
+  is($maxval->getheight, 1, "height");
+  
+  # check the pixels
+  ok(my ($white, $grey, $green) = $maxval->getpixel('x'=>[0,1,2], 'y'=>[0,0,0]), "fetch pixels");
+  is_color3($white, 255, 255, 255, "white pixel");
+  is_color3($grey,  130, 130, 130, "grey  pixel");
+  is_color3($green, 125, 125, 0,   "green pixel");
+  is($maxval->tags(name=>'pnm_type'), 6, "check pnm_type tag on maxval");
+
+  # and do the same for ASCII images
+  my $maxval_asc = Imager->new;
+  ok($maxval_asc->read(file=>"testimg/maxval_asc.ppm"),
+     "read testimg/maxval_asc.ppm");
+  
+  # this image contains three pixels, with each sample from 0 to 63
+  # the pixels are (63, 63, 63), (32, 32, 32) and (31, 31, 0)
+  
+  # check basic parameters
+  is($maxval_asc->getchannels, 3, "channel count");
+  is($maxval_asc->getwidth, 3, "width");
+  is($maxval_asc->getheight, 1, "height");
+
+  is($maxval->tags(name=>'pnm_type'), 6, "check pnm_type tag on maxval");
+  
+  # check the pixels
+  ok(my ($white_asc, $grey_asc, $green_asc) = $maxval_asc->getpixel('x'=>[0,1,2], 'y'=>[0,0,0]), "fetch pixels");
+  is_color3($white_asc, 255, 255, 255, "white asc pixel");
+  is_color3($grey_asc,  130, 130, 130, "grey  asc pixel");
+  is_color3($green_asc, 125, 125, 0,   "green asc pixel");
+}
+
+{ # previously we didn't validate maxval at all, make sure it's
+  # validated now
+  my $maxval0 = Imager->new;
+  ok(!$maxval0->read(file=>'testimg/maxval_0.ppm'),
+     "should fail to read maxval 0 image");
+  print "# ", $maxval0->errstr, "\n";
+  like($maxval0->errstr, qr/maxval is zero - invalid pnm file/,
+       "error expected from reading maxval_0.ppm");
+
+  my $maxval65536 = Imager->new;
+  ok(!$maxval65536->read(file=>'testimg/maxval_65536.ppm'),
+     "should fail reading maxval 65536 image");
+  print "# ",$maxval65536->errstr, "\n";
+  like($maxval65536->errstr, qr/maxval of 65536 is over 65535 - invalid pnm file/,
+       "error expected from reading maxval_65536.ppm");
+
+  # maxval of 256 is valid, and handled as of 0.56
+  my $maxval256 = Imager->new;
+  ok($maxval256->read(file=>'testimg/maxval_256.ppm'),
+     "should succeed reading maxval 256 image");
+  is_color3($maxval256->getpixel(x => 0, 'y' => 0),
+            0, 0, 0, "check black in maxval_256");
+  is_color3($maxval256->getpixel(x => 0, 'y' => 1),
+            255, 255, 255, "check white in maxval_256");
+  is($maxval256->bits, 16, "check bits/sample on maxval 256");
+
+  # make sure we handle maxval > 255 for ascii
+  my $maxval4095asc = Imager->new;
+  ok($maxval4095asc->read(file=>'testimg/maxval_4095_asc.ppm'),
+     "read maxval_4095_asc.ppm");
+  is($maxval4095asc->getchannels, 3, "channels");
+  is($maxval4095asc->getwidth, 3, "width");
+  is($maxval4095asc->getheight, 1, "height");
+  is($maxval4095asc->bits, 16, "check bits/sample on maxval 4095");
+
+  ok(my ($white, $grey, $green) = $maxval4095asc->getpixel('x'=>[0,1,2], 'y'=>[0,0,0]), "fetch pixels");
+  is_color3($white, 255, 255, 255, "white 4095 pixel");
+  is_color3($grey,  128, 128, 128, "grey  4095 pixel");
+  is_color3($green, 127, 127, 0,   "green 4095 pixel");
+}
+
+{ # check i_format is set when reading a pnm file
+  # doesn't really matter which file.
+  my $maxval = Imager->new;
+  ok($maxval->read(file=>"testimg/maxval.ppm"),
+      "read test file");
+  my ($type) = $maxval->tags(name=>'i_format');
+  is($type, 'pnm', "check i_format");
+}
+
+{ # check file limits are checked
+  my $limit_file = "testout/t104.ppm";
+  ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
+  my $im = Imager->new;
+  ok(!$im->read(file=>$limit_file),
+     "should fail read due to size limits");
+  print "# ",$im->errstr,"\n";
+  like($im->errstr, qr/image width/, "check message");
+
+  ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
+  ok(!$im->read(file=>$limit_file),
+     "should fail read due to size limits");
+  print "# ",$im->errstr,"\n";
+  like($im->errstr, qr/image height/, "check message");
+
+  ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
+  ok($im->read(file=>$limit_file),
+     "should succeed - just inside width limit");
+  ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
+  ok($im->read(file=>$limit_file),
+     "should succeed - just inside height limit");
+  
+  # 150 x 150 x 3 channel image uses 67500 bytes
+  ok(Imager->set_file_limits(reset=>1, bytes=>67499),
+     "set bytes limit 67499");
+  ok(!$im->read(file=>$limit_file),
+     "should fail - too many bytes");
+  print "# ",$im->errstr,"\n";
+  like($im->errstr, qr/storage size/, "check error message");
+  ok(Imager->set_file_limits(reset=>1, bytes=>67500),
+     "set bytes limit 67500");
+  ok($im->read(file=>$limit_file),
+     "should succeed - just inside bytes limit");
+  Imager->set_file_limits(reset=>1);
+}
+
+{
+  # check we correctly sync with the data stream
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/pgm.pgm', type => 'pnm'),
+     "read pgm.pgm")
+    or print "# cannot read pgm.pgm: ", $im->errstr, "\n";
+  print "# ", $im->getsamples('y' => 0), "\n";
+  is_color1($im->getpixel(x=>0, 'y' => 0), 254, "check top left");
+}
+
+{ # check error messages set correctly
+  my $im = Imager->new;
+  ok(!$im->read(file=>'t/200-file/310-pnm.t', type=>'pnm'),
+     'should fail to read script as an image file');
+  is($im->errstr, 'unable to read pnm image: bad header magic, not a PNM file',
+     "check error message");
+}
+
+{
+  # RT #30074
+  # give 4/2 channel images a background color when saving to pnm
+  my $im = Imager->new(xsize=>16, ysize=>16, channels=>4);
+  $im->box(filled => 1, xmin => 8, color => '#FFE0C0');
+  $im->box(filled => 1, color => NC(0, 192, 192, 128),
+          ymin => 8, xmax => 7);
+  push @files, "t104_alpha.ppm";
+  ok($im->write(file=>"testout/t104_alpha.ppm", type=>'pnm'),
+     "should succeed writing 4 channel image");
+  my $imread = Imager->new;
+  ok($imread->read(file => 'testout/t104_alpha.ppm'), "read it back")
+    or print "# ", $imread->errstr, "\n";
+  is_color3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, 
+           "check transparent became black");
+  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
+           "check color came through");
+  is_color3($imread->getpixel('x' => 0, 'y' => 15), 0, 96, 96,
+           "check translucent came through");
+  my $data;
+  ok($im->write(data => \$data, type => 'pnm', i_background => '#FF0000'),
+     "write with red background");
+  ok($imread->read(data => $data, type => 'pnm'),
+     "read it back");
+  is_color3($imread->getpixel('x' => 0, 'y' => 0), 255, 0, 0, 
+           "check transparent became red");
+  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
+           "check color came through");
+  is_color3($imread->getpixel('x' => 0, 'y' => 15), 127, 96, 96,
+           "check translucent came through");
+}
+
+{
+  # more RT #30074 - 16 bit images
+  my $im = Imager->new(xsize=>16, ysize=>16, channels=>4, bits => 16);
+  $im->box(filled => 1, xmin => 8, color => '#FFE0C0');
+  $im->box(filled => 1, color => NC(0, 192, 192, 128),
+          ymin => 8, xmax => 7);
+  push @files, "t104_alp16.ppm";
+  ok($im->write(file=>"testout/t104_alp16.ppm", type=>'pnm', 
+               pnm_write_wide_data => 1),
+     "should succeed writing 4 channel image");
+  my $imread = Imager->new;
+  ok($imread->read(file => 'testout/t104_alp16.ppm'), "read it back");
+  is($imread->bits, 16, "check we did produce a 16 bit image");
+  is_color3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, 
+           "check transparent became black");
+  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
+           "check color came through");
+  is_color3($imread->getpixel('x' => 0, 'y' => 15), 0, 96, 96,
+           "check translucent came through");
+  my $data;
+  ok($im->write(data => \$data, type => 'pnm', i_background => '#FF0000',
+               pnm_write_wide_data => 1),
+     "write with red background");
+  ok($imread->read(data => $data, type => 'pnm'),
+     "read it back");
+  is($imread->bits, 16, "check it's 16-bit");
+  is_color3($imread->getpixel('x' => 0, 'y' => 0), 255, 0, 0, 
+           "check transparent became red");
+  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
+           "check color came through");
+  is_color3($imread->getpixel('x' => 0, 'y' => 15), 127, 96, 96,
+           "check translucent came through");
+}
+
+# various bad input files
+print "# check error handling\n";
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/short_bin.ppm', type=>'pnm'),
+     "fail to read short bin ppm");
+  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/short_bin16.ppm', type=>'pnm'),
+     "fail to read short bin ppm (maxval 65535)");
+  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/short_bin.pgm', type=>'pnm'),
+     "fail to read short bin pgm");
+  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/short_bin16.pgm', type=>'pnm'),
+     "fail to read short bin pgm (maxval 65535)");
+  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/short_bin.pbm', type => 'pnm'),
+     "fail to read a short bin pbm");
+  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/short_asc.ppm', type => 'pnm'),
+     "fail to read a short asc ppm");
+  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/short_asc.pgm', type => 'pnm'),
+     "fail to read a short asc pgm");
+  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/short_asc.pbm', type => 'pnm'),
+     "fail to read a short asc pbm");
+  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/bad_asc.ppm', type => 'pnm'),
+     "fail to read a bad asc ppm");
+  cmp_ok($im->errstr, '=~', 'invalid data for ascii pnm', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/bad_asc.pgm', type => 'pnm'),
+     "fail to read a bad asc pgm");
+  cmp_ok($im->errstr, '=~', 'invalid data for ascii pnm', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok(!$im->read(file => 'testimg/bad_asc.pbm', type => 'pnm'),
+     "fail to read a bad asc pbm");
+  cmp_ok($im->errstr, '=~', 'invalid data for ascii pnm', 
+         "check error message");
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/short_bin.ppm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read bin ppm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/short_bin16.ppm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read bin16 ppm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+  is($im->bits, 16, "check correct bits");
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/short_bin.pgm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read bin pgm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/short_bin16.pgm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read bin16 pgm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/short_bin.pbm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read bin pbm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/short_asc.ppm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read asc ppm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/short_asc.pgm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read asc pgm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/short_asc.pbm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read asc pbm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+  my @imgs = Imager->read_multi(file => 'testimg/multiple.ppm');
+  is( 0+@imgs, 3, "Read 3 images");
+  is( $imgs[0]->tags( name => 'pnm_type' ), 1, "Image 1 is type 1" );
+  is( $imgs[0]->getwidth, 2, " ... width=2" );
+  is( $imgs[0]->getheight, 2, " ... width=2" );
+  is( $imgs[1]->tags( name => 'pnm_type' ), 6, "Image 2 is type 6" );
+  is( $imgs[1]->getwidth, 164, " ... width=164" );
+  is( $imgs[1]->getheight, 180, " ... width=180" );
+  is( $imgs[2]->tags( name => 'pnm_type' ), 5, "Image 3 is type 5" );
+  is( $imgs[2]->getwidth, 2, " ... width=2" );
+  is( $imgs[2]->getheight, 2, " ... width=2" );
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/bad_asc.ppm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read bad asc ppm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/bad_asc.pgm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read bad asc pgm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/bad_asc.pbm', type => 'pnm',
+                allow_incomplete => 1),
+     "partial read bad asc pbm");
+  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
+  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
+}
+
+{
+  print "# monochrome output\n";
+  my $im = Imager->new(xsize => 10, ysize => 10, channels => 1, type => 'paletted');
+  ok($im->addcolors(colors => [ '#000000', '#FFFFFF' ]),
+     "add black and white");
+  $im->box(filled => 1, xmax => 4, color => '#000000');
+  $im->box(filled => 1, xmin => 5, color => '#FFFFFF');
+  is($im->type, 'paletted', 'mono still paletted');
+  push @files, "t104_mono.pbm";
+  ok($im->write(file => 'testout/t104_mono.pbm', type => 'pnm'),
+     "save as pbm");
+
+  # check it
+  my $imread = Imager->new;
+  ok($imread->read(file => 'testout/t104_mono.pbm', type=>'pnm'),
+     "read it back in")
+    or print "# ", $imread->errstr, "\n";
+  is($imread->type, 'paletted', "check result is paletted");
+  is($imread->tags(name => 'pnm_type'), 4, "check type");
+  is_image($im, $imread, "check image matches");
+}
+
+{
+  print "# monochrome output - reversed palette\n";
+  my $im = Imager->new(xsize => 10, ysize => 10, channels => 1, type => 'paletted');
+  ok($im->addcolors(colors => [ '#FFFFFF', '#000000' ]),
+     "add white and black");
+  $im->box(filled => 1, xmax => 4, color => '#000000');
+  $im->box(filled => 1, xmin => 5, color => '#FFFFFF');
+  is($im->type, 'paletted', 'mono still paletted');
+  push @files, "t104_mono2.pbm";
+  ok($im->write(file => 'testout/t104_mono2.pbm', type => 'pnm'),
+     "save as pbm");
+
+  # check it
+  my $imread = Imager->new;
+  ok($imread->read(file => 'testout/t104_mono2.pbm', type=>'pnm'),
+     "read it back in")
+    or print "# ", $imread->errstr, "\n";
+  is($imread->type, 'paletted', "check result is paletted");
+  is($imread->tags(name => 'pnm_type'), 4, "check type");
+  is_image($im, $imread, "check image matches");
+}
+
+{
+  print "# 16-bit output\n";
+  my $data;
+  my $im = test_image_16();
+  
+  # without tag, it should do 8-bit output
+  ok($im->write(data => \$data, type => 'pnm'),
+     "write 16-bit image as 8-bit/sample ppm");
+  my $im8 = Imager->new;
+  ok($im8->read(data => $data), "read it back");
+  is($im8->tags(name => 'pnm_maxval'), 255, "check maxval");
+  is_image($im, $im8, "check image matches");
+
+  # try 16-bit output
+  $im->settag(name => 'pnm_write_wide_data', value => 1);
+  $data = '';
+  ok($im->write(data => \$data, type => 'pnm'),
+     "write 16-bit image as 16-bit/sample ppm");
+  push @files, "t104_16.ppm";
+  $im->write(file=>'testout/t104_16.ppm');
+  my $im16 = Imager->new;
+  ok($im16->read(data => $data), "read it back");
+  is($im16->tags(name => 'pnm_maxval'), 65535, "check maxval");
+  push @files, "t104_16b.ppm";
+  $im16->write(file=>'testout/t104_16b.ppm');
+  is_image($im, $im16, "check image matches");
+}
+
+{
+  ok(grep($_ eq 'pnm', Imager->read_types), "check pnm in read types");
+  ok(grep($_ eq 'pnm', Imager->write_types), "check pnm in write types");
+}
+
+{ # test new() loading an image
+  my $im = Imager->new(file => "testimg/penguin-base.ppm");
+  ok($im, "received an image");
+  is($im->getwidth, 164, "check width matches image");
+
+  # fail to load an image
+  my $im2 = Imager->new(file => "Imager.pm", filetype => "pnm");
+  ok(!$im2, "no image when file failed to load");
+  cmp_ok(Imager->errstr, '=~', "bad header magic, not a PNM file",
+        "check error message transferred");
+
+  # load from data
+ SKIP:
+  {
+    ok(open(FH, "< testimg/penguin-base.ppm"), "open test file")
+      or skip("couldn't open data source", 4);
+    binmode FH;
+    my $imdata = do { local $/; <FH> };
+    close FH;
+    ok(length $imdata, "we got the data");
+    my $im3 = Imager->new(data => $imdata);
+    ok($im3, "read the file data");
+    is($im3->getwidth, 164, "check width matches image");
+  }
+}
+
+{ # image too large handling
+  {
+    ok(!Imager->new(file => "testimg/toowide.ppm", filetype => "pnm"),
+       "fail to read a too wide image");
+    is(Imager->errstr, "unable to read pnm image: could not read image width: integer overflow",
+       "check error message");
+  }
+  {
+    ok(!Imager->new(file => "testimg/tootall.ppm", filetype => "pnm"),
+       "fail to read a too wide image");
+    is(Imager->errstr, "unable to read pnm image: could not read image height: integer overflow",
+       "check error message");
+  }
+}
+
+{ # make sure close is checked for each image type
+  my $fail_close = sub {
+    Imager::i_push_error(0, "synthetic close failure");
+    return 0;
+  };
+
+  for my $type (qw(basic basic16 gray gray16 mono)) {
+    my $im = test_image_named($type);
+    my $io = Imager::io_new_cb(sub { 1 }, undef, undef, $fail_close);
+    ok(!$im->write(io => $io, type => "pnm"),
+       "write $type image with a failing close handler");
+    like($im->errstr, qr/synthetic close failure/,
+        "check error message");
+  }
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t104ppm.log";
+  unlink map "testout/$_", @files;
+}
+
+sub openimage {
+  my $fname = shift;
+  local(*FH);
+  open(FH, $fname) or die "Cannot open $fname: $!\n";
+  binmode(FH);
+  return *FH;
+}
+
+sub slurp {
+  my $fh = openimage(shift);
+  local $/;
+  my $data = <$fh>;
+  close($fh);
+  return $data;
+}
+
+sub check_gray {
+  my ($c, $gray) = @_;
+
+  my ($g) = $c->rgba;
+  is($g, $gray, "compare gray");
+}
+
diff --git a/t/200-file/320-bmp.t b/t/200-file/320-bmp.t
new file mode 100644 (file)
index 0000000..356d776
--- /dev/null
@@ -0,0 +1,798 @@
+#!perl -w
+use strict;
+use Test::More tests => 215;
+use Imager qw(:all);
+use Imager::Test qw(test_image_raw is_image is_color3 test_image);
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t107bmp.log");
+
+my @files;
+my $debug_writes = 0;
+
+my $base_diff = 0;
+# if you change this make sure you generate new compressed versions
+my $green=i_color_new(0,255,0,255);
+my $blue=i_color_new(0,0,255,255);
+my $red=i_color_new(255,0,0,255);
+
+my $img = test_image_raw();
+
+Imager::i_tags_add($img, 'i_xres', 0, '300', 0);
+Imager::i_tags_add($img, 'i_yres', 0, undef, 300);
+write_test($img, "testout/t107_24bit.bmp");
+push @files, "t107_24bit.bmp";
+# 'webmap' is noticably faster than the default
+my $im8 = Imager::i_img_to_pal($img, { make_colors=>'webmap', 
+                                      translate=>'errdiff'});
+write_test($im8, "testout/t107_8bit.bmp");
+push @files, "t107_8bit.bmp";
+# use a fixed palette so we get reproducible results for the compressed
+# version
+my @pal16 = map { NC($_) } 
+  qw(605844 966600 0148b2 00f800 bf0a33 5e009e
+     2ead1b 0000f8 004b01 fd0000 0e1695 000002);
+my $im4 = Imager::i_img_to_pal($img, { colors=>\@pal16, make_colors=>'none' });
+write_test($im4, "testout/t107_4bit.bmp");
+push @files, "t107_4bit.bmp";
+my $im1 = Imager::i_img_to_pal($img, { colors=>[ NC(0, 0, 0), NC(176, 160, 144) ],
+                              make_colors=>'none', translate=>'errdiff' });
+write_test($im1, "testout/t107_1bit.bmp");
+push @files, "t107_1bit.bmp";
+my $bi_rgb = 0;
+my $bi_rle8 = 1;
+my $bi_rle4 = 2;
+my $bi_bitfields = 3;
+read_test("testout/t107_24bit.bmp", $img, 
+          bmp_compression=>0, bmp_bit_count => 24);
+read_test("testout/t107_8bit.bmp", $im8, 
+          bmp_compression=>0, bmp_bit_count => 8);
+read_test("testout/t107_4bit.bmp", $im4, 
+          bmp_compression=>0, bmp_bit_count => 4);
+read_test("testout/t107_1bit.bmp", $im1, bmp_compression=>0, 
+          bmp_bit_count=>1);
+# the following might have slight differences
+$base_diff = i_img_diff($img, $im8) * 2;
+print "# base difference $base_diff\n";
+read_test("testimg/comp4.bmp", $im4, 
+          bmp_compression=>$bi_rle4, bmp_bit_count => 4);
+read_test("testimg/comp8.bmp", $im8, 
+          bmp_compression=>$bi_rle8, bmp_bit_count => 8);
+
+my $imoo = Imager->new;
+# read via OO
+ok($imoo->read(file=>'testout/t107_24bit.bmp'), "read via OO")
+  or print "# ",$imoo->errstr,"\n";
+
+ok($imoo->write(file=>'testout/t107_oo.bmp'), "write via OO")
+  or print "# ",$imoo->errstr,"\n";
+push @files, "t107_oo.bmp";
+
+# various invalid format tests
+# we have so many different test images to try to detect all the possible
+# failure paths in the code, adding these did detect real problems
+print "# catch various types of invalid bmp files\n";
+my @tests =
+  (
+   # entries in each array ref are:
+   #  - basename of an invalid BMP file
+   #  - error message that should be produced
+   #  - description of what is being tested
+   #  - possible flag to indicate testing only on 32-bit machines
+   [ 'badplanes.bmp', 'not a BMP file', "invalid planes value" ],
+   [ 'badbits.bmp', 'unknown bit count for BMP file (5)', 
+     'should fail to read invalid bits' ],
+
+   # 1-bit/pixel BMPs
+   [ 'badused1.bmp', 'out of range colors used (3)',
+     'out of range palette size (1-bit)' ],
+   [ 'badcomp1.bmp', 'unknown 1-bit BMP compression (1)',
+     'invalid compression value (1-bit)' ],
+   [ 'bad1wid0.bmp', 'file size limit - image width of 0 is not positive',
+     'width 0 (1-bit)' ],
+   [ 'bad4oflow.bmp', 
+     'file size limit - integer overflow calculating storage',
+     'overflow integers on 32-bit machines (1-bit)', '32bitonly' ],
+   [ 'short1.bmp', 'failed reading 1-bit bmp data', 
+     'short 1-bit' ],
+
+   # 4-bit/pixel BMPs
+   [ 'badused4a.bmp', 'out of range colors used (272)', 
+     'should fail to read invalid pal size (272) (4-bit)' ],
+   [ 'badused4b.bmp', 'out of range colors used (17)',
+     'should fail to read invalid pal size (17) (4-bit)' ],
+   [ 'badcomp4.bmp', 'unknown 4-bit BMP compression (1)',
+     'invalid compression value (4-bit)' ],
+   [ 'short4.bmp', 'failed reading 4-bit bmp data', 
+     'short uncompressed 4-bit' ],
+   [ 'short4rle.bmp', 'missing data during decompression', 
+     'short compressed 4-bit' ],
+   [ 'bad4wid0.bmp', 'file size limit - image width of 0 is not positive',
+     'width 0 (4-bit)' ],
+   [ 'bad4widbig.bmp', 'file size limit - image width of -2147483628 is not positive',
+     'width big (4-bit)' ],
+   [ 'bad4oflow.bmp', 'file size limit - integer overflow calculating storage',
+     'overflow integers on 32-bit machines (4-bit)', '32bitonly' ],
+
+   # 8-bit/pixel BMPs
+   [ 'bad8useda.bmp', 'out of range colors used (257)',
+     'should fail to read invalid pal size (8-bit)' ],
+   [ 'bad8comp.bmp', 'unknown 8-bit BMP compression (2)',
+     'invalid compression value (8-bit)' ],
+   [ 'short8.bmp', 'failed reading 8-bit bmp data', 
+     'short uncompressed 8-bit' ],
+   [ 'short8rle.bmp', 'missing data during decompression', 
+     'short compressed 8-bit' ],
+   [ 'bad8wid0.bmp', 'file size limit - image width of 0 is not positive',
+     'width 0 (8-bit)' ],
+   [ 'bad8oflow.bmp', 'file size limit - integer overflow calculating storage',
+     'overflow integers on 32-bit machines (8-bit)', '32bitonly' ],
+
+   # 24-bit/pixel BMPs
+   [ 'short24.bmp', 'failed reading image data',
+     'short 24-bit' ],
+   [ 'bad24wid0.bmp', 'file size limit - image width of 0 is not positive',
+     'width 0 (24-bit)' ],
+   [ 'bad24oflow.bmp', 'file size limit - integer overflow calculating storage',
+     'overflow integers on 32-bit machines (24-bit)', '32bitonly' ],
+   [ 'bad24comp.bmp', 'unknown 24-bit BMP compression (4)',
+     'bad compression (24-bit)' ],
+  );
+use Config;
+my $ptrsize = $Config{ptrsize};
+for my $test (@tests) {
+  my ($file, $error, $comment, $bit32only) = @$test;
+ SKIP:
+  {
+    skip("only tested on 32-bit machines", 2)
+      if $bit32only && $ptrsize != 4;
+    ok(!$imoo->read(file=>"testimg/$file"), $comment);
+    print "# ", $imoo->errstr, "\n";
+    is($imoo->errstr, $error, "check error message");
+  }
+}
+
+# previously we didn't seek to the offbits position before reading
+# the image data, check we handle it correctly
+# in each case the first is an original image with a given number of
+# bits and the second is the same file with data inserted before the
+# image bits and the offset modified to suit
+my @comp =
+  (
+   [ 'winrgb2.bmp', 'winrgb2off.bmp', 1 ],
+   [ 'winrgb4.bmp', 'winrgb4off.bmp', 4 ],
+   [ 'winrgb8.bmp', 'winrgb8off.bmp', 8 ],
+   [ 'winrgb24.bmp', 'winrgb24off.bmp', 24 ],
+  );
+
+for my $comp (@comp) {
+  my ($base_file, $off_file, $bits) = @$comp;
+
+  my $base_im = Imager->new;
+  my $got_base = 
+    ok($base_im->read(file=>"testimg/$base_file"),
+        "read original")
+      or print "# ",$base_im->errstr,"\n";
+  my $off_im = Imager->new;
+  my $got_off =
+    ok($off_im->read(file=>"testimg/$off_file"),
+        "read offset file")
+      or print "# ",$off_im->errstr,"\n";
+ SKIP:
+  {
+    skip("missed one file", 1)
+      unless $got_base && $got_off;
+    is(i_img_diff($base_im->{IMG}, $off_im->{IMG}), 0,
+        "compare base and offset image ($bits bits)");
+  }
+}
+
+{ # check file limits are checked
+  my $limit_file = "testout/t107_24bit.bmp";
+  ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
+  my $im = Imager->new;
+  ok(!$im->read(file=>$limit_file),
+     "should fail read due to size limits");
+  print "# ",$im->errstr,"\n";
+  like($im->errstr, qr/image width/, "check message");
+
+  ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
+  ok(!$im->read(file=>$limit_file),
+     "should fail read due to size limits");
+  print "# ",$im->errstr,"\n";
+  like($im->errstr, qr/image height/, "check message");
+
+  ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
+  ok($im->read(file=>$limit_file),
+     "should succeed - just inside width limit");
+  ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
+  ok($im->read(file=>$limit_file),
+     "should succeed - just inside height limit");
+  
+  # 150 x 150 x 3 channel image uses 67500 bytes
+  ok(Imager->set_file_limits(reset=>1, bytes=>67499),
+     "set bytes limit 67499");
+  ok(!$im->read(file=>$limit_file),
+     "should fail - too many bytes");
+  print "# ",$im->errstr,"\n";
+  like($im->errstr, qr/storage size/, "check error message");
+  ok(Imager->set_file_limits(reset=>1, bytes=>67500),
+     "set bytes limit 67500");
+  ok($im->read(file=>$limit_file),
+     "should succeed - just inside bytes limit");
+  Imager->set_file_limits(reset=>1);
+}
+
+{ # various short read failure tests, each entry has:
+  # source filename, size, expected error
+  # these have been selected based on code coverage, to check each
+  # failure path is checked, where practical
+  my @tests =
+    (
+     [ 
+      "file truncated inside header",
+      "winrgb2.bmp", 
+      20, "file too short to be a BMP file"
+     ],
+     [
+      "1-bit, truncated inside palette",
+      "winrgb2.bmp", 
+      56, "reading BMP palette" 
+     ],
+     [ 
+      "1-bit, truncated in offset region",
+      "winrgb2off.bmp", 64, "failed skipping to image data offset" 
+     ],
+     [ 
+      "1-bit, truncated in image data",
+      "winrgb2.bmp", 96, "failed reading 1-bit bmp data"
+     ],
+     [
+      "4-bit, truncated inside palette",
+      "winrgb4.bmp",
+      56, "reading BMP palette"
+     ],
+     [
+      "4-bit, truncated in offset region",
+      "winrgb4off.bmp", 120, "failed skipping to image data offset",
+     ],
+     [
+      "4-bit, truncate in image data",
+      "winrgb4.bmp", 120, "failed reading 4-bit bmp data"
+     ],
+     [
+      "4-bit RLE, truncate in uncompressed data",
+      "comp4.bmp", 0x229, "missing data during decompression"
+     ],
+     [
+      "8-bit, truncated in palette",
+      "winrgb8.bmp", 1060, "reading BMP palette"
+      ],
+     [
+      "8-bit, truncated in offset region",
+      "winrgb8off.bmp", 1080, "failed skipping to image data offset"
+     ],
+     [
+      "8-bit, truncated in image data",
+      "winrgb8.bmp", 1080, "failed reading 8-bit bmp data"
+     ],
+     [
+      "8-bit RLE, truncate in uncompressed data",
+      "comp8.bmp", 0x68C, "missing data during decompression"
+     ],
+     [
+      "24-bit, truncate in offset region",
+      "winrgb24off.bmp", 56, "failed skipping to image data offset",
+     ],
+     [
+      "24-bit, truncate in image data",
+      "winrgb24.bmp", 100, "failed reading image data",
+     ],
+    );
+
+  my $test_index = 0;
+  for my $test (@tests) {
+    my ($desc, $srcfile, $size, $error) = @$test;
+    my $im = Imager->new;
+    open IMDATA, "< testimg/$srcfile"
+      or die "$test_index - $desc: Cannot open testimg/$srcfile: $!";
+    binmode IMDATA;
+    my $data;
+    read(IMDATA, $data, $size) == $size
+      or die "$test_index - $desc: Could not read $size data from $srcfile";
+    close IMDATA;
+    ok(!$im->read(data => $data, type =>'bmp'),
+       "$test_index - $desc: Should fail to read");
+    is($im->errstr, $error, "$test_index - $desc: check message");
+    ++$test_index;
+  }
+}
+
+{ # various short read success tests, each entry has:
+  # source filename, size, expected tags
+  print "# allow_incomplete tests\n";
+  my @tests =
+    (
+     [ 
+      "1-bit",
+      "winrgb2.bmp", 96,
+      {
+       bmp_compression_name => 'BI_RGB',
+       bmp_compression => 0,
+       bmp_used_colors => 2,
+       i_lines_read => 8,
+      },
+     ],
+     [
+      "4-bit",
+      "winrgb4.bmp", 250,
+      {
+       bmp_compression_name => 'BI_RGB',
+       bmp_compression => 0,
+       bmp_used_colors => 16,
+       i_lines_read => 11,
+      },
+     ],
+     [
+      "4-bit RLE - uncompressed seq",
+      "comp4.bmp", 0x229, 
+      {
+       bmp_compression_name => 'BI_RLE4',
+       bmp_compression => 2,
+       bmp_used_colors => 16,
+       i_lines_read => 44,
+      },
+     ],
+     [
+      "4-bit RLE - start seq",
+      "comp4.bmp", 0x97, 
+      {
+       bmp_compression_name => 'BI_RLE4',
+       bmp_compression => 2,
+       bmp_used_colors => 16,
+       i_lines_read => 8,
+      },
+     ],
+     [
+      "8-bit",
+      "winrgb8.bmp", 1250,
+      {
+       bmp_compression_name => 'BI_RGB',
+       bmp_compression => 0,
+       bmp_used_colors => 256,
+       i_lines_read => 8,
+      },
+     ],
+     [
+      "8-bit RLE - uncompressed seq",
+      "comp8.bmp", 0x68C, 
+      {
+       bmp_compression_name => 'BI_RLE8',
+       bmp_compression => 1,
+       bmp_used_colors => 256,
+       i_lines_read => 27,
+      },
+     ],
+     [
+      "8-bit RLE - initial seq",
+      "comp8.bmp", 0x487, 
+      {
+       bmp_compression_name => 'BI_RLE8',
+       bmp_compression => 1,
+       bmp_used_colors => 256,
+       i_lines_read => 20,
+      },
+     ],
+     [
+      "24-bit",
+      "winrgb24.bmp", 800,
+      {
+       bmp_compression_name => 'BI_RGB',
+       bmp_compression => 0,
+       bmp_used_colors => 0,
+       i_lines_read => 12,
+      },
+     ],
+    );
+
+  my $test_index = 0;
+  for my $test (@tests) {
+    my ($desc, $srcfile, $size, $tags) = @$test;
+    my $im = Imager->new;
+    open IMDATA, "< testimg/$srcfile"
+      or die "$test_index - $desc: Cannot open testimg/$srcfile: $!";
+    binmode IMDATA;
+    my $data;
+    read(IMDATA, $data, $size) == $size
+      or die "$test_index - $desc: Could not read $size data from $srcfile";
+    close IMDATA;
+    ok($im->read(data => $data, type =>'bmp', allow_incomplete => 1),
+       "$test_index - $desc: Should read successfully");
+    # check standard tags are set
+    is($im->tags(name => 'i_format'), 'bmp',
+       "$test_index - $desc: i_format set");
+    is($im->tags(name => 'i_incomplete'), 1, 
+       "$test_index - $desc: i_incomplete set");
+    my %check_tags;
+    for my $key (keys %$tags) {
+      $check_tags{$key} = $im->tags(name => $key);
+    }
+    is_deeply(\%check_tags, $tags, "$test_index - $desc: check tags");
+    ++$test_index;
+  }
+}
+
+{ # check handling of reading images with negative height
+  # each entry is:
+  # source file, description
+  print "# check handling of negative height values\n";
+  my @tests =
+    (
+     [ "winrgb2.bmp", "1-bit, uncompressed" ],
+     [ "winrgb4.bmp", "4-bit, uncompressed" ],
+     [ "winrgb8.bmp", "8-bit, uncompressed" ],
+     [ "winrgb24.bmp", "24-bit, uncompressed" ],
+     [ "comp4.bmp", "4-bit, RLE" ],
+     [ "comp8.bmp", "8-bit, RLE" ],
+    );
+  my $test_index = 0;
+  for my $test (@tests) {
+    my ($file, $desc) = @$test;
+    open IMDATA, "< testimg/$file"
+      or die "$test_index - Cannot open $file: $!";
+    binmode IMDATA;
+    my $data = do { local $/; <IMDATA> };
+    close IMDATA;
+    my $im_orig = Imager->new;
+    $im_orig->read(data => $data)
+      or die "Cannot load original $file: ", $im_orig->errstr;
+    
+    # now negate the height
+    my $orig_height = unpack("V", substr($data, 0x16, 4));
+    my $neg_height = 0xFFFFFFFF & ~($orig_height - 1);
+    substr($data, 0x16, 4) = pack("V", $neg_height);
+
+    # and read the modified image
+    my $im = Imager->new;
+    ok($im->read(data => $data),
+       "$test_index - $desc: read negated height image")
+      or print "# ", $im->errstr, "\n";
+
+    # flip orig to match what we should get
+    $im_orig->flip(dir => 'v');
+
+    # check it out
+    is_image($im, $im_orig, "$test_index - $desc: check image");
+
+    ++$test_index;
+  }
+}
+
+{ print "# patched data read failure tests\n";
+  # like the "various invalid format" tests, these generate fail
+  # images from other images included with Imager without providing a
+  # full bmp source, saving on dist size and focusing on the changes needed
+  # to cause the failure
+  # each entry is: source file, patches, expected error, description
+  
+  my @tests =
+    (
+     # low image data offsets
+     [ 
+      "winrgb2.bmp", 
+      { 10 => "3d 00 00 00" }, 
+      "image data offset too small (61)",
+      "1-bit, small image offset"
+     ],
+     [ 
+      "winrgb4.bmp", 
+      { 10 => "75 00 00 00" }, 
+      "image data offset too small (117)",
+      "4-bit, small image offset"
+     ],
+     [ 
+      "winrgb8.bmp", 
+      { 10 => "35 04 00 00" }, 
+      "image data offset too small (1077)",
+      "8-bit, small image offset"
+     ],
+     [ 
+      "winrgb24.bmp", 
+      { 10 => "35 00 00 00" }, 
+      "image data offset too small (53)",
+      "24-bit, small image offset"
+     ],
+     # compression issues
+     [
+      "comp8.bmp",
+      { 0x436 => "97" },
+      "invalid data during decompression",
+      "8bit, RLE run beyond edge of image"
+     ],
+     [
+      # caused glibc malloc or valgrind to complain
+      "comp8.bmp",
+      { 0x436 => "94 00 00 03" },
+      "invalid data during decompression",
+      "8bit, literal run beyond edge of image"
+     ],
+     [
+      "comp4.bmp",
+      { 0x76 => "FF bb FF BB" },
+      "invalid data during decompression",
+      "4bit - RLE run beyond edge of image"
+     ],
+     [
+      "comp4.bmp",
+      { 0x76 => "94 bb 00 FF" },
+      "invalid data during decompression",
+      "4bit - literal run beyond edge of image"
+     ],
+    );
+  my $test_index = 0;
+  for my $test (@tests) {
+    my ($filename, $patches, $error, $desc) = @$test;
+
+    my $data = load_patched_file("testimg/$filename", $patches);
+    my $im = Imager->new;
+    ok(!$im->read(data => $data, type=>'bmp'),
+       "$test_index - $desc:should fail to read");
+    is($im->errstr, $error, "$test_index - $desc:check message");
+    ++$test_index;
+  }
+}
+
+{ # various write failure tests
+  # each entry is:
+  # source, limit, expected error, description
+  my @tests =
+    (
+     [ 
+      "winrgb2.bmp", 1, 
+      "cannot write bmp header: limit reached",
+      "1-bit, writing header" 
+     ],
+     [ 
+      "winrgb4.bmp", 1, 
+      "cannot write bmp header: limit reached",
+      "4-bit, writing header" 
+     ],
+     [ 
+      "winrgb8.bmp", 1, 
+      "cannot write bmp header: limit reached",
+      "8-bit, writing header" 
+     ],
+     [ 
+      "winrgb24.bmp", 1, 
+      "cannot write bmp header: limit reached",
+      "24-bit, writing header" 
+     ],
+     [ 
+      "winrgb2.bmp", 0x38, 
+      "cannot write palette entry: limit reached",
+      "1-bit, writing palette" 
+     ],
+     [ 
+      "winrgb4.bmp", 0x38, 
+      "cannot write palette entry: limit reached",
+      "4-bit, writing palette" 
+     ],
+     [ 
+      "winrgb8.bmp", 0x38, 
+      "cannot write palette entry: limit reached",
+      "8-bit, writing palette" 
+     ],
+     [ 
+      "winrgb2.bmp", 0x40, 
+      "writing 1 bit/pixel packed data: limit reached",
+      "1-bit, writing image data" 
+     ],
+     [ 
+      "winrgb4.bmp", 0x80, 
+      "writing 4 bit/pixel packed data: limit reached",
+      "4-bit, writing image data" 
+     ],
+     [ 
+      "winrgb8.bmp", 0x440, 
+      "writing 8 bit/pixel packed data: limit reached",
+      "8-bit, writing image data" 
+     ],
+     [ 
+      "winrgb24.bmp", 0x39, 
+      "writing image data: limit reached",
+      "24-bit, writing image data" 
+     ],
+    );
+  print "# write failure tests\n";
+  my $test_index = 0;
+  for my $test (@tests) {
+    my ($file, $limit, $error, $desc) = @$test;
+
+    my $im = Imager->new;
+    $im->read(file => "testimg/$file")
+      or die "Cannot read $file: ", $im->errstr;
+
+    my $io = Imager::io_new_cb(limited_write($limit), undef, undef, undef, 1);
+    $io->set_buffered(0);
+    print "# writing with limit of $limit\n";
+    ok(!$im->write(type => 'bmp', io => $io),
+       "$test_index - $desc: write should fail");
+    is($im->errstr, $error, "$test_index - $desc: check error message");
+
+    ++$test_index;
+  }
+}
+
+{
+  ok(grep($_ eq 'bmp', Imager->read_types), "check bmp in read types");
+  ok(grep($_ eq 'bmp', Imager->write_types), "check bmp in write types");
+}
+
+{
+  # RT #30075
+  # give 4/2 channel images a background color when saving to BMP
+  my $im = Imager->new(xsize=>16, ysize=>16, channels=>4);
+  $im->box(filled => 1, xmin => 8, color => '#FFE0C0');
+  $im->box(filled => 1, color => NC(0, 192, 192, 128),
+          ymin => 8, xmax => 7);
+  ok($im->write(file=>"testout/t107_alpha.bmp", type=>'bmp'),
+     "should succeed writing 4 channel image");
+  push @files, "t107_alpha.bmp";
+  my $imread = Imager->new;
+  ok($imread->read(file => 'testout/t107_alpha.bmp'), "read it back");
+  is_color3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, 
+           "check transparent became black");
+  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
+           "check color came through");
+  is_color3($imread->getpixel('x' => 0, 'y' => 15), 0, 96, 96,
+           "check translucent came through");
+  my $data;
+  ok($im->write(data => \$data, type => 'bmp', i_background => '#FF0000'),
+     "write with red background");
+  ok($imread->read(data => $data, type => 'bmp'),
+     "read it back");
+  is_color3($imread->getpixel('x' => 0, 'y' => 0), 255, 0, 0, 
+           "check transparent became red");
+  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
+           "check color came through");
+  is_color3($imread->getpixel('x' => 0, 'y' => 15), 127, 96, 96,
+           "check translucent came through");
+}
+
+{ # RT 41406
+  my $data;
+  my $im = test_image();
+  ok($im->write(data => \$data, type => 'bmp'), "write using OO");
+  my $size = unpack("V", substr($data, 34, 4));
+  is($size, 67800, "check data size");
+}
+
+{ # check close failures are handled correctly
+  my $im = test_image();
+  my $fail_close = sub {
+    Imager::i_push_error(0, "synthetic close failure");
+    return 0;
+  };
+  ok(!$im->write(type => "bmp", callback => sub { 1 },
+                closecb => $fail_close),
+     "check failing close fails");
+    like($im->errstr, qr/synthetic close failure/,
+        "check error message");
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink map "testout/$_", @files;
+  unlink "testout/t107bmp.log";
+}
+
+sub write_test {
+  my ($im, $filename) = @_;
+  local *FH;
+
+  if (open FH, "> $filename") {
+    binmode FH;
+    my $IO = Imager::io_new_fd(fileno(FH));
+    unless (ok(Imager::i_writebmp_wiol($im, $IO), $filename)) {
+      print "# ",Imager->_error_as_msg(),"\n";
+    }
+    undef $IO;
+    close FH;
+  }
+  else {
+    fail("could not open $filename: $!");
+  }
+}
+
+sub read_test {
+  my ($filename, $im, %tags) = @_;
+  local *FH;
+  
+  print "# read_test: $filename\n";
+
+  $tags{i_format} = "bmp";
+
+  if (open FH, "< $filename") {
+    binmode FH;
+    my $IO = Imager::io_new_fd(fileno(FH));
+    my $im_read = Imager::i_readbmp_wiol($IO);
+    if ($im_read) {
+      my $diff = i_img_diff($im, $im_read);
+      if ($diff > $base_diff) {
+       fail("image mismatch reading $filename");
+      }
+      else {
+        my $tags_ok = 1;
+        for my $tag (keys %tags) {
+          if (my $index = Imager::i_tags_find($im_read, $tag, 0)) {
+            my ($name, $value) = Imager::i_tags_get($im_read, $index);
+            my $exp_value = $tags{$tag};
+            print "#   tag $name = '$value' - expect '$exp_value'\n";
+            if ($exp_value =~ /\d/) {
+              if ($value != $tags{$tag}) {
+                print "# tag $tag value mismatch $tags{$tag} != $value\n";
+                $tags_ok = 0;
+              }
+            }
+            else {
+              if ($value ne $tags{$tag}) {
+                print "# tag $tag value mismatch $tags{$tag} != $value\n";
+                $tags_ok = 0;
+              }
+            }
+          }
+        }
+        ok($tags_ok, "reading $filename");
+        #  for my $i (0 .. Imager::i_tags_count($im_read)-1) {
+        #    my ($name, $value) = Imager::i_tags_get($im_read, $i);
+        #    print "# tag '$name' => '$value'\n";
+        #}
+      }
+    }
+    else {
+      fail("could not read $filename: ".Imager->_error_as_msg());
+    }
+    undef $IO;
+    close FH;
+  }
+  else {
+    fail("could not open $filename: $!");
+  }
+}
+
+sub limited_write {
+  my ($limit) = @_;
+
+  return
+     sub {
+       my ($data) = @_;
+       $limit -= length $data;
+       if ($limit >= 0) {
+         print "# write of ", length $data, " bytes successful ($limit left)\n" if $debug_writes;
+         return 1;
+       }
+       else {
+         print "# write of ", length $data, " bytes failed\n";
+         Imager::i_push_error(0, "limit reached");
+         return;
+       }
+     };
+}
+
+sub load_patched_file {
+  my ($filename, $patches) = @_;
+
+  open IMDATA, "< $filename"
+    or die "Cannot open $filename: $!";
+  binmode IMDATA;
+  my $data = do { local $/; <IMDATA> };
+  for my $offset (keys %$patches) {
+    (my $hdata = $patches->{$offset}) =~ tr/ //d;
+    my $pdata = pack("H*", $hdata);
+    substr($data, $offset, length $pdata) = $pdata;
+  }
+
+  return $data;
+}
diff --git a/t/200-file/330-tga.t b/t/200-file/330-tga.t
new file mode 100644 (file)
index 0000000..cfdd664
--- /dev/null
@@ -0,0 +1,302 @@
+#!perl -w
+use Imager qw(:all);
+use strict;
+use Test::More tests=>68;
+use Imager::Test qw(is_color4 is_image test_image);
+
+-d "testout" or mkdir "testout";
+
+init_log("testout/t108tga.log",1);
+
+my $img = create_test_image();
+my $base_diff = 0;
+
+write_test($img, "testout/t108_24bit.tga", 0, 0, "");
+write_test($img, "testout/t108_24bit_rle.tga", 0, 1, "");
+write_test($img, "testout/t108_15bit.tga", 1, 1, "");
+write_test($img, "testout/t108_15bit_rle.tga", 1, 1, "");
+
+# 'webmap' is noticably faster than the default
+my $im8 = Imager::i_img_to_pal($img, { make_colors=>'webmap',
+                                      translate=>'errdiff'});
+
+write_test($im8, "testout/t108_8bit.tga", 0, 0, "");
+write_test($im8, "testout/t108_8bit_rle.tga", 0, 1, "");
+write_test($im8, "testout/t108_8_15bit.tga", 1, 0, "");
+write_test($im8, "testout/t108_8_15bit_rle.tga", 1, 1, "");
+
+
+# use a fixed palette so we get reproducible results for the compressed
+# version
+
+my @bit4 = map { NC($_) }
+  qw(605844 966600 0148b2 00f800 bf0a33 5e009e
+     2ead1b 0000f8 004b01 fd0000 0e1695 000002);
+
+my @bit1 = (NC(0, 0, 0), NC(176, 160, 144));
+
+my $im4 = Imager::i_img_to_pal($img, { colors=>\@bit4,
+                                      make_colors=>'none' });
+
+my $im1 = Imager::i_img_to_pal($img, { colors=>\@bit1,
+                                      make_colors=>'none',
+                                      translate=>'errdiff' });
+
+write_test($im4, "testout/t108_4bit.tga", 0, 1, "");
+write_test($im1, "testout/t108_1bit.tga", 0, 1, "This is a comment!");
+
+read_test("testout/t108_24bit.tga", $img);
+read_test("testout/t108_8bit.tga",  $im8);
+read_test("testout/t108_4bit.tga",  $im4);
+read_test("testout/t108_1bit.tga",  $im1);
+
+# the following might have slight differences
+
+$base_diff = i_img_diff($img, $im8) * 2;
+
+print "# base difference $base_diff\n";
+
+my $imoo = Imager->new;
+ok($imoo->read(file=>'testout/t108_24bit.tga'),
+   "OO read image")
+  or print "# ",$imoo->errstr,"\n";
+
+ok($imoo->write(file=>'testout/t108_oo.tga'),
+   "OO write image")
+  or print "# ",$imoo->errstr,"\n";
+
+my ($type) = $imoo->tags(name=>'i_format');
+is($type, 'tga', "check i_format tag");
+
+# in 0.44 and earlier, reading an image with an idstring of 128 or more
+# bytes would result in an allocation error, if the platform char type
+# was signed
+$imoo = Imager->new;
+ok($imoo->read(file=>'testimg/longid.tga'), "read long id image");
+my ($id) = $imoo->tags(name=>'tga_idstring');
+is($id, "X" x 128, "check tga_idstring tag");
+my ($bitspp) = $imoo->tags(name=>'tga_bitspp');
+is($bitspp, 24, "check tga_bitspp tag");
+my ($compressed) = $imoo->tags(name=>'compressed');
+is($compressed, 1, "check compressed tag");
+
+{ # check file limits are checked
+  my $limit_file = "testout/t108_24bit.tga";
+  ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
+  my $im = Imager->new;
+  ok(!$im->read(file=>$limit_file),
+     "should fail read due to size limits");
+  print "# ",$im->errstr,"\n";
+  like($im->errstr, qr/image width/, "check message");
+  
+  ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
+  ok(!$im->read(file=>$limit_file),
+     "should fail read due to size limits");
+  print "# ",$im->errstr,"\n";
+  like($im->errstr, qr/image height/, "check message");
+  
+  ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
+  ok($im->read(file=>$limit_file),
+     "should succeed - just inside width limit");
+  ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
+  ok($im->read(file=>$limit_file),
+     "should succeed - just inside height limit");
+  
+  # 150 x 150 x 3 channel image uses 67500 bytes
+  ok(Imager->set_file_limits(reset=>1, bytes=>67499),
+     "set bytes limit 67499");
+  ok(!$im->read(file=>$limit_file),
+     "should fail - too many bytes");
+  print "# ",$im->errstr,"\n";
+  like($im->errstr, qr/storage size/, "check error message");
+  ok(Imager->set_file_limits(reset=>1, bytes=>67500),
+     "set bytes limit 67500");
+  ok($im->read(file=>$limit_file),
+     "should succeed - just inside bytes limit");
+  Imager->set_file_limits(reset=>1);
+}
+
+{ # Issue # 18397
+  # the issue is for 4 channel images to jpeg, but 2 channel images have
+  # a similar problem on tga
+  my $im = Imager->new(xsize=>100, ysize=>100, channels => 2);
+  my $data;
+  ok(!$im->write(data => \$data, type=>'tga'),
+     "check failure of writing a 2 channel image");
+  is($im->errstr, "Cannot store 2 channel image in targa format",
+     "check the error message");
+}
+
+{
+  ok(grep($_ eq 'tga', Imager->read_types), "check tga in read types");
+  ok(grep($_ eq 'tga', Imager->write_types), "check tga in write types");
+}
+
+{ # Issue #32926
+  # a sample image was read as all transparent
+  # it had bitsperpixel = 16 and atribute channel set to 1, so it
+  # should have an alpha channel.
+  # So we'll do what the gimp does and treat a zero value as opaque.
+
+  my $im = Imager->new;
+  ok($im->read(file => 'testimg/alpha16.tga'),
+     "read 16-bit/pixel alpha image");
+  my $c1 = $im->getpixel('x' => 0, 'y' => 0);
+  is_color4($c1, 0, 0, 0, 0, "check transparent pixel");
+  my $c2 = $im->getpixel('x' => 19, 'y' => 0);
+  is_color4($c2, 255, 0, 0, 255, "check opaque pixel");
+
+  # since this has an effect on writing too, write,it, read it, check it
+  my $data;
+  ok($im->write(data => \$data, type => 'tga', wierdpack => 1),
+     "write 16-bit/pixel w/alpha");
+  my $im2 = Imager->new;
+  ok($im2->read(data => $data), "read it back");
+  is_image($im, $im2, "check they match");
+}
+
+{ # prior to the types re-work we treated the tga xsize/ysize as
+  # signed short, which is wrong
+ SKIP:
+  {
+    my $im = Imager->new(xsize => 40960, ysize => 1);
+    my $data;
+    ok($im->write(data => \$data, type => "tga"),
+       "write a wide (but not too wide) image out");
+    my $im2 = Imager->new(data => $data);
+    ok($im2, "read it back in")
+      or skip("Couldn't read the wide image", 2);
+    is($im2->getwidth, 40960, "make sure the width survived the trip");
+    is($im2->getheight, 1, "make sure the height survived the trip");
+  }
+
+ SKIP:
+  {
+    my $im = Imager->new(xsize => 1, ysize => 40960);
+    my $data;
+    ok($im->write(data => \$data, type => "tga"),
+       "write a tall (but not too tall) image out");
+    my $im2 = Imager->new(data => $data);
+    ok($im2, "read it back in")
+      or skip("Couldn't read the tall image", 2);
+    is($im2->getwidth, 1, "make sure the width survived the trip");
+    is($im2->getheight, 40960, "make sure the height survived the trip");
+  }
+}
+
+{
+  # TGA files are limited to 0xFFFF x 0xFFFF pixels
+  my $max_dim = 0xFFFF;
+  {
+    my $im = Imager->new(xsize => 1+$max_dim, ysize => 1);
+    my $data = '';
+    ok(!$im->write(data => \$data, type => "tga"),
+       "fail to write too wide an image");
+    is($im->errstr, "image too large for TGA",
+       "check error message");
+  }
+ SKIP:
+  {
+    my $im = Imager->new(xsize => $max_dim, ysize => 1);
+    $im->box(fill => { hatch => "check4x4" });
+    my $data = '';
+    ok($im->write(data => \$data, type => "tga"),
+       "write image at width limit")
+      or print "# ", $im->errstr, "\n";
+    my $im2 = Imager->new(data => $data, ftype => "tga");
+    ok($im2, "read it ok")
+      or skip("cannot load the wide image", 1);
+    is($im->getwidth, $max_dim, "check width");
+    is($im->getheight, 1, "check height");
+  }
+  {
+    my $im = Imager->new(xsize => 1, ysize => 1+$max_dim);
+    my $data = '';
+    ok(!$im->write(data => \$data, type => "tga"),
+       "fail to write too tall an image");
+    is($im->errstr, "image too large for TGA",
+       "check error message");
+  }
+ SKIP:
+  {
+    my $im = Imager->new(xsize => 1, ysize => $max_dim);
+    $im->box(fill => { hatch => "check2x2" });
+    my $data = '';
+    ok($im->write(data => \$data, type => "tga"),
+       "write image at width limit");
+    my $im2 = Imager->new(data => $data, ftype => "tga");
+    ok($im2, "read it ok")
+      or skip("cannot load the wide image", 1);
+    is($im->getwidth, 1, "check width");
+    is($im->getheight, $max_dim, "check height");
+  }
+}
+
+{ # check close failures are handled correctly
+  my $im = test_image();
+  my $fail_close = sub {
+    Imager::i_push_error(0, "synthetic close failure");
+    return 0;
+  };
+  ok(!$im->write(type => "tga", callback => sub { 1 },
+                closecb => $fail_close),
+     "check failing close fails");
+    like($im->errstr, qr/synthetic close failure/,
+        "check error message");
+}
+
+sub write_test {
+  my ($im, $filename, $wierdpack, $compress, $idstring) = @_;
+  local *FH;
+
+  if (open FH, "> $filename") {
+    binmode FH;
+    my $IO = Imager::io_new_fd(fileno(FH));
+    ok(Imager::i_writetga_wiol($im, $IO, $wierdpack, $compress, $idstring),
+       "write $filename")
+      or print "# ",Imager->_error_as_msg(),"\n";
+    undef $IO;
+    close FH;
+  } else {
+    fail("write $filename: open failed: $!");
+  }
+}
+
+
+sub read_test {
+  my ($filename, $im, %tags) = @_;
+  local *FH;
+
+  if (open FH, "< $filename") {
+    binmode FH;
+    my $IO = Imager::io_new_fd(fileno(FH));
+    my $im_read = Imager::i_readtga_wiol($IO,-1);
+    if ($im_read) {
+      my $diff = i_img_diff($im, $im_read);
+      cmp_ok($diff, '<=', $base_diff,
+            "check read image vs original");
+    } else {
+      fail("read $filename ".Imager->_error_as_msg());
+    }
+    undef $IO;
+    close FH;
+  } else {
+    fail("read $filename, open failure: $!");
+  }
+}
+
+sub create_test_image {
+
+  my $green  = i_color_new(0,255,0,255);
+  my $blue   = i_color_new(0,0,255,255);
+  my $red    = i_color_new(255,0,0,255);
+
+  my $img    = Imager::ImgRaw::new(150,150,3);
+
+  i_box_filled($img, 70, 25, 130, 125, $green);
+  i_box_filled($img, 20, 25,  80, 125, $blue);
+  i_arc($img, 75, 75, 30, 0, 361, $red);
+  i_conv($img, [0.1, 0.2, 0.4, 0.2, 0.1]);
+
+  return $img;
+}
diff --git a/t/200-file/400-basic.t b/t/200-file/400-basic.t
new file mode 100644 (file)
index 0000000..994032f
--- /dev/null
@@ -0,0 +1,367 @@
+#!perl -w
+######################### We start with some black magic to print on failure.
+
+# this used to do the check for the load of Imager, but I want to be able 
+# to count tests, which means I need to load Imager first
+# since many of the early tests already do this, we don't really need to
+
+use strict;
+use Imager;
+use IO::Seekable;
+
+my $buggy_giflib_file = "buggy_giflib.txt";
+
+-d "testout" or mkdir "testout";
+
+Imager::init("log"=>"testout/t50basicoo.log");
+
+# single image/file types
+my @types = qw( jpeg png raw pnm gif tiff bmp tga );
+
+# multiple image/file formats
+my @mtypes = qw(tiff gif);
+
+my %hsh=%Imager::formats;
+
+my $test_num = 0;
+my $count;
+for my $type (@types) {
+  $count += 31 if $hsh{$type};
+}
+for my $type (@mtypes) {
+  $count += 7 if $hsh{$type};
+}
+
+print "1..$count\n";
+
+print "# avaliable formats:\n";
+for(keys %hsh) { print "# $_\n"; }
+
+#print Dumper(\%hsh);
+
+my $img = Imager->new();
+
+my %files;
+@files{@types} = ({ file => "JPEG/testimg/209_yonge.jpg"  },
+                 { file => "testimg/test.png"  },
+                 { file => "testimg/test.raw", xsize=>150, ysize=>150, type=>'raw', interleave => 0},
+                 { file => "testimg/penguin-base.ppm"  },
+                 { file => "GIF/testimg/expected.gif"  },
+                 { file => "TIFF/testimg/comp8.tif" },
+                  { file => "testimg/winrgb24.bmp" },
+                  { file => "testimg/test.tga" }, );
+my %writeopts =
+  (
+   gif=> { make_colors=>'webmap', translate=>'closest', gifquant=>'gen',
+         gif_delay=>20 },
+  );
+
+for my $type (@types) {
+  next unless $hsh{$type};
+  print "# type $type\n";
+  my %opts = %{$files{$type}};
+  my @a = map { "$_=>${opts{$_}}" } keys %opts;
+  print "#opening Format: $type, options: @a\n";
+  ok($img->read( %opts ), "reading from file", $img);
+  #or die "failed: ",$img->errstr,"\n";
+
+  my %mopts = %opts;
+  delete $mopts{file};
+
+  # read from a file handle
+  my $fh = IO::File->new($opts{file}, "r");
+  if (ok($fh, "opening $opts{file}")) {
+    binmode $fh;
+    my $fhimg = Imager->new;
+    if (ok($fhimg->read(fh=>$fh, %mopts), "read from fh")) {
+      ok($fh->seek(0, SEEK_SET), "seek after read");
+      if (ok($fhimg->read(fh=>$fh, %mopts, type=>$type), "read from fh")) {
+       ok(Imager::i_img_diff($img->{IMG}, $fhimg->{IMG}) == 0,
+          "image comparison after fh read");
+      }
+      else {
+       skip("no image to compare");
+      }
+      ok($fh->seek(0, SEEK_SET), "seek after read");
+    }
+
+    # read from a fd
+    my $fdimg = Imager->new;
+    if (ok($fdimg->read(fd=>fileno($fh), %mopts, type=>$type), "read from fd")) {
+      ok(Imager::i_img_diff($img->{IMG}, $fdimg->{IMG}) == 0,
+         "image comparistion after fd read");
+    }
+    else {
+      skip("no image to compare");
+    }
+    ok($fh->seek(0, SEEK_SET), "seek after fd read");
+    ok($fh->close, "close fh after reads");
+  }
+  else {
+    skip("couldn't open the damn file: $!", 7);
+  }
+
+  # read from a memory buffer
+  open DATA, "< $opts{file}"
+    or die "Cannot open $opts{file}: $!";
+  binmode DATA;
+  my $data = do { local $/; <DATA> };
+  close DATA;
+  my $bimg = Imager->new;
+  
+  if (ok($bimg->read(data=>$data, %mopts, type=>$type), "read from buffer", 
+        $img)) {
+    ok(Imager::i_img_diff($img->{IMG}, $bimg->{IMG}) == 0,
+       "comparing buffer read image");
+  }
+  else {
+    skip("nothing to compare");
+  }
+  
+  # read from callbacks, both with minimum and maximum reads
+  my $buf = $data;
+  my $seekpos = 0;
+  my $reader_min = 
+    sub { 
+      my ($size, $maxread) = @_;
+      my $out = substr($buf, $seekpos, $size);
+      $seekpos += length $out;
+      $out;
+    };
+  my $reader_max = 
+    sub { 
+      my ($size, $maxread) = @_;
+      my $out = substr($buf, $seekpos, $maxread);
+      $seekpos += length $out;
+      $out;
+    };
+  my $seeker =
+    sub {
+      my ($offset, $whence) = @_;
+      #print "io_seeker($offset, $whence)\n";
+      if ($whence == SEEK_SET) {
+       $seekpos = $offset;
+      }
+      elsif ($whence == SEEK_CUR) {
+       $seekpos += $offset;
+      }
+      else { # SEEK_END
+       $seekpos = length($buf) + $offset;
+      }
+      #print "-> $seekpos\n";
+      $seekpos;
+    };
+  my $cbimg = Imager->new;
+  ok($cbimg->read(callback=>$reader_min, seekcb=>$seeker, type=>$type, %mopts),
+     "read from callback min", $cbimg);
+  ok(Imager::i_img_diff($cbimg->{IMG}, $img->{IMG}) == 0,
+     "comparing mincb image");
+  $seekpos = 0;
+  ok($cbimg->read(callback=>$reader_max, seekcb=>$seeker, type=>$type, %mopts),
+     "read from callback max", $cbimg);
+  ok(Imager::i_img_diff($cbimg->{IMG}, $img->{IMG}) == 0,
+     "comparing maxcb image");
+}
+
+for my $type (@types) {
+  next unless $hsh{$type};
+
+  print "# write tests for $type\n";
+  # test writes
+  next unless $hsh{$type};
+  my $file = "testout/t50out.$type";
+  my $wimg = Imager->new;
+  # if this doesn't work, we're so screwed up anyway
+  
+  ok($wimg->read(file=>"testimg/penguin-base.ppm"),
+     "cannot read base file", $wimg);
+
+  # first to a file
+  print "# writing $type to a file\n";
+  my %extraopts;
+  %extraopts = %{$writeopts{$type}} if $writeopts{$type};
+  ok($wimg->write(file=>$file, %extraopts),
+     "writing $type to a file $file", $wimg);
+
+  print "# writing $type to a FH\n";
+  # to a FH
+  my $fh = IO::File->new($file, "w+")
+    or die "Could not create $file: $!";
+  binmode $fh;
+  ok($wimg->write(fh=>$fh, %extraopts, type=>$type),
+     "writing $type to a FH", $wimg);
+  ok($fh->seek(0, SEEK_END) > 0,
+     "seek after writing $type to a FH");
+  ok(print($fh "SUFFIX\n"),
+     "write to FH after writing $type");
+  ok($fh->close, "closing FH after writing $type");
+
+  if (ok(open(DATA, "< $file"), "opening data source")) {
+    binmode DATA;
+    my $data = do { local $/; <DATA> };
+    close DATA;
+
+    # writing to a buffer
+    print "# writing $type to a buffer\n";
+    my $buf = '';
+    ok($wimg->write(data=>\$buf, %extraopts, type=>$type),
+       "writing $type to a buffer", $wimg);
+    $buf .= "SUFFIX\n";
+    open DATA, "> testout/t50_buf.$type"
+      or die "Cannot create $type buffer file: $!";
+    binmode DATA;
+    print DATA $buf;
+    close DATA;
+    ok($data eq $buf, "comparing file data to buffer");
+
+    $buf = '';
+    my $seekpos = 0;
+    my $did_close;
+    my $writer = 
+      sub {
+       my ($what) = @_;
+       if ($seekpos > length $buf) {
+         $buf .= "\0" x ($seekpos - length $buf);
+       }
+       substr($buf, $seekpos, length $what) = $what;
+       $seekpos += length $what;
+       $did_close = 0; # the close must be last
+       1;
+      };
+    my $reader_min = 
+      sub { 
+       my ($size, $maxread) = @_;
+       my $out = substr($buf, $seekpos, $size);
+       $seekpos += length $out;
+       $out;
+      };
+    my $reader_max = 
+      sub { 
+       my ($size, $maxread) = @_;
+       my $out = substr($buf, $seekpos, $maxread);
+       $seekpos += length $out;
+       $out;
+      };
+    use IO::Seekable;
+    my $seeker =
+      sub {
+       my ($offset, $whence) = @_;
+       #print "io_seeker($offset, $whence)\n";
+       if ($whence == SEEK_SET) {
+         $seekpos = $offset;
+       }
+       elsif ($whence == SEEK_CUR) {
+         $seekpos += $offset;
+       }
+       else { # SEEK_END
+         $seekpos = length($buf) + $offset;
+       }
+       #print "-> $seekpos\n";
+       $seekpos;
+      };
+
+    my $closer = sub { ++$did_close; };
+
+    print "# writing $type via callbacks (mb=1)\n";
+    ok($wimg->write(writecb=>$writer, seekcb=>$seeker, closecb=>$closer,
+                   readcb=>$reader_min,
+                   %extraopts, type=>$type, maxbuffer=>1),
+       "writing $type to callback (mb=1)", $wimg);
+
+    ok($did_close, "checking closecb called");
+    $buf .= "SUFFIX\n";
+    ok($data eq $buf, "comparing callback output to file data");
+    print "# writing $type via callbacks (no mb)\n";
+    $buf = '';
+    $did_close = 0;
+    $seekpos = 0;
+    # we don't use the closecb here - used to make sure we don't get 
+    # a warning/error on an attempt to call an undef close sub
+    ok($wimg->write(writecb=>$writer, seekcb=>$seeker, readcb=>$reader_min,
+                   %extraopts, type=>$type),
+       "writing $type to callback (no mb)", $wimg);
+    $buf .= "SUFFIX\n";
+    ok($data eq $buf, "comparing callback output to file data");
+  }
+  else {
+    skip("couldn't open data source", 7);
+  }
+}
+
+my $img2 =  $img->crop(width=>50, height=>50);
+$img2 -> write(file=> 'testout/t50.ppm', type=>'pnm');
+
+undef($img);
+
+# multi image/file tests
+print "# multi-image write tests\n";
+for my $type (@mtypes) {
+  next unless $hsh{$type};
+  print "# $type\n";
+
+  my $file = "testout/t50out.$type";
+  my $wimg = Imager->new;
+
+  # if this doesn't work, we're so screwed up anyway
+  ok($wimg->read(file=>"testout/t50out.$type"),
+     "reading base file", $wimg);
+
+  ok(my $wimg2 = $wimg->copy, "copying base image", $wimg);
+  ok($wimg2->flip(dir=>'h'), "flipping base image", $wimg2);
+
+  my @out = ($wimg, $wimg2);
+  my %extraopts;
+  %extraopts = %{$writeopts{$type}} if $writeopts{$type};
+  ok(Imager->write_multi({ file=>"testout/t50_multi.$type", %extraopts },
+                         @out),
+     "writing multiple to a file", "Imager");
+
+  # make sure we get the same back
+  my @images = Imager->read_multi(file=>"testout/t50_multi.$type");
+  if (ok(@images == @out, "checking read image count")) {
+    for my $i (0 .. $#out) {
+      my $diff = Imager::i_img_diff($out[$i]{IMG}, $images[$i]{IMG});
+      print "# diff $diff\n";
+      ok($diff == 0, "comparing image $i");
+    }
+  }
+  else {
+    skip("wrong number of images read", 2);
+  }
+}
+
+
+Imager::malloc_state();
+
+#print "ok 2\n";
+
+sub ok {
+  my ($ok, $msg, $img, $why, $skipcount) = @_;
+
+  ++$test_num;
+  if ($ok) {
+    print "ok $test_num # $msg\n";
+    Imager::i_log_entry("ok $test_num # $msg\n", 0);
+  }
+  else {
+    my $err;
+    $err = $img->errstr if $img;
+    # VMS (if we ever support it) wants the whole line in one print
+    my $line = "not ok $test_num # line ".(caller)[2].": $msg";
+    $line .= ": $err" if $err;
+    print $line, "\n";
+    Imager::i_log_entry($line."\n", 0);
+  }
+  skip($why, $skipcount) if defined $why;
+  $ok;
+}
+
+sub skip {
+  my ($why, $skipcount) = @_;
+
+  $skipcount ||= 1;
+  for (1.. $skipcount) {
+    ++$test_num;
+    print "ok $test_num # skipped $why\n";
+  }
+}
diff --git a/t/250-draw/010-draw.t b/t/250-draw/010-draw.t
new file mode 100644 (file)
index 0000000..a1f7f2a
--- /dev/null
@@ -0,0 +1,353 @@
+#!perl -w
+use strict;
+use Test::More tests => 256;
+use Imager ':all';
+use Imager::Test qw(is_color3 is_image);
+use constant PI => 3.14159265358979;
+
+-d "testout" or mkdir "testout";
+
+init_log("testout/t21draw.log",1);
+
+my $redobj = NC(255, 0, 0);
+my $red = 'FF0000';
+my $greenobj = NC(0, 255, 0);
+my $green = [ 0, 255, 0 ];
+my $blueobj = NC(0, 0, 255);
+my $blue = { hue=>240, saturation=>1, value=>1 };
+my $white = '#FFFFFF';
+
+{
+  my $img = Imager->new(xsize=>100, ysize=>500);
+
+  ok($img->box(color=>$blueobj, xmin=>10, ymin=>10, xmax=>48, ymax=>18),
+     "box with color obj");
+  ok($img->box(color=>$blue, xmin=>10, ymin=>20, xmax=>48, ymax=>28),
+     "box with color");
+  ok($img->box(color=>$redobj, xmin=>10, ymin=>30, xmax=>28, ymax=>48, filled=>1),
+     "filled box with color obj");
+  ok($img->box(color=>$red, xmin=>30, ymin=>30, xmax=>48, ymax=>48, filled=>1),
+     "filled box with color");
+
+  ok($img->arc('x'=>75, 'y'=>25, r=>24, color=>$redobj),
+     "filled arc with colorobj");
+
+  ok($img->arc('x'=>75, 'y'=>25, r=>20, color=>$green),
+     "filled arc with colorobj");
+  ok($img->arc('x'=>75, 'y'=>25, r=>18, color=>$white, d1=>325, d2=>225),
+     "filled arc with color");
+
+  ok($img->arc('x'=>75, 'y'=>25, r=>18, color=>$blue, d1=>225, d2=>325),
+     "filled arc with color");
+  ok($img->arc('x'=>75, 'y'=>25, r=>15, color=>$green, aa=>1),
+     "filled arc with color");
+
+  ok($img->line(color=>$blueobj, x1=>5, y1=>55, x2=>35, y2=>95),
+     "line with colorobj");
+
+  # FIXME - neither the start nor end-point is set for a non-aa line
+  my $c = Imager::i_get_pixel($img->{IMG}, 5, 55);
+  ok(color_cmp($c, $blueobj) == 0, "# TODO start point not set");
+
+  ok($img->line(color=>$red, x1=>10, y1=>55, x2=>40, y2=>95, aa=>1),
+     "aa line with color");
+  ok($img->line(color=>$green, x1=>15, y1=>55, x2=>45, y2=>95, antialias=>1),
+     "antialias line with color");
+
+  ok($img->polyline(points=>[ [ 55, 55 ], [ 90, 60 ], [ 95, 95] ],
+                   color=>$redobj),
+     "polyline points with color obj");
+  ok($img->polyline('x'=>[ 55, 85, 90 ], 'y'=>[60, 65, 95], color=>$green, aa=>1),
+     "polyline xy with color aa");
+  ok($img->polyline('x'=>[ 55, 80, 85 ], 'y'=>[65, 70, 95], color=>$green, 
+                   antialias=>1),
+     "polyline xy with color antialias");
+
+  ok($img->setpixel('x'=>[35, 37, 39], 'y'=>[55, 57, 59], color=>$red),
+     "set array of pixels");
+  ok($img->setpixel('x'=>39, 'y'=>55, color=>$green),
+     "set single pixel");
+  use Imager::Color::Float;
+  my $flred = Imager::Color::Float->new(1, 0, 0, 0);
+  my $flgreen = Imager::Color::Float->new(0, 1, 0, 0);
+  ok($img->setpixel('x'=>[41, 43, 45], 'y'=>[55, 57, 59], color=>$flred),
+     "set array of float pixels");
+  ok($img->setpixel('x'=>45, 'y'=>55, color=>$flgreen),
+     "set single float pixel");
+  my @gp = $img->getpixel('x'=>[41, 43, 45], 'y'=>[55, 57, 59]);
+  ok(grep($_->isa('Imager::Color'), @gp) == 3, "check getpixel result type");
+  ok(grep(color_cmp($_, NC(255, 0, 0)) == 0, @gp) == 3, 
+     "check getpixel result colors");
+  my $gp = $img->getpixel('x'=>45, 'y'=>55);
+  ok($gp->isa('Imager::Color'), "check scalar getpixel type");
+  ok(color_cmp($gp, NC(0, 255, 0)) == 0, "check scalar getpixel color");
+  @gp = $img->getpixel('x'=>[35, 37, 39], 'y'=>[55, 57, 59], type=>'float');
+  ok(grep($_->isa('Imager::Color::Float'), @gp) == 3, 
+     "check getpixel float result type");
+  ok(grep(color_cmp($_, $flred) == 0, @gp) == 3,
+     "check getpixel float result type");
+  $gp = $img->getpixel('x'=>39, 'y'=>55, type=>'float');
+  ok($gp->isa('Imager::Color::Float'), "check scalar float getpixel type");
+  ok(color_cmp($gp, $flgreen) == 0, "check scalar float getpixel color");
+
+  # more complete arc tests
+  ok($img->arc(x=>25, 'y'=>125, r=>20, d1=>315, d2=>45, color=>$greenobj),
+     "color arc through angle 0");
+  # use diff combine here to make sure double writing is noticable
+  ok($img->arc(x=>75, 'y'=>125, r=>20, d1=>315, d2=>45,
+              fill => { solid=>$blueobj, combine => 'diff' }),
+     "fill arc through angle 0");
+  ok($img->arc(x=>25, 'y'=>175, r=>20, d1=>315, d2=>225, color=>$redobj),
+     "concave color arc");
+  angle_marker($img, 25, 175, 23, 315, 225);
+  ok($img->arc(x=>75, 'y'=>175, r=>20, d1=>315, d2=>225,
+              fill => { solid=>$greenobj, combine=>'diff' }),
+     "concave fill arc");
+  angle_marker($img, 75, 175, 23, 315, 225);
+  ok($img->arc(x=>25, y=>225, r=>20, d1=>135, d2=>45, color=>$redobj),
+     "another concave color arc");
+  angle_marker($img, 25, 225, 23, 45, 135);
+  ok($img->arc(x=>75, y=>225, r=>20, d1=>135, d2=>45, 
+              fill => { solid=>$blueobj, combine=>'diff' }),
+     "another concave fillarc");
+  angle_marker($img, 75, 225, 23, 45, 135);
+  ok($img->arc(x=>25, y=>275, r=>20, d1=>135, d2=>45, color=>$redobj, aa=>1),
+     "concave color arc aa");
+  ok($img->arc(x=>75, y=>275, r=>20, d1=>135, d2=>45, 
+              fill => { solid=>$blueobj, combine=>'diff' }, aa=>1),
+     "concave fill arc aa");
+
+  ok($img->circle(x=>25, y=>325, r=>20, color=>$redobj),
+     "color circle no aa");
+  ok($img->circle(x=>75, y=>325, r=>20, color=>$redobj, aa=>1),
+     "color circle aa");
+  ok($img->circle(x=>25, 'y'=>375, r=>20, 
+                 fill => { hatch=>'stipple', fg=>$blueobj, bg=>$redobj }),
+     "fill circle no aa");
+  ok($img->circle(x=>75, 'y'=>375, r=>20, aa=>1,
+                 fill => { hatch=>'stipple', fg=>$blueobj, bg=>$redobj }),
+     "fill circle aa");
+
+  ok($img->arc(x=>50, y=>450, r=>45, d1=>135, d2=>45, 
+              fill => { solid=>$blueobj, combine=>'diff' }),
+     "another concave fillarc");
+  angle_marker($img, 50, 450, 47, 45, 135);
+
+  ok($img->write(file=>'testout/t21draw.ppm'),
+     "saving output");
+}
+
+{
+  my $im = Imager->new(xsize => 400, ysize => 400);
+  ok($im->arc(x => 200, y => 202, r => 10, filled => 0),
+     "draw circle outline");
+  is_color3($im->getpixel(x => 200, y => 202), 0, 0, 0,
+           "check center not filled");
+  ok($im->arc(x => 198, y => 200, r => 13, filled => 0, color => "#f88"),
+     "draw circle outline");
+  is_color3($im->getpixel(x => 198, y => 200), 0, 0, 0,
+           "check center not filled");
+  ok($im->arc(x => 200, y => 200, r => 24, filled => 0, color => "#0ff"),
+     "draw circle outline");
+  my $r = 40;
+  while ($r < 180) {
+    ok($im->arc(x => 200, y => 200, r => $r, filled => 0, color => "#ff0"),
+       "draw circle outline r $r");
+    $r += 15;
+  }
+  ok($im->write(file => "testout/t21circout.ppm"),
+     "save arc outline");
+}
+
+{
+  my $im = Imager->new(xsize => 400, ysize => 400);
+  {
+    my $lc = Imager::Color->new(32, 32, 32);
+    my $an = 0;
+    while ($an < 360) {
+      my $an_r = $an * PI / 180;
+      my $ca = cos($an_r);
+      my $sa = sin($an_r);
+      $im->line(aa => 1, color => $lc,
+               x1 => 198 + 5 * $ca, y1 => 202 + 5 * $sa,
+               x2 => 198 + 190 * $ca, y2 => 202 + 190 * $sa);
+      $an += 5;
+    }
+  }
+  my $d1 = 0;
+  my $r = 20;
+  while ($d1 < 350) {
+    ok($im->arc(x => 198, y => 202, r => $r, d1 => $d1, d2 => $d1+300, filled => 0),
+       "draw arc outline r$r d1$d1 len 300");
+    ok($im->arc(x => 198, y => 202, r => $r+3, d1 => $d1, d2 => $d1+40, filled => 0, color => '#FFFF00'),
+       "draw arc outline r$r d1$d1 len 40");
+    $d1 += 15;
+    $r += 6;
+  }
+  is_color3($im->getpixel(x => 198, y => 202), 0, 0, 0,
+           "check center not filled");
+  ok($im->write(file => "testout/t21arcout.ppm"),
+     "save arc outline");
+}
+
+{
+  my $im = Imager->new(xsize => 400, ysize => 400);
+  ok($im->arc(x => 197, y => 201, r => 10, filled => 0, aa => 1, color => 'white'),
+     "draw circle outline");
+  is_color3($im->getpixel(x => 197, y => 201), 0, 0, 0,
+           "check center not filled");
+  ok($im->arc(x => 197, y => 205, r => 13, filled => 0, color => "#f88", aa => 1),
+     "draw circle outline");
+  is_color3($im->getpixel(x => 197, y => 205), 0, 0, 0,
+           "check center not filled");
+  ok($im->arc(x => 190, y => 215, r => 24, filled => 0, color => [0,0, 255, 128], aa => 1),
+     "draw circle outline");
+  my $r = 40;
+  while ($r < 190) {
+    ok($im->arc(x => 197, y => 201, r => $r, filled => 0, aa => 1, color => '#ff0'), "draw aa circle rad $r");
+    $r += 7;
+  }
+  ok($im->write(file => "testout/t21aacircout.ppm"),
+     "save arc outline");
+}
+
+{
+  my $im = Imager->new(xsize => 400, ysize => 400);
+  {
+    my $lc = Imager::Color->new(32, 32, 32);
+    my $an = 0;
+    while ($an < 360) {
+      my $an_r = $an * PI / 180;
+      my $ca = cos($an_r);
+      my $sa = sin($an_r);
+      $im->line(aa => 1, color => $lc,
+               x1 => 198 + 5 * $ca, y1 => 202 + 5 * $sa,
+               x2 => 198 + 190 * $ca, y2 => 202 + 190 * $sa);
+      $an += 5;
+    }
+  }
+  my $d1 = 0;
+  my $r = 20;
+  while ($d1 < 350) {
+    ok($im->arc(x => 198, y => 202, r => $r, d1 => $d1, d2 => $d1+300, filled => 0, aa => 1),
+       "draw aa arc outline r$r d1$d1 len 300");
+    ok($im->arc(x => 198, y => 202, r => $r+3, d1 => $d1, d2 => $d1+40, filled => 0, color => '#FFFF00', aa => 1),
+       "draw aa arc outline r$r d1$d1 len 40");
+    $d1 += 15;
+    $r += 6;
+  }
+  is_color3($im->getpixel(x => 198, y => 202), 0, 0, 0,
+           "check center not filled");
+  ok($im->write(file => "testout/t21aaarcout.ppm"),
+     "save arc outline");
+}
+
+{
+  my $im = Imager->new(xsize => 400, ysize => 400);
+
+  my $an = 0;
+  my $step = 15;
+  while ($an <= 360-$step) {
+    my $cx = int(200 + 20 * cos(($an+$step/2) * PI / 180));
+    my $cy = int(200 + 20 * sin(($an+$step/2) * PI / 180));
+
+    ok($im->arc(x => $cx, y => $cy, aa => 1, color => "#fff", 
+               d1 => $an, d2 => $an+$step, filled => 0, r => 170),
+      "angle starting from $an");
+    ok($im->arc(x => $cx+0.5, y => $cy+0.5, aa => 1, color => "#ff0", 
+               d1 => $an, d2 => $an+$step, r => 168),
+      "filled angle starting from $an");
+
+    $an += $step;
+  }
+  ok($im->write(file => "testout/t21aaarcs.ppm"),
+     "save arc outline");
+}
+
+{
+  # we document that drawing from d1 to d2 where d2 > d1 will draw an
+  # arc going through 360 degrees, test that
+  my $im = Imager->new(xsize => 200, ysize => 200);
+  ok($im->arc(x => 100, y => 100, aa => 0, filled => 0, color => '#fff',
+             d1 => 270, d2 => 90, r => 90), "draw non-aa arc through 0");
+  ok($im->arc(x => 100, y => 100, aa => 1, filled => 0, color => '#fff',
+             d1 => 270, d2 => 90, r => 80), "draw aa arc through 0");
+  ok($im->write(file => "testout/t21arc0.ppm"),
+     "save arc through 0");
+}
+
+{
+  # test drawing color defaults
+  {
+    my $im = Imager->new(xsize => 10, ysize => 10);
+    ok($im->box(), "default outline the image"); # should outline the image
+    is_color3($im->getpixel(x => 0, y => 0), 255, 255, 255,
+             "check outline default color TL");
+    is_color3($im->getpixel(x => 9, y => 5), 255, 255, 255,
+             "check outline default color MR");
+  }
+
+  {
+    my $im = Imager->new(xsize => 10, ysize => 10);
+    ok($im->box(filled => 1), "default fill the image"); # should fill the image
+    is_color3($im->getpixel(x => 0, y => 0), 255, 255, 255,
+             "check fill default color TL");
+    is_color3($im->getpixel(x => 5, y => 5), 255, 255, 255,
+             "check fill default color MM");
+  }
+}
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->box(), "can't draw box to empty image");
+  is($empty->errstr, "box: empty input image", "check error message");
+  ok(!$empty->arc(), "can't draw arc to empty image");
+  is($empty->errstr, "arc: empty input image", "check error message");
+  ok(!$empty->line(x1 => 0, y1 => 0, x2 => 10, y2 => 0),
+     "can't draw line to empty image");
+  is($empty->errstr, "line: empty input image", "check error message");
+  ok(!$empty->polyline(points => [ [ 0, 0 ], [ 10, 0 ] ]),
+     "can't draw polyline to empty image");
+  is($empty->errstr, "polyline: empty input image", "check error message");
+  ok(!$empty->polygon(points => [ [ 0, 0 ], [ 10, 0 ], [ 0, 10 ] ]),
+     "can't draw polygon to empty image");
+  is($empty->errstr, "polygon: empty input image", "check error message");
+  ok(!$empty->flood_fill(x => 0, y => 0), "can't flood fill to empty image");
+  is($empty->errstr, "flood_fill: empty input image", "check error message");
+}
+
+
+malloc_state();
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t21draw.ppm";
+  unlink "testout/t21circout.ppm";
+  unlink "testout/t21aacircout.ppm";
+  unlink "testout/t21arcout.ppm";
+  unlink "testout/t21aaarcout.ppm";
+  unlink "testout/t21aaarcs.ppm";
+  unlink "testout/t21arc0.ppm";
+}
+
+sub color_cmp {
+  my ($l, $r) = @_;
+  my @l = $l->rgba;
+  my @r = $r->rgba;
+  # print "# (",join(",", @l[0..2]),") <=> (",join(",", @r[0..2]),")\n";
+  return $l[0] <=> $r[0]
+    || $l[1] <=> $r[1]
+      || $l[2] <=> $r[2];
+}
+
+sub angle_marker {
+  my ($img, $x, $y, $radius, @angles) = @_;
+
+  for my $angle (@angles) {
+    my $x1 = int($x + $radius * cos($angle * PI / 180) + 0.5);
+    my $y1 = int($y + $radius * sin($angle * PI / 180) + 0.5);
+    my $x2 = int($x + (5+$radius) * cos($angle * PI / 180) + 0.5);
+    my $y2 = int($y + (5+$radius) * sin($angle * PI / 180) + 0.5);
+    
+    $img->line(x1=>$x1, y1=>$y1, x2=>$x2, y2=>$y2, color=>'#FFF');
+  }
+}
diff --git a/t/250-draw/020-flood.t b/t/250-draw/020-flood.t
new file mode 100644 (file)
index 0000000..767cbab
--- /dev/null
@@ -0,0 +1,69 @@
+#!perl -w
+use strict;
+use Test::More tests => 15;
+use Imager;
+use Imager::Test qw(is_image);
+
+-d "testout" or mkdir "testout";
+
+{ # flood_fill wouldn't fill to the right if the area was just a
+  # single scan-line
+  my $im = Imager->new(xsize => 5, ysize => 3);
+  ok($im, "make flood_fill test image");
+  ok($im->line(x1 => 0, y1 => 1, x2 => 4, y2 => 1, color => "white"),
+     "create fill area");
+  ok($im->flood_fill(x => 3, y => 1, color => "blue"),
+     "fill it");
+  my $cmp = Imager->new(xsize => 5, ysize => 3);
+  ok($cmp, "make test image");
+  ok($cmp->line(x1 => 0, y1 => 1, x2 => 4, y2 => 1, color => "blue"),
+     "synthezied filled area");
+  is_image($im, $cmp, "flood_fill filled horizontal line");
+}
+
+SKIP:
+{ # flood_fill won't fill entire line below if line above is shorter
+  my $im = Imager->new(file => "testimg/filltest.ppm");
+  ok($im, "Load test image")
+    or skip("Couldn't load test image: " . Imager->errstr, 3);
+
+  # fill from first bad place
+  my $fill1 = $im->copy;
+  ok($fill1->flood_fill(x => 8, y => 2, color => "#000000"),
+     "fill from a top most spot");
+  my $cmp = Imager->new(xsize => $im->getwidth, ysize => $im->getheight);
+  is_image($fill1, $cmp, "check it filled the lot");
+  ok($fill1->write(file => "testout/t22fill1.ppm"), "save");
+
+  # second bad place
+  my $fill2 = $im->copy;
+  ok($fill2->flood_fill(x => 17, y => 3, color => "#000000"),
+     "fill from not quite top most spot");
+  is_image($fill2, $cmp, "check it filled the lot");
+  ok($fill2->write(file => "testout/t22fill2.ppm"), "save");
+}
+
+{ # verticals
+  my $im = vimage("FFFFFF");
+  my $cmp = vimage("FF0000");
+
+  ok($im->flood_fill(x => 4, y=> 8, color => "FF0000"),
+     "fill at bottom of vertical well");
+  is_image($im, $cmp, "check the result");
+}
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t22fill1.ppm";
+  unlink "testout/t22fill2.ppm";
+}
+
+# make a vertical test image
+sub vimage {
+  my $c = shift;
+
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  $im->line(x1 => 1, y1 => 1, x2 => 8, y2 => 1, color => $c);
+  $im->line(x1 => 4, y1 => 2, x2 => 4, y2 => 8, color => $c);
+
+  return $im;
+}
diff --git a/t/250-draw/030-paste.t b/t/250-draw/030-paste.t
new file mode 100644 (file)
index 0000000..8599823
--- /dev/null
@@ -0,0 +1,315 @@
+#!perl -w
+use strict;
+use Test::More tests => 60;
+
+use Imager;
+use Imager::Test qw(is_image);
+
+#$Imager::DEBUG=1;
+
+-d "testout" or mkdir "testout";
+
+Imager::init('log'=>'testout/t66paste.log');
+
+# the original smoke tests
+my $img=Imager->new() || die "unable to create image object\n";
+
+ok($img->open(file=>'testimg/scale.ppm',type=>'pnm'), "load test img");
+
+my $nimg=Imager->new() or die "Unable to create image object\n";
+ok($nimg->open(file=>'testimg/scale.ppm',type=>'pnm'), "load test img again");
+
+ok($img->paste(img=>$nimg, top=>30, left=>30), "paste it")
+  or print "# ", $img->errstr, "\n";;
+
+ok($img->write(type=>'pnm',file=>'testout/t66.ppm'), "save it")
+  or print "# ", $img->errstr, "\n";
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->paste(src => $nimg), "paste into empty image");
+  is($empty->errstr, "paste: empty input image",
+     "check error message");
+
+  ok(!$img->paste(src => $empty), "paste from empty image");
+  is($img->errstr, "paste: empty input image (for src)",
+     "check error message");
+
+  ok(!$img->paste(), "no source image");
+  is($img->errstr, "no source image");
+}
+
+# more stringent tests
+{
+  my $src = Imager->new(xsize => 100, ysize => 110);
+  $src->box(filled=>1, color=>'FF0000');
+
+  $src->box(filled=>1, color=>'0000FF', xmin => 20, ymin=>20,
+            xmax=>79, ymax=>79);
+
+  my $targ = Imager->new(xsize => 100, ysize => 110);
+  $targ->box(filled=>1, color =>'00FFFF');
+  $targ->box(filled=>1, color=>'00FF00', xmin=>20, ymin=>20, xmax=>79,
+             ymax=>79);
+  my $work = $targ->copy;
+  ok($work->paste(src=>$src, left => 15, top => 10), "paste whole image");
+  # build comparison image
+  my $cmp = $targ->copy;
+  $cmp->box(filled=>1, xmin=>15, ymin => 10, color=>'FF0000');
+  $cmp->box(filled=>1, xmin=>35, ymin => 30, xmax=>94, ymax=>89, 
+            color=>'0000FF');
+
+  is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
+     "compare pasted and expected");
+
+  $work = $targ->copy;
+  ok($work->paste(src=>$src, left=>2, top=>7, src_minx => 10, src_miny => 15),
+     "paste from inside src");
+  $cmp = $targ->copy;
+  $cmp->box(filled=>1, xmin=>2, ymin=>7, xmax=>91, ymax=>101, color=>'FF0000');
+  $cmp->box(filled=>1, xmin=>12, ymin=>12, xmax=>71, ymax=>71, 
+            color=>'0000FF');
+  is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
+     "compare pasted and expected");
+
+  # paste part source
+  $work = $targ->copy;
+  ok($work->paste(src=>$src, left=>15, top=>20, 
+                  src_minx=>10, src_miny=>15, src_maxx=>80, src_maxy =>70),
+     "paste src cropped all sides");
+  $cmp = $targ->copy;
+  $cmp->box(filled=>1, xmin=>15, ymin=>20, xmax=>84, ymax=>74, 
+            color=>'FF0000');
+  $cmp->box(filled=>1, xmin=>25, ymin=>25, xmax=>84, ymax=>74,
+            color=>'0000FF');
+  is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
+     "compare pasted and expected");
+
+  # go by width instead
+  $work = $targ->copy;
+  ok($work->paste(src=>$src, left=>15, top=>20,
+                  src_minx=>10, src_miny => 15, width => 70, height => 55),
+     "same but specify width/height instead");
+  is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
+     "compare pasted and expected");
+
+  # use src_coords
+  $work = $targ->copy;
+  ok($work->paste(src=>$src, left => 15, top => 20,
+                  src_coords => [ 10, 15, 80, 70 ]),
+     "using src_coords");
+  is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
+     "compare pasted and expected");
+
+  {
+    # Issue #18712
+    # supplying just src_maxx would set the internal maxy to undef
+    # supplying just src_maxy would be ignored
+    # src_maxy (or it's derived value) was being bounds checked against 
+    # the image width instead of the image height
+    $work = $targ->copy;
+    my @warns;
+    local $SIG{__WARN__} = sub { push @warns, "@_"; print "# @_"; };
+    
+    ok($work->paste(src=>$src, left => 15, top => 20,
+                   src_maxx => 50),
+       "paste with just src_maxx");
+    ok(!@warns, "shouldn't warn");
+    my $cmp = $targ->copy;
+    $cmp->box(filled=>1, color => 'FF0000', xmin => 15, ymin => 20,
+             xmax => 64, ymax => 109);
+    $cmp->box(filled=>1, color => '0000FF', xmin => 35, ymin => 40,
+             xmax => 64, ymax => 99);
+    is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
+       "check correctly pasted");
+
+    $work = $targ->copy;
+    @warns = ();
+    ok($work->paste(src=>$src, left=>15, top=>20,
+                   src_maxy => 60),
+       "paste with just src_maxy");
+    ok(!@warns, "shouldn't warn");
+    $cmp = $targ->copy;
+    $cmp->box(filled => 1, color => 'FF0000', xmin => 15, ymin => 20,
+             xmax => 99, ymax => 79);
+    $cmp->box(filled => 1, color => '0000FF', xmin => 35, ymin => 40,
+             xmax => 94, ymax => 79);
+    is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
+       "check pasted correctly");
+
+    $work = $targ->copy;
+    @warns = ();
+    ok($work->paste(src=>$src, left=>15, top=>20,
+                   src_miny => 20, src_maxy => 105),
+       "paste with src_maxy > source width");
+
+    $cmp = $targ->copy;
+    $cmp->box(filled => 1, color => 'FF0000', xmin => 15, ymin => 20,
+             ymax => 104);
+    $cmp->box(filled => 1, color => '0000FF', xmin => 35, ymin => 20,
+             xmax => 94, ymax => 79);
+    is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
+       "check pasted correctly");
+  }
+}
+
+{ # https://rt.cpan.org/Ticket/Display.html?id=30908
+  # we now adapt the source channels to the target
+  # check each combination works as expected
+
+  # various source images
+  my $src1 = Imager->new(xsize => 50, ysize => 50, channels => 1);
+  my $g_grey_full = Imager::Color->new(128, 255, 0, 0);
+  my $g_white_50 = Imager::Color->new(255, 128, 0, 0);
+  $src1->box(filled => 1, xmax => 24, color => $g_grey_full);
+
+  my $src2 = Imager->new(xsize => 50, ysize => 50, channels => 2);
+  $src2->box(filled => 1, xmax => 24, color => $g_grey_full);
+  $src2->box(filled => 1, xmin => 25, color => $g_white_50);
+
+  my $c_red_full = Imager::Color->new(255, 0, 0);
+  my $c_blue_full = Imager::Color->new(0, 0, 255);
+  my $src3 = Imager->new(xsize => 50, ysize => 50, channels => 3);
+  $src3->box(filled => 1, xmax => 24, color => $c_red_full);
+  $src3->box(filled => 1, xmin => 25, color => $c_blue_full);
+
+  my $c_green_50 = Imager::Color->new(0, 255, 0, 127);
+  my $src4 = Imager->new(xsize => 50, ysize => 50, channels => 4);
+  $src4->box(filled => 1, xmax => 24, color => $c_blue_full);
+  $src4->box(filled => 1, xmin => 25, color => $c_green_50);
+
+  my @left_box = ( box => [ 25, 25, 49, 74 ] );
+  my @right_box = ( box => [ 50, 25, 74, 74 ] );
+
+  { # 1 channel output
+    my $base = Imager->new(xsize => 100, ysize => 100, channels => 1);
+    $base->box(filled => 1, color => Imager::Color->new(64, 255, 0, 0));
+
+    my $work = $base->copy;
+    ok($work->paste(left => 25, top => 25, src => $src1), "paste 1 to 1");
+    my $comp = $base->copy;
+    $comp->box(filled => 1, color => $g_grey_full, @left_box);
+    $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(left => 25, top => 25, src => $src2), "paste 2 to 1");
+    $comp = $base->copy;
+    $comp->box(filled => 1, @left_box, color => $g_grey_full);
+    $comp->box(filled => 1, @right_box, color => [ 128, 0, 0, 0 ]);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(left => 25, top => 25, src => $src3), "paste 3 to 1");
+     $comp = $base->copy;
+    $comp->box(filled => 1, @left_box, color => [ 57, 255, 0, 0 ]);
+    $comp->box(filled => 1, @right_box, color => [ 18, 255, 0, 0 ]);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(left => 25, top => 25, src => $src4), "paste 4 to 1");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
+    $comp->box(filled => 1, color => [ 90, 255, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+  }
+
+  { # 2 channel output
+    my $base = Imager->new(xsize => 100, ysize => 100, channels => 2);
+    $base->box(filled => 1, color => [ 128, 128, 0, 0 ]);
+    
+    my $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src1), "paste 1 to 2");
+    my $comp = $base->copy;
+    $comp->box(filled => 1, color => $g_grey_full, @left_box);
+    $comp->box(filled => 1, color => [ 0, 255, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src2), "paste 2 to 2");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => $g_grey_full, @left_box);
+    $comp->box(filled => 1, color => $g_white_50, @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src3), "paste 3 to 2");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 57, 255, 0, 0 ], @left_box);
+    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src4), "paste 4 to 2");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
+    $comp->box(filled => 1, color => [ 180, 127, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+  }
+
+  { # 3 channel output
+    my $base = Imager->new(xsize => 100, ysize => 100, channels => 3);
+    $base->box(filled => 1, color => [ 128, 255, 0, 0 ]);
+    
+    my $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src1), "paste 1 to 3");
+    my $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+    $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src2), "paste 2 to 3");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src3), "paste 3 to 3");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
+    $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src4), "paste 4 to 3");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 0, 0, 255 ], @left_box);
+    $comp->box(filled => 1, color => [ 0, 127, 0 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+  }
+
+  { # 4 channel output
+    my $base = Imager->new(xsize => 100, ysize => 100, channels => 4);
+    $base->box(filled => 1, color => [ 128, 255, 64, 128 ]);
+    
+    my $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src1), "paste 1 to 4");
+    my $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+    $comp->box(filled => 1, color => [ 0, 0, 0, 255 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src2), "paste 2 to 4");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+    $comp->box(filled => 1, color => [ 255, 255, 255, 128 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src3), "paste 3 to 4");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
+    $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+
+    $work = $base->copy;
+    ok($work->paste(top => 25, left => 25, src => $src4), "paste 4 to 4");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => $c_blue_full, @left_box);
+    $comp->box(filled => 1, color => $c_green_50, @right_box);
+    is_image($work, $comp, "compare paste target to expected");
+  }
+}
diff --git a/t/250-draw/040-rubthru.t b/t/250-draw/040-rubthru.t
new file mode 100644 (file)
index 0000000..a1fa3d3
--- /dev/null
@@ -0,0 +1,327 @@
+#!perl -w
+use strict;
+use Test::More tests => 76;
+use Imager qw(:all :handy);
+use Imager::Test qw(is_image);
+
+-d "testout" or mkdir "testout";
+
+init_log("testout/t69rubthru.log", 1);
+
+my $src_height = 80;
+my $src_width = 80;
+
+# raw interface
+my $targ = Imager::ImgRaw::new(100, 100, 3);
+my $src = Imager::ImgRaw::new($src_height, $src_width, 4);
+my $halfred = NC(255, 0, 0, 128);
+i_box_filled($src, 20, 20, 60, 60, $halfred);
+ok(i_rubthru($targ, $src, 10, 10, 0, 0, $src_width, $src_height),
+   "low level rubthrough");
+my $c = Imager::i_get_pixel($targ, 10, 10);
+ok($c, "get pixel at (10, 10)");
+ok(color_cmp($c, NC(0, 0, 0)) == 0, "check for correct color");
+$c = Imager::i_get_pixel($targ, 30, 30);
+ok($c, "get pixel at (30, 30)");
+ok(color_cmp($c, NC(128, 0, 0)) == 0, "check color");
+
+my $black = NC(0, 0, 0);
+# reset the target and try a grey+alpha source
+i_box_filled($targ, 0, 0, 100, 100, $black);
+my $gsrc = Imager::ImgRaw::new($src_width, $src_height, 2);
+my $halfwhite = NC(255, 128, 0);
+i_box_filled($gsrc, 20, 20, 60, 60, $halfwhite);
+ok(i_rubthru($targ, $gsrc, 10, 10, 0, 0, $src_width, $src_height),
+   "low level with grey/alpha source");
+$c = Imager::i_get_pixel($targ, 15, 15);
+ok($c, "get at (15, 15)");
+ok(color_cmp($c, NC(0, 0, 0)) == 0, "check color");
+$c = Imager::i_get_pixel($targ, 30, 30);
+ok($c, "get pixel at (30, 30)");
+ok(color_cmp($c, NC(128, 128, 128)) == 0, "check color");
+
+# try grey target and grey alpha source
+my $gtarg = Imager::ImgRaw::new(100, 100, 1);
+ok(i_rubthru($gtarg, $gsrc, 10, 10, 0, 0, $src_width, $src_height), 
+   "low level with grey target and gray/alpha source");
+$c = Imager::i_get_pixel($gtarg, 10, 10);
+ok($c, "get pixel at 10, 10");
+is(($c->rgba)[0], 0, "check grey level");
+is((Imager::i_get_pixel($gtarg, 30, 30)->rgba)[0], 128,
+   "check grey level at 30, 30");
+
+# simple test for 16-bit/sample images
+my $targ16 = Imager::i_img_16_new(100, 100, 3);
+ok(i_rubthru($targ16, $src, 10, 10, 0, 0, $src_width, $src_height),
+   "smoke test vs 16-bit/sample image");
+$c = Imager::i_get_pixel($targ16, 30, 30);
+ok($c, "get pixel at 30, 30");
+ok(color_cmp($c, NC(128, 0, 0)) == 0, "check color");
+
+# check the OO interface
+my $ootarg = Imager->new(xsize=>100, ysize=>100);
+my $oosrc = Imager->new(xsize=>80, ysize=>80, channels=>4);
+$oosrc->box(color=>$halfred, xmin=>20, ymin=>20, xmax=>60, ymax=>60,
+            filled=>1);
+ok($ootarg->rubthrough(src=>$oosrc, tx=>10, ty=>10),
+   "oo rubthrough");
+ok(color_cmp(Imager::i_get_pixel($ootarg->{IMG}, 10, 10), NC(0, 0, 0)) == 0,
+   "check pixel at 10, 10");
+ok(color_cmp(Imager::i_get_pixel($ootarg->{IMG}, 30, 30), NC(128, 0, 0)) == 0,
+   "check pixel at 30, 30");
+
+my $oogtarg = Imager->new(xsize=>100, ysize=>100, channels=>1);
+
+{ # check empty image errors
+  my $empty = Imager->new;
+  ok(!$empty->rubthrough(src => $oosrc), "check empty target");
+  is($empty->errstr, 'rubthrough: empty input image', "check error message");
+  ok(!$oogtarg->rubthrough(src=>$empty), "check empty source");
+  is($oogtarg->errstr, 'rubthrough: empty input image (for src)',
+     "check error message");
+}
+
+{
+  # alpha source and target
+  for my $method (qw/rubthrough compose/) {
+
+    my $src = Imager->new(xsize => 10, ysize => 1, channels => 4);
+    my $targ = Imager->new(xsize => 10, ysize => 2, channels => 4);
+
+    # simple initialization
+    $targ->setscanline('y' => 1, x => 1,
+                      pixels =>
+                      [
+                       NC(255, 128, 0, 255),
+                       NC(255, 128, 0, 128),
+                       NC(255, 128, 0, 0),
+                       NC(255, 128, 0, 255),
+                       NC(255, 128, 0, 128),
+                       NC(255, 128, 0, 0),
+                       NC(255, 128, 0, 255),
+                       NC(255, 128, 0, 128),
+                       NC(255, 128, 0, 0),
+                      ]);
+    $src->setscanline('y' => 0,
+                     pixels =>
+                     [
+                      NC(0, 128, 255, 0),
+                      NC(0, 128, 255, 0),
+                      NC(0, 128, 255, 0),
+                      NC(0, 128, 255, 128),
+                      NC(0, 128, 255, 128),
+                      NC(0, 128, 255, 128),
+                      NC(0, 128, 255, 255),
+                      NC(0, 128, 255, 255),
+                      NC(0, 128, 255, 255),
+                     ]);
+    ok($targ->$method(src => $src, combine => 'normal',
+                     tx => 1, ty => 1), "do 4 on 4 $method");
+    iscolora($targ->getpixel(x => 1, 'y' => 1), NC(255, 128, 0, 255),
+            "check at zero source coverage on full targ coverage");
+    iscolora($targ->getpixel(x => 2, 'y' => 1), NC(255, 128, 0, 128),
+            "check at zero source coverage on half targ coverage");
+    iscolora($targ->getpixel(x => 3, 'y' => 1), NC(255, 128, 0, 0),
+            "check at zero source coverage on zero targ coverage");
+    iscolora($targ->getpixel(x => 4, 'y' => 1), NC(127, 128, 128, 255),
+            "check at half source_coverage on full targ coverage");
+    iscolora($targ->getpixel(x => 5, 'y' => 1), NC(85, 128, 170, 191),
+            "check at half source coverage on half targ coverage");
+    iscolora($targ->getpixel(x => 6, 'y' => 1), NC(0, 128, 255, 128),
+            "check at half source coverage on zero targ coverage");
+    iscolora($targ->getpixel(x => 7, 'y' => 1), NC(0, 128, 255, 255),
+            "check at full source_coverage on full targ coverage");
+    iscolora($targ->getpixel(x => 8, 'y' => 1), NC(0, 128, 255, 255),
+            "check at full source coverage on half targ coverage");
+    iscolora($targ->getpixel(x => 9, 'y' => 1), NC(0, 128, 255, 255),
+            "check at full source coverage on zero targ coverage");
+  }
+}
+
+{ # https://rt.cpan.org/Ticket/Display.html?id=30908
+  # we now adapt the source channels to the target
+  # check each combination works as expected
+
+  # various source images
+  my $src1 = Imager->new(xsize => 50, ysize => 50, channels => 1);
+  my $g_grey_full = Imager::Color->new(128, 255, 0, 0);
+  my $g_white_50 = Imager::Color->new(255, 128, 0, 0);
+  $src1->box(filled => 1, xmax => 24, color => $g_grey_full);
+
+  my $src2 = Imager->new(xsize => 50, ysize => 50, channels => 2);
+  $src2->box(filled => 1, xmax => 24, color => $g_grey_full);
+  $src2->box(filled => 1, xmin => 25, color => $g_white_50);
+
+  my $c_red_full = Imager::Color->new(255, 0, 0);
+  my $c_blue_full = Imager::Color->new(0, 0, 255);
+  my $src3 = Imager->new(xsize => 50, ysize => 50, channels => 3);
+  $src3->box(filled => 1, xmax => 24, color => $c_red_full);
+  $src3->box(filled => 1, xmin => 25, color => $c_blue_full);
+
+  my $c_green_50 = Imager::Color->new(0, 255, 0, 127);
+  my $src4 = Imager->new(xsize => 50, ysize => 50, channels => 4);
+  $src4->box(filled => 1, xmax => 24, color => $c_blue_full);
+  $src4->box(filled => 1, xmin => 25, color => $c_green_50);
+
+  my @left_box = ( box => [ 25, 25, 49, 74 ] );
+  my @right_box = ( box => [ 50, 25, 74, 74 ] );
+
+  { # 1 channel output
+    my $base = Imager->new(xsize => 100, ysize => 100, channels => 1);
+    $base->box(filled => 1, color => Imager::Color->new(64, 255, 0, 0));
+
+    my $work = $base->copy;
+    ok($work->rubthrough(left => 25, top => 25, src => $src1), "rubthrough 1 to 1");
+    my $comp = $base->copy;
+    $comp->box(filled => 1, color => $g_grey_full, @left_box);
+    $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(left => 25, top => 25, src => $src2), "rubthrough 2 to 1");
+    $comp = $base->copy;
+    $comp->box(filled => 1, @left_box, color => $g_grey_full);
+    $comp->box(filled => 1, @right_box, color => [ 159, 0, 0, 0 ]);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(left => 25, top => 25, src => $src3), "rubthrough 3 to 1");
+     $comp = $base->copy;
+    $comp->box(filled => 1, @left_box, color => [ 57, 255, 0, 0 ]);
+    $comp->box(filled => 1, @right_box, color => [ 18, 255, 0, 0 ]);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(left => 25, top => 25, src => $src4), "rubthrough 4 to 1");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
+    $comp->box(filled => 1, color => [ 121, 255, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+  }
+
+  { # 2 channel output
+    my $base = Imager->new(xsize => 100, ysize => 100, channels => 2);
+    $base->box(filled => 1, color => [ 128, 128, 0, 0 ]);
+    
+    my $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src1), "rubthrough 1 to 2");
+    my $comp = $base->copy;
+    $comp->box(filled => 1, color => $g_grey_full, @left_box);
+    $comp->box(filled => 1, color => [ 0, 255, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src2), "rubthrough 2 to 2");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => $g_grey_full, @left_box);
+    $comp->box(filled => 1, color => [ 213, 191, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src3), "rubthrough 3 to 2");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 57, 255, 0, 0 ], @left_box);
+    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src4), "rubthrough 4 to 2");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
+    $comp->box(filled => 1, color => [ 162, 191, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+  }
+
+  { # 3 channel output
+    my $base = Imager->new(xsize => 100, ysize => 100, channels => 3);
+    $base->box(filled => 1, color => [ 128, 255, 0, 0 ]);
+    
+    my $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src1), "rubthrough 1 to 3");
+    my $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+    $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src2), "rubthrough 2 to 3");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+    $comp->box(filled => 1, color => [ 191, 255, 128, 255 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src3), "rubthrough 3 to 3");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
+    $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src4), "rubthrough 4 to 3");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 0, 0, 255 ], @left_box);
+    $comp->box(filled => 1, color => [ 64, 255, 0 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+  }
+
+  { # 4 channel output
+    my $base = Imager->new(xsize => 100, ysize => 100, channels => 4);
+    $base->box(filled => 1, color => [ 128, 255, 64, 128 ]);
+    
+    my $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src1), "rubthrough 1 to 4");
+    my $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+    $comp->box(filled => 1, color => [ 0, 0, 0, 255 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src2), "rubthrough 2 to 4");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
+    $comp->box(filled => 1, color => [ 213, 255, 192, 191 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src3), "rubthrough 3 to 4");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
+    $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+
+    $work = $base->copy;
+    ok($work->rubthrough(top => 25, left => 25, src => $src4), "rubthrough 4 to 4");
+    $comp = $base->copy;
+    $comp->box(filled => 1, color => $c_blue_full, @left_box);
+    $comp->box(filled => 1, color => [ 43, 255, 21, 191], @right_box);
+    is_image($work, $comp, "compare rubthrough target to expected");
+  }
+}
+
+sub color_cmp {
+  my ($l, $r) = @_;
+  my @l = $l->rgba;
+  my @r = $r->rgba;
+  print "# (",join(",", @l[0..2]),") <=> (",join(",", @r[0..2]),")\n";
+  return $l[0] <=> $r[0]
+    || $l[1] <=> $r[1]
+      || $l[2] <=> $r[2];
+}
+
+sub iscolora {
+  my ($c1, $c2, $msg) = @_;
+
+  my $builder = Test::Builder->new;
+  my @c1 = $c1->rgba;
+  my @c2 = $c2->rgba;
+  if (!$builder->ok($c1[0] == $c2[0] && $c1[1] == $c2[1] && $c1[2] == $c2[2]
+                    && $c1[3] == $c2[3],
+                    $msg)) {
+    $builder->diag(<<DIAG);
+      got color: [ @c1 ]
+ expected color: [ @c2 ]
+DIAG
+  }
+}
+
diff --git a/t/250-draw/050-polyaa.t b/t/250-draw/050-polyaa.t
new file mode 100644 (file)
index 0000000..ace2e64
--- /dev/null
@@ -0,0 +1,279 @@
+#!perl -w
+
+use strict;
+use Test::More tests => 18;
+
+use Imager qw/NC/;
+use Imager::Test qw(is_image is_color3);
+
+sub PI () { 3.14159265358979323846 }
+
+-d "testout" or mkdir "testout";
+
+Imager::init_log("testout/t75aapolyaa.log",1);
+
+my $red   = Imager::Color->new(255,0,0);
+my $green = Imager::Color->new(0,255,0);
+my $blue  = Imager::Color->new(0,0,255);
+my $white = Imager::Color->new(255,255,255);
+
+{ # artifacts with multiple vertical lobes
+  # https://rt.cpan.org/Ticket/Display.html?id=43518
+  # previously this would have a full coverage pixel at (0,0) caused
+  # by the (20,0.5) point in the right lobe
+
+  my @pts = 
+    (
+     [ 0.5, -9 ],
+     [ 10, -9 ],
+     [ 10, 11 ],
+     [ 15, 11 ],
+     [ 15, -9 ],
+     [ 17, -9 ],
+     [ 20, 0.5 ],
+     [ 17, 11 ],
+     [ 0.5, 11 ],
+    );
+  my $im = Imager->new(xsize => 10, ysize => 2);
+  ok($im->polygon(points => \@pts,
+                 color => $white),
+     "draw with inside point");
+  ok($im->write(file => "testout/t75inside.ppm"), "save to file");
+  # both scanlines should be the same
+  my $line0 = $im->crop(top => 0, height => 1);
+  my $line1 = $im->crop(top => 1, height => 1);
+  is_image($line0, $line1, "both scanlines should be the same");
+}
+
+{ # check vertical edges are consistent
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  ok($im->polygon(points => [ [ 0.5, 0 ], [ 9.25, 0 ], 
+                             [ 9.25, 10 ], [ 0.5, 10 ] ],
+                 color => $white,
+                 aa => 1), 
+     "draw polygon with mid pixel vertical edges")
+    or diag $im->errstr;
+  my @line0 = $im->getscanline(y => 0);
+  my $im2 = Imager->new(xsize => 10, ysize => 10);
+  for my $y (0..9) {
+    $im2->setscanline(y => $y, pixels => \@line0);
+  }
+  is_image($im, $im2, "all scan lines should be the same");
+  is_color3($line0[0], 128, 128, 128, "(0,0) should be 50% coverage");
+  is_color3($line0[9], 64, 64, 64, "(9,0) should be 25% coverage");
+}
+
+{ # check horizontal edges are consistent
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  ok($im->polygon(points => [ [ 0, 0.5 ], [ 0, 9.25 ],
+                             [ 10, 9.25 ], [ 10, 0.5 ] ],
+                 color => $white,
+                 aa => 1),
+     "draw polygon with mid-pixel horizontal edges");
+  is_deeply([ $im->getsamples(y => 0, channels => [ 0 ]) ],
+           [ (128) x 10 ],
+           "all of line 0 should be 50% coverage");
+  is_deeply([ $im->getsamples(y => 9, channels => [ 0 ]) ],
+           [ (64) x 10 ],
+           "all of line 9 should be 25% coverage");
+}
+
+{
+  my $img = Imager->new(xsize=>20, ysize=>10);
+  my @data = translate(5.5,5,
+                      rotate(0,
+                             scale(5, 5,
+                                   get_polygon(n_gon => 5)
+                                  )
+                            )
+                     );
+  
+  
+  my ($x, $y) = array_to_refpair(@data);
+  ok(Imager::i_poly_aa($img->{IMG}, $x, $y, $white), "primitive poly");
+
+  ok($img->write(file=>"testout/t75.ppm"), "write to file")
+    or diag $img->errstr;
+
+  my $zoom = make_zoom($img, 8, \@data, $red);
+  ok($zoom, "make zoom of primitive");
+  $zoom->write(file=>"testout/t75zoom.ppm") or die $zoom->errstr;
+}
+
+{
+  my $img = Imager->new(xsize=>300, ysize=>100);
+
+  my $good = 1;
+  for my $n (0..55) {
+    my @data = translate(20+20*($n%14),18+20*int($n/14),
+                        rotate(15*$n/PI,
+                               scale(15, 15,
+                                     get_polygon('box')
+                                    )
+                              )
+                       );
+    my ($x, $y) = array_to_refpair(@data);
+    Imager::i_poly_aa($img->{IMG}, $x, $y, NC(rand(255), rand(255), rand(255)))
+       or $good = 0;
+  }
+  
+  $img->write(file=>"testout/t75big.ppm") or die $img->errstr;
+
+  ok($good, "primitive squares");
+}
+
+{
+  my $img = Imager->new(xsize => 300, ysize => 300);
+  ok($img -> polygon(color=>$white,
+                 points => [
+                            translate(150,150,
+                                      rotate(45*PI/180,
+                                             scale(70,70,
+                                                   get_polygon('wavycircle', 32*8, sub { 1.2+1*cos(4*$_) }))))
+                           ],
+                ), "method call")
+    or diag $img->errstr();
+
+  $img->write(file=>"testout/t75wave.ppm") or die $img->errstr;
+}
+
+{
+  my $img = Imager->new(xsize=>10,ysize=>6);
+  my @data = translate(165,5,
+                      scale(80,80,
+                            get_polygon('wavycircle', 32*8, sub { 1+1*cos(4*$_) })));
+  
+  ok($img -> polygon(color=>$white,
+               points => [
+                          translate(165,5,
+                                    scale(80,80,
+                                          get_polygon('wavycircle', 32*8, sub { 1+1*cos(4*$_) })))
+                         ],
+                ), "bug check")
+    or diag $img->errstr();
+
+  make_zoom($img,20,\@data, $blue)->write(file=>"testout/t75wavebug.ppm") or die $img->errstr;
+
+}
+
+{
+  my $img = Imager->new(xsize=>300, ysize=>300);
+  ok($img->polygon(fill=>{ hatch=>'cross1', fg=>'00FF00', bg=>'0000FF', dx=>3 },
+              points => [
+                         translate(150,150,
+                                   scale(70,70,
+                                         get_polygon('wavycircle', 32*8, sub { 1+1*cos(4*$_) })))
+                        ],
+             ), "poly filled with hatch")
+    or diag $img->errstr();
+  $img->write(file=>"testout/t75wave_fill.ppm") or die $img->errstr;
+}
+
+{
+  my $img = Imager->new(xsize=>300, ysize=>300, bits=>16);
+  ok($img->polygon(fill=>{ hatch=>'cross1', fg=>'00FF00', bg=>'0000FF' },
+              points => [
+                         translate(150,150,
+                                   scale(70,70,
+                                         get_polygon('wavycircle', 32*8, sub { 1+1*cos(5*$_) })))
+                        ],
+             ), "hatched to 16-bit image")
+    or diag $img->errstr();
+  $img->write(file=>"testout/t75wave_fill16.ppm") or die $img->errstr;
+}
+
+Imager::malloc_state();
+
+
+#initialized in a BEGIN, later
+my %primitives;
+my %polygens;
+
+sub get_polygon {
+  my $name = shift;
+  if (exists $primitives{$name}) {
+    return @{$primitives{$name}};
+  }
+
+  if (exists $polygens{$name}) {
+    return $polygens{$name}->(@_);
+  }
+
+  die "polygon spec: $name unknown\n";
+}
+
+
+sub make_zoom {
+  my ($img, $sc, $polydata, $linecolor) = @_;
+
+  # scale with nearest neighboor sampling
+  my $timg = $img->scale(scalefactor=>$sc, qtype=>'preview');
+
+  # draw the grid
+  for(my $lx=0; $lx<$timg->getwidth(); $lx+=$sc) {
+    $timg->line(color=>$green, x1=>$lx, x2=>$lx, y1=>0, y2=>$timg->getheight(), antialias=>0);
+  }
+
+  for(my $ly=0; $ly<$timg->getheight(); $ly+=$sc) {
+    $timg->line(color=>$green, y1=>$ly, y2=>$ly, x1=>0, x2=>$timg->getwidth(), antialias=>0);
+  }
+  my @data = scale($sc, $sc, @$polydata);
+  push(@data, $data[0]);
+  my ($x, $y) = array_to_refpair(@data);
+
+  $timg->polyline(color=>$linecolor, 'x'=>$x, 'y'=>$y, antialias=>0);
+  return $timg;
+}
+
+# utility functions to manipulate point data
+
+sub scale {
+  my ($x, $y, @data) = @_;
+  return map { [ $_->[0]*$x , $_->[1]*$y ] } @data;
+}
+
+sub translate {
+  my ($x, $y, @data) = @_;
+  map { [ $_->[0]+$x , $_->[1]+$y ] } @data;
+}
+
+sub rotate {
+  my ($rad, @data) = @_;
+  map { [ $_->[0]*cos($rad)+$_->[1]*sin($rad) , $_->[1]*cos($rad)-$_->[0]*sin($rad) ] } @data;
+}
+
+sub array_to_refpair {
+  my (@x, @y);
+  for (@_) {
+    push(@x, $_->[0]);
+    push(@y, $_->[1]);
+  }
+  return \@x, \@y;
+}
+
+
+
+BEGIN {
+%primitives = (
+              box => [ [-0.5,-0.5], [0.5,-0.5], [0.5,0.5], [-0.5,0.5] ],
+              triangle => [ [0,0], [1,0], [1,1] ],
+             );
+
+%polygens = (
+            wavycircle => sub {
+              my $numv = shift;
+              my $radfunc = shift;
+              my @radians = map { $_*2*PI/$numv } 0..$numv-1;
+              my @radius  = map { $radfunc->($_) } @radians;
+              map {
+                [ $radius[$_] * cos($radians[$_]), $radius[$_] * sin($radians[$_]) ]
+              } 0..$#radians;
+            },
+            n_gon => sub {
+              my $N = shift;
+              map {
+                [ cos($_*2*PI/$N), sin($_*2*PI/$N) ]
+              } 0..$N-1;
+            },
+);
+}
diff --git a/t/250-draw/100-fill.t b/t/250-draw/100-fill.t
new file mode 100644 (file)
index 0000000..c809f12
--- /dev/null
@@ -0,0 +1,736 @@
+#!perl -w
+use strict;
+use Test::More tests => 165;
+
+use Imager ':handy';
+use Imager::Fill;
+use Imager::Color::Float;
+use Imager::Test qw(is_image is_color4 is_fcolor4 is_color3);
+use Config;
+
+-d "testout" or mkdir "testout";
+
+Imager::init_log("testout/t20fill.log", 1);
+
+my $blue = NC(0,0,255);
+my $red = NC(255, 0, 0);
+my $redf = Imager::Color::Float->new(1, 0, 0);
+my $bluef = Imager::Color::Float->new(0, 0, 1);
+my $rsolid = Imager::i_new_fill_solid($blue, 0);
+ok($rsolid, "building solid fill");
+my $raw1 = Imager::ImgRaw::new(100, 100, 3);
+# use the normal filled box
+Imager::i_box_filled($raw1, 0, 0, 99, 99, $blue);
+my $raw2 = Imager::ImgRaw::new(100, 100, 3);
+Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rsolid);
+ok(1, "drawing with solid fill");
+my $diff = Imager::i_img_diff($raw1, $raw2);
+ok($diff == 0, "solid fill doesn't match");
+Imager::i_box_filled($raw1, 0, 0, 99, 99, $red);
+my $rsolid2 = Imager::i_new_fill_solidf($redf, 0);
+ok($rsolid2, "creating float solid fill");
+Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rsolid2);
+$diff = Imager::i_img_diff($raw1, $raw2);
+ok($diff == 0, "float solid fill doesn't match");
+
+# ok solid still works, let's try a hatch
+# hash1 is a 2x2 checkerboard
+my $rhatcha = Imager::i_new_fill_hatch($red, $blue, 0, 1, undef, 0, 0);
+my $rhatchb = Imager::i_new_fill_hatch($blue, $red, 0, 1, undef, 2, 0);
+ok($rhatcha && $rhatchb, "can't build hatched fill");
+
+# the offset should make these match
+Imager::i_box_cfill($raw1, 0, 0, 99, 99, $rhatcha);
+Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
+ok(1, "filling with hatch");
+$diff = Imager::i_img_diff($raw1, $raw2);
+ok($diff == 0, "hatch images different");
+$rhatchb = Imager::i_new_fill_hatch($blue, $red, 0, 1, undef, 4, 6);
+Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
+$diff = Imager::i_img_diff($raw1, $raw2);
+ok($diff == 0, "hatch images different");
+
+# I guess I was tired when I originally did this - make sure it keeps
+# acting the way it's meant to
+# I had originally expected these to match with the red and blue swapped
+$rhatchb = Imager::i_new_fill_hatch($red, $blue, 0, 1, undef, 2, 2);
+Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
+$diff = Imager::i_img_diff($raw1, $raw2);
+ok($diff == 0, "hatch images different");
+
+# this shouldn't match
+$rhatchb = Imager::i_new_fill_hatch($red, $blue, 0, 1, undef, 1, 1);
+Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
+$diff = Imager::i_img_diff($raw1, $raw2);
+ok($diff, "hatch images the same!");
+
+# custom hatch
+# the inverse of the 2x2 checkerboard
+my $hatch = pack("C8", 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC);
+my $rcustom = Imager::i_new_fill_hatch($blue, $red, 0, 0, $hatch, 0, 0);
+Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rcustom);
+$diff = Imager::i_img_diff($raw1, $raw2);
+ok(!$diff, "custom hatch mismatch");
+
+{
+  # basic test of floating color hatch fills
+  # this will exercise the code that the gcc shipped with OS X 10.4
+  # forgets to generate
+  # the float version is called iff we're working with a non-8-bit image
+  # i_new_fill_hatchf() makes the same object as i_new_fill_hatch() but
+  # we test the other construction code path here
+  my $fraw1 = Imager::i_img_double_new(100, 100, 3);
+  my $fhatch1 = Imager::i_new_fill_hatchf($redf, $bluef, 0, 1, undef, 0, 0);
+  ok($fraw1, "making double image 1");
+  ok($fhatch1, "making float hatch 1");
+  Imager::i_box_cfill($fraw1, 0, 0, 99, 99, $fhatch1);
+  my $fraw2 = Imager::i_img_double_new(100, 100, 3);
+  my $fhatch2 = Imager::i_new_fill_hatchf($bluef, $redf, 0, 1, undef, 0, 2);
+  ok($fraw2, "making double image 2");
+  ok($fhatch2, "making float hatch 2");
+  Imager::i_box_cfill($fraw2, 0, 0, 99, 99, $fhatch2);
+
+  $diff = Imager::i_img_diff($fraw1, $fraw2);
+  ok(!$diff, "float custom hatch mismatch");
+  save($fraw1, "testout/t20hatchf1.ppm");
+  save($fraw2, "testout/t20hatchf2.ppm");
+}
+
+# test the oo interface
+my $im1 = Imager->new(xsize=>100, ysize=>100);
+my $im2 = Imager->new(xsize=>100, ysize=>100);
+
+my $solid = Imager::Fill->new(solid=>'#FF0000');
+ok($solid, "creating oo solid fill");
+ok($solid->{fill}, "bad oo solid fill");
+$im1->box(fill=>$solid);
+$im2->box(filled=>1, color=>$red);
+$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
+ok(!$diff, "oo solid fill");
+
+my $hatcha = Imager::Fill->new(hatch=>'check2x2');
+my $hatchb = Imager::Fill->new(hatch=>'check2x2', dx=>2);
+$im1->box(fill=>$hatcha);
+$im2->box(fill=>$hatchb);
+# should be different
+$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
+ok($diff, "offset checks the same!");
+$hatchb = Imager::Fill->new(hatch=>'check2x2', dx=>2, dy=>2);
+$im2->box(fill=>$hatchb);
+$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
+ok(!$diff, "offset into similar check should be the same");
+
+# test dymanic build of fill
+$im2->box(fill=>{hatch=>'check2x2', dx=>2, fg=>NC(255,255,255), 
+                 bg=>NC(0,0,0)});
+$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
+ok(!$diff, "offset and flipped should be the same");
+
+# a simple demo
+my $im = Imager->new(xsize=>200, ysize=>200);
+
+$im->box(xmin=>10, ymin=>10, xmax=>190, ymax=>190,
+         fill=>{ hatch=>'check4x4',
+                 fg=>NC(128, 0, 0),
+                 bg=>NC(128, 64, 0) })
+  or print "# ",$im->errstr,"\n";
+$im->arc(r=>80, d1=>45, d2=>75, 
+           fill=>{ hatch=>'stipple2',
+                   combine=>1,
+                   fg=>[ 0, 0, 0, 255 ],
+                   bg=>{ rgba=>[255,255,255,160] } })
+  or print "# ",$im->errstr,"\n";
+$im->arc(r=>80, d1=>75, d2=>135,
+         fill=>{ fountain=>'radial', xa=>100, ya=>100, xb=>20, yb=>100 })
+  or print "# ",$im->errstr,"\n";
+$im->write(file=>'testout/t20_sample.ppm');
+
+# flood fill tests
+my $rffimg = Imager::ImgRaw::new(100, 100, 3);
+# build a H 
+Imager::i_box_filled($rffimg, 10, 10, 20, 90, $blue);
+Imager::i_box_filled($rffimg, 80, 10, 90, 90, $blue);
+Imager::i_box_filled($rffimg, 20, 45, 80, 55, $blue);
+my $black = Imager::Color->new(0, 0, 0);
+Imager::i_flood_fill($rffimg, 15, 15, $red);
+my $rffcmp = Imager::ImgRaw::new(100, 100, 3);
+# build a H 
+Imager::i_box_filled($rffcmp, 10, 10, 20, 90, $red);
+Imager::i_box_filled($rffcmp, 80, 10, 90, 90, $red);
+Imager::i_box_filled($rffcmp, 20, 45, 80, 55, $red);
+$diff = Imager::i_img_diff($rffimg, $rffcmp);
+ok(!$diff, "flood fill difference");
+
+my $ffim = Imager->new(xsize=>100, ysize=>100);
+my $yellow = Imager::Color->new(255, 255, 0);
+$ffim->box(xmin=>10, ymin=>10, xmax=>20, ymax=>90, color=>$blue, filled=>1);
+$ffim->box(xmin=>20, ymin=>45, xmax=>80, ymax=>55, color=>$blue, filled=>1);
+$ffim->box(xmin=>80, ymin=>10, xmax=>90, ymax=>90, color=>$blue, filled=>1);
+ok($ffim->flood_fill('x'=>50, 'y'=>50, color=>$red), "flood fill");
+$diff = Imager::i_img_diff($rffcmp, $ffim->{IMG});
+ok(!$diff, "oo flood fill difference");
+$ffim->flood_fill('x'=>50, 'y'=>50,
+                  fill=> {
+                          hatch => 'check2x2',
+                         fg => '0000FF',
+                         });
+#                  fill=>{
+#                         fountain=>'radial',
+#                         xa=>50, ya=>50,
+#                         xb=>10, yb=>10,
+#                        });
+$ffim->write(file=>'testout/t20_ooflood.ppm');
+
+my $copy = $ffim->copy;
+ok($ffim->flood_fill('x' => 50, 'y' => 50,
+                    color => $red, border => '000000'),
+   "border solid flood fill");
+is(Imager::i_img_diff($ffim->{IMG}, $rffcmp), 0, "compare");
+ok($ffim->flood_fill('x' => 50, 'y' => 50,
+                    fill => { hatch => 'check2x2', fg => '0000FF', },
+                    border => '000000'),
+   "border cfill fill");
+is(Imager::i_img_diff($ffim->{IMG}, $copy->{IMG}), 0,
+   "compare");
+
+# test combining modes
+my $fill = NC(192, 128, 128, 128);
+my $target = NC(64, 32, 64);
+my $trans_target = NC(64, 32, 64, 128);
+my %comb_tests =
+  (
+   none=>
+   { 
+    opaque => $fill,
+    trans => $fill,
+   },
+   normal=>
+   { 
+    opaque => NC(128, 80, 96),
+    trans => NC(150, 96, 107, 191),
+   },
+   multiply => 
+   { 
+    opaque => NC(56, 24, 48),
+    trans => NC(101, 58, 74, 192),
+   },
+   dissolve => 
+   { 
+    opaque => [ $target, NC(192, 128, 128, 255) ],
+    trans => [ $trans_target, NC(192, 128, 128, 255) ],
+   },
+   add => 
+   { 
+    opaque => NC(159, 96, 128),
+    trans => NC(128, 80, 96, 255),
+   },
+   subtract => 
+   { 
+    opaque => NC(0, 0, 0),
+    trans => NC(0, 0, 0, 255),
+   },
+   diff => 
+   { 
+    opaque => NC(96, 64, 64),
+    trans => NC(127, 85, 85, 192),
+   },
+   lighten => 
+   { 
+    opaque => NC(128, 80, 96), 
+    trans => NC(149, 95, 106, 192), 
+   },
+   darken => 
+   { 
+    opaque => $target,
+    trans => NC(106, 63, 85, 192),
+   },
+   # the following results are based on the results of the tests and
+   # are suspect for that reason (and were broken at one point <sigh>)
+   # but trying to work them out manually just makes my head hurt - TC
+   hue => 
+   { 
+    opaque => NC(64, 32, 47),
+    trans => NC(64, 32, 42, 128),
+   },
+   saturation => 
+   { 
+    opaque => NC(63, 37, 64),
+    trans => NC(64, 39, 64, 128),
+   },
+   value => 
+   { 
+    opaque => NC(127, 64, 128),
+    trans => NC(149, 75, 150, 128),
+   },
+   color => 
+   { 
+    opaque => NC(64, 37, 52),
+    trans => NC(64, 39, 50, 128),
+   },
+  );
+
+for my $comb (Imager::Fill->combines) {
+  my $test = $comb_tests{$comb};
+  my $fillobj = Imager::Fill->new(solid=>$fill, combine=>$comb);
+
+  for my $bits (qw(8 double)) {
+    {
+      my $targim = Imager->new(xsize=>4, ysize=>4, bits => $bits);
+      $targim->box(filled=>1, color=>$target);
+      $targim->box(fill=>$fillobj);
+      my $c = Imager::i_get_pixel($targim->{IMG}, 1, 1);
+      my $allowed = $test->{opaque};
+      $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
+      ok(scalar grep(color_close($_, $c), @$allowed), 
+        "opaque '$comb' $bits bits")
+       or print "# got:",join(",", $c->rgba),"  allowed: ", 
+         join("|", map { join(",", $_->rgba) } @$allowed),"\n";
+    }
+    
+    {
+      # make sure the alpha path in the combine function produces the same
+      # or at least as sane a result as the non-alpha path
+      my $targim = Imager->new(xsize=>4, ysize=>4, channels => 4, bits => $bits);
+      $targim->box(filled=>1, color=>$target);
+      $targim->box(fill=>$fillobj);
+      my $c = Imager::i_get_pixel($targim->{IMG}, 1, 1);
+      my $allowed = $test->{opaque};
+      $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
+      ok(scalar grep(color_close4($_, $c), @$allowed), 
+        "opaque '$comb' 4-channel $bits bits")
+       or print "# got:",join(",", $c->rgba),"  allowed: ", 
+         join("|", map { join(",", $_->rgba) } @$allowed),"\n";
+    }
+    
+    {
+      my $transim = Imager->new(xsize => 4, ysize => 4, channels => 4, bits => $bits);
+      $transim->box(filled=>1, color=>$trans_target);
+      $transim->box(fill => $fillobj);
+      my $c = $transim->getpixel(x => 1, 'y' => 1);
+      my $allowed = $test->{trans};
+      $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
+      ok(scalar grep(color_close4($_, $c), @$allowed), 
+        "translucent '$comb' $bits bits")
+       or print "# got:",join(",", $c->rgba),"  allowed: ", 
+         join("|", map { join(",", $_->rgba) } @$allowed),"\n";
+    }
+  }
+}
+
+ok($ffim->arc(r=>45, color=>$blue, aa=>1), "aa circle");
+$ffim->write(file=>"testout/t20_aacircle.ppm");
+
+# image based fills
+my $green = NC(0, 255, 0);
+my $fillim = Imager->new(xsize=>40, ysize=>40, channels=>4);
+$fillim->box(filled=>1, xmin=>5, ymin=>5, xmax=>35, ymax=>35, 
+             color=>NC(0, 0, 255, 128));
+$fillim->arc(filled=>1, r=>10, color=>$green, aa=>1);
+my $ooim = Imager->new(xsize=>150, ysize=>150);
+$ooim->box(filled=>1, color=>$green, xmin=>70, ymin=>25, xmax=>130, ymax=>125);
+$ooim->box(filled=>1, color=>$blue, xmin=>20, ymin=>25, xmax=>80, ymax=>125);
+$ooim->arc(r=>30, color=>$red, aa=>1);
+
+my $oocopy = $ooim->copy();
+ok($oocopy->arc(fill=>{image=>$fillim, 
+                       combine=>'normal',
+                       xoff=>5}, r=>40),
+   "image based fill");
+$oocopy->write(file=>'testout/t20_image.ppm');
+
+# a more complex version
+use Imager::Matrix2d ':handy';
+$oocopy = $ooim->copy;
+ok($oocopy->arc(fill=>{
+                       image=>$fillim,
+                       combine=>'normal',
+                       matrix=>m2d_rotate(degrees=>30),
+                       xoff=>5
+                       }, r=>40),
+   "transformed image based fill");
+$oocopy->write(file=>'testout/t20_image_xform.ppm');
+
+ok(!$oocopy->arc(fill=>{ hatch=>"not really a hatch" }, r=>20),
+   "error handling of automatic fill conversion");
+ok($oocopy->errstr =~ /Unknown hatch type/,
+   "error message for automatic fill conversion");
+
+# previous box fills to float images, or using the fountain fill
+# got into a loop here
+
+SKIP:
+{
+  skip("can't test without alarm()", 1) unless $Config{d_alarm};
+  local $SIG{ALRM} = sub { die; };
+
+  eval {
+    alarm(2);
+    ok($ooim->box(xmin=>20, ymin=>20, xmax=>80, ymax=>40,
+                  fill=>{ fountain=>'linear', xa=>20, ya=>20, xb=>80, 
+                          yb=>20 }), "linear box fill");
+    alarm 0;
+  };
+  $@ and ok(0, "linear box fill $@");
+}
+
+# test that passing in a non-array ref returns an error
+{
+  my $fill = Imager::Fill->new(fountain=>'linear',
+                               xa => 20, ya=>20, xb=>20, yb=>40,
+                               segments=>"invalid");
+  ok(!$fill, "passing invalid segments produces an error");
+  cmp_ok(Imager->errstr, '=~', 'array reference',
+         "check the error message");
+}
+
+# test that colors in segments are converted
+{
+  my @segs =
+    (
+     [ 0.0, 0.5, 1.0, '000000', '#FFF', 0, 0 ],
+    );
+  my $fill = Imager::Fill->new(fountain=>'linear',
+                               xa => 0, ya=>20, xb=>49, yb=>20,
+                               segments=>\@segs);
+  ok($fill, "check that color names are converted")
+    or print "# ",Imager->errstr,"\n";
+  my $im = Imager->new(xsize=>50, ysize=>50);
+  $im->box(fill=>$fill);
+  my $left = $im->getpixel('x'=>0, 'y'=>20);
+  ok(color_close($left, Imager::Color->new(0,0,0)),
+     "check black converted correctly");
+  my $right = $im->getpixel('x'=>49, 'y'=>20);
+  ok(color_close($right, Imager::Color->new(255,255,255)),
+     "check white converted correctly");
+
+  # check that invalid colors handled correctly
+  
+  my @segs2 =
+    (
+     [ 0.0, 0.5, 1.0, '000000', 'FxFxFx', 0, 0 ],
+    );
+  my $fill2 = Imager::Fill->new(fountain=>'linear',
+                               xa => 0, ya=>20, xb=>49, yb=>20,
+                               segments=>\@segs2);
+  ok(!$fill2, "check handling of invalid color names");
+  cmp_ok(Imager->errstr, '=~', 'No color named', "check error message");
+}
+
+{ # RT #35278
+  # hatch fills on a grey scale image don't adapt colors
+  for my $bits (8, 'double') {
+    my $im_g = Imager->new(xsize => 10, ysize => 10, channels => 1, bits => $bits);
+    $im_g->box(filled => 1, color => 'FFFFFF');
+    my $fill = Imager::Fill->new
+      (
+       combine => 'normal', 
+       hatch => 'weave', 
+       fg => '000000', 
+       bg => 'FFFFFF'
+      );
+    $im_g->box(fill => $fill);
+    my $im_c = Imager->new(xsize => 10, ysize => 10, channels => 3, bits => $bits);
+    $im_c->box(filled => 1, color => 'FFFFFF');
+    $im_c->box(fill => $fill);
+    my $im_cg = $im_g->convert(preset => 'rgb');
+    is_image($im_c, $im_cg, "check hatch is the same between color and greyscale (bits $bits)");
+
+    # check the same for image fills
+    my $grey_fill = Imager::Fill->new
+      (
+       image => $im_g, 
+       combine => 'normal'
+      );
+    my $im_cfg = Imager->new(xsize => 20, ysize => 20, bits => $bits);
+    $im_cfg->box(filled => 1, color => '808080');
+    $im_cfg->box(fill => $grey_fill);
+    my $rgb_fill = Imager::Fill->new
+      (
+       image => $im_cg, 
+       combine => 'normal'
+      );
+    my $im_cfc = Imager->new(xsize => 20, ysize => 20, bits => $bits);
+    $im_cfc->box(filled => 1, color => '808080');
+    $im_cfc->box(fill => $rgb_fill);
+    is_image($im_cfg, $im_cfc, "check filling from grey image matches filling from rgb (bits = $bits)");
+
+    my $im_gfg = Imager->new(xsize => 20, ysize => 20, channels => 1, bits => $bits);
+    $im_gfg->box(filled => 1, color => '808080');
+    $im_gfg->box(fill => $grey_fill);
+    my $im_gfg_c = $im_gfg->convert(preset => 'rgb');
+    is_image($im_gfg_c, $im_cfg, "check grey filled with grey against base (bits = $bits)");
+
+    my $im_gfc = Imager->new(xsize => 20, ysize => 20, channels => 1, bits => $bits);
+    $im_gfc->box(filled => 1, color => '808080');
+    $im_gfc->box(fill => $rgb_fill);
+    my $im_gfc_c = $im_gfc->convert(preset => 'rgb');
+    is_image($im_gfc_c, $im_cfg, "check grey filled with color against base (bits = $bits)");
+  }
+}
+
+{ # alpha modifying fills
+  { # 8-bit/sample
+    my $base_img = Imager->new(xsize => 4, ysize => 2, channels => 4);
+    $base_img->setscanline
+      (
+       x => 0, 
+       y => 0, 
+       pixels => 
+       [
+       map Imager::Color->new($_),
+       qw/FF000020 00FF0080 00008040 FFFF00FF/,
+       ],
+      );
+    $base_img->setscanline
+      (
+       x => 0, 
+       y => 1, 
+       pixels => 
+       [
+       map Imager::Color->new($_),
+       qw/FFFF00FF FF000000 00FF0080 00008040/
+       ]
+      );
+    my $base_fill = Imager::Fill->new
+      (
+       image => $base_img,
+       combine => "normal",
+      );
+    ok($base_fill, "make the base image fill");
+    my $fill50 = Imager::Fill->new(type => "opacity", opacity => 0.5, other => $base_fill)
+      or print "# ", Imager->errstr, "\n";
+    ok($fill50, "make 50% alpha translation fill");
+
+    { # 4 channel image
+      my $out = Imager->new(xsize => 10, ysize => 10, channels => 4);
+      $out->box(fill => $fill50);
+      is_color4($out->getpixel(x => 0, y => 0),
+               255, 0, 0, 16, "check alpha output");
+      is_color4($out->getpixel(x => 2, y => 1),
+               0, 255, 0, 64, "check alpha output");
+      $out->box(filled => 1, color => "000000");
+      is_color4($out->getpixel(x => 0, y => 0),
+               0, 0, 0, 255, "check after clear");
+      $out->box(fill => $fill50);
+      is_color4($out->getpixel(x => 4, y => 2),
+               16, 0, 0, 255, "check drawn against background");
+      is_color4($out->getpixel(x => 6, y => 3),
+               0, 64, 0, 255, "check drawn against background");
+    }
+    { # 3 channel image
+      my $out = Imager->new(xsize => 10, ysize => 10, channels => 3);
+      $out->box(fill => $fill50);
+      is_color3($out->getpixel(x => 0, y => 0),
+               16, 0, 0, "check alpha output");
+      is_color3($out->getpixel(x => 2, y => 1),
+               0, 64, 0, "check alpha output");
+      is_color3($out->getpixel(x => 0, y => 1),
+               128, 128, 0, "check alpha output");
+    }
+  }
+  { # double/sample
+    use Imager::Color::Float;
+    my $base_img = Imager->new(xsize => 4, ysize => 2, channels => 4, bits => "double");
+    $base_img->setscanline
+      (
+       x => 0, 
+       y => 0, 
+       pixels => 
+       [
+       map Imager::Color::Float->new(@$_),
+       [ 1, 0, 0, 0.125 ],
+       [ 0, 1, 0, 0.5 ],
+       [ 0, 0, 0.5, 0.25 ],
+       [ 1, 1, 0, 1 ],
+       ],
+      );
+    $base_img->setscanline
+      (
+       x => 0, 
+       y => 1, 
+       pixels => 
+       [
+       map Imager::Color::Float->new(@$_),
+       [ 1, 1, 0, 1 ],
+       [ 1, 0, 0, 0 ],
+       [ 0, 1, 0, 0.5 ],
+       [ 0, 0, 0.5, 0.25 ],
+       ]
+      );
+    my $base_fill = Imager::Fill->new
+      (
+       image => $base_img,
+       combine => "normal",
+      );
+    ok($base_fill, "make the base image fill");
+    my $fill50 = Imager::Fill->new(type => "opacity", opacity => 0.5, other => $base_fill)
+      or print "# ", Imager->errstr, "\n";
+    ok($fill50, "make 50% alpha translation fill");
+    my $out = Imager->new(xsize => 10, ysize => 10, channels => 4, bits => "double");
+    $out->box(fill => $fill50);
+    is_fcolor4($out->getpixel(x => 0, y => 0, type => "float"),
+             1, 0, 0, 0.0625, "check alpha output at 0,0");
+    is_fcolor4($out->getpixel(x => 2, y => 1, type => "float"),
+             0, 1, 0, 0.25, "check alpha output at 2,1");
+    $out->box(filled => 1, color => "000000");
+    is_fcolor4($out->getpixel(x => 0, y => 0, type => "float"),
+             0, 0, 0, 1, "check after clear");
+    $out->box(fill => $fill50);
+    is_fcolor4($out->getpixel(x => 4, y => 2, type => "float"),
+             0.0625, 0, 0, 1, "check drawn against background at 4,2");
+    is_fcolor4($out->getpixel(x => 6, y => 3, type => "float"),
+             0, 0.25, 0, 1, "check drawn against background at 6,3");
+  }
+  ok(!Imager::Fill->new(type => "opacity"),
+     "should fail to make an opacity fill with no other fill object");
+  is(Imager->errstr, "'other' parameter required to create opacity fill",
+     "check error message");
+  ok(!Imager::Fill->new(type => "opacity", other => "xx"),
+     "should fail to make an opacity fill with a bad other parameter");
+  is(Imager->errstr, "'other' parameter must be an Imager::Fill object to create an opacity fill", 
+        "check error message");
+
+  # check auto conversion of hashes
+  ok(Imager::Fill->new(type => "opacity", other => { solid => "FF0000" }),
+     "check we auto-create fills")
+    or print "# ", Imager->errstr, "\n";
+
+  {
+    # fill with combine none was modifying the wrong channel for a
+    # no-alpha target image
+    my $fill = Imager::Fill->new(solid => "#FFF", combine => "none");
+    my $fill2 = Imager::Fill->new
+      (
+       type => "opacity", 
+       opacity => 0.5,
+       other => $fill
+      );
+    my $im = Imager->new(xsize => 1, ysize => 1);
+    ok($im->box(fill => $fill2), "fill with replacement opacity fill");
+    is_color3($im->getpixel(x => 0, y => 0), 255, 255, 255,
+             "check for correct colour");
+  }
+
+  {
+    require Imager::Fountain;
+    my $fount = Imager::Fountain->new;
+    $fount->add(c1 => "FFFFFF"); # simple white to black
+    # base fill is a fountain
+    my $base_fill = Imager::Fill->new
+      (
+       fountain => "linear",
+       segments => $fount,
+       xa => 0, 
+       ya => 0,
+       xb => 100,
+       yb => 100,
+      );
+    ok($base_fill, "made fountain fill base");
+    my $op_fill = Imager::Fill->new
+      (
+       type => "opacity",
+       other => $base_fill,
+       opacity => 0.5,
+      );
+    ok($op_fill, "made opacity fountain fill");
+    my $im = Imager->new(xsize => 100, ysize => 100);
+    ok($im->box(fill => $op_fill), "draw with it");
+  }
+}
+
+{ # RT 71309
+  my $fount = Imager::Fountain->simple(colors => [ '#804041', '#804041' ],
+                                      positions => [ 0, 1 ]);
+  my $im = Imager->new(xsize => 40, ysize => 40);
+  $im->box(filled => 1, color => '#804040');
+  my $fill = Imager::Fill->new
+    (
+     combine => 0,
+     fountain => "linear",
+     segments => $fount,
+     xa => 0, ya => 0,
+     xb => 40, yb => 40,
+    );
+  $im->polygon(fill => $fill,
+              points => 
+              [
+               [ 0, 0 ],
+               [ 40, 20 ],
+               [ 20, 40 ],
+              ]
+             );
+  # the bug magnified the differences between the source and destination
+  # color, blending between the background and fill colors here only allows
+  # for those 2 colors in the result.
+  # with the bug extra colors appeared along the edge of the polygon.
+  is($im->getcolorcount, 2, "only original and fill color");
+}
+
+SKIP:
+{
+  # the wrong image dimension was used for adjusting vs yoff,
+  # producing uncovered parts of the output image
+  my $tx = Imager->new(xsize => 30, ysize => 20);
+  ok($tx, "create texture image")
+    or diag "create texture image", Imager->errstr;
+  $tx or skip "no texture image", 7;
+  ok($tx->box(filled => 1, color => "ff0000"), "fill texture image")
+    or diag "fill texture image", $tx->errstr;
+  my $cmp = Imager->new(xsize => 100, ysize => 100);
+  ok($cmp, "create comparison image")
+    or diag "create comparison image: ", Imager->errstr;
+  $cmp or skip "no comparison image", 5;
+  ok($cmp->box(filled => 1, color => "FF0000"), "fill compare image")
+    or diag "fill compare image: ", $cmp->errstr;
+  my $im = Imager->new(xsize => 100, ysize => 100);
+  ok($im, "make test image")
+    or diag "make test image: ", Imager->errstr;
+  $im or skip "no test image", 3;
+  my $fill = Imager::Fill->new(image => $tx, yoff => 10);
+  ok($fill, "make xoff=10 image fill")
+    or diag "make fill: ", Imager->errstr;
+  $fill or skip "no fill", 2;
+  ok($im->box(fill => $fill), "fill test image")
+    or diag "fill test image: ", $im->errstr;
+  is_image($im, $cmp, "check test image");
+}
+
+sub color_close {
+  my ($c1, $c2) = @_;
+
+  my @c1 = $c1->rgba;
+  my @c2 = $c2->rgba;
+
+  for my $i (0..2) {
+    if (abs($c1[$i]-$c2[$i]) > 2) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+sub color_close4 {
+  my ($c1, $c2) = @_;
+
+  my @c1 = $c1->rgba;
+  my @c2 = $c2->rgba;
+
+  for my $i (0..3) {
+    if (abs($c1[$i]-$c2[$i]) > 2) {
+      return 0;
+    }
+  }
+  return 1;
+}
+
+# for use during testing
+sub save {
+  my ($im, $name) = @_;
+
+  open FH, "> $name" or die "Cannot create $name: $!";
+  binmode FH;
+  my $io = Imager::io_new_fd(fileno(FH));
+  Imager::i_writeppm_wiol($im, $io) or die "Cannot save to $name";
+  undef $io;
+  close FH;
+}
diff --git a/t/250-draw/200-compose.t b/t/250-draw/200-compose.t
new file mode 100644 (file)
index 0000000..cbf8af3
--- /dev/null
@@ -0,0 +1,269 @@
+#!perl -w
+use strict;
+use Imager qw(:handy);
+use Test::More tests => 120;
+use Imager::Test qw(is_image is_imaged);
+
+-d "testout" or mkdir "testout";
+
+Imager::init_log("testout/t62compose.log", 1);
+
+my @files;
+
+my %types =
+  (
+   double =>
+   {
+    blue => NCF(0, 0, 1),
+    red =>  NCF(1, 0, 0),
+    green2 => NCF(0, 1, 0, 0.5),
+    green2_on_blue => NCF(0, 0.5, 0.5),
+    red3_on_blue => NCF(1/3, 0, 2/3),
+    green6_on_blue => NCF(0, 1/6, 5/6),
+    red2_on_blue => NCF(0.5, 0, 0.5),
+    green4_on_blue => NCF(0, 0.25, 0.75),
+    gray100 => NCF(1.0, 0, 0),
+    gray50 => NCF(0.5, 0, 0),
+    is_image => \&is_imaged,
+   },
+   8 =>
+   {
+    blue => NC(0, 0, 255),
+    red =>  NC(255, 0, 0),
+    green2 => NC(0, 255, 0, 128),
+    green2_on_blue => NC(0, 128, 127),
+    red3_on_blue => NC(85, 0, 170),
+    green6_on_blue => NC(0, 42, 213),
+    red2_on_blue => NC(128, 0, 127),
+    green4_on_blue => NC(0, 64, 191),
+    gray100 => NC(255, 0, 0),
+    gray50 => NC(128, 0, 0),
+    is_image => \&is_image,
+   },
+  );
+
+for my $type_id (sort keys %types) {
+  my $type = $types{$type_id};
+  my $blue = $type->{blue};
+  my $red = $type->{red};
+  my $green2 = $type->{green2};
+  my $green2_on_blue = $type->{green2_on_blue};
+  my $red3_on_blue = $type->{red3_on_blue};
+  my $green6_on_blue = $type->{green6_on_blue};
+  my $red2_on_blue = $type->{red2_on_blue};
+  my $green4_on_blue = $type->{green4_on_blue};
+  my $gray100 = $type->{gray100};
+  my $gray50 = $type->{gray50};
+  my $is_image = $type->{is_image};
+
+  print "# type $type_id\n";
+  my $targ = Imager->new(xsize => 100, ysize => 100, bits => $type_id);
+  $targ->box(color => $blue, filled => 1);
+  is($targ->type, "direct", "check target image type");
+  is($targ->bits, $type_id, "check target bits");
+
+  my $src = Imager->new(xsize => 40, ysize => 40, channels => 4, bits => $type_id);
+  $src->box(filled => 1, color => $red, xmax => 19, ymax => 19);
+  $src->box(filled => 1, xmin => 20, color => $green2);
+  save_to($src, "${type_id}_src");
+
+  my $mask_ones = Imager->new(channels => 1, xsize => 40, ysize => 40, bits => $type_id);
+  $mask_ones->box(filled => 1, color => NC(255, 255, 255));
+
+
+  # mask or full mask, should be the same
+  for my $mask_info ([ "nomask" ], [ "fullmask", mask => $mask_ones ]) {
+    my ($mask_type, @mask_extras) = @$mask_info;
+    print "# $mask_type\n";
+    {
+      my $cmp = $targ->copy;
+      $cmp->box(filled => 1, color => $red,
+               xmin=> 5, ymin => 10, xmax => 24, ymax => 29);
+      $cmp->box(filled => 1, color => $green2_on_blue,
+               xmin => 25, ymin => 10, xmax => 44, ymax => 49);
+      {
+       my $work = $targ->copy;
+       ok($work->compose(src => $src, tx => 5, ty => 10, @mask_extras),
+          "$mask_type - simple compose");
+       $is_image->($work, $cmp, "check match");
+       save_to($work, "${type_id}_${mask_type}_simple");
+      }
+      { # >1 opacity
+       my $work = $targ->copy;
+       ok($work->compose(src => $src, tx => 5, ty => 10, opacity => 2.0, @mask_extras),
+          "$mask_type - compose with opacity > 1.0 acts like opacity=1.0");
+       $is_image->($work, $cmp, "check match");
+      }
+      { # 0 opacity is a failure
+       my $work = $targ->copy;
+       ok(!$work->compose(src => $src, tx => 5, ty => 10, opacity => 0.0, @mask_extras),
+          "$mask_type - compose with opacity = 0 is an error");
+       is($work->errstr, "opacity must be positive", "check message");
+      }
+    }
+    { # compose at 1/3
+      my $work = $targ->copy;
+      ok($work->compose(src => $src, tx => 7, ty => 33, opacity => 1/3, @mask_extras),
+        "$mask_type - simple compose at 1/3");
+      my $cmp = $targ->copy;
+      $cmp->box(filled => 1, color => $red3_on_blue,
+               xmin => 7, ymin => 33, xmax => 26, ymax => 52);
+      $cmp->box(filled => 1, color => $green6_on_blue,
+               xmin => 27, ymin => 33, xmax => 46, ymax => 72);
+      $is_image->($work, $cmp, "check match");
+    }
+    { # targ off top left
+      my $work = $targ->copy;
+      ok($work->compose(src => $src, tx => -5, ty => -3, @mask_extras),
+        "$mask_type - compose off top left");
+      my $cmp = $targ->copy;
+      $cmp->box(filled => 1, color => $red,
+               xmin=> 0, ymin => 0, xmax => 14, ymax => 16);
+      $cmp->box(filled => 1, color => $green2_on_blue,
+               xmin => 15, ymin => 0, xmax => 34, ymax => 36);
+      $is_image->($work, $cmp, "check match");
+    }
+    { # targ off bottom right
+      my $work = $targ->copy;
+      ok($work->compose(src => $src, tx => 65, ty => 67, @mask_extras),
+        "$mask_type - targ off bottom right");
+      my $cmp = $targ->copy;
+      $cmp->box(filled => 1, color => $red,
+               xmin=> 65, ymin => 67, xmax => 84, ymax => 86);
+      $cmp->box(filled => 1, color => $green2_on_blue,
+               xmin => 85, ymin => 67, xmax => 99, ymax => 99);
+      $is_image->($work, $cmp, "check match");
+    }
+    { # src off top left
+      my $work = $targ->copy;
+      my @more_mask_extras;
+      if (@mask_extras) {
+       push @more_mask_extras,
+         (
+          mask_left => -5,
+          mask_top => -15,
+         );
+      }
+      ok($work->compose(src => $src, tx => 10, ty => 20,
+                       src_left => -5, src_top => -15,
+                       @mask_extras, @more_mask_extras),
+        "$mask_type - source off top left");
+      my $cmp = $targ->copy;
+      $cmp->box(filled => 1, color => $red,
+               xmin=> 15, ymin => 35, xmax => 34, ymax => 54);
+      $cmp->box(filled => 1, color => $green2_on_blue,
+             xmin => 35, ymin => 35, xmax => 54, ymax => 74);
+      $is_image->($work, $cmp, "check match");
+    }
+    {
+      # src off bottom right
+      my $work = $targ->copy;
+      ok($work->compose(src => $src, tx => 10, ty => 20,
+                       src_left => 10, src_top => 15,
+                       width => 40, height => 40, @mask_extras),
+        "$mask_type - source off bottom right");
+      my $cmp = $targ->copy;
+      $cmp->box(filled => 1, color => $red,
+               xmin=> 10, ymin => 20, xmax => 19, ymax => 24);
+      $cmp->box(filled => 1, color => $green2_on_blue,
+               xmin => 20, ymin => 20, xmax => 39, ymax => 44);
+      $is_image->($work, $cmp, "check match");
+    }
+    {
+      # simply out of bounds
+      my $work = $targ->copy;
+      ok(!$work->compose(src => $src, tx => 100, @mask_extras),
+        "$mask_type - off the right of the target");
+      $is_image->($work, $targ, "no changes");
+      ok(!$work->compose(src => $src, ty => 100, @mask_extras),
+        "$mask_type - off the bottom of the target");
+      $is_image->($work, $targ, "no changes");
+      ok(!$work->compose(src => $src, tx => -40, @mask_extras),
+        "$mask_type - off the left of the target");
+      $is_image->($work, $targ, "no changes");
+      ok(!$work->compose(src => $src, ty => -40, @mask_extras),
+        "$mask_type - off the top of the target");
+      $is_image->($work, $targ, "no changes");
+    }
+  }
+
+  # masked tests
+  my $mask = Imager->new(xsize => 40, ysize => 40, channels => 1, bits => $type_id);
+  $mask->box(filled => 1, xmax => 19, color => $gray100);
+  $mask->box(filled => 1, xmin => 20, ymax => 14, xmax => 34,
+            color => $gray50);
+  is($mask->bits, $type_id, "check mask bits");
+  {
+    my $work = $targ->copy;
+    ok($work->compose(src => $src, tx => 5, ty => 7,
+                     mask => $mask),
+       "simple draw masked");
+    my $cmp = $targ->copy;
+    $cmp->box(filled => 1, color => $red,
+             xmin => 5, ymin => 7, xmax => 24, ymax => 26);
+    $cmp->box(filled => 1, color => $green4_on_blue,
+             xmin => 25, ymin => 7, xmax => 39, ymax => 21);
+    $is_image->($work, $cmp, "check match");
+    save_to($work, "${type_id}_simp_masked");
+    save_to($work, "${type_id}_simp_masked_cmp");
+  }
+  {
+    my $work = $targ->copy;
+    ok($work->compose(src => $src, tx => 5, ty => 7,
+                     mask_left => 5, mask_top => 2, 
+                     mask => $mask),
+       "draw with mask offset");
+    my $cmp = $targ->copy;
+    $cmp->box(filled => 1, color => $red,
+             xmin => 5, ymin => 7, xmax => 19, ymax => 26);
+    $cmp->box(filled => 1, color => $red2_on_blue,
+             xmin => 20, ymin => 7, xmax => 24, ymax => 19);
+    $cmp->box(filled => 1, color => $green4_on_blue,
+             xmin => 25, ymin => 7, xmax => 34, ymax => 19);
+    $is_image->($work, $cmp, "check match");
+  }
+  {
+    my $work = $targ->copy;
+    ok($work->compose(src => $src, tx => 5, ty => 7,
+                     mask_left => -3, mask_top => -2, 
+                     mask => $mask),
+       "draw with negative mask offsets");
+    my $cmp = $targ->copy;
+    $cmp->box(filled => 1, color => $red,
+             xmin => 8, ymin => 9, xmax => 24, ymax => 26);
+    $cmp->box(filled => 1, color => $green2_on_blue,
+             xmin => 25, ymin => 9, xmax => 27, ymax => 46);
+    $cmp->box(filled => 1, color => $green4_on_blue,
+             xmin => 28, ymin => 9, xmax => 42, ymax => 23);
+    $is_image->($work, $cmp, "check match");
+  }
+}
+
+{
+  my $empty = Imager->new;
+  my $good = Imager->new(xsize => 1, ysize => 1);
+  ok(!$empty->compose(src => $good), "can't compose to empty image");
+  is($empty->errstr, "compose: empty input image",
+     "check error message");
+  ok(!$good->compose(src => $empty), "can't compose from empty image");
+  is($good->errstr, "compose: empty input image (for src)",
+     "check error message");
+  ok(!$good->compose(src => $good, mask => $empty),
+     "can't compose with empty mask");
+  is($good->errstr, "compose: empty input image (for mask)",
+     "check error message");
+}
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink @files;
+}
+
+sub save_to {
+  my ($im, $name) = @_;
+
+  my $type = $ENV{IMAGER_SAVE_TYPE} || "ppm";
+  $name = "testout/t62_$name.$type";
+  $im->write(file => $name,
+            pnm_write_wide_data => 1);
+  push @files, $name;
+}
diff --git a/t/300-transform/010-scale.t b/t/300-transform/010-scale.t
new file mode 100644 (file)
index 0000000..c01eaad
--- /dev/null
@@ -0,0 +1,255 @@
+#!perl -w
+use strict;
+use Test::More tests => 232;
+
+BEGIN { use_ok(Imager=>':all') }
+use Imager::Test qw(is_image is_color4 is_image_similar);
+
+-d "testout" or mkdir "testout";
+
+Imager::init('log'=>'testout/t40scale.log');
+my $img=Imager->new();
+
+ok($img->open(file=>'testimg/scale.ppm',type=>'pnm'),
+   "load test image") or print "# ",$img->errstr,"\n";
+
+my $scaleimg=$img->scale(scalefactor=>0.25)
+  or print "# ",$img->errstr,"\n";
+ok($scaleimg, "scale it (good mode)");
+
+ok($scaleimg->write(file=>'testout/t40scale1.ppm',type=>'pnm'),
+   "save scaled image") or print "# ",$img->errstr,"\n";
+
+$scaleimg=$img->scale(scalefactor=>0.25,qtype=>'preview');
+ok($scaleimg, "scale it (preview)") or print "# ",$img->errstr,"\n";
+
+ok($scaleimg->write(file=>'testout/t40scale2.ppm',type=>'pnm'),
+   "write preview scaled image")  or print "# ",$img->errstr,"\n";
+
+$scaleimg = $img->scale(scalefactor => 0.25, qtype => 'mixing');
+ok($scaleimg, "scale it (mixing)") or print "# ", $img->errstr, "\n";
+ok($scaleimg->write(file=>'testout/t40scale3.ppm', type=>'pnm'),
+   "write mixing scaled image") or print "# ", $img->errstr, "\n";
+
+{ # double image scaling with mixing, since it has code to handle it
+  my $dimg = Imager->new(xsize => $img->getwidth, ysize => $img->getheight,
+                         channels => $img->getchannels,
+                         bits => 'double');
+  ok($dimg, "create double/sample image");
+  $dimg->paste(src => $img);
+  $scaleimg = $dimg->scale(scalefactor => 0.25, qtype => 'mixing');
+  ok($scaleimg, "scale it (mixing, double)");
+  ok($scaleimg->write(file => 'testout/t40mixdbl.ppm', type => 'pnm'),
+     "write double/mixing scaled image");
+  is($scaleimg->bits, 'double', "got the right image type as output");
+
+  # hscale only, mixing
+  $scaleimg = $dimg->scale(xscalefactor => 0.33, yscalefactor => 1.0,
+                           qtype => 'mixing');
+  ok($scaleimg, "scale it (hscale, mixing, double)");
+  is($scaleimg->getheight, $dimg->getheight, "same height");
+  ok($scaleimg->write(file => 'testout/t40hscdmix.ppm', type => 'pnm'),
+     "save it");
+
+  # vscale only, mixing
+  $scaleimg = $dimg->scale(xscalefactor => 1.0, yscalefactor => 0.33,
+                           qtype => 'mixing');
+  ok($scaleimg, "scale it (vscale, mixing, double)");
+  is($scaleimg->getwidth, $dimg->getwidth, "same width");
+  ok($scaleimg->write(file => 'testout/t40vscdmix.ppm', type => 'pnm'),
+     "save it");
+}
+
+{
+  # check for a warning when scale() is called in void context
+  my $warning;
+  local $SIG{__WARN__} = 
+    sub { 
+      $warning = "@_";
+      my $printed = $warning;
+      $printed =~ s/\n$//;
+      $printed =~ s/\n/\n\#/g; 
+      print "# ",$printed, "\n";
+    };
+  $img->scale(scalefactor=>0.25);
+  cmp_ok($warning, '=~', qr/void/, "check warning");
+  cmp_ok($warning, '=~', qr/scale\.t/, "check filename");
+  $warning = '';
+  $img->scaleX(scalefactor=>0.25);
+  cmp_ok($warning, '=~', qr/void/, "check warning");
+  cmp_ok($warning, '=~', qr/scale\.t/, "check filename");
+  $warning = '';
+  $img->scaleY(scalefactor=>0.25);
+  cmp_ok($warning, '=~', qr/void/, "check warning");
+  cmp_ok($warning, '=~', qr/scale\.t/, "check filename");
+}
+{ # https://rt.cpan.org/Ticket/Display.html?id=7467
+  # segfault in Imager 0.43
+  # make sure scale() doesn't let us make an image zero pixels high or wide
+  # it does this by making the given axis as least 1 pixel high
+  my $out = $img->scale(scalefactor=>0.00001);
+  is($out->getwidth, 1, "min scale width");
+  is($out->getheight, 1, "min scale height");
+
+  $out = $img->scale(scalefactor=>0.00001, qtype => 'preview');
+  is($out->getwidth, 1, "min scale width (preview)");
+  is($out->getheight, 1, "min scale height (preview)");
+
+  $out = $img->scale(scalefactor=>0.00001, qtype => 'mixing');
+  is($out->getwidth, 1, "min scale width (mixing)");
+  is($out->getheight, 1, "min scale height (mixing)");
+}
+
+{ # error handling - NULL image
+  my $im = Imager->new;
+  ok(!$im->scale(scalefactor => 0.5), "try to scale empty image");
+  is($im->errstr, "scale: empty input image", "check error message");
+
+  # scaleX/scaleY
+  ok(!$im->scaleX(scalefactor => 0.5), "try to scaleX empty image");
+  is($im->errstr, "scaleX: empty input image", "check error message");
+  ok(!$im->scaleY(scalefactor => 0.5), "try to scaleY empty image");
+  is($im->errstr, "scaleY: empty input image", "check error message");
+}
+
+{ # invalid qtype value
+  my $im = Imager->new(xsize => 100, ysize => 100);
+  ok(!$im->scale(scalefactor => 0.5, qtype=>'unknown'), "unknown qtype");
+  is($im->errstr, "invalid value for qtype parameter", "check error message");
+  
+  # invalid type value
+  ok(!$im->scale(xpixels => 10, ypixels=>50, type=>"unknown"), "unknown type");
+  is($im->errstr, "invalid value for type parameter", "check error message");
+}
+
+SKIP:
+{ # Image::Math::Constrain support
+  eval "require Image::Math::Constrain;";
+  $@ and skip "optional module Image::Math::Constrain not installed", 3;
+  my $constrain = Image::Math::Constrain->new(20, 100);
+  my $im = Imager->new(xsize => 160, ysize => 96);
+  my $result = $im->scale(constrain => $constrain);
+  ok($result, "successful scale with Image::Math::Constrain");
+  is($result->getwidth, 20, "check result width");
+  is($result->getheight, 12, "check result height");
+}
+
+{ # scale size checks
+  my $im = Imager->new(xsize => 160, ysize => 96); # some random size
+
+  scale_test($im, 'scale', 80, 48, "48 x 48 def type",
+            xpixels => 48, ypixels => 48);
+  scale_test($im, 'scale', 80, 48, "48 x 48 max type",
+            xpixels => 48, ypixels => 48, type => 'max');
+  scale_test($im, 'scale', 80, 48, "80 x 80 min type",
+            xpixels => 80, ypixels => 80, type => 'min');
+  scale_test($im, 'scale', 80, 48, "no scale parameters (default to 0.5 scalefactor)");
+  scale_test($im, 'scale', 120, 72, "0.75 scalefactor",
+            scalefactor => 0.75);
+  scale_test($im, 'scale', 80, 48, "80 width",
+            xpixels => 80);
+  scale_test($im, 'scale', 120, 72, "72 height",
+            ypixels => 72);
+
+  # new scaling parameters in 0.54
+  scale_test($im, 'scale', 80, 48, "xscale 0.5",
+            xscalefactor => 0.5);
+  scale_test($im, 'scale', 80, 48, "yscale 0.5",
+            yscalefactor => 0.5);
+  scale_test($im, 'scale', 40, 48, "xscale 0.25 yscale 0.5",
+            xscalefactor => 0.25, yscalefactor => 0.5);
+  scale_test($im, 'scale', 160, 48, "xscale 1.0 yscale 0.5",
+            xscalefactor => 1.0, yscalefactor => 0.5);
+  scale_test($im, 'scale', 160, 48, "xpixels 160 ypixels 48 type nonprop",
+            xpixels => 160, ypixels => 48, type => 'nonprop');
+  scale_test($im, 'scale', 160, 96, "xpixels 160 ypixels 96",
+            xpixels => 160, ypixels => 96);
+  scale_test($im, 'scale', 80, 96, "xpixels 80 ypixels 96 type nonprop",
+            xpixels => 80, ypixels => 96, type => 'nonprop');
+
+  # scaleX
+  scale_test($im, 'scaleX', 80, 96, "defaults");
+  scale_test($im, 'scaleX', 40, 96, "0.25 scalefactor",
+             scalefactor => 0.25);
+  scale_test($im, 'scaleX', 120, 96, "pixels 120",
+             pixels => 120);
+
+  # scaleY
+  scale_test($im, 'scaleY', 160, 48, "defaults");
+  scale_test($im, 'scaleY', 160, 192, "2.0 scalefactor",
+             scalefactor => 2.0);
+  scale_test($im, 'scaleY', 160, 144, "pixels 144",
+             pixels => 144);
+}
+
+{ # check proper alpha handling for mixing
+  my $im = Imager->new(xsize => 40, ysize => 40, channels => 4);
+  $im->box(filled => 1, color => 'C0C0C0');
+  my $rot = $im->rotate(degrees => -4)
+    or die;
+  $rot = $rot->to_rgb16;
+  my $sc = $rot->scale(qtype => 'mixing', xpixels => 40);
+  my $out = Imager->new(xsize => $sc->getwidth, ysize => $sc->getheight);
+  $out->box(filled => 1, color => 'C0C0C0');
+  my $cmp = $out->copy;
+  $out->rubthrough(src => $sc);
+  is_image($out, $cmp, "check we get the right image after scaling (mixing)");
+
+  # we now set alpha=0 pixels to zero on scaling
+  is_color4($sc->getpixel('x' => 39, 'y' => 39), 0, 0, 0, 0,
+           "check we set alpha=0 pixels to zero on scaling");
+}
+
+{ # check proper alpha handling for default scaling
+  my $im = Imager->new(xsize => 40, ysize => 40, channels => 4);
+  $im->box(filled => 1, color => 'C0C0C0');
+  my $rot = $im->rotate(degrees => -4)
+    or die;
+  my $sc = $rot->scale(qtype => "normal", xpixels => 40);
+  my $out = Imager->new(xsize => $sc->getwidth, ysize => $sc->getheight);
+  $out->box(filled => 1, color => 'C0C0C0');
+  my $cmp = $out->copy;
+  $out->rubthrough(src => $sc);
+  is_image_similar($out, $cmp, 100, "check we get the right image after scaling (normal)");
+
+  # we now set alpha=0 pixels to zero on scaling
+  is_color4($sc->getpixel('x' => 39, 'y' => 39), 0, 0, 0, 0,
+           "check we set alpha=0 pixels to zero on scaling");
+}
+
+{ # scale_calculate
+  my $im = Imager->new(xsize => 100, ysize => 120);
+  is_deeply([ $im->scale_calculate(scalefactor => 0.5) ],
+           [ 0.5, 0.5, 50, 60 ],
+           "simple scale_calculate");
+  is_deeply([ Imager->scale_calculate(scalefactor => 0.5) ],
+           [], "failed scale_calculate");
+  is_deeply([ Imager->scale_calculate(width => 120, height => 150,
+                                     xpixels => 240) ],
+           [ 2.0, 2.0, 240, 300 ],
+           "class method scale_factor");
+}
+
+{ # passing a reference for scaling parameters should fail
+  # RT #35172
+  my $im = Imager->new(xsize => 100, ysize => 100);
+  ok(!$im->scale(xpixels => {}), "can't use a reference as a size");
+  cmp_ok($im->errstr, '=~', "xpixels parameter cannot be a reference",
+        "check error message");
+}
+
+sub scale_test {
+  my ($in, $method, $exp_width, $exp_height, $note, @parms) = @_;
+
+  print "# $note: @parms\n";
+  for my $qtype (qw(normal preview mixing)) {
+  SKIP:
+    {
+      my $scaled = $in->$method(@parms, qtype => $qtype);
+      ok($scaled, "$method $note qtype $qtype")
+       or skip("failed to scale", 2);
+      is($scaled->getwidth, $exp_width, "check width");
+      is($scaled->getheight, $exp_height, "check height");
+    }
+  }
+}
diff --git a/t/300-transform/020-combine.t b/t/300-transform/020-combine.t
new file mode 100644 (file)
index 0000000..6cbe88b
--- /dev/null
@@ -0,0 +1,102 @@
+#!perl -w
+use strict;
+use Imager;
+use Test::More tests => 31;
+use Imager::Test qw/test_image test_image_double is_image/;
+
+my $test_im = test_image;
+my $test_im_dbl = test_image_double;
+
+{
+  # split out channels and put it back together
+  my $red = Imager->combine(src => [ $test_im ]);
+  ok($red, "extracted the red channel");
+  is($red->getchannels, 1, "red should be a single channel");
+  my $green = Imager->combine(src => [ $test_im ], channels => [ 1 ]);
+  ok($green, "extracted the green channel");
+  is($green->getchannels, 1, "green should be a single channel");
+  my $blue = $test_im->convert(preset => "blue");
+  ok($blue, "extracted blue (via convert)");
+
+  # put them back together
+  my $combined = Imager->combine(src => [ $red, $green, $blue ]);
+  is($combined->getchannels, 3, "check we got a three channel image");
+  is_image($combined, $test_im, "presto! check it's the same");
+}
+
+{
+  # no src
+  ok(!Imager->combine(), "no src");
+  is(Imager->errstr, "src parameter missing", "check message");
+}
+
+{
+  # bad image error
+  my $im = Imager->new;
+  ok(!Imager->combine(src => [ $im ]), "empty image");
+  is(Imager->errstr, "combine: empty input image (src->[0])",
+     "check message");
+}
+
+{
+  # not an image
+  my $im = {};
+  ok(!Imager->combine(src => [ $im ]), "not an image");
+  is(Imager->errstr, "src must contain image objects", "check message");
+}
+
+{
+  # no images
+  ok(!Imager->combine(src => []), "no images");
+  is(Imager->errstr, "At least one image must be supplied",
+     "check message");
+}
+
+{
+  # too many images
+  ok(!Imager->combine(src => [ ($test_im) x 5 ]), "too many source images");
+  is(Imager->errstr, "Maximum of 4 channels, you supplied 5",
+     "check message");
+}
+
+{
+  # negative channel
+  ok(!Imager->combine(src => [ $test_im ], channels => [ -1 ]),
+     "negative channel");
+  is(Imager->errstr, "Channel numbers must be zero or positive",
+     "check message");
+}
+
+{
+  # channel too high
+  ok(!Imager->combine(src => [ $test_im ], channels => [ 3 ]),
+     "too high channel");
+  is(Imager->errstr, "Channel 3 for image 0 is too high (3 channels)",
+     "check message");
+}
+
+{
+  # make sure we get the higher of the bits
+  my $out = Imager->combine(src => [ $test_im, $test_im_dbl ]);
+  ok($out, "make from 8 and double/sample images");
+  is($out->bits, "double", "check output bits");
+}
+
+{
+  # check high-bit processing
+  # split out channels and put it back together
+  my $red = Imager->combine(src => [ $test_im_dbl ]);
+  ok($red, "extracted the red channel");
+  is($red->getchannels, 1, "red should be a single channel");
+  my $green = Imager->combine(src => [ $test_im_dbl ], channels => [ 1 ]);
+  ok($green, "extracted the green channel");
+  is($green->getchannels, 1, "green should be a single channel");
+  my $blue = $test_im_dbl->convert(preset => "blue");
+  ok($blue, "extracted blue (via convert)");
+
+  # put them back together
+  my $combined = Imager->combine(src => [ $red, $green, $blue ]);
+  is($combined->getchannels, 3, "check we got a three channel image");
+  is_image($combined, $test_im_dbl, "presto! check it's the same");
+  is($combined->bits, "double", "and we got a double image output");
+}
diff --git a/t/300-transform/030-copyflip.t b/t/300-transform/030-copyflip.t
new file mode 100644 (file)
index 0000000..e08224f
--- /dev/null
@@ -0,0 +1,285 @@
+#!perl -w
+use strict;
+use Test::More tests => 95;
+use Imager;
+use Imager::Test qw(is_color3 is_image is_imaged test_image_double test_image isnt_image is_image_similar);
+
+#$Imager::DEBUG=1;
+
+-d "testout" or mkdir "testout";
+
+Imager::init('log'=>'testout/t64copyflip.log');
+
+my $img=Imager->new() or die "unable to create image object\n";
+
+$img->open(file=>'testimg/scale.ppm',type=>'pnm');
+my $nimg = $img->copy();
+ok($nimg, "copy returned something");
+
+# test if ->copy() works
+
+my $diff = Imager::i_img_diff($img->{IMG}, $nimg->{IMG});
+is_image($img, $nimg, "copy matches source");
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->copy, "fail to copy an empty image");
+  is($empty->errstr, "copy: empty input image", "check error message");
+}
+
+# test if ->flip(dir=>'h')->flip(dir=>'h') doesn't alter the image
+$nimg->flip(dir=>"h")->flip(dir=>"h");
+is_image($nimg, $img, "double horiz flipped matches original");
+
+# test if ->flip(dir=>'v')->flip(dir=>'v') doesn't alter the image
+$nimg->flip(dir=>"v")->flip(dir=>"v");
+is_image($nimg, $img, "double vertically flipped image matches original");
+
+
+# test if ->flip(dir=>'h')->flip(dir=>'v') is same as ->flip(dir=>'hv')
+$nimg->flip(dir=>"v")->flip(dir=>"h")->flip(dir=>"hv");;
+is_image($img, $nimg, "check flip with hv matches flip v then flip h");
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->flip(dir => "v"), "fail to flip an empty image");
+  is($empty->errstr, "flip: empty input image", "check error message");
+}
+
+{
+  my $imsrc = test_image_double;
+  my $imcp = $imsrc->copy;
+  is_imaged($imsrc, $imcp, "copy double image");
+  $imcp->flip(dir=>"v")->flip(dir=>"v");
+  is_imaged($imsrc, $imcp, "flip v twice");
+  $imcp->flip(dir=>"h")->flip(dir=>"h");
+  is_imaged($imsrc, $imcp, "flip h twice");
+  $imcp->flip(dir=>"h")->flip(dir=>"v")->flip(dir=>"hv");
+  is_imaged($imsrc, $imcp, "flip h,v,hv twice");
+}
+
+{
+  my $impal = test_image()->to_paletted;
+  my $imcp = $impal->copy;
+  is($impal->type, "paletted", "check paletted test image is");
+  is($imcp->type, "paletted", "check copy test image is paletted");
+  ok($impal->flip(dir => "h"), "flip paletted h");
+  isnt_image($impal, $imcp, "check it changed");
+  ok($impal->flip(dir => "v"), "flip paletted v");
+  ok($impal->flip(dir => "hv"), "flip paletted hv");
+  is_image($impal, $imcp, "should be back to original image");
+  is($impal->type, "paletted", "and still paletted");
+}
+
+rot_test($img, 90, 4);
+rot_test($img, 180, 2);
+rot_test($img, 270, 4);
+rot_test($img, 0, 1);
+
+my $pimg = $img->to_paletted();
+rot_test($pimg, 90, 4);
+rot_test($pimg, 180, 2);
+rot_test($pimg, 270, 4);
+rot_test($pimg, 0, 1);
+
+my $timg = $img->rotate(right=>90)->rotate(right=>270);
+is(Imager::i_img_diff($img->{IMG}, $timg->{IMG}), 0,
+   "check rotate 90 then 270 matches original");
+$timg = $img->rotate(right=>90)->rotate(right=>180)->rotate(right=>90);
+is(Imager::i_img_diff($img->{IMG}, $timg->{IMG}), 0,
+     "check rotate 90 then 180 then 90 matches original");
+
+# this could use more tests
+my $rimg = $img->rotate(degrees=>10);
+ok($rimg, "rotation by 10 degrees gave us an image");
+if (!$rimg->write(file=>"testout/t64_rot10.ppm")) {
+  print "# Cannot save: ",$rimg->errstr,"\n";
+}
+
+# rotate with background
+$rimg = $img->rotate(degrees=>10, back=>Imager::Color->new(builtin=>'red'));
+ok($rimg, "rotate with background gave us an image");
+if (!$rimg->write(file=>"testout/t64_rot10_back.ppm")) {
+  print "# Cannot save: ",$rimg->errstr,"\n";
+}
+
+{
+  # rotate with text background
+  my $rimg = $img->rotate(degrees => 45, back => '#FF00FF');
+  ok($rimg, "rotate with background as text gave us an image");
+  
+  # check the color set correctly
+  my $c = $rimg->getpixel(x => 0, 'y' => 0);
+  is_deeply([ 255, 0, 255 ], [ ($c->rgba)[0, 1, 2] ],
+            "check background set correctly");
+
+  # check error handling for background color
+  $rimg = $img->rotate(degrees => 45, back => "some really unknown color");
+  ok(!$rimg, "should fail due to bad back color");
+  cmp_ok($img->errstr, '=~', "^No color named ", "check error message");
+}
+SKIP:
+{ # rotate in double mode
+  my $dimg = $img->to_rgb16;
+  my $rimg = $dimg->rotate(degrees => 10);
+  ok($rimg, "rotate 16-bit image gave us an image")
+    or skip("could not rotate", 3);
+  ok($rimg->write(file => "testout/t64_rotf10.ppm", pnm_write_wide_data => 1),
+     "save wide data rotated")
+    or diag($rimg->errstr);
+
+  # with a background color
+  my $rimgb = $dimg->rotate(degrees => 10, back => "#FF8000");
+  ok($rimgb, "rotate 16-bit image with back gave us an image")
+    or skip("could not rotate", 1);
+  ok($rimgb->write(file => "testout/t64_rotfb10.ppm", pnm_write_wide_data => 1),
+     "save wide data rotated")
+    or diag($rimgb->errstr);
+}
+{ # rotate in paletted mode
+  my $rimg = $pimg->rotate(degrees => 10);
+  ok($rimg, "rotated paletted image 10 degrees");
+  ok($rimg->write(file => "testout/t64_rotp10.ppm"),
+     "save paletted rotated")
+    or diag($rimg->errstr);
+}
+
+my $trimg = $img->matrix_transform(matrix=>[ 1.2, 0, 0,
+                                             0,   1, 0,
+                                             0,   0, 1]);
+ok($trimg, "matrix_transform() returned an image");
+$trimg->write(file=>"testout/t64_trans.ppm")
+  or print "# Cannot save: ",$trimg->errstr,"\n";
+
+$trimg = $img->matrix_transform(matrix=>[ 1.2, 0, 0,
+                                             0,   1, 0,
+                                             0,   0, 1],
+                                  back=>Imager::Color->new(builtin=>'blue'));
+ok($trimg, "matrix_transform() with back returned an image");
+
+$trimg->write(file=>"testout/t64_trans_back.ppm")
+  or print "# Cannot save: ",$trimg->errstr,"\n";
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->matrix_transform(matrix => [ 1, 0, 0,
+                                          0, 1, 0,
+                                          0, 0, 1 ]),
+     "can't transform an empty image");
+  is($empty->errstr, "matrix_transform: empty input image",
+     "check error message");
+}
+
+sub rot_test {
+  my ($src, $degrees, $count) = @_;
+
+  my $cimg = $src->copy();
+  my $in;
+  for (1..$count) {
+    $in = $cimg;
+    $cimg = $cimg->rotate(right=>$degrees)
+      or last;
+  }
+ SKIP:
+  {
+    ok($cimg, "got a rotated image")
+      or skip("no image to check", 4);
+    my $diff = Imager::i_img_diff($src->{IMG}, $cimg->{IMG});
+    is($diff, 0, "check it matches source")
+      or skip("didn't match", 3);
+
+    # check that other parameters match
+    is($src->type, $cimg->type, "type check");
+    is($src->bits, $cimg->bits, "bits check");
+    is($src->getchannels, $cimg->getchannels, "channels check");
+  }
+}
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=9672
+  my $warning;
+  local $SIG{__WARN__} = 
+    sub { 
+      $warning = "@_";
+      my $printed = $warning;
+      $printed =~ s/\n$//;
+      $printed =~ s/\n/\n\#/g; 
+      print "# ",$printed, "\n";
+    };
+  my $img = Imager->new(xsize=>10, ysize=>10);
+  $img->copy();
+  cmp_ok($warning, '=~', 'void', "correct warning");
+  cmp_ok($warning, '=~', 'copyflip\\.t', "correct file");
+  $warning = '';
+  $img->rotate(degrees=>5);
+  cmp_ok($warning, '=~', 'void', "correct warning");
+  cmp_ok($warning, '=~', 'copyflip\\.t', "correct file");
+  $warning = '';
+  $img->matrix_transform(matrix=>[1, 1, 1]);
+  cmp_ok($warning, '=~', 'void', "correct warning");
+  cmp_ok($warning, '=~', 'copyflip\\.t', "correct file");
+}
+
+{
+  # 29936 - matrix_transform() should use fabs() instead of abs()
+  # range checking sz 
+
+  # this meant that when sz was < 1 (which it often is for these
+  # transformations), it treated the values out of range, producing a
+  # blank output image
+
+  my $src = Imager->new(xsize => 20, ysize => 20);
+  $src->box(filled => 1, color => 'FF0000');
+  my $out = $src->matrix_transform(matrix => [ 1, 0, 0,
+                                              0, 1, 0,
+                                              0, 0, 0.9999 ])
+    or print "# ", $src->errstr, "\n";
+  my $blank = Imager->new(xsize => 20, ysize => 20);
+  # they have to be different, surely that would be easy
+  my $diff = Imager::i_img_diff($out->{IMG}, $blank->{IMG});
+  ok($diff, "RT#29936 - check non-blank output");
+}
+
+{
+  my $im = Imager->new(xsize => 10, ysize => 10, channels => 4);
+  $im->box(filled => 1, color => 'FF0000');
+  my $back = Imager::Color->new(0, 0, 0, 0);
+  my $rot = $im->rotate(degrees => 10, back => $back);
+  # drop the alpha and make sure there's only 2 colors used
+  my $work = $rot->convert(preset => 'noalpha');
+  my $im_pal = $work->to_paletted(make_colors => 'mediancut');
+  my @colors = $im_pal->getcolors;
+  is(@colors, 2, "should be only 2 colors")
+    or do {
+      print "# ", join(",", $_->rgba), "\n" for @colors;
+    };
+  @colors = sort { ($a->rgba)[0] <=> ($b->rgba)[0] } @colors;
+  is_color3($colors[0], 0, 0, 0, "check we got black");
+  is_color3($colors[1], 255, 0, 0, "and red");
+}
+
+{ # RT #77063 rotate with degrees => 270 gives a black border
+  # so be a little less strict about rounding up
+  # I've also:
+  #  - improved calculation of the rotation matrix
+  #  - added rounding to interpolation for 1/3 channel images
+  my $im = test_image;
+  $im->box(color => "#00F");
+  my $right = $im->rotate(right => 270);
+  my $deg = $im->rotate(degrees => 270, back => "#FFF");
+  is($deg->getwidth, 150, "check degrees => 270 width");
+  is($deg->getheight, 150, "check degrees => 270 height");
+  ok($deg->write(file => "testout/t64rotdeg270.ppm"), "save it");
+  $right->write(file => "testout/t64rotright270.ppm");
+  is_image($deg, $right, "check right and degrees result the same");
+  #$deg = $deg->convert(preset => "addalpha");
+  # $right = $right->convert(preset => "addalpha");
+  # my $diff = $right->difference(other => $deg, mindist => 1);
+  # $diff->write(file => "testout/t64rotdiff.png");
+}
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->rotate(degrees => 90), "can't rotate an empty image");
+  is($empty->errstr, "rotate: empty input image",
+     "check error message");
+}
diff --git a/t/300-transform/040-crop.t b/t/300-transform/040-crop.t
new file mode 100644 (file)
index 0000000..074acce
--- /dev/null
@@ -0,0 +1,190 @@
+#!perl -w
+use strict;
+use Test::More tests => 66;
+use Imager;
+use Imager::Test qw(test_image);
+
+#$Imager::DEBUG=1;
+
+-d "testout" or mkdir "testout";
+
+Imager::init('log'=>'testout/t65crop.log');
+
+my $img=Imager->new() || die "unable to create image object\n";
+
+ok($img, "created image ph");
+
+SKIP:
+{
+  skip("couldn't load source image", 2)
+    unless ok($img->open(file=>'testimg/scale.ppm',type=>'pnm'), "loaded source");
+  my $nimg = $img->crop(top=>10, left=>10, bottom=>25, right=>25);
+  ok($nimg, "got an image");
+  ok($nimg->write(file=>"testout/t65.ppm"), "save to file");
+}
+
+{ # https://rt.cpan.org/Ticket/Display.html?id=7578
+  # make sure we get the right type of image on crop
+  my $src = Imager->new(xsize=>50, ysize=>50, channels=>2, bits=>16);
+  is($src->getchannels, 2, "check src channels");
+  is($src->bits, 16, "check src bits");
+  my $out = $src->crop(left=>10, right=>40, top=>10, bottom=>40);
+  is($out->getchannels, 2, "check out channels");
+  is($out->bits, 16, "check out bits");
+}
+{ # https://rt.cpan.org/Ticket/Display.html?id=7578
+  print "# try it for paletted too\n";
+  my $src = Imager->new(xsize=>50, ysize=>50, channels=>3, type=>'paletted');
+  # make sure color index zero is defined so there's something to copy
+  $src->addcolors(colors=>[Imager::Color->new(0,0,0)]);
+  is($src->type, 'paletted', "check source type");
+  my $out = $src->crop(left=>10, right=>40, top=>10, bottom=>40);
+  is($out->type, 'paletted', 'check output type');
+}
+
+{ # https://rt.cpan.org/Ticket/Display.html?id=7581
+  # crop() documentation says width/height takes precedence, but is unclear
+  # from looking at the existing code, setting width/height will go from
+  # the left of the image, even if left/top are provided, despite the
+  # sample in the docs
+  # Let's make sure that things happen as documented
+  my $src = test_image();
+  # make sure we get what we want
+  is($src->getwidth, 150, "src width");
+  is($src->getheight, 150, "src height");
+
+  # the test data is: 
+  #  - description
+  #  - hash ref containing args to crop()
+  #  - expected left, top, right, bottom values
+  # we call crop using the given arguments then call it using the 
+  # hopefully stable left/top/right/bottom/arguments
+  # this is kind of lame, but I don't want to include a rewritten
+  # crop in this file
+  my @tests = 
+    (
+     [ 
+      "basic",
+      { left=>10, top=>10, right=>70, bottom=>80 },
+      10, 10, 70, 80,
+     ],
+     [
+      "middle",
+      { width=>50, height=>50 },
+      50, 50, 100, 100,
+     ],
+     [
+      "lefttop",
+      { left=>20, width=>70, top=>30, height=>90 },
+      20, 30, 90, 120,
+     ],
+     [
+      "bottomright",
+      { right=>140, width=>50, bottom=>130, height=>60 },
+      90, 70, 140, 130,
+     ],
+     [
+      "acrossmiddle",
+      { top=>40, bottom=>110 },
+      0, 40, 150, 110,
+     ],
+     [
+      "downmiddle",
+      { left=>40, right=>110 },
+      40, 0, 110, 150,
+     ],
+     [
+      "rightside",
+      { left=>80, },
+      80, 0, 150, 150,
+     ],
+     [
+      "leftside",
+      { right=>40 },
+      0, 0, 40, 150,
+     ],
+     [
+      "topside",
+      { bottom=>40, },
+      0, 0, 150, 40,
+     ],
+     [
+      "bottomside",
+      { top=>90 },
+      0, 90, 150, 150,
+     ],
+     [
+      "overright",
+      { left=>100, right=>200 },
+      100, 0, 150, 150,
+     ],
+     [
+      "overtop",
+      { bottom=>50, height=>70 },
+      0, 0, 150, 50,
+     ],
+     [
+      "overleft",
+      { right=>30, width=>60 },
+      0, 0, 30, 150,
+     ],
+     [ 
+      "overbottom",
+      { top=>120, height=>60 },
+      0, 120, 150, 150,
+     ],
+    );
+  for my $test (@tests) {
+    my ($desc, $args, $left, $top, $right, $bottom) = @$test;
+    my $out = $src->crop(%$args);
+    ok($out, "got output for $desc");
+    my $cmp = $src->crop(left=>$left, top=>$top, right=>$right, bottom=>$bottom);
+    ok($cmp, "got cmp for $desc");
+    # make sure they're the same
+    my $diff = Imager::i_img_diff($out->{IMG}, $cmp->{IMG});
+    is($diff, 0, "difference should be 0 for $desc");
+  }
+}
+{ # https://rt.cpan.org/Ticket/Display.html?id=7581
+  # previously we didn't check that the result had some pixels
+  # make sure we do
+  my $src = test_image();
+  ok(!$src->crop(left=>50, right=>50), "nothing across");
+  cmp_ok($src->errstr, '=~', qr/resulting image would have no content/,
+        "and message");
+  ok(!$src->crop(top=>60, bottom=>60), "nothing down");
+  cmp_ok($src->errstr, '=~', qr/resulting image would have no content/,
+        "and message");
+}
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=9672
+  my $warning;
+  local $SIG{__WARN__} = 
+    sub { 
+      $warning = "@_";
+      my $printed = $warning;
+      $printed =~ s/\n$//;
+      $printed =~ s/\n/\n\#/g; 
+      print "# ",$printed, "\n";
+    };
+  my $img = Imager->new(xsize=>10, ysize=>10);
+  $img->crop(left=>5);
+  cmp_ok($warning, '=~', 'void', "correct warning");
+  cmp_ok($warning, '=~', 'crop\\.t', "correct file");
+}
+
+{
+    my $src = test_image();
+    ok(!$src->crop( top=>1000, bottom=>1500, left=>0, right=>100 ),
+                "outside of image" );
+    cmp_ok($src->errstr, '=~', qr/outside of the image/, "and message");
+    ok(!$src->crop( top=>100, bottom=>1500, left=>1000, right=>1500 ),
+                "outside of image" );
+    cmp_ok($src->errstr, '=~', qr/outside of the image/, "and message");
+}
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->crop(left => 10), "can't crop an empty image");
+  is($empty->errstr, "crop: empty input image", "check message");
+}
diff --git a/t/300-transform/050-convert.t b/t/300-transform/050-convert.t
new file mode 100644 (file)
index 0000000..7a68d80
--- /dev/null
@@ -0,0 +1,157 @@
+#!perl -w
+use strict;
+use Imager qw(:all :handy);
+use Test::More tests => 31;
+use Imager::Test qw(test_colorf_gpix is_fcolor1 is_fcolor3);
+
+-d "testout" or mkdir "testout";
+
+Imager::init("log"=>'testout/t67convert.log');
+
+my $imbase = Imager::ImgRaw::new(200,300,3);
+
+# first a basic test, make sure the basic things happen ok
+# make a 1 channel image from the above (black) image
+# but with 1 as the 'extra' value
+SKIP:
+{
+  my $im_white = i_convert($imbase, [ [ 0, 0, 0, 1 ] ]);
+  skip("convert to white failed", 3)
+    unless ok($im_white, "convert to white");
+
+  my ($w, $h, $ch) = i_img_info($im_white);
+
+  # the output image should now have one channel
+  is($ch, 1, "one channel image now");
+  # should have the same width and height
+  ok($w == 200 && $h == 300, "check converted size is the same");
+
+  # should be a white image now, let's check
+  my $c = Imager::i_get_pixel($im_white, 20, 20);
+  my @c = $c->rgba;
+  print "# @c\n";
+  is($c[0], 255, "check image is white");
+}
+
+# test the highlevel interface
+# currently this requires visual inspection of the output files
+my $im = Imager->new;
+SKIP:
+{
+  skip("could not load scale.ppm", 3)
+    unless $im->read(file=>'testimg/scale.ppm');
+  my $out = $im->convert(preset=>'gray');
+  ok($out, "convert preset gray");
+  ok($out->write(file=>'testout/t67_gray.ppm', type=>'pnm'),
+    "save grey image");
+  $out = $im->convert(preset=>'blue');
+  ok($out, "convert preset blue");
+
+  ok($out->write(file=>'testout/t67_blue.ppm', type=>'pnm'),
+     "save blue image");
+}
+
+# test against 16-bit/sample images
+{
+ SKIP:
+  {
+    my $imbase16 = Imager::i_img_16_new(200, 200, 3);
+
+    my $im16targ = i_convert($imbase16, [ [ 0, 0, 0, 1 ],
+                                         [ 0, 0, 0, 0 ],
+                                         [ 0, 0, 0, 0 ] ]);
+    ok($im16targ, "convert 16/bit sample image")
+      or skip("could not convert 16-bit image", 2);
+
+    # image should still be 16-bit
+    is(Imager::i_img_bits($im16targ), 16, "Image still 16-bit/sample");
+
+    # make sure that it's roughly red
+    test_colorf_gpix($im16targ, 0, 0, NCF(1, 0, 0), 0.001, "image roughly red");
+  }
+ SKIP:
+  {
+    my $imbase16 = Imager->new(xsize => 10, ysize => 10, bits => 16);
+    ok($imbase16->setpixel
+       (x => 5, y => 2, color => Imager::Color::Float->new(0.1, 0.2, 0.3)),
+       "set a sample pixel");
+    my $c1 = $imbase16->getpixel(x => 5, y => 2, type => "float");
+    is_fcolor3($c1, 0.1, 0.2, 0.3, "check it was set")
+      or print "#", join(",", $c1->rgba), "\n";
+    
+    my $targ16 = $imbase16->convert(matrix => [ [ 0.05, 0.15, 0.01, 0.5 ] ]);
+    ok($targ16, "convert another 16/bit sample image")
+      or skip("could not convert", 3);
+    is($targ16->getchannels, 1, "convert should be 1 channel");
+    is($targ16->bits, 16, "and 16-bits");
+    my $c = $targ16->getpixel(x => 5, y => 2, type => "float");
+    is_fcolor1($c, 0.538, 1/32768, "check grey value");
+  }
+}
+
+# test against palette based images
+my $impal = Imager::i_img_pal_new(200, 300, 3, 256);
+my $black = NC(0, 0, 0);
+my $blackindex = Imager::i_addcolors($impal, $black);
+ok($blackindex, "add black to paletted");
+for my $y (0..299) {
+  Imager::i_ppal($impal, 0, $y, ($blackindex) x 200);
+}
+
+SKIP:
+{
+  my $impalout = i_convert($impal, [ [ 0, 0, 0, 0 ],
+                                    [ 0, 0, 0, 1 ],
+                                    [ 0, 0, 0, 0 ] ]);
+  skip("could not convert paletted", 3)
+    unless ok($impalout, "convert paletted");
+  is(Imager::i_img_type($impalout), 1, "image still paletted");
+  is(Imager::i_colorcount($impalout), 1, "still only one colour");
+  my $c = Imager::i_getcolors($impalout, $blackindex);
+  ok($c, "get color from palette");
+  my @ch = $c->rgba;
+  print "# @ch\n";
+  ok($ch[0] == 0 && $ch[1] == 255 && $ch[2] == 0, 
+     "colour is as expected");
+}
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=9672
+  # methods that return a new image should warn in void context
+  my $warning;
+  local $SIG{__WARN__} = 
+    sub { 
+      $warning = "@_";
+      my $printed = $warning;
+      $printed =~ s/\n$//;
+      $printed =~ s/\n/\n\#/g; 
+      print "# ",$printed, "\n";
+    };
+  my $img = Imager->new(xsize=>10, ysize=>10);
+  $img->convert(preset=>"grey");
+  cmp_ok($warning, '=~', 'void', "correct warning");
+  cmp_ok($warning, '=~', 'convert\\.t', "correct file");
+}
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=28492
+  # convert() doesn't preserve image sample size
+  my $im = Imager->new(xsize => 20, ysize => 20, channels => 3, 
+                      bits => 'double');
+  is($im->bits, 'double', 'check source bits');
+  my $conv = $im->convert(preset => 'grey');
+  is($conv->bits, 'double', 'make sure result has extra bits');
+}
+
+{ # http://rt.cpan.org/NoAuth/Bug.html?id=79922
+  # Segfault in convert with bad params
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  ok(!$im->convert(matrix => [ 10, 10, 10 ]),
+     "this would crash");
+  is($im->errstr, "convert: invalid matrix: element 0 is not an array ref",
+     "check the error message");
+}
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->convert(preset => "addalpha"), "can't convert an empty image");
+  is($empty->errstr, "convert: empty input image", "check error message");
+}
diff --git a/t/300-transform/060-map.t b/t/300-transform/060-map.t
new file mode 100644 (file)
index 0000000..b91f43d
--- /dev/null
@@ -0,0 +1,48 @@
+#!perl -w
+use strict;
+use Test::More tests => 8;
+
+-d "testout" or mkdir "testout";
+
+Imager::init("log"=>'testout/t68map.log');
+
+use Imager qw(:all :handy);
+
+my $imbase = Imager::ImgRaw::new(200,300,3);
+
+
+my @map1 = map { int($_/2) } 0..255;
+my @map2 = map { 255-int($_/2) } 0..255;
+my @map3 = 0..255;
+my @maps = 0..24;
+my @mapl = 0..400;
+
+my $tst = 1;
+
+ok(i_map($imbase, [ [],     [],     \@map1 ]), "map1 in ch 3");
+ok(i_map($imbase, [ \@map1, \@map1, \@map1 ]), "map1 in ch1-3");
+
+ok(i_map($imbase, [ \@map1, \@map2, \@map3 ]), "map1-3 in ch 1-3");
+
+ok(i_map($imbase, [ \@maps, \@mapl, \@map3 ]), "incomplete maps");
+
+# test the highlevel interface
+# currently this requires visual inspection of the output files
+
+SKIP: {
+  my $im = Imager->new;
+  $im->read(file=>'testimg/scale.ppm')
+    or skip "Cannot load test image testimg/scale.ppm", 2;
+
+  ok( $im->map(red=>\@map1, green=>\@map2, blue=>\@map3),
+      "test OO interface (maps by color)");
+  ok( $im->map(maps=>[\@map1, [], \@map2]),
+      "test OO interface (maps by maps)");
+}
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->map(maps => [ \@map1, \@map2, \@map3 ]),
+     "can't map an empty image");
+  is($empty->errstr, "map: empty input image", "check error message");
+}
diff --git a/t/300-transform/500-trans.t b/t/300-transform/500-trans.t
new file mode 100644 (file)
index 0000000..5554f2a
--- /dev/null
@@ -0,0 +1,56 @@
+#!perl -w
+use strict;
+use Test::More;
+use Imager;
+
+eval "use Affix::Infix2Postfix; 1;"
+  or plan skip_all => "No Affix::Infix2Postfix";
+
+plan tests => 8;
+
+#$Imager::DEBUG=1;
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log('log'=>'testout/t55trans.log');
+
+my $img=Imager->new();
+
+SKIP:
+{
+  ok($img, "make image object")
+    or skip("can't make image object", 5);
+
+  ok($img->open(file=>'testimg/scale.ppm',type=>'pnm'),
+     "read sample image")
+    or skip("couldn't load test image", 4);
+
+ SKIP:
+  {
+    my $nimg=$img->transform(xexpr=>'x',yexpr=>'y+10*sin((x+y)/10)');
+    ok($nimg, "do transformation")
+      or skip ( "warning ".$img->errstr, 1 );
+
+    #  xopcodes=>[qw( x y Add)],yopcodes=>[qw( x y Sub)],parm=>[]
+
+    ok($nimg->write(type=>'pnm',file=>'testout/t55.ppm'), "save to file");
+  }
+
+ SKIP:
+  {
+    my $nimg=$img->transform(xexpr=>'x+0.1*y+5*sin(y/10.0+1.57)',
+                            yexpr=>'y+10*sin((x+y-0.785)/10)');
+    ok($nimg, "more complex transform")
+      or skip("couldn't make image", 1);
+
+    ok($nimg->write(type=>'pnm',file=>'testout/t55b.ppm'), "save to file");
+  }
+}
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->transform(xexpr => "x", yexpr => "y"),
+     "fail to transform an empty image");
+  is($empty->errstr, "transform: empty input image",
+     "check error message");
+}
diff --git a/t/300-transform/600-trans2.t b/t/300-transform/600-trans2.t
new file mode 100644 (file)
index 0000000..b9ebf58
--- /dev/null
@@ -0,0 +1,195 @@
+#!perl -w
+use strict;
+use Test::More tests => 40;
+BEGIN { use_ok('Imager'); }
+use Imager::Test qw(is_color3);
+
+-d "testout" or mkdir "testout";
+
+Imager::init('log'=>'testout/t58trans2.log');
+
+my $im1 = Imager->new();
+$im1->open(file=>'testimg/penguin-base.ppm', type=>'pnm')
+        || die "Cannot read image";
+my $im2 = Imager->new();
+$im2->open(file=>'testimg/scale.ppm',type=>'pnm')
+       || die "Cannot read testimg/scale.ppm";
+
+# error handling
+my $opts = { rpnexpr=>'x x 10 / sin 10 * y + get1' };
+my $im3 = Imager::transform2($opts);
+ok(!$im3, "returned an image on error");
+ok(defined($Imager::ERRSTR), "No error message on failure");
+
+# image synthesis
+my $im4 = Imager::transform2({
+       width=>300, height=>300,
+       rpnexpr=>'x y cx cy distance !d y cy - x cx - atan2 !a @d 10 / @a + 3.1416 2 * % !a2 @a2 cy * 3.1416 / 1 @a2 sin 1 + 2 / hsv'});
+ok($im4, "synthesis failed");
+
+if ($im4) {
+  $im4->write(type=>'pnm', file=>'testout/t56a.ppm')
+    || die "Cannot write testout/t56a.ppm";
+}
+
+# image distortion
+my $im5 = Imager::transform2({
+       rpnexpr=>'x x 10 / sin 10 * y + getp1'
+}, $im1);
+ok($im5, "image distortion");
+if ($im5) {
+  $im5->write(type=>'pnm', file=>'testout/t56b.ppm')
+    || die "Cannot write testout/t56b.ppm";
+}
+
+# image combination
+$opts = {
+rpnexpr=>'x h / !rat x w2 % y h2 % getp2 !pat x y getp1 @rat * @pat 1 @rat - * +'
+};
+my $im6 = Imager::transform2($opts,$im1,$im2);
+ok($im6, "image combination");
+if ($im6) {
+  $im6->write(type=>'pnm', file=>'testout/t56c.ppm')
+    || die "Cannot write testout/t56c.ppm";
+}
+
+# alpha
+$opts = 
+  {
+   rpnexpr => '0 0 255 x y + w h + 2 - / 255 * rgba',
+   channels => 4,
+   width => 50,
+   height => 50,
+  };
+my $im8 = Imager::transform2($opts);
+ok($im8, "alpha output");
+my $c = $im8->getpixel(x=>0, 'y'=>0);
+is(($c->rgba)[3], 0, "zero alpha");
+$c = $im8->getpixel(x=>49, 'y'=>49);
+is(($c->rgba)[3], 255, "max alpha");
+
+$opts = { rpnexpr => 'x 1 + log 50 * y 1 + log 50 * getp1' };
+my $im9 = Imager::transform2($opts, $im1);
+ok($im9, "log function");
+if ($im9) {
+  $im9->write(type=>'pnm', file=>'testout/t56-9.ppm');
+}
+
+# op tests
+sub op_test($$$$$$);
+print "# op tests\n";
+op_test('7F0000', <<EOS, 0, 127, 0, 'value hsv getp1');
+120 1.0
+0 0 getp1 value
+hsv
+EOS
+op_test("7F0000", <<EOS, 255, 0, 0, 'hue');
+0 0 getp1 hue
+1.0 1.0 hsv
+EOS
+op_test("7F0000", <<EOS, 0, 255, 0, 'sat');
+120 0 0 getp1 sat 1.0 hsv
+EOS
+op_test("4060A0", <<'EOS', 128, 128, 128, "add mult sub rgb red green blue");
+0 0 getp1 !p @p red 2 * @p green 32 + @p blue 32 - rgb
+EOS
+op_test('806040', <<'EOS', 64, 64, 64, "div uminus");
+0 0 getp1 !p @p red 2 / @p green 32 uminus add @p blue rgb
+EOS
+op_test('40087f', <<'EOS', 8, 64, 31, 'pow mod');
+0 0 getp1 !p @p red 0.5 pow @p green 2 pow @p blue 32 mod rgb
+EOS
+op_test('202122', '0 0 getp1 4 *', 128, 132, 136, 'multp');
+op_test('404040', '0 0 getp1 1 2 3 rgb +', 65, 66, 67, 'addp');
+op_test('414243', '0 0 getp1 3 2 1 rgb -', 62, 64, 66, 'subp');
+op_test('808040', <<'EOS', 64, 64, 8, 'sin cos pi sqrt');
+0 0 getp1 !p pi 6 / sin @p red * 0.1 + pi 3 / cos @p green * 0.1 + 
+@p blue sqrt rgb
+EOS
+op_test('008080', <<'EOS', 0, 0, 0, 'atan2');
+0 0 0 0 getp1 !p @p red 128 / @p green 128 / atan2 hsv
+EOS
+op_test('000000', <<'EOS', 150, 150, 150, 'distance');
+0 100 120 10 distance !d @d @d @d rgb
+EOS
+op_test('000000', <<'EOS', 100, 100, 100, 'int');
+50.75 int 2 * !i @i @i @i rgb
+EOS
+op_test('000100', <<'EOS', 128, 0, 0, 'if');
+0 0 getp1 !p @p red 0 128 if @p green 0 128 if 0 rgb
+EOS
+op_test('FF0000', <<'EOS', 0, 255, 0, 'ifp');
+0 0 0 getp1 0 255 0 rgb ifp
+EOS
+op_test('000000', <<'EOS', 1, 0, 1, 'le lt gt');
+0 1 le 1 0 lt 1 0 gt rgb
+EOS
+op_test('000000', <<'EOS', 0, 1, 0, 'ge eq ne');
+0 1 ge 0 0 eq 0 0 ne rgb
+EOS
+op_test('000000', <<'EOS', 0, 1, 1, 'and or not');
+1 0 and 1 0 or 0 not rgb
+EOS
+op_test('000000', <<'EOS', 255, 0, 255, 'abs');
+-255 abs 0 abs 255 abs rgb
+EOS
+op_test('000000', <<'EOS', 50, 82, 0, 'exp log');
+1 exp log 50 * 0.5 + 0.5 exp 50 * 0 rgb
+EOS
+op_test('800000', <<'EOS', 128, 0, 0, 'det');
+1 0 0 1 det 128 * 1 1 1 1 det 128 * 0 rgb
+EOS
+op_test('FF80C0', <<'EOS', 127, 0, 0, 'sat');
+0 0 getp1 sat 255 * 0.01 + 0 0 rgb
+EOS
+
+
+{
+  my $empty = Imager->new;
+  my $good = Imager->new(xsize => 1, ysize => 1);
+  ok(!Imager::transform2({ rpnexpr => "x y getp1" }, $good, $empty),
+     "can't transform an empty image");
+  is(Imager->errstr, "transform2: empty input image (input image 2)",
+     "check error message");
+}
+
+use Imager::Transform;
+
+# some simple tests
+print "# Imager::Transform\n";
+my @funcs = Imager::Transform->list;
+ok(@funcs, "funcs");
+
+my $tran = Imager::Transform->new($funcs[0]);
+ok($tran, "got tranform");
+ok($tran->describe() eq Imager::Transform->describe($funcs[0]),
+   "description");
+# look for a function that takes inputs (at least one does)
+my @needsinputs = grep Imager::Transform->new($_)->inputs, @funcs;
+# make sure they're 
+my @inputs = Imager::Transform->new($needsinputs[0])->inputs;
+ok($inputs[0]{desc}, "input description");
+# at some point I might want to test the actual transformations
+
+# check lower level error handling
+my $im7 = Imager::transform2({rpnexpr=>'x y getp2', width=>100, height=>100});
+ok(!$im7, "expected failure on accessing invalid image");
+print "# ", Imager->errstr, "\n";
+ok(Imager->errstr =~ /not enough images/, "didn't get expected error");
+
+sub op_test ($$$$$$) {
+  my ($in_color, $code, $r, $g, $b, $comment) = @_;
+
+  my $im = Imager->new(xsize => 1, ysize => 1);
+  $im->setpixel(x => 0, y => 0, color => $in_color);
+ SKIP:
+  {
+    my $out = Imager::transform2({ rpnexpr => $code }, $im);
+    unless ($out) {
+      fail("$comment: could not compile $code - ".Imager->errstr);
+      return;
+    }
+    my $found = $out->getpixel(x => 0, y => 0);
+    is_color3($found, $r, $g, $b, $comment);
+  }
+}
diff --git a/t/300-transform/610-postfix.t b/t/300-transform/610-postfix.t
new file mode 100644 (file)
index 0000000..6366e8b
--- /dev/null
@@ -0,0 +1,25 @@
+#!perl -w
+use strict;
+use Test::More tests => 6;
+BEGIN { use_ok('Imager::Expr') }
+
+SKIP:
+{
+  my $expr = Imager::Expr->new({rpnexpr=><<EXPR, variables=>[ qw(x y) ], constants=>{one=>1, two=>2}});
+x two * # see if comments work
+y one + 
+getp1
+EXPR
+  ok($expr, "compile postfix")
+    or print "# ", Imager::Expr->error, "\n";
+  $expr
+    or skip("Could not compile", 4);
+
+  # perform some basic validation on the code
+  my $code = $expr->dumpcode();
+  my @code = split /\n/, $code;
+  ok($code[-1] =~ /:\s+ret/, "ret at the end");
+  ok(grep(/:\s+mult.*x/, @code), "found mult");
+  ok(grep(/:\s+add.*y/, @code), "found add");
+  ok(grep(/:\s+getp1/, @code), "found getp1");
+}
diff --git a/t/300-transform/620-infix.t b/t/300-transform/620-infix.t
new file mode 100644 (file)
index 0000000..8c4d5d6
--- /dev/null
@@ -0,0 +1,28 @@
+#!perl -w
+use strict;
+use Test::More tests => 7;
+
+BEGIN { use_ok('Imager::Expr') }
+
+# only test this if Parse::RecDescent was loaded successfully
+SKIP:
+{
+  Imager::Expr->type_registered('expr')
+      or skip("Imager::Expr::Infix not available", 6);
+
+  my $opts = {expr=>'z=0.8;return hsv(x/w*360,y/h,z)', variables=>[ qw(x y) ], constants=>{h=>100,w=>100}};
+  my $expr = Imager::Expr->new($opts);
+  ok($expr, "make infix expression")
+    or skip("Could not make infix expression", 5);
+  my $code = $expr->dumpcode();
+  my @code = split /\n/,$code;
+  #print $code;
+  ok($code[-1] =~ /:\s+ret/, "final op a ret");
+  ok(grep(/:\s+mult.*360/, @code), "mult by 360 found");
+  # strength reduction converts these to mults
+  #print grep(/:\s+div.*x/, @code) ? "ok 5\n" : "not ok 5\n";
+  #print grep(/:\s+div.*y/, @code) ? "ok 6\n" : "not ok 6\n";
+  ok(grep(/:\s+mult.*x/, @code), "mult by x found");
+  ok(grep(/:\s+mult.*y/, @code), "mult by y found");
+  ok(grep(/:\s+hsv.*0\.8/, @code), "hsv op found");
+}
diff --git a/t/300-transform/630-assem.t b/t/300-transform/630-assem.t
new file mode 100644 (file)
index 0000000..065cb3e
--- /dev/null
@@ -0,0 +1,36 @@
+#!perl -w
+use strict;
+use Test::More tests => 6;
+
+BEGIN { use_ok('Imager::Expr::Assem') }
+
+SKIP:
+{
+  my $expr = Imager::Expr->new
+    ({assem=><<EOS,
+       var count:n ; var p:p
+       count = 0
+       p = getp1 x y
+loop:
+# this is just a delay
+       count = add count 1
+       var temp:n
+       temp = lt count totalcount
+       jumpnz temp loop
+       ret p
+EOS
+      variables=>[qw(x y)],
+      constants=>{totalcount=>5}
+     });
+  ok($expr, "compile simple assembler")
+    or do {
+      print "# ", Imager::Expr->error, "\n";
+      skip("didn't compile", 4);
+    };
+  my $code = $expr->dumpcode();
+  my @code = split /\n/, $code;
+  ok($code[-1] =~ /:\s+ret/, "last op is a ret");
+  ok($code[0] =~ /:\s+set/, "first op is a set");
+  ok($code[1] =~ /:\s+getp1/, "next is a getp1");
+  ok($code[3] =~ /:\s+lt/, "found comparison");
+}
diff --git a/t/350-font/010-font.t b/t/350-font/010-font.t
new file mode 100644 (file)
index 0000000..0d1bd65
--- /dev/null
@@ -0,0 +1,59 @@
+#!perl -w
+use strict;
+use Imager;
+use Test::More tests => 14;
+
+unshift @INC, "t";
+
+ok(Imager::Font->register(type => "test",
+                         class=>"GoodTestFont",
+                         files => "\\.ppm\$"),
+   "register a test font");
+
+ok(Imager::Font->register(type => "bad",
+                         class => "BadTestFont",
+                         files => "\\.ppm\$"),
+   "register a bad test font");
+
+ok(!Imager::Font->register(), "no register parameters");
+like(Imager->errstr, qr/No type parameter/, "check message");
+
+ok(!Imager::Font->register(type => "bad1"), "no class parameter");
+like(Imager->errstr, qr/No class parameter/, "check message");
+
+ok(!Imager::Font->register(type => "bad2", class => "BadFont", files => "**"),
+   "bad files parameter");
+is(Imager->errstr, "files isn't a valid regexp", "check message");
+
+Imager::Font->priorities("bad", "test");
+
+# RT #62855
+# previously we'd select the first file matched font driver, even if
+# it wasn't available, then crash loading it.
+
+SKIP:
+{
+  my $good;
+  ok(eval {
+    $good = Imager::Font->new(file => "testimg/penguin-base.ppm");
+  }, "load good font avoiding RT 62855")
+    or skip("Failed to load", 1);
+  ok($good->isa("GoodTestFont"), "and it's the right type");
+}
+
+
+use Imager::Font::Test;
+
+# check string() and align_string() handle an empty image
+{
+  my $font = Imager::Font::Test->new;
+  my $empty = Imager->new;
+  ok(!$empty->string(text => "foo", x => 0, y => 10, size => 10, font => $font),
+     "can't draw text on an empty image");
+  is($empty->errstr, "string: empty input image",
+     "check error message");
+  ok(!$empty->align_string(text => "foo", x => 0, y => 10, size => 10, font => $font),
+     "can't draw text on an empty image");
+  is($empty->errstr, "align_string: empty input image",
+     "check error message");
+}
diff --git a/t/350-font/020-tt.t b/t/350-font/020-tt.t
new file mode 100644 (file)
index 0000000..a06b7f8
--- /dev/null
@@ -0,0 +1,334 @@
+#!perl -w
+use strict;
+use Test::More tests => 97;
+
+$|=1;
+
+BEGIN { use_ok(Imager => ':all') }
+use Imager::Test qw(diff_text_with_nul is_color3 is_image);
+
+-d "testout" or mkdir "testout";
+
+init_log("testout/t35ttfont.log",2);
+
+SKIP:
+{
+  skip("freetype 1.x unavailable or disabled", 96) 
+    unless $Imager::formats{"tt"};
+  print "# has tt\n";
+  
+  my $deffont = './fontfiles/dodge.ttf';
+  my $fontname=$ENV{'TTFONTTEST'} || $deffont;
+
+  if (!ok(-f $fontname, "check test font file exists")) {
+    print "# cannot find fontfile for truetype test $fontname\n";
+    skip('Cannot load test font', 89);
+  }
+
+  #i_init_fonts();
+  #     i_tt_set_aa(1);
+  
+  my $bgcolor = i_color_new(255,0,0,0);
+  my $overlay = Imager::ImgRaw::new(320,140,3);
+  i_box_filled($overlay, 0, 0, 319, 139, i_color_new(128, 128, 128));
+  
+  my $ttraw = Imager::i_tt_new($fontname);
+  ok($ttraw, "create font");
+
+  my @bbox = i_tt_bbox($ttraw,50.0,'XMCLH',0);
+  is(@bbox, 8, "bounding box");
+  print "#bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n";
+
+  ok(i_tt_cp($ttraw,$overlay,5,50,1,50.0,'XM CLH',6,1,0), "cp output");
+  ok(i_tt_cp($ttraw,$overlay,5,120,1,50.0,'XM CLH',6,0,0), "cp output (non AA)");
+  i_line($overlay,0,50,100,50,$bgcolor,1);
+
+  open(FH,">testout/t35ttfont.ppm") || die "cannot open testout/t35ttfont.ppm\n";
+  binmode(FH);
+  my $IO = Imager::io_new_fd( fileno(FH) );
+  ok(i_writeppm_wiol($overlay, $IO), "save t35ttfont.ppm");
+  close(FH);
+
+  $bgcolor=i_color_set($bgcolor,200,200,200,0);
+  my $backgr=Imager::ImgRaw::new(500,300,3);
+  
+  #     i_tt_set_aa(2);
+  
+  ok(i_tt_text($ttraw,$backgr,100,120,$bgcolor,50.0,'te st',5,1,0),
+      "normal output");
+  ok(i_tt_text($ttraw,$backgr,100,200,$bgcolor,50.0,'te st',5,0,0),
+      "normal output (non AA)");
+
+  my $ugly = Imager::i_tt_new("./fontfiles/ImUgly.ttf");
+  ok($ugly, "create ugly font");
+  # older versions were dropping the bottom of g and the right of a
+  ok(i_tt_text($ugly, $backgr,100, 80, $bgcolor, 14, 'g%g', 3, 1, 0), 
+     "draw g%g");
+  ok(i_tt_text($ugly, $backgr,150, 80, $bgcolor, 14, 'delta', 6, 1, 0),
+      "draw delta");
+  i_line($backgr,0,20,499,20,i_color_new(0,127,0,0),1);
+  ok(i_tt_text($ttraw, $backgr, 20, 20, $bgcolor, 14, 'abcdefghijklmnopqrstuvwxyz{|}', 29, 1, 0), "alphabet");
+  ok(i_tt_text($ttraw, $backgr, 20, 50, $bgcolor, 14, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 26, 1, 0), "ALPHABET");
+  
+  # UTF8 tests
+  # for perl < 5.6 we can hand-encode text
+  # the following is "A\x{2010}A"
+  # 
+  my $text = pack("C*", 0x41, 0xE2, 0x80, 0x90, 0x41);
+  my $alttext = "A-A";
+  
+  my @utf8box = i_tt_bbox($ttraw, 50.0, $text, 1);
+  is(@utf8box, 8, "utf8 bbox element count");
+  my @base = i_tt_bbox($ttraw, 50.0, $alttext, 0);
+  is(@base, 8, "alt bbox element count");
+  my $maxdiff = $fontname eq $deffont ? 0 : $base[2] / 3;
+  print "# (@utf8box vs @base)\n";
+  ok(abs($utf8box[2] - $base[2]) <= $maxdiff, 
+     "compare box sizes $utf8box[2] vs $base[2] (maxerror $maxdiff)");
+  
+  # hand-encoded UTF8 drawing
+  ok(i_tt_text($ttraw, $backgr, 200, 80, $bgcolor, 14, $text, length($text), 1, 1), "draw hand-encoded UTF8");
+
+  ok(i_tt_cp($ttraw, $backgr, 250, 80, 1, 14, $text, length($text), 1, 1), 
+      "cp hand-encoded UTF8");
+
+  # ok, try native perl UTF8 if available
+ SKIP:
+  {
+    skip("perl too old to test native UTF8 support", 5) unless $] >= 5.006;
+
+    my $text;
+    # we need to do this in eval to prevent compile time errors in older
+    # versions
+    eval q{$text = "A\x{2010}A"}; # A, HYPHEN, A in our test font
+    #$text = "A".chr(0x2010)."A"; # this one works too
+    ok(i_tt_text($ttraw, $backgr, 300, 80, $bgcolor, 14, $text, 0, 1, 0),
+       "draw UTF8");
+    ok(i_tt_cp($ttraw, $backgr, 350, 80, 0, 14, $text, 0, 1, 0),
+       "cp UTF8");
+    @utf8box = i_tt_bbox($ttraw, 50.0, $text, 0);
+    is(@utf8box, 8, "native utf8 bbox element count");
+    ok(abs($utf8box[2] - $base[2]) <= $maxdiff, 
+       "compare box sizes native $utf8box[2] vs $base[2] (maxerror $maxdiff)");
+    eval q{$text = "A\x{0905}\x{0906}\x{0103}A"}; # Devanagari
+    ok(i_tt_text($ugly, $backgr, 100, 160, $bgcolor, 36, $text, 0, 1, 0),
+       "more complex output");
+  }
+
+  open(FH,">testout/t35ttfont2.ppm") || die "cannot open testout/t35ttfont.ppm\n";
+  binmode(FH);
+  $IO = Imager::io_new_fd( fileno(FH) );
+  ok(i_writeppm_wiol($backgr, $IO), "save t35ttfont2.ppm");
+  close(FH);
+  
+  my $exists_font = "fontfiles/ExistenceTest.ttf";
+  my $hcfont = Imager::Font->new(file=>$exists_font, type=>'tt');
+ SKIP:
+  {
+    ok($hcfont, "loading existence test font")
+      or skip("could not load test font", 20);
+
+    # list interface
+    my @exists = $hcfont->has_chars(string=>'!A');
+    ok(@exists == 2, "check return count");
+    ok($exists[0], "we have an exclamation mark");
+    ok(!$exists[1], "we have no exclamation mark");
+    
+    # scalar interface
+    my $exists = $hcfont->has_chars(string=>'!A');
+    ok(length($exists) == 2, "check return length");
+    ok(ord(substr($exists, 0, 1)), "we have an exclamation mark");
+    ok(!ord(substr($exists, 1, 1)), "we have no upper-case A");
+    
+    my $face_name = Imager::i_tt_face_name($hcfont->{id});
+    print "# face $face_name\n";
+    is($face_name, 'ExistenceTest', "face name (function)");
+    $face_name = $hcfont->face_name;
+    is($face_name, 'ExistenceTest', "face name (OO)");
+    
+    # FT 1.x cheats and gives names even if the font doesn't have them
+    my @glyph_names = $hcfont->glyph_names(string=>"!J/");
+    is($glyph_names[0], 'exclam', "check exclam name OO");
+    ok(!defined($glyph_names[1]), "check for no J name OO");
+    is($glyph_names[2], 'slash', "check slash name OO");
+    
+    print "# ** name table of the test font **\n";
+    Imager::i_tt_dump_names($hcfont->{id});
+
+    # the test font is known to have a shorter advance width for that char
+    my @bbox = $hcfont->bounding_box(string=>"/", size=>100);
+    is(@bbox, 8, "should be 8 entries");
+    isnt($bbox[6], $bbox[2], "different advance width from pos width");
+    print "# @bbox\n";
+    my $bbox = $hcfont->bounding_box(string=>"/", size=>100);
+    isnt($bbox->pos_width, $bbox->advance_width, "OO check");
+
+    cmp_ok($bbox->right_bearing, '<', 0, "check right bearing");
+
+    cmp_ok($bbox->display_width, '>', $bbox->advance_width,
+           "check display width (roughly)");
+
+    # check with a char that fits inside the box
+    $bbox = $hcfont->bounding_box(string=>"!", size=>100);
+    print "# @$bbox\n";
+    print "# pos width ", $bbox->pos_width, "\n";
+    is($bbox->pos_width, $bbox->advance_width, 
+       "check backwards compatibility");
+    cmp_ok($bbox->left_bearing, '>', 0, "left bearing positive");
+    cmp_ok($bbox->right_bearing, '>', 0, "right bearing positive");
+    cmp_ok($bbox->display_width, '<', $bbox->advance_width,
+           "display smaller than advance");
+  }
+  undef $hcfont;
+  
+  my $name_font = "fontfiles/NameTest.ttf";
+  $hcfont = Imager::Font->new(file=>$name_font, type=>'tt');
+ SKIP:
+  {
+    ok($hcfont, "loading name font")
+      or skip("could not load name font $name_font", 3);
+    # make sure a missing string parameter is handled correctly
+    eval {
+      $hcfont->glyph_names();
+    };
+    is($@, "", "correct error handling");
+    cmp_ok(Imager->errstr, '=~', qr/no string parameter/, "error message");
+    
+    my $text = pack("C*", 0xE2, 0x80, 0x90); # "\x{2010}" as utf-8
+    my @names = $hcfont->glyph_names(string=>$text, utf8=>1);
+    is($names[0], "hyphentwo", "check utf8 glyph name");
+  }
+
+  undef $hcfont;
+  
+ SKIP:
+  { print "# alignment tests\n";
+    my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'tt');
+    ok($font, "loaded deffont OO")
+      or skip("could not load font:".Imager->errstr, 4);
+    my $im = Imager->new(xsize=>140, ysize=>150);
+    my %common = 
+      (
+       font=>$font, 
+       size=>40, 
+       aa=>1,
+      );
+    $im->line(x1=>0, y1=>40, x2=>139, y2=>40, color=>'blue');
+    $im->line(x1=>0, y1=>90, x2=>139, y2=>90, color=>'blue');
+    $im->line(x1=>0, y1=>110, x2=>139, y2=>110, color=>'blue');
+    for my $args ([ x=>5,   text=>"A", color=>"white" ],
+                  [ x=>40,  text=>"y", color=>"white" ],
+                  [ x=>75,  text=>"A", channel=>1 ],
+                  [ x=>110, text=>"y", channel=>1 ]) {
+      ok($im->string(%common, @$args, 'y'=>40), "A no alignment");
+      ok($im->string(%common, @$args, 'y'=>90, align=>1), "A align=1");
+      ok($im->string(%common, @$args, 'y'=>110, align=>0), "A align=0");
+    }
+    ok($im->write(file=>'testout/t35align.ppm'), "save align image");
+  }
+
+  { # Ticket #14804 Imager::Font->new() doesn't report error details
+    # when using freetype 1
+    # make sure we're using C locale for messages
+    use POSIX qw(setlocale LC_ALL);
+    setlocale(LC_ALL, "C");
+
+    my $font = Imager::Font->new(file=>'t/350-font/020-tt.t', type=>'tt');
+    ok(!$font, "font creation should have failed for invalid file");
+    cmp_ok(Imager->errstr, 'eq', 'Invalid file format.',
+         "test error message");
+
+    setlocale(LC_ALL, "");
+  }
+
+  { # check errstr set correctly
+    my $font = Imager::Font->new(file=>$fontname, type=>'tt',
+                               size => undef);
+    ok($font, "made size error test font");
+    my $im = Imager->new(xsize=>100, ysize=>100);
+    ok($im, "made size error test image");
+    ok(!$im->string(font=>$font, x=>10, 'y'=>50, string=>"Hello"),
+       "drawing should fail with no size");
+    is($im->errstr, "No font size provided", "check error message");
+
+    # try no string
+    ok(!$im->string(font=>$font, x=>10, 'y'=>50, size=>15),
+       "drawing should fail with no string");
+    is($im->errstr, "missing required parameter 'string'",
+       "check error message");
+  }
+
+  { # introduced in 0.46 - outputting just space crashes
+    my $im = Imager->new(xsize=>100, ysize=>100);
+    my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', size=>14);
+    ok($im->string(font=>$font, x=> 5, 'y' => 50, string=>' '),
+      "outputting just a space was crashing");
+  }
+
+  { # string output cut off at NUL ('\0')
+    # https://rt.cpan.org/Ticket/Display.html?id=21770 cont'd
+    my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'tt');
+    ok($font, "loaded imugly");
+
+    diff_text_with_nul("a\\0b vs a", "a\0b", "a", 
+                      font => $font, color => '#FFFFFF');
+    diff_text_with_nul("a\\0b vs a", "a\0b", "a", 
+                      font => $font, channel => 1);
+
+    # UTF8 encoded \x{2010}
+    my $dash = pack("C*", 0xE2, 0x80, 0x90);
+    diff_text_with_nul("utf8 dash\\0dash vs dash", "$dash\0$dash", $dash,
+                      font => $font, color => '#FFFFFF', utf8 => 1);
+    diff_text_with_nul("utf8 dash\\0dash vs dash", "$dash\0$dash", $dash,
+                      font => $font, channel => 1, utf8 => 1);
+  }
+
+ SKIP:
+  { # RT 11972
+    # when rendering to a transparent image the coverage should be
+    # expressed in terms of the alpha channel rather than the color
+    my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'tt');
+    ok($font, "loaded fontfiles/ImUgly.ttf")
+      or skip("Could not load test font: ".Imager->errstr, 4);
+    my $im = Imager->new(xsize => 40, ysize => 20, channels => 4);
+    ok($im->string(string => "AB", size => 20, aa => 1, color => '#F00',
+                  x => 0, y => 15, font => $font),
+       "draw to transparent image");
+    #$im->write(file => "foo.png");
+    my $im_noalpha = $im->convert(preset => 'noalpha');
+    my $im_pal = $im->to_paletted(make_colors => 'mediancut');
+    my @colors = $im_pal->getcolors;
+    is(@colors, 2, "should be only 2 colors");
+    @colors = sort { ($a->rgba)[0] <=> ($b->rgba)[0] } @colors;
+    is_color3($colors[0], 0, 0, 0, "check we got black");
+    is_color3($colors[1], 255, 0, 0, "and red");
+  }
+
+ SKIP:
+  { # RT 71564
+    my $noalpha = Imager::Color->new(255, 255, 255, 0);
+    my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'tt',
+                                color => $noalpha);
+    ok($font, "loaded fontfiles/ImUgly.ttf")
+      or skip("Could not load test font: ".Imager->errstr, 4);
+    {
+      my $im = Imager->new(xsize => 40, ysize => 20);
+      my $copy = $im->copy;
+      ok($im->string(string => "AB", size => 20, aa => 1,
+                    x => 0, y => 15, font => $font),
+        "draw with transparent color, aa");
+      is_image($im, $copy, "should draw nothing");
+    }
+    {
+      my $im = Imager->new(xsize => 40, ysize => 20);
+      my $copy = $im->copy;
+      ok($im->string(string => "AB", size => 20, aa => 0,
+                    x => 0, y => 15, font => $font),
+        "draw with transparent color, non-aa");
+      local $TODO = "RT 73359 - non-AA text isn't normal mode rendered";
+      is_image($im, $copy, "should draw nothing");
+    }
+  }
+
+  ok(1, "end of code");
+}
diff --git a/t/350-font/030-ttoo.t b/t/350-font/030-ttoo.t
new file mode 100644 (file)
index 0000000..2982681
--- /dev/null
@@ -0,0 +1,102 @@
+#!/usr/bin/perl -w
+use strict;
+
+#use lib qw(blib/lib blib/arch);
+
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.pl'
+
+######################### We start with some black magic to print on failure.
+
+# Change 1..1 below to 1..last_test_to_print .
+# (It may become useful if the test is moved to ./t subdirectory.)
+use Test::More tests => 16;
+
+BEGIN { use_ok('Imager') };
+
+BEGIN {
+  require Imager::Test;
+  Imager::Test->import(qw(isnt_image));
+}
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t36oofont.log");
+
+my $fontname_tt=$ENV{'TTFONTTEST'}||'./fontfiles/dodge.ttf';
+
+my $green=Imager::Color->new(92,205,92,128);
+die $Imager::ERRSTR unless $green;
+my $red=Imager::Color->new(205, 92, 92, 255);
+die $Imager::ERRSTR unless $red;
+
+SKIP:
+{
+  $Imager::formats{"tt"} && -f $fontname_tt
+    or skip("FT1.x missing or disabled", 14);
+
+  my $img=Imager->new(xsize=>300, ysize=>100) or die "$Imager::ERRSTR\n";
+
+  my $font=Imager::Font->new(file=>$fontname_tt,size=>25)
+    or die $img->{ERRSTR};
+
+  ok(1, "create TT font object");
+
+  ok($img->string(font=>$font, text=>"XMCLH", 'x'=>100, 'y'=>100),
+      "draw text");
+
+  $img->line(x1=>0, x2=>300, y1=>50, y2=>50, color=>$green);
+
+  my $text="LLySja";
+  my @bbox=$font->bounding_box(string=>$text, 'x'=>0, 'y'=>50);
+
+  is(@bbox, 8, "bbox list size");
+
+  $img->box(box=>\@bbox, color=>$green);
+
+  $text = pack("C*", 0x41, 0xE2, 0x80, 0x90, 0x41);
+  ok($img->string(font=>$font, text=>$text, 'x'=>100, 'y'=>50, utf8=>1),
+      "draw hand-encoded UTF8 text");
+
+ SKIP:
+  {
+    $] >= 5.006
+      or skip("perl too old for native utf8", 1);
+    eval q{$text = "A\x{2010}A"};
+    ok($img->string(font=>$font, text=>$text, 'x'=>200, 'y'=>50),
+       "draw native UTF8 text");
+  }
+
+  ok($img->write(file=>"testout/t36oofont2.ppm", type=>'pnm'),
+      "write t36oofont2.ppm")
+    or print "# ", $img->errstr,"\n";
+
+  ok($font->utf8, "make sure utf8 method returns true");
+
+  my $has_chars = $font->has_chars(string=>"\x01A");
+  is($has_chars, "\x00\x01", "has_chars scalar");
+  my @has_chars = $font->has_chars(string=>"\x01A");
+  ok(!$has_chars[0], "has_chars list 0");
+  ok($has_chars[1], "has_chars list 1");
+
+  { # RT 71469
+    my $font1 = Imager::Font->new(file => $fontname_tt, type => "tt");
+    my $font2 = Imager::Font::Truetype->new(file => $fontname_tt);
+
+    for my $font ($font1, $font2) {
+      print "# ", join(",", $font->{color}->rgba), "\n";
+
+      my $im = Imager->new(xsize => 20, ysize => 20, channels => 4);
+
+      ok($im->string(text => "T", font => $font, y => 15),
+        "draw with default color")
+       or print "# ", $im->errstr, "\n";
+      my $work = Imager->new(xsize => 20, ysize => 20);
+      my $cmp = $work->copy;
+      $work->rubthrough(src => $im);
+      isnt_image($work, $cmp, "make sure something was drawn");
+    }
+  }
+}
+
+ok(1, "end");
diff --git a/t/350-font/040-ttstd.t b/t/350-font/040-ttstd.t
new file mode 100644 (file)
index 0000000..73e28f5
--- /dev/null
@@ -0,0 +1,33 @@
+#!perl -w
+use strict;
+use Imager::Test qw(std_font_tests std_font_test_count);
+use Imager::Font;
+use Test::More;
+
+$Imager::formats{tt}
+       or plan skip_all => "No tt available";
+
+Imager->open_log(log => "testout/t37std.log");
+
+plan tests => std_font_test_count();
+
+my $font = Imager::Font->new(file => "fontfiles/dodge.ttf",
+                            type => "tt");
+my $name_font =
+  Imager::Font->new(file => "fontfiles/ImUgly.ttf",
+                   type => "tt");
+
+SKIP:
+{
+  $font
+    or skip "Cannot load font", std_font_test_count();
+  std_font_tests
+    ({
+      font => $font,
+      has_chars => [ 1, 1, 1 ],
+      glyph_name_font => $name_font,
+      glyph_names => [ qw(A uni2010 A) ],
+     });
+}
+
+Imager->close_log;
diff --git a/t/350-font/100-texttools.t b/t/350-font/100-texttools.t
new file mode 100644 (file)
index 0000000..b685c7c
--- /dev/null
@@ -0,0 +1,94 @@
+#!perl -w
+use strict;
+use Test::More tests => 13;
+
+BEGIN { use_ok('Imager') }
+
+-d "testout" or mkdir "testout";
+
+require_ok('Imager::Font::Wrap');
+
+my $img = Imager->new(xsize=>400, ysize=>400);
+
+my $text = <<EOS;
+This is a test of text wrapping. This is a test of text wrapping. This =
+is a test of text wrapping. This is a test of text wrapping. This is a =
+test of text wrapping. This is a test of text wrapping. This is a test =
+of text wrapping. This is a test of text wrapping. This is a test of =
+text wrapping. XX.
+
+Xxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww xxxx.
+
+This is a test of text wrapping. This is a test of text wrapping. This =
+is a test of text wrapping. This is a test of text wrapping. This is a =
+test of text wrapping. This is a test of text wrapping. This is a test =
+of text wrapping. This is a test of text wrapping. This is a test of =
+text wrapping. This is a test of text wrapping. This is a test of text =
+wrapping. This is a test of text wrapping. This is a test of text =
+wrapping. This is a test of text wrapping. This is a test of text =
+wrapping. This is a test of text wrapping. This is a test of text =
+wrapping. XX.
+EOS
+
+$text =~ s/=\n//g;
+
+my $fontfile = $ENV{WRAPTESTFONT} || $ENV{TTFONTTEST} || "fontfiles/ImUgly.ttf";
+
+my $font = Imager::Font->new(file=>$fontfile);
+
+SKIP:
+{
+  $Imager::formats{'tt'} || $Imager::formats{'ft2'}
+      or skip("Need Freetype 1.x or 2.x to test", 11);
+
+  ok($font, "loading font")
+    or skip("Could not load test font", 8);
+
+  Imager::Font->priorities(qw(t1 ft2 tt));
+  ok(scalar Imager::Font::Wrap->wrap_text(string => $text,
+                                font=>$font,
+                                image=>$img,
+                                size=>13,
+                                width => 380, aa=>1,
+                                x=>10, 'y'=>10,
+                                justify=>'fill',
+                                color=>'FFFFFF'),
+      "basic test");
+  ok($img->write(file=>'testout/t80wrapped.ppm'), "save to file");
+  ok(scalar Imager::Font::Wrap->wrap_text(string => $text,
+                                font=>$font,
+                                image=>undef,
+                                size=>13,
+                                width => 380,
+                                x=>10, 'y'=>10,
+                                justify=>'left',
+                                color=>'FFFFFF'),
+      "no image test");
+  my $bbox = $font->bounding_box(string=>"Xx", size=>13);
+  ok($bbox, "get height for check");
+
+  my $used;
+  ok(scalar Imager::Font::Wrap->wrap_text
+      (string=>$text, font=>$font, image=>undef, size=>13, width=>380,
+       savepos=> \$used, height => $bbox->font_height), "savepos call");
+  ok($used > 20 && $used < length($text), "savepos value");
+  print "# $used\n";
+  my @box = Imager::Font::Wrap->wrap_text
+    (string=>substr($text, 0, $used), font=>$font, image=>undef, size=>13,
+     width=>380);
+
+  ok(@box == 4, "bounds list count");
+  print "# @box\n";
+  ok($box[3] == $bbox->font_height, "check height");
+
+  { # regression
+    # http://rt.cpan.org/Ticket/Display.html?id=29771
+    # the length of the trailing line wasn't included in the text consumed
+    my $used;
+    ok(scalar Imager::Font::Wrap->wrap_text
+       ( string => "test", font => $font, image => undef, size => 12,
+        width => 200, savepos => \$used, height => $bbox->font_height),
+       "regression 29771 - call wrap_text");
+    is($used, 4, "all text should be consumed");
+  }
+}
diff --git a/t/400-filter/010-filters.t b/t/400-filter/010-filters.t
new file mode 100644 (file)
index 0000000..0fe334e
--- /dev/null
@@ -0,0 +1,471 @@
+#!perl -w
+use strict;
+use Imager qw(:handy);
+use Test::More tests => 122;
+
+-d "testout" or mkdir "testout";
+
+Imager::init_log("testout/t61filters.log", 1);
+use Imager::Test qw(is_image_similar test_image is_image is_color4 is_fcolor4);
+# meant for testing the filters themselves
+
+my $imbase = test_image();
+
+my $im_other = Imager->new(xsize=>150, ysize=>150);
+$im_other->box(xmin=>30, ymin=>60, xmax=>120, ymax=>90, filled=>1);
+
+test($imbase, {type=>'autolevels'}, 'testout/t61_autolev.ppm');
+
+test($imbase, {type=>'contrast', intensity=>0.5}, 
+     'testout/t61_contrast.ppm');
+
+# this one's kind of cool
+test($imbase, {type=>'conv', coef=>[ 0.3, 1, 0.3, ], },
+     'testout/t61_conv_blur.ppm');
+
+{
+  my $work = $imbase->copy;
+  ok(!Imager::i_conv($work->{IMG}, []), "conv should fail with empty array");
+  ok(!$work->filter(type => 'conv', coef => []),
+     "check the conv OO intergave too");
+  is($work->errstr, "there must be at least one coefficient",
+     "check conv error message");
+}
+
+{
+  my $work8 = $imbase->copy;
+  ok(!$work8->filter(type => "conv", coef => "ABC"),
+     "coef not an array");
+}
+{
+  my $work8 = $imbase->copy;
+  ok(!$work8->filter(type => "conv", coef => [ -1, 2, -1 ]),
+     "should fail if sum of coef is 0");
+  is($work8->errstr, "sum of coefficients is zero", "check message");
+}
+
+{
+  my $work8 = $imbase->copy;
+  my $work16 = $imbase->to_rgb16;
+  my $coef = [ -0.2, 1, -0.2 ];
+  ok($work8->filter(type => "conv", coef => $coef),
+     "filter 8 bit image");
+  ok($work16->filter(type => "conv", , coef => $coef),
+     "filter 16 bit image");
+  is_image_similar($work8, $work16, 80000, "8 and 16 bit conv match");
+}
+
+{
+  my $gauss = test($imbase, {type=>'gaussian', stddev=>5 },
+                  'testout/t61_gaussian.ppm');
+
+  my $imbase16 = $imbase->to_rgb16;
+  my $gauss16 = test($imbase16,  {type=>'gaussian', stddev=>5 },
+                    'testout/t61_gaussian16.ppm');
+  is_image_similar($gauss, $gauss16, 250000, "8 and 16 gaussian match");
+}
+
+
+test($imbase, { type=>'gradgen', dist=>1,
+                   xo=>[ 10,  10, 120 ],
+                   yo=>[ 10, 140,  60 ],
+                   colors=> [ NC('#FF0000'), NC('#FFFF00'), NC('#00FFFF') ]},
+     'testout/t61_gradgen.ppm');
+
+test($imbase, {type=>'mosaic', size=>8}, 'testout/t61_mosaic.ppm');
+
+test($imbase, {type=>'hardinvert'}, 'testout/t61_hardinvert.ppm');
+
+{ # invert - 8 bit
+  my $im = Imager->new(xsize => 1, ysize => 1, channels => 4);
+  ok($im, "make test image for invert test");
+  ok($im->setpixel(x => 0, y => 0, color => "000010C0"),
+     "set a test pixel");
+  my $copy = $im->copy;
+  ok($im->filter(type => "hardinvert"), "hardinvert it");
+  is_color4($im->getpixel(x => 0, y => 0), 255, 255, 0xEF, 0xC0,
+           "check only colour inverted");
+  ok($copy->filter(type => "hardinvertall"), "hardinvertall copy");
+  is_color4($copy->getpixel(x => 0, y => 0), 255, 255, 0xEF, 0x3f,
+           "check all inverted");
+}
+
+{ # invert - double image
+  my $im = Imager->new(xsize => 1, ysize => 1, channels => 4, bits => "double");
+  ok($im, "make double test image for invert test");
+  ok($im->setpixel(x => 0, y => 0, color => Imager::Color::Float->new(0, 0, 0.125, 0.75)),
+     "set a test pixel");
+  my $copy = $im->copy;
+  ok($im->filter(type => "hardinvert"), "hardinvert it");
+  is_fcolor4($im->getpixel(x => 0, y => 0, type => "double"),
+            1.0, 1.0, 0.875, 0.75, 1e-5,
+            "check only colour inverted");
+  ok($copy->filter(type => "hardinvertall"), "hardinvertall copy");
+  is_fcolor4($copy->getpixel(x => 0, y => 0, type =>"double"),
+            1.0, 1.0, 0.875, 0.25, 1e-5,
+            "check all inverted");
+}
+
+test($imbase, {type=>'noise'}, 'testout/t61_noise.ppm');
+
+test($imbase, {type=>'radnoise'}, 'testout/t61_radnoise.ppm');
+
+test($imbase, {type=>'turbnoise'}, 'testout/t61_turbnoise.ppm');
+
+test($imbase, {type=>'bumpmap', bump=>$im_other, lightx=>30, lighty=>30},
+     'testout/t61_bumpmap.ppm');
+
+test($imbase, {type=>'bumpmap_complex', bump=>$im_other}, 'testout/t61_bumpmap_complex.ppm');
+
+test($imbase, {type=>'postlevels', levels=>3}, 'testout/t61_postlevels.ppm');
+
+test($imbase, {type=>'watermark', wmark=>$im_other },
+     'testout/t61_watermark.ppm');
+
+test($imbase, {type=>'fountain', xa=>75, ya=>75, xb=>85, yb=>30,
+               repeat=>'triangle', #ftype=>'radial', 
+               super_sample=>'circle', ssample_param => 16,
+              },
+     'testout/t61_fountain.ppm');
+use Imager::Fountain;
+
+my $f1 = Imager::Fountain->new;
+$f1->add(end=>0.2, c0=>NC(255, 0,0), c1=>NC(255, 255,0));
+$f1->add(start=>0.2, c0=>NC(255,255,0), c1=>NC(0,0,255,0));
+test($imbase, { type=>'fountain', xa=>20, ya=>130, xb=>130, yb=>20,
+                #repeat=>'triangle',
+                segments=>$f1
+              },
+     'testout/t61_fountain2.ppm');
+my $f2 = Imager::Fountain->new
+  ->add(end=>0.5, c0=>NC(255,0,0), c1=>NC(255,0,0), color=>'hueup')
+  ->add(start=>0.5, c0=>NC(255,0,0), c1=>NC(255,0,0), color=>'huedown');
+#use Data::Dumper;
+#print Dumper($f2);
+test($imbase, { type=>'fountain', xa=>20, ya=>130, xb=>130, yb=>20,
+                    segments=>$f2 },
+     'testout/t61_fount_hsv.ppm');
+my $f3 = Imager::Fountain->read(gimp=>'testimg/gimpgrad');
+ok($f3, "read gimpgrad");
+test($imbase, { type=>'fountain', xa=>75, ya=>75, xb=>90, yb=>15,
+                    segments=>$f3, super_sample=>'grid',
+                    ftype=>'radial_square', combine=>'color' },
+     'testout/t61_fount_gimp.ppm');
+{ # test new fountain with no parameters
+  my $warn = '';
+  local $SIG{__WARN__} = sub { $warn .= "@_" };
+  my $f4 = Imager::Fountain->read();
+  ok(!$f4, "read with no parameters does nothing");
+  like($warn, qr/Nothing to do!/, "check the warning");
+}
+{ # test with missing file
+  my $warn = '';
+  local $SIG{__WARN__} = sub { $warn .= "@_" };
+  my $f = Imager::Fountain->read(gimp => "no-such-file");
+  ok(!$f, "try to read a fountain defintion that doesn't exist");
+  is($warn, "", "should be no warning");
+  like(Imager->errstr, qr/^Cannot open no-such-file: /, "check message");
+}
+SKIP:
+{
+  my $fh = IO::File->new("testimg/gimpgrad", "r");
+  ok($fh, "opened gradient")
+    or skip "Couldn't open gradient: $!", 1;
+  my $f = Imager::Fountain->read(gimp => $fh);
+  ok($f, "read gradient from file handle");
+}
+{
+  # not a gradient
+  my $f = Imager::Fountain->read(gimp => "t/400-filter/010-filters.t");
+  ok(!$f, "fail to read non-gradient");
+  is(Imager->errstr, "t/400-filter/010-filters.t is not a GIMP gradient file",
+     "check error message");
+}
+{ # an invalid gradient file
+  my $f = Imager::Fountain->read(gimp => "testimg/gradbad.ggr");
+  ok(!$f, "fail to read bad gradient (bad seg count)");
+  is(Imager->errstr, "testimg/gradbad.ggr is missing the segment count",
+     "check error message");
+}
+{ # an invalid gradient file
+  my $f = Imager::Fountain->read(gimp => "testimg/gradbad2.ggr");
+  ok(!$f, "fail to read bad gradient (bad segment)");
+  is(Imager->errstr, "Bad segment definition",
+     "check error message");
+}
+test($imbase, { type=>'unsharpmask', stddev=>2.0 },
+     'testout/t61_unsharp.ppm');
+test($imbase, {type=>'conv', coef=>[ -1, 3, -1, ], },
+     'testout/t61_conv_sharp.ppm');
+
+test($imbase, { type=>'nearest_color', dist=>1,
+                   xo=>[ 10,  10, 120 ],
+                   yo=>[ 10, 140,  60 ],
+                   colors=> [ NC('#FF0000'), NC('#FFFF00'), NC('#00FFFF') ]},
+     'testout/t61_nearest.ppm');
+
+# Regression test: the checking of the segment type was incorrect
+# (the comparison was checking the wrong variable against the wrong value)
+my $f4 = [ [ 0, 0.5, 1, NC(0,0,0), NC(255,255,255), 5, 0 ] ];
+test($imbase, {type=>'fountain',  xa=>75, ya=>75, xb=>90, yb=>15,
+               segments=>$f4, super_sample=>'grid',
+               ftype=>'linear', combine=>'color' },
+     'testout/t61_regress_fount.ppm');
+my $im2 = $imbase->copy;
+$im2->box(xmin=>20, ymin=>20, xmax=>40, ymax=>40, color=>'FF0000', filled=>1);
+$im2->write(file=>'testout/t61_diff_base.ppm');
+my $im3 = Imager->new(xsize=>150, ysize=>150, channels=>3);
+$im3->box(xmin=>20, ymin=>20, xmax=>40, ymax=>40, color=>'FF0000', filled=>1);
+my $diff = $imbase->difference(other=>$im2);
+ok($diff, "got difference image");
+SKIP:
+{
+  skip(1, "missing comp or diff image") unless $im3 && $diff;
+
+  is(Imager::i_img_diff($im3->{IMG}, $diff->{IMG}), 0,
+     "compare test image and diff image");
+}
+
+# newer versions of gimp add a line to the gradient file
+my $name;
+my $f5 = Imager::Fountain->read(gimp=>'testimg/newgimpgrad.ggr',
+                                name => \$name);
+ok($f5, "read newer gimp gradient")
+  or print "# ",Imager->errstr,"\n";
+is($name, "imager test gradient", "check name read correctly");
+$f5 = Imager::Fountain->read(gimp=>'testimg/newgimpgrad.ggr');
+ok($f5, "check we handle case of no name reference correctly")
+  or print "# ",Imager->errstr,"\n";
+
+# test writing of gradients
+ok($f2->write(gimp=>'testout/t61grad1.ggr'), "save a gradient")
+  or print "# ",Imager->errstr,"\n";
+undef $name;
+my $f6 = Imager::Fountain->read(gimp=>'testout/t61grad1.ggr', 
+                                name=>\$name);
+ok($f6, "read what we wrote")
+  or print "# ",Imager->errstr,"\n";
+ok(!defined $name, "we didn't set the name, so shouldn't get one");
+
+# try with a name
+ok($f2->write(gimp=>'testout/t61grad2.ggr', name=>'test gradient'),
+   "write gradient with a name")
+  or print "# ",Imager->errstr,"\n";
+undef $name;
+my $f7 = Imager::Fountain->read(gimp=>'testout/t61grad2.ggr', name=>\$name);
+ok($f7, "read what we wrote")
+  or print "# ",Imager->errstr,"\n";
+is($name, "test gradient", "check the name matches");
+
+# we attempt to convert color names in segments to segments now
+{
+  my @segs =
+    (
+     [ 0.0, 0.5, 1.0, '000000', '#FFF', 0, 0 ],
+    );
+  my $im = Imager->new(xsize=>50, ysize=>50);
+  ok($im->filter(type=>'fountain', segments => \@segs,
+                 xa=>0, ya=>30, xb=>49, yb=>30), 
+     "fountain with color names instead of objects in segments");
+  my $left = $im->getpixel('x'=>0, 'y'=>20);
+  ok(color_close($left, Imager::Color->new(0,0,0)),
+     "check black converted correctly");
+  my $right = $im->getpixel('x'=>49, 'y'=>20);
+  ok(color_close($right, Imager::Color->new(255,255,255)),
+     "check white converted correctly");
+
+  # check that invalid color names are handled correctly
+  my @segs2 =
+    (
+     [ 0.0, 0.5, 1.0, '000000', 'FxFxFx', 0, 0 ],
+    );
+  ok(!$im->filter(type=>'fountain', segments => \@segs2,
+                  xa=>0, ya=>30, xb=>49, yb=>30), 
+     "fountain with invalid color name");
+  cmp_ok($im->errstr, '=~', 'No color named', "check error message");
+}
+
+{
+  # test simple gradient creation
+  my @colors = map Imager::Color->new($_), qw/white blue red/;
+  my $s = Imager::Fountain->simple(positions => [ 0, 0.3, 1.0 ],
+                                  colors => \@colors);
+  ok($s, "made simple gradient");
+  my $start = $s->[0];
+  is($start->[0], 0, "check start of first correct");
+  is_color4($start->[3], 255, 255, 255, 255, "check color at start");
+}
+{
+  # simple gradient error modes
+  {
+    my $warn = '';
+    local $SIG{__WARN__} = sub { $warn .= "@_" };
+    my $s = Imager::Fountain->simple();
+    ok(!$s, "no parameters to simple()");
+    like($warn, qr/Nothing to do/);
+  }
+  {
+    my $s = Imager::Fountain->simple(positions => [ 0, 1 ],
+                                    colors => [ NC(0, 0, 0) ]);
+    ok(!$s, "mismatch of positions and colors fails");
+    is(Imager->errstr, "positions and colors must be the same size",
+       "check message");
+  }
+  {
+    my $s = Imager::Fountain->simple(positions => [ 0 ],
+                                    colors => [ NC(0, 0, 0) ]);
+    ok(!$s, "not enough positions");
+    is(Imager->errstr, "not enough segments");
+  }
+}
+
+{
+  my $im = Imager->new(xsize=>100, ysize=>100);
+  # build the gradient the hard way - linear from black to white,
+  # then back again
+  my @simple =
+   (
+     [   0, 0.25, 0.5, 'black', 'white', 0, 0 ],
+     [ 0.5. 0.75, 1.0, 'white', 'black', 0, 0 ],
+   );
+  # across
+  my $linear = $im->filter(type   => "fountain",
+                           ftype  => 'linear',
+                           repeat => 'sawtooth',
+                           xa     => 0,
+                           ya     => $im->getheight / 2,
+                           xb     => $im->getwidth - 1,
+                           yb     => $im->getheight / 2);
+  ok($linear, "linear fountain sample");
+  # around
+  my $revolution = $im->filter(type   => "fountain",
+                               ftype  => 'revolution',
+                               xa     => $im->getwidth / 2,
+                               ya     => $im->getheight / 2,
+                               xb     => $im->getwidth / 2,
+                               yb     => 0);
+  ok($revolution, "revolution fountain sample");
+  # out from the middle
+  my $radial = $im->filter(type   => "fountain",
+                           ftype  => 'radial',
+                           xa     => $im->getwidth / 2,
+                           ya     => $im->getheight / 2,
+                           xb     => $im->getwidth / 2,
+                           yb     => 0);
+  ok($radial, "radial fountain sample");
+}
+
+{
+  # try a simple custom filter that uses the Perl image interface
+  sub perl_filt {
+    my %args = @_;
+
+    my $im = $args{imager};
+
+    my $channels = $args{channels};
+    unless (@$channels) {
+      $channels = [ reverse(0 .. $im->getchannels-1) ];
+    }
+    my @chans = @$channels;
+    push @chans, 0 while @chans < 4;
+
+    for my $y (0 .. $im->getheight-1) {
+      my $row = $im->getsamples(y => $y, channels => \@chans);
+      $im->setscanline(y => $y, pixels => $row);
+    }
+  }
+  Imager->register_filter(type => 'perl_test',
+                          callsub => \&perl_filt,
+                          defaults => { channels => [] },
+                          callseq => [ qw/imager channels/ ]);
+  test($imbase, { type => 'perl_test' }, 'testout/t61perl.ppm');
+}
+
+{ # check the difference method out
+  my $im1 = Imager->new(xsize => 3, ysize => 2);
+  $im1->box(filled => 1, color => '#FF0000');
+  my $im2 = $im1->copy;
+  $im1->setpixel(x => 1, 'y' => 0, color => '#FF00FF');
+  $im2->setpixel(x => 1, 'y' => 0, color => '#FF01FF');
+  $im1->setpixel(x => 2, 'y' => 0, color => '#FF00FF');
+  $im2->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
+
+  my $diff1 = $im1->difference(other => $im2);
+  my $cmp1 = Imager->new(xsize => 3, ysize => 2, channels => 4);
+  $cmp1->setpixel(x => 1, 'y' => 0, color => '#FF01FF');
+  $cmp1->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
+  is_image($diff1, $cmp1, "difference() - check image with mindist 0");
+
+  my $diff2 = $im1->difference(other => $im2, mindist => 1);
+  my $cmp2 = Imager->new(xsize => 3, ysize => 2, channels => 4);
+  $cmp2->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
+  is_image($diff2, $cmp2, "difference() - check image with mindist 1");
+}
+
+{
+  # and again with large samples
+  my $im1 = Imager->new(xsize => 3, ysize => 2, bits => 'double');
+  $im1->box(filled => 1, color => '#FF0000');
+  my $im2 = $im1->copy;
+  $im1->setpixel(x => 1, 'y' => 0, color => '#FF00FF');
+  $im2->setpixel(x => 1, 'y' => 0, color => '#FF01FF');
+  $im1->setpixel(x => 2, 'y' => 0, color => '#FF00FF');
+  $im2->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
+
+  my $diff1 = $im1->difference(other => $im2);
+  my $cmp1 = Imager->new(xsize => 3, ysize => 2, channels => 4);
+  $cmp1->setpixel(x => 1, 'y' => 0, color => '#FF01FF');
+  $cmp1->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
+  is_image($diff1, $cmp1, "difference() - check image with mindist 0 - large samples");
+
+  my $diff2 = $im1->difference(other => $im2, mindist => 1.1);
+  my $cmp2 = Imager->new(xsize => 3, ysize => 2, channels => 4);
+  $cmp2->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
+  is_image($diff2, $cmp2, "difference() - check image with mindist 1.1 - large samples");
+}
+
+{
+  my $empty = Imager->new;
+  ok(!$empty->filter(type => "hardinvert"), "can't filter an empty image");
+  is($empty->errstr, "filter: empty input image",
+     "check error message");
+  ok(!$empty->difference(other => $imbase), "can't difference empty image");
+  is($empty->errstr, "difference: empty input image",
+     "check error message");
+  ok(!$imbase->difference(other => $empty),
+     "can't difference against empty image");
+  is($imbase->errstr, "difference: empty input image (other image)",
+     "check error message");
+}
+
+sub test {
+  my ($in, $params, $out) = @_;
+
+  my $copy = $in->copy;
+  if (ok($copy->filter(%$params), $params->{type})) {
+    ok($copy->write(file=>$out), "write $params->{type}") 
+      or print "# ",$copy->errstr,"\n";
+  }
+  else {
+    diag($copy->errstr);
+  SKIP: 
+    {
+      skip("couldn't filter", 1);
+    }
+  }
+  $copy;
+}
+
+sub color_close {
+  my ($c1, $c2) = @_;
+
+  my @c1 = $c1->rgba;
+  my @c2 = $c2->rgba;
+
+  for my $i (0..2) {
+    if (abs($c1[$i]-$c2[$i]) > 2) {
+      return 0;
+    }
+  }
+  return 1;
+}
diff --git a/t/450-api/100-inline.t b/t/450-api/100-inline.t
new file mode 100644 (file)
index 0000000..16d231a
--- /dev/null
@@ -0,0 +1,671 @@
+#!perl -w
+#
+# this tests both the Inline interface and the API
+use strict;
+use Test::More;
+use Imager::Test qw(is_color3 is_color4);
+eval "require Inline::C;";
+plan skip_all => "Inline required for testing API" if $@;
+
+eval "require Parse::RecDescent;";
+plan skip_all => "Could not load Parse::RecDescent" if $@;
+
+use Cwd 'getcwd';
+plan skip_all => "Inline won't work in directories with spaces"
+  if getcwd() =~ / /;
+
+plan skip_all => "perl 5.005_04, 5.005_05 too buggy"
+  if $] =~ /^5\.005_0[45]$/;
+
+-d "testout" or mkdir "testout";
+
+print STDERR "Inline version $Inline::VERSION\n";
+
+plan tests => 117;
+require Inline;
+Inline->import(with => 'Imager');
+Inline->import("FORCE"); # force rebuild
+#Inline->import(C => Config => OPTIMIZE => "-g");
+
+Inline->bind(C => <<'EOS');
+#include <math.h>
+
+int pixel_count(Imager::ImgRaw im) {
+  return im->xsize * im->ysize;
+}
+
+int count_color(Imager::ImgRaw im, Imager::Color c) {
+  int count = 0, x, y, chan;
+  i_color read_c;
+
+  for (x = 0; x < im->xsize; ++x) {
+    for (y = 0; y < im->ysize; ++y) {
+      int match = 1;
+      i_gpix(im, x, y, &read_c);
+      for (chan = 0; chan < im->channels; ++chan) {
+        if (read_c.channel[chan] != c->channel[chan]) {
+          match = 0;
+          break;
+        }
+      }
+      if (match)
+        ++count;
+    }
+  }
+
+  return count;
+}
+
+Imager make_10x10() {
+  i_img *im = i_img_8_new(10, 10, 3);
+  i_color c;
+  c.channel[0] = c.channel[1] = c.channel[2] = 255;
+  i_box_filled(im, 0, 0, im->xsize-1, im->ysize-1, &c);
+
+  return im;
+}
+
+/* tests that all of the APIs are visible - most of them anyway */
+Imager do_lots(Imager src) {
+  i_img *im = i_img_8_new(100, 100, 3);
+  i_img *fill_im = i_img_8_new(5, 5, 3);
+  i_img *testim;
+  i_color red, blue, green, black, temp_color;
+  i_fcolor redf, bluef;
+  i_fill_t *hatch, *fhatch_fill;
+  i_fill_t *im_fill;
+  i_fill_t *solid_fill, *fsolid_fill;
+  i_fill_t *fount_fill;
+  void *block;
+  double matrix[9] = /* 30 degree rotation */
+    {
+      0.866025,  -0.5,      0, 
+      0.5,       0.866025,  0, 
+      0,         0,         1,      
+    };
+  i_fountain_seg fseg;
+  i_img_tags tags;
+  int entry;
+  double temp_double;
+
+  red.channel[0] = 255; red.channel[1] = 0; red.channel[2] = 0;
+  red.channel[3] = 255;
+  blue.channel[0] = 0; blue.channel[1] = 0; blue.channel[2] = 255;
+  blue.channel[3] = 255;
+  green.channel[0] = 0; green.channel[1] = 255; green.channel[2] = 0;
+  green.channel[3] = 255;
+  black.channel[0] = black.channel[1] = black.channel[2] = 0;
+  black.channel[3] = 255;
+  hatch = i_new_fill_hatch(&red, &blue, 0, 1, NULL, 0, 0);
+
+  i_box(im, 0, 0, 9, 9, &red);
+  i_box_filled(im, 10, 0, 19, 9, &blue);
+  i_box_cfill(im, 20, 0, 29, 9, hatch);
+
+  /* make an image fill, and try it */
+  i_box_cfill(fill_im, 0, 0, 4, 4, hatch);
+  im_fill = i_new_fill_image(fill_im, matrix, 2, 2, 0);
+
+  i_box_cfill(im, 30, 0, 39, 9, im_fill);
+
+  /* make a solid fill and try it */
+  solid_fill = i_new_fill_solid(&red, 0);
+  i_box_cfill(im, 40, 0, 49, 9, solid_fill);
+
+  /* floating fills */
+  redf.channel[0] = 1.0; redf.channel[1] = 0; redf.channel[2] = 0;
+  redf.channel[3] = 1.0;
+  bluef.channel[0] = 0; bluef.channel[1] = 0; bluef.channel[2] = 1.0;
+  bluef.channel[3] = 1.0;
+
+  fsolid_fill = i_new_fill_solidf(&redf, 0);
+  i_box_cfill(im, 50, 0, 59, 9, fsolid_fill);
+  fhatch_fill = i_new_fill_hatchf(&redf, &bluef, 0, 2, NULL, 0, 0);
+  i_box_cfill(im, 60, 0, 69, 9, fhatch_fill);
+
+  /* fountain fill */
+  fseg.start = 0;
+  fseg.middle = 0.5;
+  fseg.end = 1.0;
+  fseg.c[0] = redf;
+  fseg.c[1] = bluef;
+  fseg.type = i_fst_linear;
+  fseg.color = i_fc_hue_down;
+  fount_fill = i_new_fill_fount(70, 0, 80, 0, i_ft_linear, i_fr_triangle, 0, i_fts_none, 1, 1, &fseg);
+
+  i_box_cfill(im, 70, 0, 79, 9, fount_fill);
+
+  i_line(im, 0, 10, 10, 15, &blue, 1);
+  i_line_aa(im, 0, 19, 10, 15, &red, 1);
+  
+  i_arc(im, 15, 15, 4, 45, 160, &blue);
+  i_arc_aa(im, 25, 15, 4, 75, 280, &red);
+  i_arc_cfill(im, 35, 15, 4, 0, 215, hatch);
+  i_arc_aa_cfill(im, 45, 15, 4, 30, 210, hatch);
+  i_circle_aa(im, 55, 15, 4, &red);
+  
+  i_box(im, 61, 11, 68, 18, &red);
+  i_flood_fill(im, 65, 15, &blue);
+  i_box(im, 71, 11, 78, 18, &red);
+  i_flood_cfill(im, 75, 15, hatch);
+
+  i_box_filled(im, 1, 21, 9, 24, &red);
+  i_box_filled(im, 1, 25, 9, 29, &blue);
+  i_flood_fill_border(im, 5, 25, &green, &black);
+
+  i_box_filled(im, 11, 21, 19, 24, &red);
+  i_box_filled(im, 11, 25, 19, 29, &blue);
+  i_flood_cfill_border(im, 15, 25, hatch, &black);
+
+  i_fill_destroy(fount_fill);
+  i_fill_destroy(fhatch_fill);
+  i_fill_destroy(solid_fill);
+  i_fill_destroy(fsolid_fill);
+  i_fill_destroy(hatch);
+  i_fill_destroy(im_fill);
+  i_img_destroy(fill_im);
+
+  /* make sure we can make each image type */
+  testim = i_img_16_new(100, 100, 3);
+  i_img_destroy(testim);
+  testim = i_img_double_new(100, 100, 3);
+  i_img_destroy(testim);
+  testim = i_img_pal_new(100, 100, 3, 256);
+  i_img_destroy(testim);
+  testim = i_sametype(im, 50, 50);
+  i_img_destroy(testim);
+  testim = i_sametype_chans(im, 50, 50, 4);
+  i_img_destroy(testim);
+
+  i_clear_error();
+  i_push_error(0, "Hello");
+  i_push_errorf(0, "%s", "World");
+
+  /* make sure tags create/destroy work */
+  i_tags_new(&tags);
+  i_tags_destroy(&tags);  
+
+  block = mymalloc(20);
+  block = myrealloc(block, 50);
+  myfree(block);
+
+  i_tags_set(&im->tags, "lots_string", "foo", -1);
+  i_tags_setn(&im->tags, "lots_number", 101);
+
+  if (!i_tags_find(&im->tags, "lots_number", 0, &entry)) {
+    i_push_error(0, "lots_number tag not found");
+    i_img_destroy(im);
+    return NULL;
+  }
+  i_tags_delete(&im->tags, entry);
+
+  /* these won't delete anything, but it makes sure the macros and function
+     pointers are correct */
+  i_tags_delbyname(&im->tags, "unknown");
+  i_tags_delbycode(&im->tags, 501);
+  i_tags_set_float(&im->tags, "lots_float", 0, 3.14);
+  if (!i_tags_get_float(&im->tags, "lots_float", 0, &temp_double)) {
+    i_push_error(0, "lots_float not found");
+    i_img_destroy(im);
+    return NULL;
+  }
+  if (fabs(temp_double - 3.14) > 0.001) {
+    i_push_errorf(0, "lots_float incorrect %g", temp_double);
+    i_img_destroy(im);
+    return NULL;
+  }
+  i_tags_set_float2(&im->tags, "lots_float2", 0, 100 * sqrt(2.0), 5);
+  if (!i_tags_get_int(&im->tags, "lots_float2", 0, &entry)) {
+    i_push_error(0, "lots_float2 not found as int");
+    i_img_destroy(im);
+    return NULL;
+  }
+  if (entry != 141) { 
+    i_push_errorf(0, "lots_float2 unexpected value %d", entry);
+    i_img_destroy(im);
+    return NULL;
+  }
+
+  i_tags_set_color(&im->tags, "lots_color", 0, &red);
+  if (!i_tags_get_color(&im->tags, "lots_color", 0, &temp_color)) {
+    i_push_error(0, "lots_color not found as color");
+    i_img_destroy(im);
+    return NULL;
+  }
+    
+  return im;
+}
+
+void
+io_fd(int fd) {
+  Imager::IO io = io_new_fd(fd);
+  i_io_write(io, "test", 4);
+  i_io_close(io);
+  io_glue_destroy(io);
+}
+
+int
+io_bufchain_test() {
+  Imager::IO io = io_new_bufchain();
+  unsigned char *result;
+  size_t size;
+  if (i_io_write(io, "test2", 5) != 5) {
+    fprintf(stderr, "write failed\n");
+    return 0;
+  }
+  if (!i_io_flush(io)) {
+    fprintf(stderr, "flush failed\n");
+    return 0;
+  }
+  if (i_io_close(io) != 0) {
+    fprintf(stderr, "close failed\n");
+    return 0;
+  }
+  size = io_slurp(io, &result);
+  if (size != 5) {
+    fprintf(stderr, "wrong size\n");
+    return 0;
+  }
+  if (memcmp(result, "test2", 5)) {
+    fprintf(stderr, "data mismatch\n");
+    return 0;
+  }
+  if (i_io_seek(io, 0, 0) != 0) {
+    fprintf(stderr, "seek failure\n");
+    return 0;
+  }
+  myfree(result);
+  io_glue_destroy(io);
+
+  return 1;
+}
+
+const char *
+io_buffer_test(SV *in) {
+  STRLEN len;
+  const char *in_str = SvPV(in, len);
+  static char buf[100];
+  Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
+  ssize_t read_size;
+
+  read_size = i_io_read(io, buf, sizeof(buf)-1);
+  io_glue_destroy(io);
+  if (read_size < 0 || read_size >= sizeof(buf)) {
+    return "";
+  }
+
+  buf[read_size] = '\0';
+
+  return buf;
+}
+
+const char *
+io_peekn_test(SV *in) {
+  STRLEN len;
+  const char *in_str = SvPV(in, len);
+  static char buf[100];
+  Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
+  ssize_t read_size;
+
+  read_size = i_io_peekn(io, buf, sizeof(buf)-1);
+  io_glue_destroy(io);
+  if (read_size < 0 || read_size >= sizeof(buf)) {
+    return "";
+  }
+
+  buf[read_size] = '\0';
+
+  return buf;
+}
+
+const char *
+io_gets_test(SV *in) {
+  STRLEN len;
+  const char *in_str = SvPV(in, len);
+  static char buf[100];
+  Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
+  ssize_t read_size;
+
+  read_size = i_io_gets(io, buf, sizeof(buf), 's');
+  io_glue_destroy(io);
+  if (read_size < 0 || read_size >= sizeof(buf)) {
+    return "";
+  }
+
+  return buf;
+}
+
+int
+io_getc_test(SV *in) {
+  STRLEN len;
+  const char *in_str = SvPV(in, len);
+  static char buf[100];
+  Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
+  int result;
+
+  result = i_io_getc(io);
+  io_glue_destroy(io);
+
+  return result;
+}
+
+int
+io_peekc_test(SV *in) {
+  STRLEN len;
+  const char *in_str = SvPV(in, len);
+  static char buf[100];
+  Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
+  int result;
+
+  i_io_set_buffered(io, 0);
+
+  result = i_io_peekc(io);
+  io_glue_destroy(io);
+
+  return result;
+}
+
+
+
+int
+test_render_color(Imager work_8) {
+  i_render *r8;
+  i_color c;
+  unsigned char render_coverage[3];
+
+  render_coverage[0] = 0;
+  render_coverage[1] = 128;
+  render_coverage[2] = 255;
+
+  r8 = i_render_new(work_8, 10);
+  c.channel[0] = 128;
+  c.channel[1] = 255;
+  c.channel[2] = 0;
+  c.channel[3] = 255;
+  i_render_color(r8, 0, 0, sizeof(render_coverage), render_coverage, &c);
+
+  c.channel[3] = 128;
+  i_render_color(r8, 0, 1, sizeof(render_coverage), render_coverage, &c);
+
+  c.channel[3] = 0;
+  i_render_color(r8, 0, 2, sizeof(render_coverage), render_coverage, &c);
+
+  i_render_delete(r8);
+
+  return 1;
+}
+
+int
+raw_psamp(Imager im, int chan_count) {
+  static i_sample_t samps[] = { 0, 127, 255 };
+
+  i_clear_error();
+  return i_psamp(im, 0, 1, 0, samps, NULL, chan_count);
+}
+
+int
+raw_psampf(Imager im, int chan_count) {
+  static i_fsample_t samps[] = { 0, 0.5, 1.0 };
+
+  i_clear_error();
+  return i_psampf(im, 0, 1, 0, samps, NULL, chan_count);
+}
+
+int
+test_mutex() {
+  i_mutex_t m;
+
+  m = i_mutex_new();
+  i_mutex_lock(m);
+  i_mutex_unlock(m);
+  i_mutex_destroy(m);
+
+  return 1;
+}
+
+int
+test_slots() {
+  im_slot_t slot = im_context_slot_new(NULL);
+
+  if (im_context_slot_get(aIMCTX, slot)) {
+    fprintf(stderr, "slots should default to NULL\n");
+    return 0;
+  }
+  if (!im_context_slot_set(aIMCTX, slot, &slot)) {
+    fprintf(stderr, "set slot failed\n");
+    return 0;
+  }
+
+  if (im_context_slot_get(aIMCTX, slot) != &slot) {
+    fprintf(stderr, "get slot didn't match\n");
+    return 0;
+  }
+
+  return 1;
+}
+
+EOS
+
+my $im = Imager->new(xsize=>50, ysize=>50);
+is(pixel_count($im), 2500, "pixel_count");
+
+my $black = Imager::Color->new(0,0,0);
+is(count_color($im, $black), 2500, "count_color black on black image");
+
+my $im2 = make_10x10();
+my $white = Imager::Color->new(255, 255, 255);
+is(count_color($im2, $white), 100, "check new image white count");
+ok($im2->box(filled=>1, xmin=>1, ymin=>1, xmax => 8, ymax=>8, color=>$black),
+   "try new image");
+is(count_color($im2, $black), 64, "check modified black count");
+is(count_color($im2, $white), 36, "check modified white count");
+
+my $im3 = do_lots($im2);
+ok($im3, "do_lots()")
+  or print "# ", Imager->_error_as_msg, "\n";
+ok($im3->write(file=>'testout/t82lots.ppm'), "write t82lots.ppm");
+
+{ # RT #24992
+  # the T_IMAGER_FULL_IMAGE typemap entry was returning a blessed
+  # hash with an extra ref, causing memory leaks
+
+  my $im = make_10x10();
+  my $im2 = Imager->new(xsize => 10, ysize => 10);
+  require B;
+  my $imb = B::svref_2object($im);
+  my $im2b = B::svref_2object($im2);
+  is ($imb->REFCNT, $im2b->REFCNT, 
+      "check refcnt of imager object hash between normal and typemap generated");
+}
+
+SKIP:
+{
+  use IO::File;
+  my $fd_filename = "testout/t82fd.txt";
+  {
+    my $fh = IO::File->new($fd_filename, "w")
+      or skip("Can't create file: $!", 1);
+    io_fd(fileno($fh));
+    $fh->close;
+  }
+  {
+    my $fh = IO::File->new($fd_filename, "r")
+      or skip("Can't open file: $!", 1);
+    my $data = <$fh>;
+    is($data, "test", "make sure data written to fd");
+  }
+  unlink $fd_filename;
+}
+
+ok(io_bufchain_test(), "check bufchain functions");
+
+is(io_buffer_test("test3"), "test3", "check io_new_buffer() and i_io_read");
+
+is(io_peekn_test("test5"), "test5", "check i_io_peekn");
+
+is(io_gets_test("test"), "tes", "check i_io_gets()");
+
+is(io_getc_test("ABC"), ord "A", "check i_io_getc(_imp)?");
+
+is(io_getc_test("XYZ"), ord "X", "check i_io_peekc(_imp)?");
+
+for my $bits (8, 16) {
+  print "# bits: $bits\n";
+
+  # the floating point processing is a little more accurate
+  my $bump = $bits == 16 ? 1 : 0;
+  {
+    my $im = Imager->new(xsize => 10, ysize => 10, channels => 3, bits => $bits);
+    ok($im->box(filled => 1, color => '#808080'), "fill work image with gray");
+    ok(test_render_color($im),
+       "call render_color on 3 channel image");
+    is_color3($im->getpixel(x => 0, y => 0), 128, 128, 128,
+             "check zero coverage, alpha 255 color, bits $bits");
+    is_color3($im->getpixel(x => 1, y => 0), 128, 191+$bump, 63+$bump,
+             "check 128 coverage, alpha 255 color, bits $bits");
+    is_color3($im->getpixel(x => 2, y => 0), 128, 255, 0,
+             "check 255 coverage, alpha 255 color, bits $bits");
+
+    is_color3($im->getpixel(x => 0, y => 1), 128, 128, 128,
+             "check zero coverage, alpha 128 color, bits $bits");
+    is_color3($im->getpixel(x => 1, y => 1), 128, 159+$bump, 95+$bump,
+             "check 128 coverage, alpha 128 color, bits $bits");
+    is_color3($im->getpixel(x => 2, y => 1), 128, 191+$bump, 63+$bump,
+             "check 255 coverage, alpha 128 color, bits $bits");
+
+    is_color3($im->getpixel(x => 0, y => 2), 128, 128, 128,
+             "check zero coverage, alpha 0 color, bits $bits");
+    is_color3($im->getpixel(x => 1, y => 2), 128, 128, 128,
+             "check 128 coverage, alpha 0 color, bits $bits");
+    is_color3($im->getpixel(x => 2, y => 2), 128, 128, 128,
+             "check 255 coverage, alpha 0 color, bits $bits");
+  }
+  {
+    my $im = Imager->new(xsize => 10, ysize => 10, channels => 4, bits => $bits);
+    ok($im->box(filled => 1, color => '#808080'), "fill work image with opaque gray");
+    ok(test_render_color($im),
+       "call render_color on 4 channel image");
+    is_color4($im->getpixel(x => 0, y => 0), 128, 128, 128, 255,
+             "check zero coverage, alpha 255 color, bits $bits");
+    is_color4($im->getpixel(x => 1, y => 0), 128, 191+$bump, 63+$bump, 255,
+             "check 128 coverage, alpha 255 color, bits $bits");
+    is_color4($im->getpixel(x => 2, y => 0), 128, 255, 0, 255,
+             "check 255 coverage, alpha 255 color, bits $bits");
+
+    is_color4($im->getpixel(x => 0, y => 1), 128, 128, 128, 255,
+             "check zero coverage, alpha 128 color, bits $bits");
+    is_color4($im->getpixel(x => 1, y => 1), 128, 159+$bump, 95+$bump, 255,
+             "check 128 coverage, alpha 128 color, bits $bits");
+    is_color4($im->getpixel(x => 2, y => 1), 128, 191+$bump, 63+$bump, 255,
+             "check 255 coverage, alpha 128 color, bits $bits");
+
+    is_color4($im->getpixel(x => 0, y => 2), 128, 128, 128, 255,
+             "check zero coverage, alpha 0 color, bits $bits");
+    is_color4($im->getpixel(x => 1, y => 2), 128, 128, 128, 255,
+             "check 128 coverage, alpha 0 color, bits $bits");
+    is_color4($im->getpixel(x => 2, y => 2), 128, 128, 128, 255,
+             "check 255 coverage, alpha 0 color, bits $bits");
+  }
+
+  {
+    my $im = Imager->new(xsize => 10, ysize => 10, channels => 4, bits => $bits);
+    ok($im->box(filled => 1, color => Imager::Color->new(128, 128, 128, 64)), "fill work image with translucent gray");
+    ok(test_render_color($im),
+       "call render_color on 4 channel image");
+    is_color4($im->getpixel(x => 0, y => 0), 128, 128, 128, 64,
+             "check zero coverage, alpha 255 color, bits $bits");
+    is_color4($im->getpixel(x => 1, y => 0), 128, 230, 25+$bump, 159+$bump,
+             "check 128 coverage, alpha 255 color, bits $bits");
+    is_color4($im->getpixel(x => 2, y => 0), 128, 255, 0, 255,
+             "check 255 coverage, alpha 255 color, bits $bits");
+
+    is_color4($im->getpixel(x => 0, y => 1), 128, 128, 128, 64,
+             "check zero coverage, alpha 128 color, bits $bits");
+    is_color4($im->getpixel(x => 1, y => 1), 129-$bump, 202-$bump, 55, 111+$bump,
+             "check 128 coverage, alpha 128 color, bits $bits");
+    is_color4($im->getpixel(x => 2, y => 1), 128, 230, 25+$bump, 159+$bump,
+             "check 255 coverage, alpha 128 color, bits $bits");
+
+    is_color4($im->getpixel(x => 0, y => 2), 128, 128, 128, 64,
+             "check zero coverage, alpha 0 color, bits $bits");
+    is_color4($im->getpixel(x => 1, y => 2), 128, 128, 128, 64,
+             "check 128 coverage, alpha 0 color, bits $bits");
+    is_color4($im->getpixel(x => 2, y => 2), 128, 128, 128, 64,
+             "check 255 coverage, alpha 0 color, bits $bits");
+  }
+}
+
+{
+  my $im = Imager->new(xsize => 10, ysize => 10);
+  is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail");
+  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psamp($im, 0), -1, "bad channel list (0) for psamp should fail");
+  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail");
+  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail");
+  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
+     "check message");
+}
+
+{
+  my $im = Imager->new(xsize => 10, ysize => 10, bits => 16);
+  is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail (16-bit)");
+  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psamp($im, 0), -1, "bad channel list (0) for psamp should fail (16-bit)");
+  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail (16-bit)");
+  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail (16-bit)");
+  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
+     "check message");
+}
+
+{
+  my $im = Imager->new(xsize => 10, ysize => 10, bits => 'double');
+  is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail (double)");
+  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psamp($im, 0), -1,, "bad channel list (0) for psamp should fail (double)");
+  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail (double)");
+  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail (double)");
+  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
+     "check message");
+}
+
+{
+  my $im = Imager->new(xsize => 10, ysize => 10, type => "paletted");
+  is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail (paletted)");
+  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psamp($im, 0), -1, "bad channel list (0) for psamp should fail (paletted)");
+  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail (paletted)");
+  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
+     "check message");
+  is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail (paletted)");
+  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
+     "check message");
+  is($im->type, "paletted", "make sure we kept the image type");
+}
+
+ok(test_mutex(), "call mutex APIs");
+
+ok(test_slots(), "call slot APIs");
+
+sub _get_error {
+  my @errors = Imager::i_errors();
+  return join(": ", map $_->[0], @errors);
+}
diff --git a/t/450-api/110-inlinectx.t b/t/450-api/110-inlinectx.t
new file mode 100644 (file)
index 0000000..90665f4
--- /dev/null
@@ -0,0 +1,92 @@
+#!perl -w
+#
+# this tests both the Inline interface and the API with IMAGER_NO_CONTEXT
+use strict;
+use Test::More;
+use Imager::Test qw(is_color3 is_color4);
+eval "require Inline::C;";
+plan skip_all => "Inline required for testing API" if $@;
+
+eval "require Parse::RecDescent;";
+plan skip_all => "Could not load Parse::RecDescent" if $@;
+
+use Cwd 'getcwd';
+plan skip_all => "Inline won't work in directories with spaces"
+  if getcwd() =~ / /;
+
+plan skip_all => "perl 5.005_04, 5.005_05 too buggy"
+  if $] =~ /^5\.005_0[45]$/;
+
+-d "testout" or mkdir "testout";
+
+plan tests => 5;
+require Inline;
+Inline->import(C => Config => AUTO_INCLUDE => "#define IMAGER_NO_CONTEXT\n");
+Inline->import(with => 'Imager');
+Inline->import("FORCE"); # force rebuild
+#Inline->import(C => Config => OPTIMIZE => "-g");
+
+Inline->bind(C => <<'EOS');
+#include <math.h>
+
+Imager make_10x10() {
+  dIMCTX;
+  i_img *im = i_img_8_new(10, 10, 3);
+  i_color c;
+  c.channel[0] = c.channel[1] = c.channel[2] = 255;
+  i_box_filled(im, 0, 0, im->xsize-1, im->ysize-1, &c);
+
+  return im;
+}
+
+void error_dIMCTX() {
+  dIMCTX;
+  im_clear_error(aIMCTX);
+  im_push_error(aIMCTX, 0, "test1");
+  im_push_errorf(aIMCTX, 0, "test%d", 2);
+
+  im_log((aIMCTX, 0, "test logging\n"));
+}
+
+void error_dIMCTXim(Imager im) {
+  dIMCTXim(im);
+  im_clear_error(aIMCTX);
+  im_push_error(aIMCTX, 0, "test1");
+}
+
+int context_refs() {
+  dIMCTX;
+
+  im_context_refinc(aIMCTX, "context_refs");
+  im_context_refdec(aIMCTX, "context_refs");
+
+  return 1;
+}
+
+EOS
+
+Imager->open_log(log => "testout/t84inlinectx.log");
+
+my $im2 = make_10x10();
+ok($im2, "make an image");
+is_color3($im2->getpixel(x => 0, y => 0), 255, 255, 255,
+         "check the colors");
+error_dIMCTX();
+is(_get_error(), "test2: test1", "check dIMCTX");
+
+my $im = Imager->new(xsize => 1, ysize => 1);
+error_dIMCTXim($im);
+is(_get_error(), "test1", "check dIMCTXim");
+
+ok(context_refs(), "check refcount functions");
+
+Imager->close_log();
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t84inlinectx.log";
+}
+
+sub _get_error {
+  my @errors = Imager::i_errors();
+  return join(": ", map $_->[0], @errors);
+}
diff --git a/t/850-thread/010-base.t b/t/850-thread/010-base.t
new file mode 100644 (file)
index 0000000..f03cd24
--- /dev/null
@@ -0,0 +1,96 @@
+#!perl
+use strict;
+use Imager;
+use Imager::Color::Float;
+use Imager::Fill;
+use Config;
+my $loaded_threads;
+BEGIN {
+  if ($Config{useithreads} && $] > 5.008007) {
+    $loaded_threads =
+      eval {
+       require threads;
+       threads->import;
+       1;
+      };
+  }
+}
+use Test::More;
+
+$Config{useithreads}
+  or plan skip_all => "can't test Imager's threads support with no threads";
+$] > 5.008007
+  or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support";
+$loaded_threads
+  or plan skip_all => "couldn't load threads";
+
+$INC{"Devel/Cover.pm"}
+  and plan skip_all => "threads and Devel::Cover don't get along";
+
+# https://rt.cpan.org/Ticket/Display.html?id=65812
+# https://github.com/schwern/test-more/issues/labels/Test-Builder2#issue/100
+$Test::More::VERSION =~ /^2\.00_/
+  and plan skip_all => "threads are hosed in 2.00_06 and presumably all 2.00_*";
+
+plan tests => 13;
+
+my $thread = threads->create(sub { 1; });
+ok($thread->join, "join first thread");
+
+# these are all, or contain, XS allocated objects, if we don't handle
+# CLONE requests, or provide a CLONE_SKIP, we'll probably see a
+# double-free, one from the thread, and the other from the main line
+# of control.
+#
+# So make one of each
+
+my $im = Imager->new(xsize => 10, ysize => 10);
+my $c = Imager::Color->new(0, 0, 0); # make some sort of color
+ok($c, "made the color");
+my $cf = Imager::Color::Float->new(0, 0, 0);
+ok($cf, "made the float color");
+my $hl;
+SKIP:
+{
+  Imager::Internal::Hlines::testing()
+      or skip "no hlines visible to test", 1;
+  $hl = Imager::Internal::Hlines::new(0, 100, 0, 100);
+  ok($hl, "made the hlines");
+}
+my $io = Imager::io_new_bufchain();
+ok($io, "made the io");
+my $tt;
+SKIP:
+{
+  $Imager::formats{tt}
+    or skip("No TT font support", 1);
+  $tt = Imager::Font->new(type => "tt", file => "fontfiles/dodge.ttf");
+  ok($tt, "made the font");
+}
+my $ft2;
+SKIP:
+{
+  $Imager::formats{ft2}
+    or skip "No FT2 support", 1;
+  $ft2 = Imager::Font->new(type => "ft2", file => "fontfiles/dodge.ttf");
+  ok($ft2, "made ft2 font");
+}
+my $fill = Imager::Fill->new(solid => $c);
+ok($fill, "made the fill");
+
+my $t2 = threads->create
+  (
+   sub {
+     ok(!UNIVERSAL::isa($im->{IMG}, "Imager::ImgRaw"),
+       "the low level image object should become unblessed");
+     ok(!$im->_valid_image, "image no longer considered valid");
+     is($im->errstr, "images do not cross threads",
+       "check error message");
+     1;
+   }
+  );
+ok($t2->join, "join second thread");
+#print STDERR $im->{IMG}, "\n";
+ok(UNIVERSAL::isa($im->{IMG}, "Imager::ImgRaw"),
+   "but the object should be fine in the main thread");
+
diff --git a/t/850-thread/100-error.t b/t/850-thread/100-error.t
new file mode 100644 (file)
index 0000000..4eacbd0
--- /dev/null
@@ -0,0 +1,78 @@
+#!perl -w
+use strict;
+
+# avoiding this prologue would be nice, but it seems to be unavoidable,
+# see "It is also important to note ..." in perldoc threads
+use Config;
+my $loaded_threads;
+BEGIN {
+  if ($Config{useithreads} && $] > 5.008007) {
+    $loaded_threads =
+      eval {
+       require threads;
+       threads->import;
+       1;
+      };
+  }
+}
+use Test::More;
+
+$Config{useithreads}
+  or plan skip_all => "can't test Imager's threads support with no threads";
+$] > 5.008007
+  or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support";
+$loaded_threads
+  or plan skip_all => "couldn't load threads";
+
+$INC{"Devel/Cover.pm"}
+  and plan skip_all => "threads and Devel::Cover don't get along";
+
+use Imager;
+
+# test that the error contexts are separate under threads
+
+plan tests => 11;
+
+Imager->open_log(log => "testout/t081error.log");
+
+Imager::i_clear_error();
+Imager::i_push_error(0, "main thread a");
+
+my @threads;
+for my $tid (1..5) {
+  my $t1 = threads->create
+    (
+     sub {
+       my $id = shift;
+       Imager::i_push_error(0, "$id: child thread a");
+       sleep(1+rand(4));
+       Imager::i_push_error(1, "$id: child thread b");
+
+       is_deeply([ Imager::i_errors() ],
+                [
+                 [ "$id: child thread b", 1 ],
+                 [ "$id: child thread a", 0 ],
+                ], "$id: check errors in child");
+       1;
+     },
+     $tid
+    );
+  push @threads, [ $tid, $t1 ];
+}
+
+Imager::i_push_error(1, "main thread b");
+
+for my $thread (@threads) {
+  my ($id, $t1) = @$thread;
+  ok($t1->join, "join child $id");
+}
+
+Imager::i_push_error(2, "main thread c");
+
+is_deeply([ Imager::i_errors() ],
+         [
+          [ "main thread c", 2 ],
+          [ "main thread b", 1 ],
+          [ "main thread a", 0 ],
+         ], "check errors in parent");
+
diff --git a/t/850-thread/110-log.t b/t/850-thread/110-log.t
new file mode 100644 (file)
index 0000000..2aa7219
--- /dev/null
@@ -0,0 +1,108 @@
+#!perl -w
+use strict;
+
+# avoiding this prologue would be nice, but it seems to be unavoidable,
+# see "It is also important to note ..." in perldoc threads
+use Config;
+my $loaded_threads;
+BEGIN {
+  if ($Config{useithreads} && $] > 5.008007) {
+    $loaded_threads =
+      eval {
+       require threads;
+       threads->import;
+       1;
+      };
+  }
+}
+use Test::More;
+
+$Config{useithreads}
+  or plan skip_all => "can't test Imager's threads support with no threads";
+$] > 5.008007
+  or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support";
+$loaded_threads
+  or plan skip_all => "couldn't load threads";
+
+$INC{"Devel/Cover.pm"}
+  and plan skip_all => "threads and Devel::Cover don't get along";
+
+use Imager;
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t080log1.log")
+  or plan skip_all => "Cannot open log file: " . Imager->errstr;
+
+plan tests => 3;
+
+Imager->log("main thread a\n");
+
+my $t1 = threads->create
+  (
+   sub {
+     Imager->log("child thread a\n");
+     Imager->open_log(log => "testout/t080log2.log")
+       or die "Cannot open second log file: ", Imager->errstr;
+     Imager->log("child thread b\n");
+     sleep(1);
+     Imager->log("child thread c\n");
+     sleep(1);
+     1;
+   }
+   );
+
+Imager->log("main thread b\n");
+sleep(1);
+Imager->log("main thread c\n");
+ok($t1->join, "join child thread");
+Imager->log("main thread d\n");
+Imager->close_log();
+
+my %log1 = parse_log("testout/t080log1.log");
+my %log2 = parse_log("testout/t080log2.log");
+
+my @log1 =
+  (
+   "main thread a",
+   "main thread b",
+   "child thread a",
+   "main thread c",
+   "main thread d",
+  );
+
+my @log2 =
+  (
+   "child thread b",
+   "child thread c",
+  );
+
+is_deeply(\%log1, { map {; $_ => 1 } @log1 },
+         "check messages in main thread log");
+is_deeply(\%log2, { map {; $_ => 1 } @log2 },
+         "check messages in child thread log");
+
+# grab the messages from the given log
+sub parse_log {
+  my ($filename) = @_;
+
+  open my $fh, "<", $filename
+    or die "Cannot open log file $filename: $!";
+
+  my %lines;
+  while (<$fh>) {
+    chomp;
+    my ($date, $time, $file_line, $level, $message) = split ' ', $_, 5;
+    $lines{$message} = 1;
+  }
+
+  delete $lines{"Imager - log started (level = 1)"};
+  delete $lines{"Imager $Imager::VERSION starting"};
+
+  return %lines;
+}
+
+END {
+  unlink "testout/t080log1.log", "testout/t080log2.log"
+    unless $ENV{IMAGER_KEEP_FILES};
+}
diff --git a/t/900-util/010-test.t b/t/900-util/010-test.t
new file mode 100644 (file)
index 0000000..984d212
--- /dev/null
@@ -0,0 +1,65 @@
+#!perl -w
+use strict;
+use Imager;
+use Imager::Test qw(test_image test_image_16 test_image_mono test_image_gray test_image_gray_16 test_image_double test_image_named);
+use Test::More tests => 60;
+
+# test Imager::Test
+
+for my $named (0, 1) {
+  my $named_desc = $named ? " (by name)" : "";
+  {
+    my $im = $named ? test_image_named("basic") : test_image();
+    ok($im, "got basic test image$named_desc");
+    is($im->type, "direct", "check basic image type");
+    is($im->getchannels, 3, "check basic image channels");
+    is($im->bits, 8, "check basic image bits");
+    ok(!$im->is_bilevel, "check basic isn't mono");
+  }
+  {
+    my $im = $named ? test_image_named("basic16") : test_image_16();
+    ok($im, "got 16-bit basic test image$named_desc");
+    is($im->type, "direct", "check 16-bit basic image type");
+    is($im->getchannels, 3, "check 16-bit basic image channels");
+    is($im->bits, 16, "check 16-bit basic image bits");
+    ok(!$im->is_bilevel, "check 16-bit basic isn't mono");
+  }
+  
+  {
+    my $im = $named ? test_image_named("basic_double") : test_image_double();
+    ok($im, "got double basic test image$named_desc");
+    is($im->type, "direct", "check double basic image type");
+    is($im->getchannels, 3, "check double basic image channels");
+    is($im->bits, "double", "check double basic image bits");
+    ok(!$im->is_bilevel, "check double basic isn't mono");
+  }
+  {
+    my $im = $named ? test_image_named("gray") : test_image_gray();
+    ok($im, "got gray test image$named_desc");
+    is($im->type, "direct", "check gray image type");
+    is($im->getchannels, 1, "check gray image channels");
+    is($im->bits, 8, "check gray image bits");
+    ok(!$im->is_bilevel, "check gray isn't mono");
+    $im->write(file => "testout/t03gray.pgm");
+  }
+  
+  {
+    my $im = $named ? test_image_named("gray16") : test_image_gray_16();
+    ok($im, "got gray test image$named_desc");
+    is($im->type, "direct", "check 16-bit gray image type");
+    is($im->getchannels, 1, "check 16-bit gray image channels");
+    is($im->bits, 16, "check 16-bit gray image bits");
+    ok(!$im->is_bilevel, "check 16-bit isn't mono");
+    $im->write(file => "testout/t03gray16.pgm");
+  }
+  
+  {
+    my $im = $named ? test_image_named("mono") : test_image_mono();
+    ok($im, "got mono image$named_desc");
+    is($im->type, "paletted", "check mono image type");
+    is($im->getchannels, 3, "check mono image channels");
+    is($im->bits, 8, "check mono image bits");
+    ok($im->is_bilevel, "check mono is mono");
+    $im->write(file => "testout/t03mono.pbm");
+  }
+}
diff --git a/t/900-util/020-error.t b/t/900-util/020-error.t
new file mode 100644 (file)
index 0000000..e3ac15a
--- /dev/null
@@ -0,0 +1,43 @@
+#!perl -w
+use strict;
+use Test::More tests => 7;
+BEGIN { use_ok("Imager", ":all") }
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/t05error.log");
+
+# try to read an invalid pnm file
+open FH, "< testimg/junk.ppm"
+  or die "Cannot open testin/junk: $!";
+binmode(FH);
+my $IO = Imager::io_new_fd(fileno(FH));
+my $im = i_readpnm_wiol($IO, -1);
+SKIP:{
+  ok(!$im, "read of junk.ppm should have failed")
+    or skip("read didn't fail!", 5);
+
+  my @errors = Imager::i_errors();
+
+  is(scalar @errors, 1, "got the errors")
+    or skip("no errors to check", 4);
+
+ SKIP:
+  {
+    my $error0 = $errors[0];
+    is(ref $error0, "ARRAY", "entry 0 is an array ref")
+      or skip("entry 0 not an array", 3);
+
+    is(scalar @$error0, 2, "entry 0 has 2 elements")
+      or skip("entry 0 doesn't have enough elements", 2);
+
+    is($error0->[0], "while skipping to height", "check message");
+    is($error0->[1], "0", "error code should be 0");
+  }
+}
+
+Imager->close_log;
+
+unless ($ENV{IMAGER_KEEP_FILES}) {
+  unlink "testout/t05error.log";
+}
diff --git a/t/900-util/030-log.t b/t/900-util/030-log.t
new file mode 100644 (file)
index 0000000..ce3323a
--- /dev/null
@@ -0,0 +1,34 @@
+#!perl -w
+use strict;
+use Imager;
+use Test::More tests => 6;
+
+my $log_name = "testout/t95log.log";
+
+my $log_message = "test message 12345";
+
+SKIP: {
+  skip("Logging not build", 3)
+    unless Imager::i_log_enabled();
+  ok(Imager->open_log(log => $log_name), "open log")
+    or diag("Open log: " . Imager->errstr);
+  ok(-f $log_name, "file is there");
+  Imager->log($log_message);
+  Imager->close_log();
+
+  my $data = '';
+  if (open LOG, "< $log_name") {
+    $data = do { local $/; <LOG> };
+    close LOG;
+  }
+  like($data, qr/\Q$log_message/, "check message made it to the log");
+}
+
+SKIP: {
+  skip("Logging built", 3)
+    if Imager::i_log_enabled();
+
+  ok(!Imager->open_log(log => $log_name), "should be no logfile");
+  is(Imager->errstr, "Logging disabled", "check error message");
+  ok(!-f $log_name, "file shouldn't be there");
+}
diff --git a/t/900-util/040-limit.t b/t/900-util/040-limit.t
new file mode 100644 (file)
index 0000000..ac3d841
--- /dev/null
@@ -0,0 +1,80 @@
+#!perl -w
+use strict;
+
+# avoiding this prologue would be nice, but it seems to be unavoidable,
+# see "It is also important to note ..." in perldoc threads
+use Config;
+my $loaded_threads;
+BEGIN {
+  if ($Config{useithreads} && $] > 5.008007) {
+    $loaded_threads =
+      eval {
+       require threads;
+       threads->import;
+       1;
+      };
+  }
+}
+use Test::More;
+
+$Config{useithreads}
+  or plan skip_all => "can't test Imager's threads support with no threads";
+$] > 5.008007
+  or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support";
+$loaded_threads
+  or plan skip_all => "couldn't load threads";
+
+$INC{"Devel/Cover.pm"}
+  and plan skip_all => "threads and Devel::Cover don't get along";
+
+use Imager;
+
+# test that image file limits are localized to a thread
+
+plan tests => 31;
+
+Imager->open_log(log => "testout/t082limit.log");
+
+ok(Imager->set_file_limits(width => 10, height => 10, bytes => 300),
+   "set limits to 10, 10, 300");
+
+ok(Imager->check_file_limits(width => 10, height => 10),
+   "successful check limits in parent");
+
+ok(!Imager->check_file_limits(width => 10, height => 10, sample_size => 2),
+   "failed check limits in parent");
+
+my @threads;
+for my $tid (1..5) {
+  my $t1 = threads->create
+    (
+     sub {
+       my $id = shift;
+       my $dlimit = $tid * 5;
+       my $blimit = $dlimit * $dlimit * 3;
+       ok(Imager->set_file_limits(width => $dlimit, height => $dlimit,
+                                 bytes => $blimit),
+         "$tid: set limits to $dlimit x $dlimit, $blimit bytes");
+       ok(Imager->check_file_limits(width => $dlimit, height => $dlimit),
+         "$tid: successful check $dlimit x $dlimit");
+       ok(!Imager->check_file_limits(width => $dlimit, height => $dlimit, sample_size => 2),
+         "$tid: failed check $dlimit x $dlimit, ssize 2");
+       is_deeply([ Imager->get_file_limits ], [ $dlimit, $dlimit, $blimit ],
+                "check limits are still $dlimit x $dlimit , $blimit bytes");
+     },
+     $tid
+    );
+  push @threads, [ $tid, $t1 ];
+}
+
+for my $thread (@threads) {
+  my ($id, $t1) = @$thread;
+  ok($t1->join, "join child $id");
+}
+
+ok(Imager->check_file_limits(width => 10, height => 10),
+   "test we still pass");
+ok(!Imager->check_file_limits(width => 10, height => 10, sample_size => 2),
+   "test we still fail");
+is_deeply([ Imager->get_file_limits ], [ 10, 10, 300 ],
+         "check original main thread limits still set");
diff --git a/t/900-util/050-matrix.t b/t/900-util/050-matrix.t
new file mode 100644 (file)
index 0000000..4604602
--- /dev/null
@@ -0,0 +1,132 @@
+#!perl -w
+use strict;
+use Test::More tests => 23;
+use Imager;
+
+BEGIN { use_ok('Imager::Matrix2d', ':handy') }
+
+my $id = Imager::Matrix2d->identity;
+
+ok(almost_equal($id, [ 1, 0, 0,
+                       0, 1, 0,
+                       0, 0, 1 ]), "identity matrix");
+my $trans = Imager::Matrix2d->translate('x'=>10, 'y'=>-11);
+ok(almost_equal($trans, [ 1, 0, 10,
+                          0, 1, -11,
+                          0, 0, 1 ]), "translate matrix");
+my $trans_x = Imager::Matrix2d->translate(x => 10);
+ok(almost_equal($trans_x, [ 1, 0, 10,
+                          0, 1, 0,
+                          0, 0, 1 ]), "translate just x");
+my $trans_y = Imager::Matrix2d->translate('y' => 11);
+ok(almost_equal($trans_y, [ 1, 0, 0,
+                          0, 1, 11,
+                          0, 0, 1 ]), "translate just y");
+
+my $rotate = Imager::Matrix2d->rotate(degrees=>90);
+ok(almost_equal($rotate, [ 0, -1, 0,
+                           1, 0,  0,
+                           0, 0,  1 ]), "rotate matrix");
+
+my $shear = Imager::Matrix2d->shear('x'=>0.2, 'y'=>0.3);
+ok(almost_equal($shear, [ 1,   0.2, 0,
+                          0.3, 1,   0,
+                          0,   0,   1 ]), "shear matrix");
+
+my $scale = Imager::Matrix2d->scale('x'=>1.2, 'y'=>0.8);
+ok(almost_equal($scale, [ 1.2, 0,   0,
+                          0,   0.8, 0,
+                          0,   0,   1 ]), "scale matrix");
+
+my $custom = Imager::Matrix2d->matrix(1, 0, 0, 0, 1, 0, 0, 0, 1);
+ok(almost_equal($custom, [ 1, 0, 0,
+                       0, 1, 0,
+                       0, 0, 1 ]), "custom matrix");
+
+my $trans_called;
+$rotate = Imager::Matrix2d::Test->rotate(degrees=>90, x=>50);
+ok($trans_called, "translate called on rotate with just x");
+
+$trans_called = 0;
+$rotate = Imager::Matrix2d::Test->rotate(degrees=>90, 'y'=>50);
+ok($trans_called, "translate called on rotate with just y");
+
+ok(!Imager::Matrix2d->matrix(), "bad custom matrix");
+is(Imager->errstr, "9 co-efficients required", "check error");
+
+{
+  my @half = ( 0.5, 0, 0,
+              0, 0.5, 0,
+              0, 0, 1 );
+  my @quart = ( 0, 0.25, 0,
+               1, 0, 0,
+               0, 0, 1 );
+  my $half_matrix = Imager::Matrix2d->matrix(@half);
+  my $quart_matrix = Imager::Matrix2d->matrix(@quart);
+  my $result = $half_matrix * $quart_matrix;
+  is_deeply($half_matrix * \@quart, $result, "mult by unblessed matrix");
+  is_deeply(\@half * $quart_matrix, $result, "mult with unblessed matrix");
+
+  my $half_three = Imager::Matrix2d->matrix(1.5, 0, 0, 0, 1.5, 0, 0, 0, 3);
+  is_deeply($half_matrix * 3, $half_three, "mult by three");
+  is_deeply(3 * $half_matrix, $half_three, "mult with three");
+
+  {
+    # check error handling - bad ref type
+    my $died = 
+      !eval {
+      my $foo = $half_matrix * +{};
+      1;
+    };
+    ok($died, "mult by hash ref died");
+    like($@, qr/multiply by array ref or number/, "check message");
+  }
+
+  {
+    # check error handling - bad array
+    $@ = '';
+    my $died = 
+      !eval {
+      my $foo = $half_matrix * [ 1 .. 8 ];
+      1;
+    };
+    ok($died, "mult by short array ref died");
+    like($@, qr/9 elements required in array ref/, "check message");
+  }
+
+  {
+    # check error handling - bad value
+    $@ = '';
+    my $died = 
+      !eval {
+      my $foo = $half_matrix * "abc";
+      1;
+    };
+    ok($died, "mult by bad scalar died");
+    like($@, qr/multiply by array ref or number/, "check message");
+  }
+  
+}
+
+
+sub almost_equal {
+  my ($m1, $m2) = @_;
+
+  for my $i (0..8) {
+    abs($m1->[$i] - $m2->[$i]) < 0.00001 or return undef;
+  }
+  return 1;
+}
+
+# this is used to ensure translate() is called correctly by rotate
+package Imager::Matrix2d::Test;
+use vars qw(@ISA);
+BEGIN { @ISA = qw(Imager::Matrix2d); }
+
+sub translate {
+  my ($class, %opts) = @_;
+
+  ++$trans_called;
+  return $class->SUPER::translate(%opts);
+}
+
diff --git a/t/900-util/060-extutil.t b/t/900-util/060-extutil.t
new file mode 100644 (file)
index 0000000..15080f3
--- /dev/null
@@ -0,0 +1,32 @@
+#!perl -w
+use strict;
+use Test::More tests => 6;
+use File::Spec;
+
+{ # RT 37353
+  local @INC = @INC;
+
+  unshift @INC, File::Spec->catdir('blib', 'lib');
+  unshift @INC, File::Spec->catdir('blib', 'arch');
+  require Imager::ExtUtils;
+  my $path = Imager::ExtUtils->base_dir;
+  ok(File::Spec->file_name_is_absolute($path), "check dirs absolute")
+    or print "# $path\n";
+}
+
+{ # includes
+  my $includes = Imager::ExtUtils->includes;
+  ok($includes =~ s/^-I//, "has the -I");
+  ok(-e File::Spec->catfile($includes, "imext.h"), "found a header");
+}
+
+{ # typemap
+  my $typemap = Imager::ExtUtils->typemap;
+  ok($typemap, "got a typemap path");
+  ok(-f $typemap, "it exists");
+  open TYPEMAP, "< $typemap";
+  my $tm_content = do { local $/; <TYPEMAP>; };
+  close TYPEMAP;
+  cmp_ok($tm_content, '=~', "Imager::Color\\s+T_PTROBJ",
+        "it seems to be the right file");
+}
diff --git a/t/900-util/060-hlines.t b/t/900-util/060-hlines.t
new file mode 100644 (file)
index 0000000..594f010
--- /dev/null
@@ -0,0 +1,103 @@
+#!perl -w
+use strict;
+use Test::More;
+use Imager;
+
+# this script tests an internal set of functions for Imager, they 
+# aren't intended to be used at the perl level.
+# these functions aren't present in all Imager builds
+
+unless (Imager::Internal::Hlines::testing()) {
+  plan skip_all => 'Imager not built to run this test';
+}
+
+plan tests => 15;
+
+my $hline = Imager::Internal::Hlines::new(0, 100, 0, 100);
+my $base_text = 'start_y: 0 limit_y: 100 start_x: 0 limit_x: 100';
+ok($hline, "made hline");
+is($hline->dump, "$base_text\n", "check values");
+$hline->add(5, -5, 7);
+is($hline->dump, <<EOS, "check (-5, 7) added");
+$base_text
+ 5 (1): [0, 2)
+EOS
+$hline->add(5, 8, 4);
+is($hline->dump, <<EOS, "check (8, 4) added");
+$base_text
+ 5 (2): [0, 2) [8, 12)
+EOS
+$hline->add(5, 3, 3);
+is($hline->dump, <<EOS, "check (3, 3) added");
+$base_text
+ 5 (3): [0, 2) [3, 6) [8, 12)
+EOS
+$hline->add(5, 2, 6);
+is($hline->dump, <<EOS, "check (2, 6) added");
+$base_text
+ 5 (1): [0, 12)
+EOS
+# adding out of range should do nothing
+my $current = <<EOS;
+$base_text
+ 5 (1): [0, 12)
+EOS
+$hline->add(6, -5, 5);
+is($hline->dump, $current, "check (6, -5, 5) not added");
+$hline->add(6, 100, 5);
+is($hline->dump, $current, "check (6, 100, 5) not added");
+$hline->add(-1, 5, 2);
+is($hline->dump, $current, "check (-1, 5, 2) not added");
+$hline->add(100, 5, 2);
+is($hline->dump, $current, "check (10, 5, 2) not added");
+
+# overlapped add check
+$hline->add(6, 2, 6);
+$hline->add(6, 3, 4);
+is($hline->dump, <<EOS, "check internal overlap merged");
+$base_text
+ 5 (1): [0, 12)
+ 6 (1): [2, 8)
+EOS
+
+# white box test: try to force reallocation of an entry
+for my $i (0..20) {
+  $hline->add(7, $i*2, 1);
+}
+is($hline->dump, <<EOS, "lots of segments");
+$base_text
+ 5 (1): [0, 12)
+ 6 (1): [2, 8)
+ 7 (21): [0, 1) [2, 3) [4, 5) [6, 7) [8, 9) [10, 11) [12, 13) [14, 15) [16, 17) [18, 19) [20, 21) [22, 23) [24, 25) [26, 27) [28, 29) [30, 31) [32, 33) [34, 35) [36, 37) [38, 39) [40, 41)
+EOS
+# now merge them
+$hline->add(7, 1, 39);
+is($hline->dump, <<EOS, "merge lots of segments");
+$base_text
+ 5 (1): [0, 12)
+ 6 (1): [2, 8)
+ 7 (1): [0, 41)
+EOS
+
+# clean object
+$hline = Imager::Internal::Hlines::new(50, 50, 50, 50);
+$base_text = 'start_y: 50 limit_y: 100 start_x: 50 limit_x: 100';
+
+# left merge
+$hline->add(51, 45, 10);
+$hline->add(51, 55, 4);
+is($hline->dump, <<EOS, "left merge");
+$base_text
+ 51 (1): [50, 59)
+EOS
+
+# right merge
+$hline->add(52, 90, 5);
+$hline->add(52, 87, 5);
+is($hline->dump, <<EOS, "right merge");
+$base_text
+ 51 (1): [50, 59)
+ 52 (1): [87, 95)
+EOS
+
+undef $hline;
diff --git a/t/950-kwalitee/010-pod.t b/t/950-kwalitee/010-pod.t
new file mode 100644 (file)
index 0000000..45d6ef5
--- /dev/null
@@ -0,0 +1,12 @@
+#!perl -w
+use strict;
+use Test::More;
+use ExtUtils::Manifest qw(maniread);
+eval "use Test::Pod 1.00;";
+plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
+my $manifest = maniread();
+my @pod = grep /\.(pm|pl|pod|PL)$/, keys %$manifest;
+plan tests => scalar(@pod);
+for my $file (@pod) {
+  pod_file_ok($file, "pod ok in $file");
+}
diff --git a/t/950-kwalitee/020-samples.t b/t/950-kwalitee/020-samples.t
new file mode 100644 (file)
index 0000000..7ca1598
--- /dev/null
@@ -0,0 +1,24 @@
+#!perl -w
+# packaging test - make sure we included the samples in the MANIFEST <sigh>
+use Test::More;
+use ExtUtils::Manifest qw(maniread);
+
+# first build a list of samples from samples/README
+open SAMPLES, "< samples/README"
+  or die "Cannot open samples/README: $!";
+my @sample_files;
+while (<SAMPLES>) {
+  chomp;
+  /^\w[\w.-]+\.\w+$/ and push @sample_files, $_;
+}
+
+close SAMPLES;
+
+plan tests => scalar(@sample_files);
+
+my $manifest = maniread();
+
+for my $filename (@sample_files) {
+  ok(exists($manifest->{"samples/$filename"}), 
+     "sample file $filename in manifest");
+}
diff --git a/t/950-kwalitee/030-podcover.t b/t/950-kwalitee/030-podcover.t
new file mode 100644 (file)
index 0000000..05efe11
--- /dev/null
@@ -0,0 +1,107 @@
+#!perl -w
+use strict;
+use lib 't';
+use Test::More;
+use ExtUtils::Manifest qw(maniread);
+#sub Pod::Coverage::TRACE_ALL() { 1 }
+eval "use Test::Pod::Coverage 1.08;";
+# 1.08 required for coverage_class support
+plan skip_all => "Test::Pod::Coverage 1.08 required for POD coverage" if $@;
+
+# scan for a list of files to get Imager method documentation from
+my $manifest = maniread();
+my @pods = ( 'Imager.pm', grep /\.pod$/, keys %$manifest );
+
+my @private = 
+  ( 
+   '^io?_',
+   '^DSO_',
+   '^Inline$',
+   '^yatf$',
+   '^malloc_state$',
+   '^init_log$',
+   '^polybezier$', # not ready for public consumption
+   '^border$', # I don't know what it is, expect it to go away
+  );
+my @trustme = ( '^open$',  );
+
+plan tests => 20;
+
+{
+  pod_coverage_ok('Imager', { also_private => \@private,
+                             pod_from => \@pods,
+                             trustme => \@trustme,
+                             coverage_class => 'Pod::Coverage::Imager' });
+  pod_coverage_ok('Imager::Font');
+  my @color_private = ( '^i_', '_internal$' );
+  pod_coverage_ok('Imager::Color', 
+                 { also_private => \@color_private });
+  pod_coverage_ok('Imager::Color::Float', 
+                 { also_private => \@color_private });
+  pod_coverage_ok('Imager::Color::Table');
+  pod_coverage_ok('Imager::ExtUtils');
+  pod_coverage_ok('Imager::Expr');
+  my $trust_parents = { coverage_class => 'Pod::Coverage::CountParents' };
+  pod_coverage_ok('Imager::Expr::Assem', $trust_parents);
+  pod_coverage_ok('Imager::Fill');
+  pod_coverage_ok('Imager::Font::BBox');
+  pod_coverage_ok('Imager::Font::Wrap');
+  pod_coverage_ok('Imager::Fountain');
+  pod_coverage_ok('Imager::Matrix2d');
+  pod_coverage_ok('Imager::Regops');
+  pod_coverage_ok('Imager::Transform');
+  pod_coverage_ok('Imager::Test');
+  pod_coverage_ok('Imager::IO',
+                 {
+                  pod_from => "lib/Imager/IO.pod",
+                  coverage_class => "Pod::Coverage::Imager",
+                  module => "Imager",
+                 });
+}
+
+{
+  # check all documented methods/functions are in the method index
+  my $coverage = 
+    Pod::Coverage::Imager->new(package => 'Imager',
+                              pod_from => \@pods,
+                              trustme => \@trustme,
+                              also_private => \@private);
+  my %methods = map { $_ => 1 } $coverage->covered;
+  open IMAGER, "< Imager.pm"
+    or die "Cannot open Imager.pm: $!";
+  while (<IMAGER>) {
+    last if /^=head1 METHOD INDEX/;
+  }
+  my @indexed;
+  my @unknown_indexed;
+  while (<IMAGER>) {
+    last if /^=\w/ && !/^=for\b/;
+
+    if (/^(\w+)\(/) {
+      push @indexed, $1;
+      unless (delete $methods{$1}) {
+       push @unknown_indexed, $1;
+      }
+    }
+  }
+
+  unless (is(keys %methods, 0, "all methods in method index")) {
+    diag "the following methods are documented but not in the index:";
+    diag $_ for sort keys %methods;
+  }
+  unless (is(@unknown_indexed, 0, "only methods in method index")) {
+    diag "the following names are in the method index but not documented";
+    diag $_ for sort @unknown_indexed;
+  }
+
+  sub dict_cmp_func;
+  is_deeply(\@indexed, [ sort dict_cmp_func @indexed ],
+           "check method index is alphabetically sorted");
+}
+
+sub dict_cmp_func {
+  (my $tmp_a = lc $a) =~ tr/_//d;
+  (my $tmp_b = lc $b) =~ tr/_//d;
+
+  $tmp_a cmp $tmp_b;
+}
diff --git a/t/950-kwalitee/040-strict.t b/t/950-kwalitee/040-strict.t
new file mode 100644 (file)
index 0000000..b3e73dc
--- /dev/null
@@ -0,0 +1,27 @@
+#!perl -w
+# this is intended for various kwalitee tests
+use strict;
+use Test::More;
+use ExtUtils::Manifest qw(maniread);
+
+my $manifest = maniread;
+
+# work up counts first
+
+my @pl_files = grep /\.(p[lm]|PL|perl)$/, keys %$manifest;
+
+plan tests => scalar(@pl_files);
+
+for my $filename (@pl_files) {
+  open PL, "< $filename"
+    or die "Cannot open $filename: $!";
+  my $found_strict;
+  while (<PL>) {
+    if (/^use strict;/) {
+      ++$found_strict;
+      last;
+    }
+  }
+  close PL;
+  ok($found_strict, "file $filename has use strict");
+}
diff --git a/t/950-kwalitee/050-meta.t b/t/950-kwalitee/050-meta.t
new file mode 100644 (file)
index 0000000..079e84a
--- /dev/null
@@ -0,0 +1,19 @@
+#!perl -w
+use strict;
+use Test::More;
+plan skip_all => "Only run as part of the dist"
+  unless -f "META.yml";
+eval "use CPAN::Meta 2.110580;";
+plan skip_all => "CPAN::Meta required for testing META.yml"
+  if $@;
+plan skip_all => "Only if automated or author testing"
+  unless $ENV{AUTOMATED_TESTING} || -d "../.git";
+plan tests => 1;
+
+my $meta;
+unless (ok(eval {
+  $meta = CPAN::Meta->load_file("META.yml",
+                               { lazy_validation => 0 }) },
+          "loaded META.yml successfully")) {
+  diag($@);
+}
diff --git a/t/t00basic.t b/t/t00basic.t
deleted file mode 100644 (file)
index 883af43..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 15;
-
-use_ok('Imager');
-use_ok('Imager::Font');
-use_ok('Imager::Color');
-use_ok('Imager::Color::Float');
-use_ok('Imager::Color::Table');
-use_ok('Imager::Matrix2d');
-use_ok('Imager::ExtUtils');
-use_ok('Imager::Expr');
-use_ok('Imager::Expr::Assem');
-use_ok('Imager::Font::BBox');
-use_ok('Imager::Font::Wrap');
-use_ok('Imager::Fountain');
-use_ok('Imager::Regops');
-use_ok('Imager::Test');
-use_ok('Imager::Transform');
diff --git a/t/t01introvert.t b/t/t01introvert.t
deleted file mode 100644 (file)
index 4a1e4b5..0000000
+++ /dev/null
@@ -1,1160 +0,0 @@
-#!perl -w
-# t/t01introvert.t - tests internals of image formats
-# to make sure we get expected values
-
-use strict;
-use Test::More tests => 466;
-
-BEGIN { use_ok(Imager => qw(:handy :all)) }
-
-use Imager::Test qw(image_bounds_checks is_color3 is_color4 is_fcolor4 color_cmp mask_tests is_fcolor3);
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t01introvert.log");
-
-my $im_g = Imager::ImgRaw::new(100, 101, 1);
-
-my $red = NC(255, 0, 0);
-my $green = NC(0, 255, 0);
-my $blue = NC(0, 0, 255);
-
-use Imager::Color::Float;
-my $f_black = Imager::Color::Float->new(0, 0, 0);
-my $f_red = Imager::Color::Float->new(1.0, 0, 0);
-my $f_green = Imager::Color::Float->new(0, 1.0, 0);
-my $f_blue = Imager::Color::Float->new(0, 0, 1.0);
-
-is(Imager::i_img_getchannels($im_g), 1, "1 channel image channel count");
-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");
-is($ginfo[1], 101, "1 channel image height");
-
-undef $im_g; # can we check for release after this somehow?
-
-my $im_rgb = Imager::ImgRaw::new(100, 101, 3);
-
-is(Imager::i_img_getchannels($im_rgb), 3, "3 channel image channel count");
-is((Imager::i_img_getmask($im_rgb) & 7), 7, "3 channel image mask");
-is(Imager::i_img_bits($im_rgb), 8, "3 channel image has 8 bits/sample");
-is(Imager::i_img_type($im_rgb), 0, "3 channel image is direct");
-
-undef $im_rgb;
-
-my $im_pal = Imager::i_img_pal_new(100, 101, 3, 256);
-
-ok($im_pal, "make paletted image");
-is(Imager::i_img_getchannels($im_pal), 3, "pal img channel count");
-is(Imager::i_img_bits($im_pal), 8, "pal img bits");
-is(Imager::i_img_type($im_pal), 1, "pal img is paletted");
-
-my $red_idx = check_add($im_pal, $red, 0);
-my $green_idx = check_add($im_pal, $green, 1);
-my $blue_idx = check_add($im_pal, $blue, 2);
-
-# basic writing of palette indicies
-# fill with red
-is(Imager::i_ppal($im_pal, 0, 0, ($red_idx) x 100), 100, 
-   "write red 100 times");
-# and blue
-is(Imager::i_ppal($im_pal, 50, 0, ($blue_idx) x 50), 50,
-   "write blue 50 times");
-
-# make sure we get it back
-my @pals = Imager::i_gpal($im_pal, 0, 100, 0);
-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 ]);
-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 ]);
-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");
-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");
-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");
-# that was in the palette, should still be paletted
-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");
-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");
-
-my $black = NC(0, 0, 0);
-# this should convert the image to RGB
-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],
-     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");
-  my @samples = Imager::i_gsamp($im_pal2, 0, 100, 0, [ 0, 1, 2 ]);
-  my @expect = unpack("C*", "\0\xFF\0\0\0\0"."\xFF\0\0" x 48 . "\0\0\xFF" x 50);
-  my $match_list = is_deeply(\@samples, \@expect, "colors are still correct");
-  my $samples = Imager::i_gsamp($im_pal2, 0, 100, 0, [ 0, 1, 2 ]);
-  my $match_scalar = is_deeply([ unpack("C*", $samples) ],
-                              \@expect, "colors are still correct (scalar)");
-  unless ($match_list && $match_scalar) {
-    # this has been failing on a particular smoker, provide more
-    # diagnostic information
-    print STDERR "Pallete:\n";
-    print STDERR "  $_: ", join(",", $colors[$_]->rgba), "\n" for 0..$#colors;
-    print STDERR "Samples (list): ", join(",", @samples), "\n";
-    print STDERR "Samples (scalar): ", join(",", unpack("C*", $samples)), "\n";
-    print STDERR "Indexes: ", join(",", Imager::i_gpal($im_pal2, 0, 100, 0)), "\n";
-  }
-}
-
-# test the OO interfaces
-my $impal2 = Imager->new(type=>'pseudo', xsize=>200, ysize=>201);
-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]);
-  ok($red_idx, "add red to OO");
-  is(0+$red_idx, 0, "and it's expected index for red");
-  my $blue_idx = $impal2->addcolors(colors=>[$blue, $green]);
-  ok($blue_idx, "add blue/green via OO");
-  is($blue_idx, 1, "and it's expected index for blue");
-  my $green_idx = $blue_idx + 1;
-  my $c = $impal2->getcolors(start=>$green_idx);
-  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 );
-  my $good = 1;
-  for my $i (0..2) {
-    if (color_cmp($cols[$i], $exp[$i])) {
-      $good = 0;
-      last;
-    }
-  }
-  ok($good, "all colors in palette as expected");
-  is($impal2->colorcount, 3, "and colorcount returns 3");
-  is($impal2->maxcolors, 256, "maxcolors as expected");
-  is($impal2->findcolor(color=>$blue), 1, "findcolors found blue");
-  ok($impal2->setcolors(start=>0, colors=>[ $blue, $red ]),
-     "we can setcolors");
-
-  # make an rgb version
-  my $imrgb2 = $impal2->to_rgb8()
-    or diag($impal2->errstr);
-  is($imrgb2->type, 'direct', "converted is direct");
-
-  # and back again, specifying the palette
-  my @colors = ( $red, $blue, $green );
-  my $impal3 = $imrgb2->to_paletted(colors=>\@colors,
-                                    make_colors=>'none',
-                                    translate=>'closest');
-  ok($impal3, "got a paletted image from conversion");
-  dump_colors(@colors);
-  print "# in image\n";
-  dump_colors($impal3->getcolors);
-  is($impal3->colorcount, 3, "new image has expected color table size");
-  is($impal3->type, 'paletted', "and is paletted");
-}
-
-{
-  my $im = Imager->new;
-  ok($im, "make empty image");
-  ok(!$im->to_rgb8, "convert to rgb8");
-  is($im->errstr, "to_rgb8: empty input image", "check message");
-  is($im->bits, undef, "can't call bits on an empty image");
-  is($im->errstr, "bits: empty input image", "check message");
-  is($im->type, undef, "can't call type on an empty image");
-  is($im->errstr, "type: empty input image", "check message");
-  is($im->virtual, undef, "can't call virtual on an empty image");
-  is($im->errstr, "virtual: empty input image", "check message");
-  is($im->is_bilevel, undef, "can't call virtual on an empty image");
-  is($im->errstr, "is_bilevel: empty input image", "check message");
-  ok(!$im->getscanline(y => 0), "can't call getscanline on an empty image");
-  is($im->errstr, "getscanline: empty input image", "check message");
-  ok(!$im->setscanline(y => 0, pixels => [ $red, $blue ]),
-     "can't call setscanline on an empty image");
-  is($im->errstr, "setscanline: empty input image", "check message");
-  ok(!$im->getsamples(y => 0), "can't call getsamples on an empty image");
-  is($im->errstr, "getsamples: empty input image", "check message");
-  is($im->getwidth, undef, "can't get width of empty image");
-  is($im->errstr, "getwidth: empty input image", "check message");
-  is($im->getheight, undef, "can't get height of empty image");
-  is($im->errstr, "getheight: empty input image", "check message");
-  is($im->getchannels, undef, "can't get channels of empty image");
-  is($im->errstr, "getchannels: empty input image", "check message");
-  is($im->getmask, undef, "can't get mask of empty image");
-  is($im->errstr, "getmask: empty input image", "check message");
-  is($im->setmask, undef, "can't set mask of empty image");
-  is($im->errstr, "setmask: 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");
-ok(!Imager->new(xsize=>1, ysize=>0), "fail to create 0 width image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "0 width error message check");
-ok(!Imager->new(xsize=>-1, ysize=>1), "fail to create -ve height image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "-ve width error message check");
-ok(!Imager->new(xsize=>1, ysize=>-1), "fail to create -ve width image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "-ve height error message check");
-ok(!Imager->new(xsize=>-1, ysize=>-1), "fail to create -ve width/height image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "-ve width/height error message check");
-
-ok(!Imager->new(xsize=>1, ysize=>1, channels=>0),
-   "fail to create a zero channel image");
-cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
-       "out of range channel message check");
-ok(!Imager->new(xsize=>1, ysize=>1, channels=>5),
-   "fail to create a five channel image");
-cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
-       "out of range channel message check");
-
-{
-  # https://rt.cpan.org/Ticket/Display.html?id=8213
-  # check for handling of memory allocation of very large images
-  # only test this on 32-bit machines - on a 64-bit machine it may
-  # result in trying to allocate 4Gb of memory, which is unfriendly at
-  # least and may result in running out of memory, causing a different
-  # type of exit
- SKIP:
-  {
-    use Config;
-    skip("don't want to allocate 4Gb", 8) unless $Config{ptrsize} == 4;
-
-    my $uint_range = 256 ** $Config{intsize};
-    print "# range $uint_range\n";
-    my $dim1 = int(sqrt($uint_range))+1;
-    
-    my $im_b = Imager->new(xsize=>$dim1, ysize=>$dim1, channels=>1);
-    is($im_b, undef, "integer overflow check - 1 channel");
-    
-    $im_b = Imager->new(xisze=>$dim1, ysize=>1, channels=>1);
-    ok($im_b, "but same width ok");
-    $im_b = Imager->new(xisze=>1, ysize=>$dim1, channels=>1);
-    ok($im_b, "but same height ok");
-    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
-           "check the error message");
-
-    # do a similar test with a 3 channel image, so we're sure we catch
-    # the same case where the third dimension causes the overflow
-    my $dim3 = int(sqrt($uint_range / 3))+1;
-    
-    $im_b = Imager->new(xsize=>$dim3, ysize=>$dim3, channels=>3);
-    is($im_b, undef, "integer overflow check - 3 channel");
-    
-    $im_b = Imager->new(xisze=>$dim3, ysize=>1, channels=>3);
-    ok($im_b, "but same width ok");
-    $im_b = Imager->new(xisze=>1, ysize=>$dim3, channels=>3);
-    ok($im_b, "but same height ok");
-
-    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
-           "check the error message");
-  }
-}
-
-{ # http://rt.cpan.org/NoAuth/Bug.html?id=9672
-  my $warning;
-  local $SIG{__WARN__} = 
-    sub { 
-      $warning = "@_";
-      my $printed = $warning;
-      $printed =~ s/\n$//;
-      $printed =~ s/\n/\n\#/g; 
-      print "# ",$printed, "\n";
-    };
-  my $img = Imager->new(xsize=>10, ysize=>10);
-  $img->to_rgb8(); # doesn't really matter what the source is
-  cmp_ok($warning, '=~', 'void', "correct warning");
-  cmp_ok($warning, '=~', 't01introvert\\.t', "correct file");
-}
-
-{ # http://rt.cpan.org/NoAuth/Bug.html?id=11860
-  my $im = Imager->new(xsize=>2, ysize=>2);
-  $im->setpixel(x=>0, 'y'=>0, color=>$red);
-  $im->setpixel(x=>1, 'y'=>0, color=>$blue);
-
-  my @row = Imager::i_glin($im->{IMG}, 0, 2, 0);
-  is(@row, 2, "got 2 pixels from i_glin");
-  is_color3($row[0], 255, 0, 0, "red first");
-  is_color3($row[1], 0, 0, 255, "then blue");
-}
-
-{ # general tag tests
-  
-  # we don't care much about the image itself
-  my $im = Imager::ImgRaw::new(10, 10, 1);
-
-  ok(Imager::i_tags_addn($im, 'alpha', 0, 101), "i_tags_addn(...alpha, 0, 101)");
-  ok(Imager::i_tags_addn($im, undef, 99, 102), "i_tags_addn(...undef, 99, 102)");
-  is(Imager::i_tags_count($im), 2, "should have 2 tags");
-  ok(Imager::i_tags_addn($im, undef, 99, 103), "i_tags_addn(...undef, 99, 103)");
-  is(Imager::i_tags_count($im), 3, "should have 3 tags, despite the dupe");
-  is(Imager::i_tags_find($im, 'alpha', 0), '0 but true', "find alpha");
-  is(Imager::i_tags_findn($im, 99, 0), 1, "find 99");
-  is(Imager::i_tags_findn($im, 99, 2), 2, "find 99 again");
-  is(Imager::i_tags_get($im, 0), 101, "check first");
-  is(Imager::i_tags_get($im, 1), 102, "check second");
-  is(Imager::i_tags_get($im, 2), 103, "check third");
-
-  ok(Imager::i_tags_add($im, 'beta', 0, "hello", 0), 
-     "add string with string key");
-  ok(Imager::i_tags_add($im, 'gamma', 0, "goodbye", 0),
-     "add another one");
-  ok(Imager::i_tags_add($im, undef, 199, "aloha", 0),
-     "add one keyed by number");
-  is(Imager::i_tags_find($im, 'beta', 0), 3, "find beta");
-  is(Imager::i_tags_find($im, 'gamma', 0), 4, "find gamma");
-  is(Imager::i_tags_findn($im, 199, 0), 5, "find 199");
-  ok(Imager::i_tags_delete($im, 2), "delete");
-  is(Imager::i_tags_find($im, 'beta', 0), 2, 'find beta after deletion');
-  ok(Imager::i_tags_delbyname($im, 'beta'), 'delete beta by name');
-  is(Imager::i_tags_find($im, 'beta', 0), undef, 'beta not there now');
-  is(Imager::i_tags_get_string($im, "gamma"), "goodbye", 
-     'i_tags_get_string() on a string');
-  is(Imager::i_tags_get_string($im, 99), 102, 
-     'i_tags_get_string() on a number entry');
-  ok(Imager::i_tags_delbycode($im, 99), 'delete by code');
-  is(Imager::i_tags_findn($im, 99, 0), undef, '99 not there now');
-  is(Imager::i_tags_count($im), 3, 'final count of 3');
-}
-
-{ 
-  print "# low-level scan line function tests\n";
-  my $im = Imager::ImgRaw::new(10, 10, 4);
-  Imager::i_ppix($im, 5, 0, $red);
-
-  # i_glin/i_glinf
-  my @colors = Imager::i_glin($im, 0, 10, 0);
-  is_deeply([ (0) x 20, (255, 0, 0, 255), (0) x 16 ], 
-           [ map $_->rgba, @colors ],
-           "i_glin - list context");
-  my $colors = Imager::i_glin($im, 0, 10, 0);
-  is("00" x 20 . "FF0000FF" . "00" x 16, 
-     uc unpack("H*", $colors), "i_glin - scalar context");
-  my @fcolors = Imager::i_glinf($im, 0, 10, 0);
-  is_deeply([ (0.0) x 20, (1.0, 0, 0, 1.0) , (0) x 16 ],
-           [ map $_->rgba, @fcolors ],
-           "i_glinf - list context");
-  my $fcolors = Imager::i_glinf($im, 0, 10, 0);
-  is_deeply([ (0.0) x 20, (1.0, 0, 0, 1.0) , (0) x 16 ],
-           [ unpack "d*", $fcolors ],
-           "i_glinf - scalar context");
-
-  # i_plin/i_plinf
-  my @plin_colors = (($black) x 4, $red, $blue, ($black) x 4);
-  is(Imager::i_plin($im, 0, 1, @plin_colors),
-     10, "i_plin - pass in a list");
-  # make sure we get it back
-  is_deeply([ map [ $_->rgba ], @plin_colors ],
-           [ map [ $_->rgba ], Imager::i_glin($im, 0, 10, 1) ],
-           "check i_plin wrote to the image");
-  my @scalar_plin = 
-    (
-     (0,0,0,0) x 4, 
-     (0, 255, 0, 255),
-     (0, 0, 255, 255), 
-     (0, 0, 0, 0) x 4,
-    );
-  is(Imager::i_plin($im, 0, 2, pack("C*", @scalar_plin)),
-     10, "i_plin - pass in a scalar");
-  is_deeply(\@scalar_plin,
-           [ map $_->rgba , Imager::i_glin($im, 0, 10, 2) ],
-           "check i_plin scalar wrote to the image");
-
-  my @plinf_colors = # Note: only 9 pixels
-    ( 
-     ($f_blue) x 4, 
-     $f_red, 
-     ($f_black) x 3, 
-     $f_black
-    );
-  is(Imager::i_plinf($im, 0, 3, @plinf_colors), 9,
-     "i_plinf - list");
-  is_deeply([ map $_->rgba, Imager::i_glinf($im, 0, 9, 3) ],
-           [ map $_->rgba, @plinf_colors ],
-           "check colors were written");
-  my @scalar_plinf =
-    (
-     ( 1.0, 1.0,   0, 1.0 ) x 3,
-     (   0, 1.0, 1.0, 1.0 ) x 2,
-     (   0,   0,   0,   0 ),
-     ( 1.0,   0, 1.0, 1.0 ),
-    );
-  is(Imager::i_plinf($im, 2, 4, pack("d*", @scalar_plinf)),
-     7, "i_plinf - scalar");
-  is_deeply(\@scalar_plinf,
-           [ map $_->rgba, Imager::i_glinf($im, 2, 9, 4) ],
-           "check colors were written");
-
-  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 ])),
-     "i_gsamp scalar context");
-  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 ])) ],
-           [ (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, 128, 64,
-             "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), 64, 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), 64, 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, 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_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 ]) ],
-             [ (128, 64, 32) 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);
-  $im->setpixel(color=>$red, 'x'=>5, 'y'=>0);
-  ok(!$im->getscanline(), "getscanline() - supply nothing, get nothing");
-  is($im->errstr, "missing y parameter", "check message");
-  is_deeply([ map [ $_->rgba ], $im->getscanline('y'=>0) ],
-           [ ([ 0,0,0,0]) x 5, [ 255, 0, 0, 255 ], ([ 0,0,0,0]) x 4 ],
-           "getscanline, list context, default x, width");
-  is_deeply([ map [ $_->rgba ], $im->getscanline('y'=>0, 'x'=>3) ],
-           [ ([0,0,0,0]) x 2, [ 255, 0, 0, 255 ], ([0,0,0,0]) x 4 ],
-           "getscanline, list context, default width");
-  is_deeply([ map [ $_->rgba ], $im->getscanline('y'=>0, 'x'=>4, width=>4) ],
-           [ [0,0,0,0], [ 255, 0, 0, 255 ], ([0,0,0,0]) x 2 ],
-           "getscanline, list context, no defaults");
-  is(uc unpack("H*",  $im->getscanline('y'=>0)),
-     "00000000" x 5 .  "FF0000FF" . "00000000" x 4,
-     "getscanline, scalar context, default x, width");
-  is_deeply([ map [ $_->rgba ], 
-             $im->getscanline('y'=>0, 'x'=>4, width=>4, type=>'float') ],
-           [ [0,0,0,0], [ 1.0, 0, 0, 1.0 ], ([0,0,0,0]) x 2 ],
-           "getscanline float, list context, no defaults");
-  is_deeply([ unpack "d*",
-             $im->getscanline('y'=>0, 'x'=>4, width=>4, type=>'float') ],
-           [ (0,0,0,0), ( 1.0, 0, 0, 1.0 ), (0,0,0,0) x 2 ],
-           "getscanline float, scalar context, no defaults");
-
-  ok(!$im->getscanline('y'=>0, type=>'invalid'),
-     "check invalid type checking");
-  like($im->errstr, qr/invalid type parameter/, 
-       "check message for invalid type");
-
-  my @plin_colors = (($black) x 4, $red, $blue, ($green) x 4);
-  is($im->setscanline('y'=>1, pixels=>\@plin_colors), 10,
-     "setscanline - arrayref, default x");
-  is_deeply([ map [ $_->rgba ], @plin_colors ],
-           [ map [ $_->rgba ], $im->getscanline('y'=>1) ],
-           "check colors were written");
-
-  my @plin_colors2 = ( $green, $red, $blue, $red );
-  is($im->setscanline('y'=>2, 'x'=>3, pixels=>\@plin_colors2), 4,
-     "setscanline - arrayref");
-
-  # 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");
-  
-  my $raw_colors = pack "H*", "FF00FFFF"."FF0000FF"."FFFFFFFF";
-  is($im->setscanline('y'=>3, 'x'=>2, pixels=>$raw_colors), 3,
-     "setscanline - scalar, default raw type")
-    or print "# ",$im->errstr,"\n";
-  is(uc unpack("H*", $im->getscanline('y'=>3, 'x'=>1, 'width'=>5)),
-     "00000000".uc(unpack "H*", $raw_colors)."00000000",
-     "check write");
-
-  # float colors
-  my @fcolors = ( $f_red, $f_blue, $f_black, $f_green );
-  is($im->setscanline('y'=>4, 'x'=>3, pixels=>\@fcolors), 4,
-     "setscanline - float arrayref");
-  is_deeply([ map [ $_->rgba ], @fcolors ],
-           [ map [ $_->rgba ], $im->getscanline('y'=>4, 'x'=>3, width=>4, type=>'float') ],
-           "check write");
-  # packed
-  my $packed_fcolors = pack "d*", map $_->rgba, @fcolors;
-  is($im->setscanline('y'=>5, 'x'=>4, pixels=>$packed_fcolors, type=>'float'), 4,
-     "setscanline - float scalar");
-  is_deeply([ map [ $_->rgba ], @fcolors ],
-           [ map [ $_->rgba ], $im->getscanline('y'=>5, 'x'=>4, width=>4, type=>'float') ],
-           "check write");
-
-  # get samples
-  is_deeply([ $im->getsamples('y'=>1, channels=>[ 0 ]) ],
-           [ map +($_->rgba)[0], @plin_colors ],
-           "get channel 0, list context, default x, width");
-  is_deeply([ unpack "C*", $im->getsamples('y'=>1, channels=>[0, 2]) ],
-           [ map { ($_->rgba)[0, 2] } @plin_colors ],
-           "get channel 0, 1, scalar context");
-  is_deeply([ $im->getsamples('y'=>4, 'x'=>3, width=>4, type=>'float',
-                             channels=>[1,3]) ],
-           [ map { ($_->rgba)[1,3] } @fcolors ],
-           "get channels 1,3, list context, float samples");
-  is_deeply([ unpack "d*", 
-             $im->getsamples('y'=>4, 'x'=>3, width=>4,
-                             type=>'float', channels=>[3,2,1,0]) ],
-           [ map { ($_->rgba)[3,2,1,0] } @fcolors ],
-           "get channels 3..0 as scalar, float samples");
-  
-  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);
-
-  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");
-
-  is($im->setsamples(y => 5, data => undef),
-     undef, "setsamples with undef data");
-  is($im->errstr, "setsamples: data parameter not defined",
-     "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");
-  }
-}
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->addtag(name => "foo", value => 1),
-     "can't addtag on an empty image");
-  is($empty->errstr, "addtag: empty input image",
-    "check error message");
-  ok(!$empty->settag(name => "foo", value => 1),
-     "can't settag on an empty image");
-  is($empty->errstr, "settag: empty input image",
-    "check error message");
-  ok(!$empty->deltag(name => "foo"), "can't deltag on an empty image");
-  is($empty->errstr, "deltag: empty input image",
-     "check error message");
-  ok(!$empty->tags(name => "foo"), "can't tags on an empty image");
-  is($empty->errstr, "tags: empty input image",
-     "check error message");
-}
-
-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);
-  ok($index, "got index");
-  print "# $index\n";
-  is(0+$index, $expected, "index matched expected");
-  my ($new) = Imager::i_getcolors($im, $index);
-  ok($new, "got the color");
-  ok(color_cmp($new, $color) == 0, "color matched what was added");
-
-  $index;
-}
-
-# sub array_ncmp {
-#   my ($a1, $a2) = @_;
-#   my $len = @$a1 < @$a2 ? @$a1 : @$a2;
-#   for my $i (0..$len-1) {
-#     my $diff = $a1->[$i] <=> $a2->[$i] 
-#       and return $diff;
-#   }
-#   return @$a1 <=> @$a2;
-# }
-
-sub dump_colors {
-  for my $col (@_) {
-    print "# ", map(sprintf("%02X", $_), ($col->rgba)[0..2]),"\n";
-  }
-}
-
-sub _get_error {
-  my @errors = Imager::i_errors();
-  return join(": ", map $_->[0], @errors);
-}
diff --git a/t/t020masked.t b/t/t020masked.t
deleted file mode 100644 (file)
index 95d5f1a..0000000
+++ /dev/null
@@ -1,706 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 244;
-use Imager qw(:all :handy);
-use Imager::Test qw(is_color3 is_fcolor3);
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t020masked.log");
-
-my $base_rgb = Imager::ImgRaw::new(100, 100, 3);
-# put something in there
-my $black = NC(0, 0, 0);
-my $red = NC(255, 0, 0);
-my $green = NC(0, 255, 0);
-my $blue = NC(0, 0, 255);
-my $white = NC(255, 255, 255);
-my $grey = NC(128, 128, 128);
-use Imager::Color::Float;
-my $redf = Imager::Color::Float->new(1, 0, 0);
-my $greenf = Imager::Color::Float->new(0, 1, 0);
-my $bluef = Imager::Color::Float->new(0, 0, 1);
-my $greyf = Imager::Color::Float->new(0.5, 0.5, 0.5);
-my @cols = ($red, $green, $blue);
-for my $y (0..99) {
-  Imager::i_plin($base_rgb, 0, $y, ($cols[$y % 3] ) x 100);
-}
-
-# first a simple subset image
-my $s_rgb = Imager::i_img_masked_new($base_rgb, undef, 25, 25, 50, 50);
-
-is(Imager::i_img_getchannels($s_rgb), 3,
-   "1 channel image channel count match");
-ok(Imager::i_img_getmask($s_rgb) & 1,
-   "1 channel image mask");
-ok(Imager::i_img_virtual($s_rgb),
-   "1 channel image thinks it isn't virtual");
-is(Imager::i_img_bits($s_rgb), 8,
-   "1 channel image has bits == 8");
-is(Imager::i_img_type($s_rgb), 0, # direct
-   "1 channel image is direct");
-
-my @ginfo = i_img_info($s_rgb);
-is($ginfo[0], 50, "check width");
-is($ginfo[1], 50, "check height");
-
-# sample some pixels through the subset
-my $c = Imager::i_get_pixel($s_rgb, 0, 0);
-is_color3($c, 0, 255, 0, "check (0,0)");
-$c = Imager::i_get_pixel($s_rgb, 49, 49);
-# (25+49)%3 = 2
-is_color3($c, 0, 0, 255, "check (49,49)");
-
-# try writing to it
-for my $y (0..49) {
-  Imager::i_plin($s_rgb, 0, $y, ($cols[$y % 3]) x 50);
-}
-pass("managed to write to it");
-# and checking the target image
-$c = Imager::i_get_pixel($base_rgb, 25, 25);
-is_color3($c, 255, 0, 0, "check (25,25)");
-$c = Imager::i_get_pixel($base_rgb, 29, 29);
-is_color3($c, 0, 255, 0, "check (29,29)");
-
-undef $s_rgb;
-
-# a basic background
-for my $y (0..99) {
-  Imager::i_plin($base_rgb, 0, $y, ($red ) x 100);
-}
-my $mask = Imager::ImgRaw::new(50, 50, 1);
-# some venetian blinds
-for my $y (4..20) {
-  Imager::i_plin($mask, 5, $y*2, ($white) x 40);
-}
-# with a strip down the middle
-for my $y (0..49) {
-  Imager::i_plin($mask, 20, $y, ($white) x 8);
-}
-my $m_rgb = Imager::i_img_masked_new($base_rgb, $mask, 25, 25, 50, 50);
-ok($m_rgb, "make masked with mask");
-for my $y (0..49) {
-  Imager::i_plin($m_rgb, 0, $y, ($green) x 50);
-}
-my @color_tests =
-  (
-   [ 25+0,  25+0,  $red ],
-   [ 25+19, 25+0,  $red ],
-   [ 25+20, 25+0,  $green ],
-   [ 25+27, 25+0,  $green ],
-   [ 25+28, 25+0,  $red ],
-   [ 25+49, 25+0,  $red ],
-   [ 25+19, 25+7,  $red ],
-   [ 25+19, 25+8,  $green ],
-   [ 25+19, 25+9,  $red ],
-   [ 25+0,  25+8,  $red ],
-   [ 25+4,  25+8,  $red ],
-   [ 25+5,  25+8,  $green ],
-   [ 25+44, 25+8,  $green ],
-   [ 25+45, 25+8,  $red ],
-   [ 25+49, 25+49, $red ],
-  );
-my $test_num = 15;
-for my $test (@color_tests) {
-  my ($x, $y, $testc) = @$test;
-  my ($r, $g, $b) = $testc->rgba;
-  my $c = Imager::i_get_pixel($base_rgb, $x, $y);
-  is_color3($c, $r, $g, $b, "at ($x, $y)");
-}
-
-{
-  # tests for the OO versions, fairly simple, since the basic functionality
-  # is covered by the low-level interface tests
-   
-  my $base = Imager->new(xsize=>100, ysize=>100);
-  ok($base, "make base OO image");
-  $base->box(color=>$blue, filled=>1); # fill it all
-  my $mask = Imager->new(xsize=>80, ysize=>80, channels=>1);
-  $mask->box(color=>$white, filled=>1, xmin=>5, xmax=>75, ymin=>5, ymax=>75);
-  my $m_img = $base->masked(mask=>$mask, left=>5, top=>5);
-  ok($m_img, "make masked OO image");
-  is($m_img->getwidth, 80, "check width");
-  $m_img->box(color=>$green, filled=>1);
-  my $c = $m_img->getpixel(x=>0, y=>0);
-  is_color3($c, 0, 0, 255, "check (0,0)");
-  $c = $m_img->getpixel(x => 5, y => 5);
-  is_color3($c, 0, 255, 0, "check (5,5)");
-
-  # older versions destroyed the Imager::ImgRaw object manually in 
-  # Imager::DESTROY rather than letting Imager::ImgRaw::DESTROY 
-  # destroy the object
-  # so we test here by destroying the base and mask objects and trying 
-  # to draw to the masked wrapper
-  # you may need to test with ElectricFence to trigger the problem
-  undef $mask;
-  undef $base;
-  $m_img->box(color=>$blue, filled=>1);
-  pass("didn't crash unreffing base or mask for masked image");
-}
-
-# 35.7% cover on maskimg.c up to here
-
-{ # error handling:
-  my $base = Imager->new(xsize => 100, ysize => 100);
-  ok($base, "make base");
-  { #  make masked image subset outside of the base image
-    my $masked = $base->masked(left => 100);
-    ok (!$masked, "fail to make empty masked");
-    is($base->errstr, "subset outside of target image", "check message");
-  }
-}
-
-{ # size limiting
-  my $base = Imager->new(xsize => 10, ysize => 10);
-  ok($base, "make base for size limit tests");
-  {
-    my $masked = $base->masked(left => 5, right => 15);
-    ok($masked, "make masked");
-    is($masked->getwidth, 5, "check width truncated");
-  }
-  {
-    my $masked = $base->masked(top => 5, bottom => 15);
-    ok($masked, "make masked");
-    is($masked->getheight, 5, "check height truncated");
-  }
-}
-# 36.7% up to here
-
-$mask = Imager->new(xsize => 80, ysize => 80, channels => 1);
-$mask->box(filled => 1, color => $white, xmax => 39, ymax => 39);
-$mask->box(fill => { hatch => "check1x1" }, ymin => 40, xmax => 39);
-
-{
-  my $base = Imager->new(xsize => 100, ysize => 100, bits => "double");
-  ok($base, "base for single pixel tests");
-  is($base->type, "direct", "check type");
-  my $masked = $base->masked(mask => $mask, left => 1, top => 2);
-  my $limited = $base->masked(left => 1, top => 2);
-
-  is($masked->type, "direct", "check masked is same type as base");
-  is($limited->type, "direct", "check limited is same type as base");
-
-  {
-    # single pixel writes, masked
-    {
-      ok($masked->setpixel(x => 1, y => 3, color => $green),
-        "set (1,3) in masked (2, 5) in based");
-      my $c = $base->getpixel(x => 2, y => 5);
-      is_color3($c, 0, 255, 0, "check it wrote through");
-      ok($masked->setpixel(x => 45, y => 2, color => $red),
-        "set (45,2) in masked (46,4) in base (no mask)");
-    $c = $base->getpixel(x => 46, y => 4);
-      is_color3($c, 0, 0, 0, "shouldn't have written through");
-    }
-    {
-      ok($masked->setpixel(x => 2, y => 3, color => $redf),
-        "write float red to (2,3) base(3,5)");
-      my $c = $base->getpixel(x => 3, y => 5);
-      is_color3($c, 255, 0, 0, "check it wrote through");
-      ok($masked->setpixel(x => 45, y => 3, color => $greenf),
-        "set float (45,3) in masked (46,5) in base (no mask)");
-      $c = $base->getpixel(x => 46, y => 5);
-      is_color3($c, 0, 0, 0, "check it didn't write");
-    }
-    {
-      # write out of range should fail
-      ok(!$masked->setpixel(x => 80, y => 0, color => $green),
-        "write 8-bit color out of range");
-      ok(!$masked->setpixel(x => 0, y => 80, color => $greenf),
-        "write float color out of range");
-    }
-  }
-
-  # 46.9
-
-  {
-    print "# plin coverage\n";
-    {
-      $base->box(filled => 1, color => $black);
-      # plin masked
-      # simple path
-      is($masked->setscanline(x => 76, y => 1, pixels => [ ($red, $green) x 3 ]),
-        4, "try to write 6 pixels, but only write 4");
-      is_deeply([ $base->getsamples(x => 77, y => 3, width => 4) ],
-               [ ( 0 ) x 12 ],
-               "check not written through");
-      # !simple path
-      is($masked->setscanline(x => 4, y => 2, pixels => [ ($red, $green, $blue, $grey) x (72/4) ]),
-        72, "write many pixels (masked)");
-      is_deeply([ $base->getsamples(x => 5, y => 4, width => 72) ],
-               [ ( (255, 0, 0), (0, 255, 0), (0, 0, 255), (128, 128, 128)) x 9,
-                 ( 0, 0, 0 ) x 36 ],
-               "check written through to base");
-      
-      # simple path, due to number of transitions
-      is($masked->setscanline(x => 0, y => 40, pixels => [ ($red, $green, $blue, $grey) x 5 ]),
-        20, "try to write 20 pixels, with alternating write through");
-      is_deeply([ $base->getsamples(x => 1, y => 42, width => 20) ],
-               [ ( (0, 0, 0), (0,255,0), (0,0,0), (128,128,128) ) x 5 ],
-               "check correct pixels written through");
-    }
-    
-    {
-      $base->box(filled => 1, color => $black);
-      # plin, non-masked path
-      is($limited->setscanline(x => 4, y => 2, pixels => [ ($red, $green, $blue, $grey) x (72/4) ]),
-        72, "write many pixels (limited)");
-      is_deeply([ $base->getsamples(x => 5, y => 4, width => 72) ],
-               [ ( (255, 0, 0), (0, 255, 0), (0, 0, 255), (128, 128, 128)) x 18 ],
-               "check written through to based");
-    }
-    
-    {
-      # draw outside fails
-      is($masked->setscanline(x => 80, y => 2, pixels => [ $red, $green ]),
-        0, "check writing no pixels");
-    }
-  }
-
-  {
-    print "# plinf coverage\n";
-    {
-      $base->box(filled => 1, color => $black);
-      # plinf masked
-      # simple path
-      is($masked->setscanline(x => 76, y => 1, pixels => [ ($redf, $greenf) x 3 ]),
-        4, "try to write 6 pixels, but only write 4");
-      is_deeply([ $base->getsamples(x => 77, y => 3, width => 4, type => "float") ],
-               [ ( 0 ) x 12 ],
-               "check not written through");
-      # !simple path
-      is($masked->setscanline(x => 4, y => 2, pixels => [ ($redf, $greenf, $bluef, $greyf) x (72/4) ]),
-        72, "write many pixels (masked)");
-      is_deeply([ $base->getsamples(x => 5, y => 4, width => 72, type => "float") ],
-               [ ( (1, 0, 0), (0, 1, 0), (0, 0, 1), (0.5, 0.5, 0.5)) x 9,
-                 ( 0, 0, 0 ) x 36 ],
-               "check written through to base");
-      
-      # simple path, due to number of transitions
-      is($masked->setscanline(x => 0, y => 40, pixels => [ ($redf, $greenf, $bluef, $greyf) x 5 ]),
-        20, "try to write 20 pixels, with alternating write through");
-      is_deeply([ $base->getsamples(x => 1, y => 42, width => 20, type => "float") ],
-               [ ( (0, 0, 0), (0,1,0), (0,0,0), (0.5,0.5,0.5) ) x 5 ],
-               "check correct pixels written through");
-    }
-    
-    {
-      $base->box(filled => 1, color => $black);
-      # plinf, non-masked path
-      is($limited->setscanline(x => 4, y => 2, pixels => [ ($redf, $greenf, $bluef, $greyf) x (72/4) ]),
-        72, "write many pixels (limited)");
-      is_deeply([ $base->getsamples(x => 5, y => 4, width => 72, type => "float") ],
-               [ ( (1, 0, 0), (0, 1, 0), (0, 0, 1), (0.5, 0.5, 0.5)) x 18 ],
-               "check written through to based");
-    }
-    
-    {
-      # draw outside fails
-      is($masked->setscanline(x => 80, y => 2, pixels => [ $redf, $greenf ]),
-        0, "check writing no pixels");
-    }
-  }
-  # 71.4%
-  {
-    {
-      print "# gpix\n";
-      # gpix
-      $base->box(filled => 1, color => $black);
-      ok($base->setpixel(x => 4, y => 10, color => $red),
-        "set base(4,10) to red");
-      is_fcolor3($masked->getpixel(x => 3, y => 8),
-                255, 0, 0, "check pixel written");
-
-      # out of range
-      is($masked->getpixel(x => -1, y => 1),
-        undef, "check failure to left");
-      is($masked->getpixel(x => 0, y => -1),
-        undef, "check failure to top");
-      is($masked->getpixel(x => 80, y => 1),
-        undef, "check failure to right");
-      is($masked->getpixel(x => 0, y => 80),
-        undef, "check failure to bottom");
-    }
-    {
-      print "# gpixf\n";
-      # gpixf
-      $base->box(filled => 1, color => $black);
-      ok($base->setpixel(x => 4, y => 10, color => $redf),
-        "set base(4,10) to red");
-      is_fcolor3($masked->getpixel(x => 3, y => 8, type => "float"),
-                1.0, 0, 0, 0, "check pixel written");
-
-      # out of range
-      is($masked->getpixel(x => -1, y => 1, type => "float"),
-        undef, "check failure to left");
-      is($masked->getpixel(x => 0, y => -1, type => "float"),
-        undef, "check failure to top");
-      is($masked->getpixel(x => 80, y => 1, type => "float"),
-        undef, "check failure to right");
-      is($masked->getpixel(x => 0, y => 80, type => "float"),
-        undef, "check failure to bottom");
-    }
-  }
-  # 74.5
-  {
-    {
-      print "# glin\n";
-      $base->box(filled => 1, color => $black);
-      is($base->setscanline(x => 31, y => 3, 
-                           pixels => [ ( $red, $green) x 10 ]),
-        20, "write 20 pixels to base image");
-      my @colors = $masked->
-       getscanline(x => 30, y => 1, width => 20);
-      is(@colors, 20, "check we got right number of colors");
-      is_color3($colors[0], 255, 0, 0, "check first pixel");
-      is_color3($colors[19], 0, 255, 0, "check last pixel");
-
-      @colors = $masked->getscanline(x => 76, y => 2, width => 10);
-      is(@colors, 4, "read line from right edge");
-      is_color3($colors[0], 0, 0, 0, "check pixel");
-
-      is_deeply([ $masked->getscanline(x => -1, y => 0, width => 1) ],
-        [], "fail read left of image");
-      is_deeply([ $masked->getscanline(x => 0, y => -1, width => 1) ],
-        [], "fail read top of image");
-      is_deeply([$masked->getscanline(x => 80, y => 0, width => 1)],
-        [], "fail read right of image");
-      is_deeply([$masked->getscanline(x => 0, y => 80, width => 1)],
-        [], "fail read bottom of image");
-    }
-    {
-      print "# glinf\n";
-      $base->box(filled => 1, color => $black);
-      is($base->setscanline(x => 31, y => 3, 
-                           pixels => [ ( $redf, $greenf) x 10 ]),
-        20, "write 20 pixels to base image");
-      my @colors = $masked->
-       getscanline(x => 30, y => 1, width => 20, type => "float");
-      is(@colors, 20, "check we got right number of colors");
-      is_fcolor3($colors[0], 1.0, 0, 0, 0, "check first pixel");
-      is_fcolor3($colors[19], 0, 1.0, 0, 0, "check last pixel");
-
-      @colors = $masked->
-       getscanline(x => 76, y => 2, width => 10, type => "float");
-      is(@colors, 4, "read line from right edge");
-      is_fcolor3($colors[0], 0, 0, 0, 0, "check pixel");
-
-      is_deeply([ $masked->getscanline(x => -1, y => 0, width => 1, type => "float") ],
-        [], "fail read left of image");
-      is_deeply([ $masked->getscanline(x => 0, y => -1, width => 1, type => "float") ],
-        [], "fail read top of image");
-      is_deeply([$masked->getscanline(x => 80, y => 0, width => 1, type => "float")],
-        [], "fail read right of image");
-      is_deeply([$masked->getscanline(x => 0, y => 80, width => 1, type => "float")],
-        [], "fail read bottom of image");
-    }
-  }
-  # 81.6%
-  {
-    {
-      print "# gsamp\n";
-      $base->box(filled => 1, color => $black);
-      is($base->setscanline(x => 31, y => 3, 
-                           pixels => [ ( $red, $green) x 10 ]),
-        20, "write 20 pixels to base image");
-      my @samps = $masked->
-       getsamples(x => 30, y => 1, width => 20);
-      is(@samps, 60, "check we got right number of samples");
-      is_deeply(\@samps,
-               [ (255, 0, 0, 0, 255, 0) x 10 ],
-               "check it");
-
-      @samps = $masked->
-       getsamples(x => 76, y => 2, width => 10);
-      is(@samps, 12, "read line from right edge");
-      is_deeply(\@samps, [ (0, 0, 0) x 4], "check result");
-
-      is_deeply([ $masked->getsamples(x => -1, y => 0, width => 1) ],
-        [], "fail read left of image");
-      is_deeply([ $masked->getsamples(x => 0, y => -1, width => 1) ],
-        [], "fail read top of image");
-      is_deeply([$masked->getsamples(x => 80, y => 0, width => 1)],
-        [], "fail read right of image");
-      is_deeply([$masked->getsamples(x => 0, y => 80, width => 1)],
-        [], "fail read bottom of image");
-    }
-    {
-      print "# gsampf\n";
-      $base->box(filled => 1, color => $black);
-      is($base->setscanline(x => 31, y => 3, 
-                           pixels => [ ( $redf, $greenf) x 10 ]),
-        20, "write 20 pixels to base image");
-      my @samps = $masked->
-       getsamples(x => 30, y => 1, width => 20, type => "float");
-      is(@samps, 60, "check we got right number of samples");
-      is_deeply(\@samps,
-               [ (1.0, 0, 0, 0, 1.0, 0) x 10 ],
-               "check it");
-
-      @samps = $masked->
-       getsamples(x => 76, y => 2, width => 10, type => "float");
-      is(@samps, 12, "read line from right edge");
-      is_deeply(\@samps, [ (0, 0, 0) x 4], "check result");
-
-      is_deeply([ $masked->getsamples(x => -1, y => 0, width => 1, type => "float") ],
-        [], "fail read left of image");
-      is_deeply([ $masked->getsamples(x => 0, y => -1, width => 1, type => "float") ],
-        [], "fail read top of image");
-      is_deeply([$masked->getsamples(x => 80, y => 0, width => 1, type => "float")],
-        [], "fail read right of image");
-      is_deeply([$masked->getsamples(x => 0, y => 80, width => 1, type => "float")],
-        [], "fail read bottom of image");
-    }
-  }
-  # 86.2%
-}
-
-{
-  my $base = Imager->new(xsize => 100, ysize => 100, type => "paletted");
-  ok($base, "make paletted base");
-  is($base->type, "paletted", "check we got paletted");
-  is($base->addcolors(colors => [ $black, $red, $green, $blue ]),
-     "0 but true",
-     "add some colors to base");
-  my $masked = $base->masked(mask => $mask, left => 1, top => 2);
-  my $limited = $base->masked(left => 1, top => 2);
-
-  is($masked->type, "paletted", "check masked is same type as base");
-  is($limited->type, "paletted", "check limited is same type as base");
-
-  {
-    # make sure addcolors forwarded
-    is($masked->addcolors(colors => [ $grey ]), 4,
-       "test addcolors forwarded");
-    my @colors = $masked->getcolors();
-    is(@colors, 5, "check getcolors forwarded");
-    is_color3($colors[1], 255, 0, 0, "check color from palette");
-  }
-
-  my ($blacki, $redi, $greeni, $bluei, $greyi) = 0 .. 4;
-
-  { # gpal
-    print "# gpal\n";
-    $base->box(filled => 1, color => $black);
-    is($base->setscanline(x => 0, y => 5, type => "index",
-                         pixels => [ ( $redi, $greeni, $bluei, $greyi) x 25 ]),
-       100, "write some pixels to base");
-    my @indexes = $masked->getscanline(y => 3, type => "index", width => "81");
-    is(@indexes, 80, "got 80 indexes");
-    is_deeply(\@indexes,
-             [ ( $greeni, $bluei, $greyi, $redi) x 20 ],
-             "check values");
-
-    is_deeply([ $masked->getscanline(x => -1, y => 3, type => "index") ],
-             [], "fail read left of image");
-  }
-  # 89.8%
-
-  { # ppal, unmasked
-    print "# ppal\n";
-    $base->box(filled => 1, color => $black);
-    is($limited->setscanline(x => 1, y => 1, type => "index",
-                            pixels => [ ( $redi, $greeni, $bluei) x 3 ]),
-       9, "ppal limited");
-    is_deeply([ $base->getscanline(x => 2, y => 3, type => "index", 
-                                  width => 9) ],
-             [ ( $redi, $greeni, $bluei) x 3 ],
-             "check set in base");
-  }
-  { # ppal, masked
-    $base->box(filled => 1, color => $black);
-    is($masked->setscanline(x => 1, y => 2, type => "index",
-                           pixels => [ ( $redi, $greeni, $bluei, $greyi) x 12 ]),
-       48, "ppal masked");
-    is_deeply([ $base->getscanline(x => 0, y => 4, type => "index") ],
-             [ 0, 0,
-               ( $redi, $greeni, $bluei, $greyi ) x 9,
-               $redi, $greeni, $bluei, ( 0 ) x 59 ],
-             "check written");
-  }
-  {
-    # ppal, errors
-    is($masked->setscanline(x => -1, y => 0, type => "index",
-                           pixels => [ $redi, $bluei ]),
-       0, "fail to write ppal");
-
-    is($masked->setscanline(x => 78, y => 0, type => "index",
-                          pixels => [ $redi, $bluei, $greeni, $greyi ]),
-       2, "write over right side");
-  }
-}
-
-my $full_mask = Imager->new(xsize => 10, ysize => 10, channels => 1);
-$full_mask->box(filled => 1, color => NC(255, 0, 0));
-
-# no mask and mask with full coverage should behave the same
-my $psamp_outside_error = "Image position outside of image";
-for my $masked (0, 1){ # psamp
-  print "# psamp masked: $masked\n";
-  my $imback = Imager::ImgRaw::new(20, 20, 3);
-  my $mask;
-  if ($masked) {
-    $mask = $full_mask->{IMG};
-  }
-  my $imraw = Imager::i_img_masked_new($imback, $mask, 3, 4, 10, 10);
-  {
-    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");
-  }
-  { # 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, 10, 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");
-  }
-  print "# end psamp tests\n";
-}
-
-for my $masked (0, 1) { # psampf
-  print "# psampf\n";
-  my $imback = Imager::ImgRaw::new(20, 20, 3);
-  my $mask;
-  if ($masked) {
-    $mask = $full_mask->{IMG};
-  }
-  my $imraw = Imager::i_img_masked_new($imback, $mask, 3, 4, 10, 10);
-  {
-    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, 128, 64,
-             "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), 64, 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), 64, 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, 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_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 ]) ],
-             [ (128, 64, 32) 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");
-  }
-  { # 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, 10, 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");
-  }
-  print "# end psampf tests\n";
-}
-
-{
-  my $sub_mask = $full_mask->copy;
-  $sub_mask->box(filled => 1, color => NC(0,0,0), xmin => 4, xmax => 6);
-  my $base = Imager::ImgRaw::new(20, 20, 3);
-  my $masked = Imager::i_img_masked_new($base, $sub_mask->{IMG}, 3, 4, 10, 10);
-
-  is(Imager::i_psamp($masked, 0, 2, undef, [ ( 0, 127, 255) x 10 ]), 30,
-     "psamp() to masked image");
-  is_deeply([ Imager::i_gsamp($base, 0, 20, 6, undef) ],
-           [ ( 0, 0, 0 ) x 3, # left of mask
-             ( 0, 127, 255 ) x 4, # masked area
-             ( 0, 0, 0 ) x 3, # unmasked area
-             ( 0, 127, 255 ) x 3, # masked area
-             ( 0, 0, 0 ) x 7 ], # right of mask
-           "check values written");
-  is(Imager::i_psampf($masked, 0, 2, undef, [ ( 0, 0.5, 1.0) x 10 ]), 30,
-     "psampf() to masked image");
-  is_deeply([ Imager::i_gsamp($base, 0, 20, 6, undef) ],
-           [ ( 0, 0, 0 ) x 3, # left of mask
-             ( 0, 128, 255 ) x 4, # masked area
-             ( 0, 0, 0 ) x 3, # unmasked area
-             ( 0, 128, 255 ) x 3, # masked area
-             ( 0, 0, 0 ) x 7 ], # right of mask
-           "check values written");
-}
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->masked, "fail to make a masked image from an empty");
-  is($empty->errstr, "masked: empty input image",
-    "check error message");
-}
-
-Imager->close_log();
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t020masked.log";
-}
-
-sub _get_error {
-  my @errors = Imager::i_errors();
-  return join(": ", map $_->[0], @errors);
-}
diff --git a/t/t021sixteen.t b/t/t021sixteen.t
deleted file mode 100644 (file)
index a1054e5..0000000
+++ /dev/null
@@ -1,369 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 155;
-
-BEGIN { use_ok(Imager=>qw(:all :handy)) }
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t021sixteen.log");
-
-use Imager::Color::Float;
-use Imager::Test qw(test_image is_image image_bounds_checks test_colorf_gpix
-                    test_colorf_glin mask_tests is_color3);
-
-my $im_g = Imager::i_img_16_new(100, 101, 1);
-
-is(Imager::i_img_getchannels($im_g), 1, "1 channel image channel count");
-ok(Imager::i_img_getmask($im_g) & 1, "1 channel image mask");
-ok(!Imager::i_img_virtual($im_g), "shouldn't be marked virtual");
-is(Imager::i_img_bits($im_g), 16, "1 channel image has bits == 16");
-is(Imager::i_img_type($im_g), 0, "1 channel image isn't direct");
-
-my @ginfo = i_img_info($im_g);
-is($ginfo[0], 100, "1 channel image width");
-is($ginfo[1], 101, "1 channel image height");
-
-undef $im_g;
-
-my $im_rgb = Imager::i_img_16_new(100, 101, 3);
-
-is(Imager::i_img_getchannels($im_rgb), 3, "3 channel image channel count");
-ok((Imager::i_img_getmask($im_rgb) & 7) == 7, "3 channel image mask");
-is(Imager::i_img_bits($im_rgb), 16, "3 channel image bits");
-is(Imager::i_img_type($im_rgb), 0, "3 channel image type");
-
-my $redf = NCF(1, 0, 0);
-my $greenf = NCF(0, 1, 0);
-my $bluef = NCF(0, 0, 1);
-
-# fill with red
-for my $y (0..101) {
-  Imager::i_plinf($im_rgb, 0, $y, ($redf) x 100);
-}
-pass("fill with red");
-# basic sanity
-test_colorf_gpix($im_rgb, 0,  0,   $redf, 0, "top-left");
-test_colorf_gpix($im_rgb, 99, 0,   $redf, 0, "top-right");
-test_colorf_gpix($im_rgb, 0,  100, $redf, 0, "bottom left");
-test_colorf_gpix($im_rgb, 99, 100, $redf, 0, "bottom right");
-test_colorf_glin($im_rgb, 0,  0,   [ ($redf) x 100 ], "first line");
-test_colorf_glin($im_rgb, 0,  100, [ ($redf) x 100 ], "last line");
-
-Imager::i_plinf($im_rgb, 20, 1, ($greenf) x 60);
-test_colorf_glin($im_rgb, 0, 1, 
-                 [ ($redf) x 20, ($greenf) x 60, ($redf) x 20 ],
-               "added some green in the middle");
-{
-  my @samples;
-  is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 16, \@samples, 0, [ 0 .. 2 ]), 12, 
-     "i_gsamp_bits all channels - count")
-    or print "# ", Imager->_error_as_msg(), "\n";
-  is_deeply(\@samples, [ 65535, 0, 0,   65535, 0, 0,
-                        0, 65535, 0,   0, 65535, 0 ],
-           "check samples retrieved");
-  @samples = ();
-  is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 16, \@samples, 0, [ 0, 2 ]), 8, 
-     "i_gsamp_bits some channels - count")
-    or print "# ", Imager->_error_as_msg(), "\n";
-  is_deeply(\@samples, [ 65535, 0,   65535, 0,
-                        0, 0,       0, 0     ],
-           "check samples retrieved");
-  # fail gsamp
-  is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 16, \@samples, 0, [ 0, 3 ]), undef,
-     "i_gsamp_bits fail bad channel");
-  is(Imager->_error_as_msg(), 'No channel 3 in this image', 'check message');
-
-  is(Imager::i_gsamp_bits($im_rgb, 18, 22, 1, 17, \@samples, 0, [ 0, 2 ]), 8, 
-     "i_gsamp_bits succeed high bits");
-  is($samples[0], 131071, "check correct with high bits");
-
-  # write some samples back
-  my @wr_samples = 
-    ( 
-     0, 0, 65535,
-     65535, 0, 0,  
-     0, 65535, 0,  
-     65535, 65535, 0 
-    );
-  is(Imager::i_psamp_bits($im_rgb, 18, 2, 16, [ 0 .. 2 ], \@wr_samples),
-     12, "write 16-bit samples")
-    or print "# ", Imager->_error_as_msg(), "\n";
-  @samples = ();
-  is(Imager::i_gsamp_bits($im_rgb, 18, 22, 2, 16, \@samples, 0, [ 0 .. 2 ]), 12, 
-     "read them back")
-    or print "# ", Imager->_error_as_msg(), "\n";
-  is_deeply(\@samples, \@wr_samples, "check they match");
-  my $c = Imager::i_get_pixel($im_rgb, 18, 2);
-  is_color3($c, 0, 0, 255, "check it write to the right places");
-}
-
-# basic OO tests
-my $oo16img = Imager->new(xsize=>200, ysize=>201, bits=>16);
-ok($oo16img, "make a 16-bit oo image");
-is($oo16img->bits,  16, "test bits");
-isnt($oo16img->is_bilevel, "should not be considered mono");
-# make sure of error handling
-ok(!Imager->new(xsize=>0, ysize=>1, bits=>16),
-    "fail to create a 0 pixel wide image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct error message");
-
-ok(!Imager->new(xsize=>1, ysize=>0, bits=>16),
-    "fail to create a 0 pixel high image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct error message");
-
-ok(!Imager->new(xsize=>-1, ysize=>1, bits=>16),
-    "fail to create a negative width image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct error message");
-
-ok(!Imager->new(xsize=>1, ysize=>-1, bits=>16),
-    "fail to create a negative height image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct error message");
-
-ok(!Imager->new(xsize=>-1, ysize=>-1, bits=>16),
-    "fail to create a negative width/height image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct error message");
-
-ok(!Imager->new(xsize=>1, ysize=>1, bits=>16, channels=>0),
-    "fail to create a zero channel image");
-cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
-       "and correct error message");
-ok(!Imager->new(xsize=>1, ysize=>1, bits=>16, channels=>5),
-    "fail to create a five channel image");
-cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
-       "and correct error message");
-
-{
-  # https://rt.cpan.org/Ticket/Display.html?id=8213
-  # check for handling of memory allocation of very large images
-  # only test this on 32-bit machines - on a 64-bit machine it may
-  # result in trying to allocate 4Gb of memory, which is unfriendly at
-  # least and may result in running out of memory, causing a different
-  # type of exit
- SKIP: {
-    use Config;
-    $Config{ptrsize} == 4
-      or skip("don't want to allocate 4Gb", 10);
-    my $uint_range = 256 ** $Config{intsize};
-    print "# range $uint_range\n";
-    my $dim1 = int(sqrt($uint_range/2))+1;
-    
-    my $im_b = Imager->new(xsize=>$dim1, ysize=>$dim1, channels=>1, bits=>16);
-    is($im_b, undef, "integer overflow check - 1 channel");
-    
-    $im_b = Imager->new(xisze=>$dim1, ysize=>1, channels=>1, bits=>16);
-    ok($im_b, "but same width ok");
-    $im_b = Imager->new(xisze=>1, ysize=>$dim1, channels=>1, bits=>16);
-    ok($im_b, "but same height ok");
-    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
-           "check the error message");
-
-    # do a similar test with a 3 channel image, so we're sure we catch
-    # the same case where the third dimension causes the overflow
-    my $dim3 = int(sqrt($uint_range / 3 / 2))+1;
-    
-    $im_b = Imager->new(xsize=>$dim3, ysize=>$dim3, channels=>3, bits=>16);
-    is($im_b, undef, "integer overflow check - 3 channel");
-    
-    $im_b = Imager->new(xisze=>$dim3, ysize=>1, channels=>3, bits=>16);
-    ok($im_b, "but same width ok");
-    $im_b = Imager->new(xisze=>1, ysize=>$dim3, channels=>3, bits=>16);
-    ok($im_b, "but same height ok");
-
-    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
-           "check the error message");
-
-    # check we can allocate a scanline, unlike double images the scanline
-    # in the image itself is smaller than a line of i_fcolor
-    # divide by 2 to get to int range, by 2 for 2 bytes/pixel, by 3 to 
-    # fit the image allocation in, but for the floats to overflow
-    my $dim4 = $uint_range / 2 / 2 / 3;
-    my $im_o = Imager->new(xsize=>$dim4, ysize=>1, channels=>1, bits=>16);
-    is($im_o, undef, "integer overflow check - scanline");
-    cmp_ok(Imager->errstr, '=~',
-           qr/integer overflow calculating scanline allocation/,
-           "check error message");
-  }
-}
-
-{ # check the channel mask function
-  
-  my $im = Imager->new(xsize => 10, ysize=>10, bits=>16);
-
-  mask_tests($im, 1.0/65535);
-}
-
-{ # convert to rgb16
-  my $im = test_image();
-  my $im16 = $im->to_rgb16;
-  print "# check conversion to 16 bit\n";
-  is($im16->bits, 16, "check bits");
-  is_image($im, $im16, "check image data matches");
-}
-
-{ # empty image handling
-  my $im = Imager->new;
-  ok($im, "make empty image");
-  ok(!$im->to_rgb16, "convert empty image to 16-bit");
-  is($im->errstr, "to_rgb16: empty input image", "check message");
-}
-
-{ # bounds checks
-  my $im = Imager->new(xsize => 10, ysize => 10, bits => 16);
-  image_bounds_checks($im);
-}
-
-{
-  my $im = Imager->new(xsize => 10, ysize => 10, bits => 16, channels => 3);
-  my @wr_samples = map int(rand 65536), 1..30;
-  is($im->setsamples('y' => 1, data => \@wr_samples, type => '16bit'),
-     30, "write 16-bit to OO image")
-    or print "# ", $im->errstr, "\n";
-  my @samples;
-  is($im->getsamples(y => 1, target => \@samples, type => '16bit'),
-     30, "read 16-bit from OO image")
-    or print "# ", $im->errstr, "\n";
-  is_deeply(\@wr_samples, \@samples, "check it matches");
-}
-
-my $psamp_outside_error = "Image position outside of image";
-{ # psamp
-  print "# psamp\n";
-  my $imraw = Imager::i_img_16_new(10, 10, 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");
-  }
-  { # 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, 10, 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");
-  }
-  print "# end psamp tests\n";
-}
-
-{ # psampf
-  print "# psampf\n";
-  my $imraw = Imager::i_img_16_new(10, 10, 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, 64,
-             "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), 64, 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), 64, 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), 64, 32, 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, 64, 32) 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");
-  }
-  { # 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, 10, 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");
-  }
-  print "# end psampf tests\n";
-}
-
-Imager->close_log;
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t021sixteen.log";
-}
-
-sub _get_error {
-  my @errors = Imager::i_errors();
-  return join(": ", map $_->[0], @errors);
-}
-
diff --git a/t/t022double.t b/t/t022double.t
deleted file mode 100644 (file)
index 6a2f757..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 136;
-BEGIN { use_ok(Imager => qw(:all :handy)) }
-
-use Imager::Test qw(test_image is_image is_color3);
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t022double.log");
-
-use Imager::Test qw(image_bounds_checks test_colorf_gpix test_colorf_glin mask_tests);
-
-use Imager::Color::Float;
-
-my $im_g = Imager::i_img_double_new(100, 101, 1);
-
-ok(Imager::i_img_getchannels($im_g) == 1, 
-   "1 channel image channel count mismatch");
-ok(Imager::i_img_getmask($im_g) & 1, "1 channel image bad mask");
-ok(Imager::i_img_virtual($im_g) == 0, 
-  "1 channel image thinks it is virtual");
-my $double_bits = length(pack("d", 1)) * 8;
-print "# $double_bits double bits\n";
-ok(Imager::i_img_bits($im_g) == $double_bits, 
-   "1 channel image has bits != $double_bits");
-ok(Imager::i_img_type($im_g) == 0, "1 channel image isn't direct");
-
-my @ginfo = i_img_info($im_g);
-ok($ginfo[0] == 100, "1 channel image width incorrect");
-ok($ginfo[1] == 101, "1 channel image height incorrect");
-
-undef $im_g;
-
-my $im_rgb = Imager::i_img_double_new(100, 101, 3);
-
-ok(Imager::i_img_getchannels($im_rgb) == 3,
-   "3 channel image channel count mismatch");
-ok((Imager::i_img_getmask($im_rgb) & 7) == 7, "3 channel image bad mask");
-ok(Imager::i_img_bits($im_rgb) == $double_bits,
-  "3 channel image has bits != $double_bits");
-ok(Imager::i_img_type($im_rgb) == 0, "3 channel image isn't direct");
-
-my $redf = NCF(1, 0, 0);
-my $greenf = NCF(0, 1, 0);
-my $bluef = NCF(0, 0, 1);
-
-# fill with red
-for my $y (0..101) {
-  Imager::i_plinf($im_rgb, 0, $y, ($redf) x 100);
-}
-
-# basic sanity
-test_colorf_gpix($im_rgb, 0,  0,   $redf);
-test_colorf_gpix($im_rgb, 99, 0,   $redf);
-test_colorf_gpix($im_rgb, 0,  100, $redf);
-test_colorf_gpix($im_rgb, 99, 100, $redf);
-test_colorf_glin($im_rgb, 0,  0,   [ ($redf) x 100 ], 'sanity glin @0');
-test_colorf_glin($im_rgb, 0,  100, [ ($redf) x 100 ], 'sanity glin @100');
-
-Imager::i_plinf($im_rgb, 20, 1, ($greenf) x 60);
-test_colorf_glin($im_rgb, 0, 1, 
-                 [ ($redf) x 20, ($greenf) x 60, ($redf) x 20 ],
-                'check after write');
-
-# basic OO tests
-my $ooimg = Imager->new(xsize=>200, ysize=>201, bits=>'double');
-ok($ooimg, "couldn't make double image");
-is($ooimg->bits, 'double', "oo didn't give double image");
-ok(!$ooimg->is_bilevel, 'not monochrome');
-
-# check that the image is copied correctly
-my $oocopy = $ooimg->copy;
-is($oocopy->bits, 'double', "oo copy didn't give double image");
-
-ok(!Imager->new(xsize=>0, ysize=>1, bits=>'double'),
-    "fail making 0 width image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct message");
-ok(!Imager->new(xsize=>1, ysize=>0, bits=>'double'),
-    "fail making 0 height image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct message");
-ok(!Imager->new(xsize=>-1, ysize=>1, bits=>'double'),
-    "fail making -ve width image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct message");
-ok(!Imager->new(xsize=>1, ysize=>-1, bits=>'double'),
-    "fail making -ve height image");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct message");
-ok(!Imager->new(xsize=>1, ysize=>1, bits=>'double', channels=>0),
-    "fail making 0 channel image");
-cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
-       "and correct message");
-ok(!Imager->new(xsize=>1, ysize=>1, bits=>'double', channels=>5),
-    "fail making 5 channel image");
-cmp_ok(Imager->errstr, '=~', qr/channels must be between 1 and 4/,
-       "and correct message");
-
-{
-  # https://rt.cpan.org/Ticket/Display.html?id=8213
-  # check for handling of memory allocation of very large images
-  # only test this on 32-bit machines - on a 64-bit machine it may
-  # result in trying to allocate 4Gb of memory, which is unfriendly at
-  # least and may result in running out of memory, causing a different
-  # type of exit
-  use Config;
-  SKIP: 
-  {
-    $Config{ptrsize} == 4
-      or skip "don't want to allocate 4Gb", 8;
-    my $uint_range = 256 ** $Config{intsize};
-    my $dbl_size = $Config{doublesize} || 8;
-    my $dim1 = int(sqrt($uint_range/$dbl_size))+1;
-    
-    my $im_b = Imager->new(xsize=>$dim1, ysize=>$dim1, channels=>1, bits=>'double');
-    is($im_b, undef, "integer overflow check - 1 channel");
-    
-    $im_b = Imager->new(xisze=>$dim1, ysize=>1, channels=>1, bits=>'double');
-    ok($im_b, "but same width ok");
-    $im_b = Imager->new(xisze=>1, ysize=>$dim1, channels=>1, bits=>'double');
-    ok($im_b, "but same height ok");
-    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
-           "check the error message");
-
-    # do a similar test with a 3 channel image, so we're sure we catch
-    # the same case where the third dimension causes the overflow
-    my $dim3 = int(sqrt($uint_range / 3 / $dbl_size))+1;
-    
-    $im_b = Imager->new(xsize=>$dim3, ysize=>$dim3, channels=>3, bits=>'double');
-    is($im_b, undef, "integer overflow check - 3 channel");
-    
-    $im_b = Imager->new(xsize=>$dim3, ysize=>1, channels=>3, bits=>'double');
-    ok($im_b, "but same width ok");
-    $im_b = Imager->new(xsize=>1, ysize=>$dim3, channels=>3, bits=>'double');
-    ok($im_b, "but same height ok");
-
-    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
-           "check the error message");
-  }
-}
-
-{ # check the channel mask function
-  
-  my $im = Imager->new(xsize => 10, ysize=>10, bits=>'double');
-
-  mask_tests($im);
-}
-
-{ # bounds checking
-  my $im = Imager->new(xsize => 10, ysize=>10, bits=>'double');
-  image_bounds_checks($im);
-}
-
-
-{ # convert to rgb double
-  my $im = test_image();
-  my $imdb = $im->to_rgb_double;
-  print "# check conversion to double\n";
-  is($imdb->bits, "double", "check bits");
-  is_image($im, $imdb, "check image data matches");
-}
-
-{ # empty image handling
-  my $im = Imager->new;
-  ok($im, "make empty image");
-  ok(!$im->to_rgb_double, "convert empty image to double");
-  is($im->errstr, "to_rgb_double: empty input image", "check message");
-}
-
-my $psamp_outside_error = "Image position outside of image";
-{ # psamp
-  print "# psamp\n";
-  my $imraw = Imager::i_img_double_new(10, 10, 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");
-  }
-  { # 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, 10, 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");
-  }
-  print "# end psamp tests\n";
-}
-
-{ # psampf
-  print "# psampf\n";
-  my $imraw = Imager::i_img_double_new(10, 10, 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, 128, 64,
-             "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), 64, 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), 64, 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, 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_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 ]) ],
-             [ (128, 64, 32) 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");
-  }
-  { # 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, 10, 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");
-  }
-  print "# end psampf tests\n";
-}
-
-Imager->close_log;
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t022double.log";
-}
-
-sub _get_error {
-  my @errors = Imager::i_errors();
-  return join(": ", map $_->[0], @errors);
-}
diff --git a/t/t023palette.t b/t/t023palette.t
deleted file mode 100644 (file)
index db37a0b..0000000
+++ /dev/null
@@ -1,679 +0,0 @@
-#!perl -w
-# some of this is tested in t01introvert.t too
-use strict;
-use Test::More tests => 226;
-BEGIN { use_ok("Imager", ':handy'); }
-
-use Imager::Test qw(image_bounds_checks test_image is_color3 isnt_image is_color4 is_fcolor3);
-
-Imager->open_log(log => "testout/t023palette.log");
-
-sub isbin($$$);
-
-my $img = Imager->new(xsize=>50, ysize=>50, type=>'paletted');
-
-ok($img, "paletted image created");
-
-is($img->type, 'paletted', "got a paletted image");
-
-my $black = Imager::Color->new(0,0,0);
-my $red = Imager::Color->new(255,0,0);
-my $green = Imager::Color->new(0,255,0);
-my $blue = Imager::Color->new(0,0,255);
-
-my $white = Imager::Color->new(255,255,255);
-
-# add some color
-my $blacki = $img->addcolors(colors=>[ $black, $red, $green, $blue ]);
-
-print "# blacki $blacki\n";
-ok(defined $blacki && $blacki == 0, "we got the first color");
-
-is($img->colorcount(), 4, "should have 4 colors");
-is($img->maxcolors, 256, "maxcolors always 256");
-
-my ($redi, $greeni, $bluei) = 1..3;
-
-my @all = $img->getcolors;
-ok(@all == 4, "all colors is 4");
-coloreq($all[0], $black, "first black");
-coloreq($all[1], $red, "then red");
-coloreq($all[2], $green, "then green");
-coloreq($all[3], $blue, "and finally blue");
-
-# keep this as an assignment, checking for scalar context
-# we don't want the last color, otherwise if the behaviour changes to
-# get all up to the last (count defaulting to size-index) we'd get a
-# false positive
-my $one_color = $img->getcolors(start=>$redi);
-ok($one_color->isa('Imager::Color'), "check scalar context");
-coloreq($one_color, $red, "and that it's what we want");
-
-# make sure we can find colors
-ok(!defined($img->findcolor(color=>$white)), 
-    "shouldn't be able to find white");
-ok($img->findcolor(color=>$black) == $blacki, "find black");
-ok($img->findcolor(color=>$red) == $redi, "find red");
-ok($img->findcolor(color=>$green) == $greeni, "find green");
-ok($img->findcolor(color=>$blue) == $bluei, "find blue");
-
-# various failure tests for setcolors
-ok(!defined($img->setcolors(start=>-1, colors=>[$white])),
-    "expect failure: low index");
-ok(!defined($img->setcolors(start=>1, colors=>[])),
-    "expect failure: no colors");
-ok(!defined($img->setcolors(start=>5, colors=>[$white])),
-    "expect failure: high index");
-
-# set the green index to white
-ok($img->setcolors(start => $greeni, colors => [$white]),
-    "set a color");
-# and check it
-coloreq(scalar($img->getcolors(start=>$greeni)), $white,
-       "make sure it was set");
-ok($img->findcolor(color=>$white) == $greeni, "and that we can find it");
-ok(!defined($img->findcolor(color=>$green)), "and can't find the old color");
-
-# write a few colors
-ok(scalar($img->setcolors(start=>$redi, colors=>[ $green, $red])),
-          "save multiple");
-coloreq(scalar($img->getcolors(start=>$redi)), $green, "first of multiple");
-coloreq(scalar($img->getcolors(start=>$greeni)), $red, "second of multiple");
-
-# put it back
-$img->setcolors(start=>$red, colors=>[$red, $green]);
-
-# draw on the image, make sure it stays paletted when it should
-ok($img->box(color=>$red, filled=>1), "fill with red");
-is($img->type, 'paletted', "paletted after fill");
-ok($img->box(color=>$green, filled=>1, xmin=>10, ymin=>10,
-             xmax=>40, ymax=>40), "green box");
-is($img->type, 'paletted', 'still paletted after box');
-# an AA line will almost certainly convert the image to RGB, don't use
-# an AA line here
-ok($img->line(color=>$blue, x1=>10, y1=>10, x2=>40, y2=>40),
-    "draw a line");
-is($img->type, 'paletted', 'still paletted after line');
-
-# draw with white - should convert to direct
-ok($img->box(color=>$white, filled=>1, xmin=>20, ymin=>20, 
-             xmax=>30, ymax=>30), "white box");
-is($img->type, 'direct', "now it should be direct");
-
-# various attempted to make a paletted image from our now direct image
-my $palimg = $img->to_paletted;
-ok($palimg, "we got an image");
-# they should be the same pixel for pixel
-ok(Imager::i_img_diff($img->{IMG}, $palimg->{IMG}) == 0, "same pixels");
-
-# strange case: no color picking, and no colors
-# this was causing a segmentation fault
-$palimg = $img->to_paletted(colors=>[ ], make_colors=>'none');
-ok(!defined $palimg, "to paletted with an empty palette is an error");
-print "# ",$img->errstr,"\n";
-ok(scalar($img->errstr =~ /no colors available for translation/),
-    "and got the correct msg");
-
-ok(!Imager->new(xsize=>1, ysize=>-1, type=>'paletted'), 
-    "fail on -ve height");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct error message");
-ok(!Imager->new(xsize=>-1, ysize=>1, type=>'paletted'), 
-    "fail on -ve width");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct error message");
-ok(!Imager->new(xsize=>-1, ysize=>-1, type=>'paletted'), 
-    "fail on -ve width/height");
-cmp_ok(Imager->errstr, '=~', qr/Image sizes must be positive/,
-       "and correct error message");
-
-ok(!Imager->new(xsize=>1, ysize=>1, type=>'paletted', channels=>0),
-    "fail on 0 channels");
-cmp_ok(Imager->errstr, '=~', qr/Channels must be positive and <= 4/,
-       "and correct error message");
-ok(!Imager->new(xsize=>1, ysize=>1, type=>'paletted', channels=>5),
-    "fail on 5 channels");
-cmp_ok(Imager->errstr, '=~', qr/Channels must be positive and <= 4/,
-       "and correct error message");
-
-{
-  # https://rt.cpan.org/Ticket/Display.html?id=8213
-  # check for handling of memory allocation of very large images
-  # only test this on 32-bit machines - on a 64-bit machine it may
-  # result in trying to allocate 4Gb of memory, which is unfriendly at
-  # least and may result in running out of memory, causing a different
-  # type of exit
-  use Config;
- SKIP:
-  {
-    skip("don't want to allocate 4Gb", 10)
-      unless $Config{ptrsize} == 4;
-
-    my $uint_range = 256 ** $Config{intsize};
-    my $dim1 = int(sqrt($uint_range))+1;
-    
-    my $im_b = Imager->new(xsize=>$dim1, ysize=>$dim1, channels=>1, type=>'paletted');
-    is($im_b, undef, "integer overflow check - 1 channel");
-    
-    $im_b = Imager->new(xisze=>$dim1, ysize=>1, channels=>1, type=>'paletted');
-    ok($im_b, "but same width ok");
-    $im_b = Imager->new(xisze=>1, ysize=>$dim1, channels=>1, type=>'paletted');
-    ok($im_b, "but same height ok");
-    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
-           "check the error message");
-
-    # do a similar test with a 3 channel image, so we're sure we catch
-    # the same case where the third dimension causes the overflow
-    # for paletted images the third dimension can't cause an overflow
-    # but make sure we didn't anything too dumb in the checks
-    my $dim3 = $dim1;
-    
-    $im_b = Imager->new(xsize=>$dim3, ysize=>$dim3, channels=>3, type=>'paletted');
-    is($im_b, undef, "integer overflow check - 3 channel");
-    
-    $im_b = Imager->new(xsize=>$dim3, ysize=>1, channels=>3, type=>'paletted');
-    ok($im_b, "but same width ok");
-    $im_b = Imager->new(xsize=>1, ysize=>$dim3, channels=>3, type=>'paletted');
-    ok($im_b, "but same height ok");
-
-    cmp_ok(Imager->errstr, '=~', qr/integer overflow/,
-           "check the error message");
-
-    # test the scanline allocation check
-    # divide by 2 to get int range, by 3 so that the image (one byte/pixel)
-    # doesn't integer overflow, but the scanline of i_color (4/pixel) does
-    my $dim4 = $uint_range / 3;
-    my $im_o = Imager->new(xsize=>$dim4, ysize=>1, channels=>3, type=>'paletted');
-    is($im_o, undef, "integer overflow check - scanline size");
-    cmp_ok(Imager->errstr, '=~', 
-           qr/integer overflow calculating scanline allocation/,
-           "check error message");
-  }
-}
-
-{ # http://rt.cpan.org/NoAuth/Bug.html?id=9672
-  my $warning;
-  local $SIG{__WARN__} = 
-    sub { 
-      $warning = "@_";
-      my $printed = $warning;
-      $printed =~ s/\n$//;
-      $printed =~ s/\n/\n\#/g; 
-      print "# ",$printed, "\n";
-    };
-  my $img = Imager->new(xsize=>10, ysize=>10);
-  $img->to_paletted();
-  cmp_ok($warning, '=~', 'void', "correct warning");
-  cmp_ok($warning, '=~', 't023palette\\.t', "correct file");
-}
-
-{ # http://rt.cpan.org/NoAuth/Bug.html?id=12676
-  # setcolors() has a fencepost error
-  my $img = Imager->new(xsize=>10, ysize=>10, type=>'paletted');
-
-  is($img->addcolors(colors=>[ $black, $red ]), "0 but true",
-     "add test colors");
-  ok($img->setcolors(start=>1, colors=>[ $green ]), "set the last color");
-  ok(!$img->setcolors(start=>2, colors=>[ $black ]), 
-     "set after the last color");
-}
-
-{ # https://rt.cpan.org/Ticket/Display.html?id=20056
-  # added named color support to addcolor/setcolor
-  my $img = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
-  is($img->addcolors(colors => [ qw/000000 FF0000/ ]), "0 but true",
-     "add colors as strings instead of objects");
-  my @colors = $img->getcolors;
-  iscolor($colors[0], $black, "check first color");
-  iscolor($colors[1], $red, "check second color");
-  ok($img->setcolors(colors => [ qw/00FF00 0000FF/ ]),
-     "setcolors as strings instead of objects");
-  @colors = $img->getcolors;
-  iscolor($colors[0], $green, "check first color");
-  iscolor($colors[1], $blue, "check second color");
-
-  # make sure we handle bad colors correctly
-  is($img->colorcount, 2, "start from a known state");
-  is($img->addcolors(colors => [ 'XXFGXFXGXFX' ]), undef,
-     "fail to add unknown color");
-  is($img->errstr, 'No color named XXFGXFXGXFX found', 'check error message');
-  is($img->setcolors(colors => [ 'XXFGXFXGXFXZ' ]), undef,
-     "fail to set to unknown color");
-  is($img->errstr, 'No color named XXFGXFXGXFXZ found', 'check error message');
-}
-
-{ # https://rt.cpan.org/Ticket/Display.html?id=20338
-  # OO interface to i_glin/i_plin
-  my $im = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
-  is($im->addcolors(colors => [ "#000", "#F00", "#0F0", "#00F" ]), "0 but true",
-     "add some test colors")
-    or print "# ", $im->errstr, "\n";
-  # set a pixel to check
-  $im->setpixel(x => 1, 'y' => 0, color => "#0F0");
-  is_deeply([ $im->getscanline('y' => 0, type=>'index') ],
-            [ 0, 2, (0) x 8 ], "getscanline index in list context");
-  isbin($im->getscanline('y' => 0, type=>'index'),
-        "\x00\x02" . "\x00" x 8,
-        "getscanline index in scalar context");
-  is($im->setscanline('y' => 0, pixels => [ 1, 2, 0, 3 ], type => 'index'),
-     4, "setscanline with list");
-  is($im->setscanline('y' => 0, x => 4, pixels => pack("C*", 3, 2, 1, 0, 3),
-                      type => 'index'),
-     5, "setscanline with pv");
-  is_deeply([ $im->getscanline(type => 'index', 'y' => 0) ],
-            [ 1, 2, 0, 3, 3, 2, 1, 0, 3, 0 ],
-            "check values set");
-  eval { # should croak on OOR index
-    $im->setscanline('y' => 1, pixels => [ 255 ], type=>'index');
-  };
-  ok($@, "croak on setscanline() to invalid index");
-  eval { # same again with pv
-    $im->setscanline('y' => 1, pixels => "\xFF", type => 'index');
-  };
-  ok($@, "croak on setscanline() with pv to invalid index");
-}
-
-{
-  print "# make_colors => mono\n";
-  # test mono make_colors
-  my $imrgb = Imager->new(xsize => 10, ysize => 10);
-  $imrgb->setpixel(x => 0, 'y' => 0, color => '#FFF');
-  $imrgb->setpixel(x => 1, 'y' => 0, color => '#FF0');
-  $imrgb->setpixel(x => 2, 'y' => 0, color => '#000');
-  my $mono = $imrgb->to_paletted(make_colors => 'mono',
-                                  translate => 'closest');
-  is($mono->type, 'paletted', "check we get right image type");
-  is($mono->colorcount, 2, "only 2 colors");
-  my ($is_mono, $ziw) = $mono->is_bilevel;
-  ok($is_mono, "check monochrome check true");
-  is($ziw, 0, "check ziw false");
-  my @colors = $mono->getcolors;
-  iscolor($colors[0], $black, "check first entry");
-  iscolor($colors[1], $white, "check second entry");
-  my @pixels = $mono->getscanline(x => 0, 'y' => 0, width => 3, type=>'index');
-  is($pixels[0], 1, "check white pixel");
-  is($pixels[1], 1, "check yellow pixel");
-  is($pixels[2], 0, "check black pixel");
-}
-
-{ # check for the various mono images we accept
-  my $mono_8_bw_3 = Imager->new(xsize => 2, ysize => 2, channels => 3, 
-                             type => 'paletted');
-  ok($mono_8_bw_3->addcolors(colors => [ qw/000000 FFFFFF/ ]), 
-     "mono8bw3 - add colors");
-  ok($mono_8_bw_3->is_bilevel, "it's mono");
-  is(($mono_8_bw_3->is_bilevel)[1], 0, 'zero not white');
-  
-  my $mono_8_wb_3 = Imager->new(xsize => 2, ysize => 2, channels => 3, 
-                             type => 'paletted');
-  ok($mono_8_wb_3->addcolors(colors => [ qw/FFFFFF 000000/ ]), 
-     "mono8wb3 - add colors");
-  ok($mono_8_wb_3->is_bilevel, "it's mono");
-  is(($mono_8_wb_3->is_bilevel)[1], 1, 'zero is white');
-  
-  my $mono_8_bw_1 = Imager->new(xsize => 2, ysize => 2, channels => 1, 
-                             type => 'paletted');
-  ok($mono_8_bw_1->addcolors(colors => [ qw/000000 FFFFFF/ ]), 
-     "mono8bw - add colors");
-  ok($mono_8_bw_1->is_bilevel, "it's mono");
-  is(($mono_8_bw_1->is_bilevel)[1], 0, 'zero not white');
-  
-  my $mono_8_wb_1 = Imager->new(xsize => 2, ysize => 2, channels => 1, 
-                             type => 'paletted');
-  ok($mono_8_wb_1->addcolors(colors => [ qw/FFFFFF 000000/ ]), 
-     "mono8wb - add colors");
-  ok($mono_8_wb_1->is_bilevel, "it's mono");
-  is(($mono_8_wb_1->is_bilevel)[1], 1, 'zero is white');
-}
-
-{ # check bounds checking
-  my $im = Imager->new(xsize => 10, ysize => 10, type=>'paletted');
-  ok($im->addcolors(colors => [ $black ]), "add color of pixel bounds check writes");
-
-  image_bounds_checks($im);
-}
-
-{ # test colors array returns colors
-  my $data;
-  my $im = test_image();
-  my @colors;
-  my $imp = $im->to_paletted(colors => \@colors, 
-                            make_colors => 'webmap', 
-                            translate => 'closest');
-  ok($imp, "made paletted");
-  is(@colors, 216, "should be 216 colors in the webmap");
-  is_color3($colors[0], 0, 0, 0, "first should be 000000");
-  is_color3($colors[1], 0, 0, 0x33, "second should be 000033");
-  is_color3($colors[8], 0, 0x33, 0x66, "9th should be 003366");
-}
-
-{ # RT 68508
-  my $im = Imager->new(xsize => 10, ysize => 10);
-  $im->box(filled => 1, color => Imager::Color->new(255, 0, 0));
-  my $palim = $im->to_paletted(make_colors => "mono", translate => "errdiff");
-  ok($palim, "convert to mono with error diffusion");
-  my $blank = Imager->new(xsize => 10, ysize => 10);
-  isnt_image($palim, $blank, "make sure paletted isn't all black");
-}
-
-{ # check validation of palette entries
-  my $im = Imager->new(xsize => 10, ysize => 10, type => 'paletted');
-  $im->addcolors(colors => [ $black, $red ]);
-  {
-    my $no_croak = eval {
-      $im->setscanline(y => 0, type => 'index', pixels => [ 0, 1 ]);
-      1;
-    };
-    ok($no_croak, "valid values don't croak");
-  }
-  {
-    my $no_croak = eval {
-      $im->setscanline(y => 0, type => 'index', pixels => pack("C*", 0, 1));
-      1;
-    };
-    ok($no_croak, "valid values don't croak (packed)");
-  }
-  {
-    my $no_croak = eval {
-      $im->setscanline(y => 0, type => 'index', pixels => [ 2, 255 ]);
-      1;
-    };
-    ok(!$no_croak, "invalid values do croak");
-  }
-  {
-    my $no_croak = eval {
-      $im->setscanline(y => 0, type => 'index', pixels => pack("C*", 2, 255));
-      1;
-    };
-    ok(!$no_croak, "invalid values do croak (packed)");
-  }
-}
-
-{
-  my $im = Imager->new(xsize => 1, ysize => 1);
-  my $im_bad = Imager->new;
-  {
-    my @map = Imager->make_palette({});
-    ok(!@map, "make_palette should fail with no images");
-    is(Imager->errstr, "make_palette: supply at least one image",
-       "check error message");
-  }
-  {
-    my @map = Imager->make_palette({}, $im, $im_bad, $im);
-    ok(!@map, "make_palette should fail with an empty image");
-    is(Imager->errstr, "make_palette: image 2 is empty",
-       "check error message");
-  }
-  {
-    my @map = Imager->make_palette({ make_colors => "mono" }, $im);
-    is(@map, 2, "mono should make 2 color palette")
-      or skip("unexpected color count", 2);
-    is_color4($map[0], 0, 0, 0, 255, "check map[0]");
-    is_color4($map[1], 255, 255, 255, 255, "check map[1]");
-  }
-  {
-    my @map = Imager->make_palette({ make_colors => "gray4" }, $im);
-    is(@map, 4, "gray4 should make 4 color palette")
-      or skip("unexpected color count", 4);
-    is_color4($map[0], 0, 0, 0, 255, "check map[0]");
-    is_color4($map[1], 85, 85, 85, 255, "check map[1]");
-    is_color4($map[2], 170, 170, 170, 255, "check map[2]");
-    is_color4($map[3], 255, 255, 255, 255, "check map[3]");
-  }
-  {
-    my @map = Imager->make_palette({ make_colors => "gray16" }, $im);
-    is(@map, 16, "gray16 should make 16 color palette")
-      or skip("unexpected color count", 4);
-    is_color4($map[0], 0, 0, 0, 255, "check map[0]");
-    is_color4($map[1], 17, 17, 17, 255, "check map[1]");
-    is_color4($map[2], 34, 34, 34, 255, "check map[2]");
-    is_color4($map[15], 255, 255, 255, 255, "check map[15]");
-  }
-  {
-    my @map = Imager->make_palette({ make_colors => "gray" }, $im);
-    is(@map, 256, "gray16 should make 256 color palette")
-      or skip("unexpected color count", 4);
-    is_color4($map[0], 0, 0, 0, 255, "check map[0]");
-    is_color4($map[1], 1, 1, 1, 255, "check map[1]");
-    is_color4($map[33], 33, 33, 33, 255, "check map[2]");
-    is_color4($map[255], 255, 255, 255, 255, "check map[15]");
-  }
-}
-
-my $psamp_outside_error = "Image position outside of image";
-{ # psamp
-  print "# psamp\n";
-  my $imraw = Imager::i_img_pal_new(10, 10, 3, 255);
-  my @colors =
-    (
-     NC(0, 0, 0), NC(255, 128, 64), NC(64, 128, 192),
-     NC(64, 0, 192), NC(255, 128, 0), NC(64, 32, 0),
-     NC(128, 63, 32), NC(255, 128, 32), NC(64, 32, 16),
-    );
-  is(Imager::i_addcolors($imraw, @colors), "0 but true",
-     "add colors needed for testing");
-  {
-    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");
-  }
-  { # 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 message");
-    is(Imager::i_psamp($imraw, 0, 10, undef, [ 0, 0, 0 ]), undef,
-       "y overflow");
-    is(_get_error(), $psamp_outside_error, "check message");
-    is(Imager::i_psamp($imraw, -1, 0, undef, [ 0, 0, 0 ]), undef,
-       "negative x");
-    is(_get_error(), $psamp_outside_error, "check message");
-    is(Imager::i_psamp($imraw, 10, 0, undef, [ 0, 0, 0 ]), undef,
-       "x overflow");
-    is(_get_error(), $psamp_outside_error, "check message");
-  }
-  ok(Imager::i_img_type($imraw), "still paletted");
-  print "# end psamp tests\n";
-}
-
-{ # psampf
-  print "# psampf\n";
-  my $imraw = Imager::i_img_pal_new(10, 10, 3, 255);
-  my @colors =
-    (
-     NC(0, 0, 0), NC(255, 128, 64), NC(64, 128, 192),
-     NC(64, 0, 191), NC(255, 128, 0), NC(64, 32, 0),
-     NC(128, 64, 32), NC(255, 128, 32), NC(64, 32, 16),
-    );
-  is(Imager::i_addcolors($imraw, @colors), "0 but true",
-     "add colors needed for testing");
-  {
-    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, 128, 64,
-             "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), 64, 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), 64, 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, 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_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 ]) ],
-             [ (128, 64, 32) 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");
-  }
-  { # 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 message");
-    is(Imager::i_psampf($imraw, 0, 10, undef, [ 0, 0, 0 ]), undef,
-       "y overflow");
-    is(_get_error(), $psamp_outside_error, "check message");
-    is(Imager::i_psampf($imraw, -1, 0, undef, [ 0, 0, 0 ]), undef,
-       "negative x");
-    is(_get_error(), $psamp_outside_error, "check message");
-    is(Imager::i_psampf($imraw, 10, 0, undef, [ 0, 0, 0 ]), undef,
-       "x overflow");
-    is(_get_error(), $psamp_outside_error, "check message");
-  }
-  ok(Imager::i_img_type($imraw), "still paletted");
-  print "# end psampf tests\n";
-}
-
-{ # 75258 - gpixf() broken for paletted images
-  my $im = Imager->new(xsize => 10, ysize => 10, type => "paletted");
-  ok($im, "make a test image");
-  my @colors = ( $black, $red, $green, $blue );
-  is($im->addcolors(colors => \@colors), "0 but true",
-     "add some colors");
-  $im->setpixel(x => 0, y => 0, color => $red);
-  $im->setpixel(x => 1, y => 0, color => $green);
-  $im->setpixel(x => 2, y => 0, color => $blue);
-  is_fcolor3($im->getpixel(x => 0, y => 0, type => "float"),
-            1.0, 0, 0, "get a pixel in float form, make sure it's red");
-  is_fcolor3($im->getpixel(x => 1, y => 0, type => "float"),
-            0, 1.0, 0, "get a pixel in float form, make sure it's green");
-  is_fcolor3($im->getpixel(x => 2, y => 0, type => "float"),
-            0, 0, 1.0, "get a pixel in float form, make sure it's blue");
-}
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->to_paletted, "can't convert an empty image");
-  is($empty->errstr, "to_paletted: empty input image",
-    "check error message");
-
-  is($empty->addcolors(colors => [ $black ]), -1,
-     "can't addcolors() to an empty image");
-  is($empty->errstr, "addcolors: empty input image",
-     "check error message");
-
-  ok(!$empty->setcolors(colors => [ $black ]),
-     "can't setcolors() to an empty image");
-  is($empty->errstr, "setcolors: empty input image",
-     "check error message");
-
-  ok(!$empty->getcolors(),
-     "can't getcolors() from an empty image");
-  is($empty->errstr, "getcolors: empty input image",
-     "check error message");
-
-  is($empty->colorcount, -1, "can't colorcount() an empty image");
-  is($empty->errstr, "colorcount: empty input image",
-     "check error message");
-
-  is($empty->maxcolors, -1, "can't maxcolors() an empty image");
-  is($empty->errstr, "maxcolors: empty input image",
-     "check error message");
-
-  is($empty->findcolor(color => $blue), undef,
-     "can't findcolor an empty image");
-  is($empty->errstr, "findcolor: empty input image",
-     "check error message");
-}
-
-Imager->close_log;
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t023palette.log"
-}
-
-sub iscolor {
-  my ($c1, $c2, $msg) = @_;
-
-  my $builder = Test::Builder->new;
-  my @c1 = $c1->rgba;
-  my @c2 = $c2->rgba;
-  if (!$builder->ok($c1[0] == $c2[0] && $c1[1] == $c2[1] && $c1[2] == $c2[2],
-                    $msg)) {
-    $builder->diag(<<DIAG);
-      got color: [ @c1 ]
- expected color: [ @c2 ]
-DIAG
-  }
-}
-
-sub isbin ($$$) {
-  my ($got, $expected, $msg) = @_;
-
-  my $builder = Test::Builder->new;
-  if (!$builder->ok($got eq $expected, $msg)) {
-    (my $got_dec = $got) =~ s/([^ -~])/sprintf("\\x%02x", ord $1)/ge;
-    (my $exp_dec = $expected)  =~ s/([^ -~])/sprintf("\\x%02x", ord $1)/ge;
-    $builder->diag(<<DIAG);
-      got: "$got_dec"
- expected: "$exp_dec"
-DIAG
-  }
-}
-
-sub coloreq {
-  my ($left, $right, $comment) = @_;
-
-  my ($rl, $gl, $bl, $al) = $left->rgba;
-  my ($rr, $gr, $br, $ar) = $right->rgba;
-
-  print "# comparing color($rl,$gl,$bl,$al) with ($rr,$gr,$br,$ar)\n";
-  ok($rl == $rr && $gl == $gr && $bl == $br && $al == $ar,
-      $comment);
-}
-
-sub _get_error {
-  my @errors = Imager::i_errors();
-  return join(": ", map $_->[0], @errors);
-}
diff --git a/t/t03test.t b/t/t03test.t
deleted file mode 100644 (file)
index 984d212..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#!perl -w
-use strict;
-use Imager;
-use Imager::Test qw(test_image test_image_16 test_image_mono test_image_gray test_image_gray_16 test_image_double test_image_named);
-use Test::More tests => 60;
-
-# test Imager::Test
-
-for my $named (0, 1) {
-  my $named_desc = $named ? " (by name)" : "";
-  {
-    my $im = $named ? test_image_named("basic") : test_image();
-    ok($im, "got basic test image$named_desc");
-    is($im->type, "direct", "check basic image type");
-    is($im->getchannels, 3, "check basic image channels");
-    is($im->bits, 8, "check basic image bits");
-    ok(!$im->is_bilevel, "check basic isn't mono");
-  }
-  {
-    my $im = $named ? test_image_named("basic16") : test_image_16();
-    ok($im, "got 16-bit basic test image$named_desc");
-    is($im->type, "direct", "check 16-bit basic image type");
-    is($im->getchannels, 3, "check 16-bit basic image channels");
-    is($im->bits, 16, "check 16-bit basic image bits");
-    ok(!$im->is_bilevel, "check 16-bit basic isn't mono");
-  }
-  
-  {
-    my $im = $named ? test_image_named("basic_double") : test_image_double();
-    ok($im, "got double basic test image$named_desc");
-    is($im->type, "direct", "check double basic image type");
-    is($im->getchannels, 3, "check double basic image channels");
-    is($im->bits, "double", "check double basic image bits");
-    ok(!$im->is_bilevel, "check double basic isn't mono");
-  }
-  {
-    my $im = $named ? test_image_named("gray") : test_image_gray();
-    ok($im, "got gray test image$named_desc");
-    is($im->type, "direct", "check gray image type");
-    is($im->getchannels, 1, "check gray image channels");
-    is($im->bits, 8, "check gray image bits");
-    ok(!$im->is_bilevel, "check gray isn't mono");
-    $im->write(file => "testout/t03gray.pgm");
-  }
-  
-  {
-    my $im = $named ? test_image_named("gray16") : test_image_gray_16();
-    ok($im, "got gray test image$named_desc");
-    is($im->type, "direct", "check 16-bit gray image type");
-    is($im->getchannels, 1, "check 16-bit gray image channels");
-    is($im->bits, 16, "check 16-bit gray image bits");
-    ok(!$im->is_bilevel, "check 16-bit isn't mono");
-    $im->write(file => "testout/t03gray16.pgm");
-  }
-  
-  {
-    my $im = $named ? test_image_named("mono") : test_image_mono();
-    ok($im, "got mono image$named_desc");
-    is($im->type, "paletted", "check mono image type");
-    is($im->getchannels, 3, "check mono image channels");
-    is($im->bits, 8, "check mono image bits");
-    ok($im->is_bilevel, "check mono is mono");
-    $im->write(file => "testout/t03mono.pbm");
-  }
-}
diff --git a/t/t05error.t b/t/t05error.t
deleted file mode 100644 (file)
index e3ac15a..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 7;
-BEGIN { use_ok("Imager", ":all") }
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t05error.log");
-
-# try to read an invalid pnm file
-open FH, "< testimg/junk.ppm"
-  or die "Cannot open testin/junk: $!";
-binmode(FH);
-my $IO = Imager::io_new_fd(fileno(FH));
-my $im = i_readpnm_wiol($IO, -1);
-SKIP:{
-  ok(!$im, "read of junk.ppm should have failed")
-    or skip("read didn't fail!", 5);
-
-  my @errors = Imager::i_errors();
-
-  is(scalar @errors, 1, "got the errors")
-    or skip("no errors to check", 4);
-
- SKIP:
-  {
-    my $error0 = $errors[0];
-    is(ref $error0, "ARRAY", "entry 0 is an array ref")
-      or skip("entry 0 not an array", 3);
-
-    is(scalar @$error0, 2, "entry 0 has 2 elements")
-      or skip("entry 0 doesn't have enough elements", 2);
-
-    is($error0->[0], "while skipping to height", "check message");
-    is($error0->[1], "0", "error code should be 0");
-  }
-}
-
-Imager->close_log;
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t05error.log";
-}
diff --git a/t/t07iolayer.t b/t/t07iolayer.t
deleted file mode 100644 (file)
index 76388a9..0000000
+++ /dev/null
@@ -1,987 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 274;
-use Imager::Test qw(is_image);
-# for SEEK_SET etc, Fcntl doesn't provide these in 5.005_03
-use IO::Seekable;
-use Config;
-
-BEGIN { use_ok(Imager => ':all') };
-
--d "testout" or mkdir "testout";
-
-$| = 1;
-
-Imager->open_log(log => "testout/t07iolayer.log");
-
-undef($/);
-# start by testing io buffer
-
-my $data="P2\n2 2\n255\n 255 0\n0 255\n";
-my $IO = Imager::io_new_buffer($data);
-my $im = Imager::i_readpnm_wiol($IO, -1);
-
-ok($im, "read from data io");
-
-open(FH, ">testout/t07.ppm") or die $!;
-binmode(FH);
-my $fd = fileno(FH);
-my $IO2 = Imager::io_new_fd( $fd );
-Imager::i_writeppm_wiol($im, $IO2);
-close(FH);
-undef($im);
-
-open(FH, "<testimg/penguin-base.ppm");
-binmode(FH);
-$data = <FH>;
-close(FH);
-my $IO3 = Imager::IO->new_buffer($data);
-#undef($data);
-$im = Imager::i_readpnm_wiol($IO3, -1);
-
-ok($im, "read from buffer, for compare");
-undef $IO3;
-
-open(FH, "<testimg/penguin-base.ppm") or die $!;
-binmode(FH);
-$fd = fileno(FH);
-my $IO4 = Imager::IO->new_fd( $fd );
-my $im2 = Imager::i_readpnm_wiol($IO4, -1);
-close(FH);
-undef($IO4);
-
-ok($im2, "read from file, for compare");
-
-is(i_img_diff($im, $im2), 0, "compare images");
-undef($im2);
-
-my $IO5 = Imager::io_new_bufchain();
-Imager::i_writeppm_wiol($im, $IO5);
-my $data2 = Imager::io_slurp($IO5);
-undef($IO5);
-
-ok($data2, "check we got data from bufchain");
-
-my $IO6 = Imager::io_new_buffer($data2);
-my $im3 = Imager::i_readpnm_wiol($IO6, -1);
-
-is(Imager::i_img_diff($im, $im3), 0, "read from buffer");
-
-my $work = $data;
-my $pos = 0;
-sub io_reader {
-  my ($size, $maxread) = @_;
-  my $out = substr($work, $pos, $maxread);
-  $pos += length $out;
-  $out;
-}
-sub io_reader2 {
-  my ($size, $maxread) = @_;
-  my $out = substr($work, $pos, $maxread);
-  $pos += length $out;
-  $out;
-}
-my $IO7 = Imager::IO->new_cb(undef, \&io_reader, undef, undef);
-ok($IO7, "making readcb object");
-my $im4 = Imager::i_readpnm_wiol($IO7, -1);
-ok($im4, "read from cb");
-ok(Imager::i_img_diff($im, $im4) == 0, "read from cb image match");
-
-$pos = 0;
-$IO7 = Imager::io_new_cb(undef, \&io_reader2, undef, undef);
-ok($IO7, "making short readcb object");
-my $im5 = Imager::i_readpnm_wiol($IO7, -1);
-ok($im4, "read from cb2");
-is(Imager::i_img_diff($im, $im5), 0, "read from cb2 image match");
-
-sub io_writer {
-  my ($what) = @_;
-  substr($work, $pos, $pos+length $what) = $what;
-  $pos += length $what;
-
-  1;
-}
-
-my $did_close;
-sub io_close {
-  ++$did_close;
-}
-
-my $IO8 = Imager::io_new_cb(\&io_writer, undef, undef, \&io_close);
-ok($IO8, "making writecb object");
-$pos = 0;
-$work = '';
-ok(Imager::i_writeppm_wiol($im, $IO8), "write to cb");
-# I originally compared this to $data, but that doesn't include the
-# Imager header
-is($work, $data2, "write image match");
-ok($did_close, "did close");
-
-# with a short buffer, no closer
-my $IO9 = Imager::io_new_cb(\&io_writer, undef, undef, undef, 1);
-ok($IO9, "making short writecb object");
-$pos = 0;
-$work = '';
-ok(Imager::i_writeppm_wiol($im, $IO9), "write to short cb");
-is($work, $data2, "short write image match");
-
-{
-  my $buf_data = "Test data";
-  my $io9 = Imager::io_new_buffer($buf_data);
-  is(ref $io9, "Imager::IO", "check class");
-  my $work;
-  is($io9->raw_read($work, 4), 4, "read 4 from buffer object");
-  is($work, "Test", "check data read");
-  is($io9->raw_read($work, 5), 5, "read the rest");
-  is($work, " data", "check data read");
-  is($io9->raw_seek(5, SEEK_SET), 5, "seek");
-  is($io9->raw_read($work, 5), 4, "short read");
-  is($work, "data", "check data read");
-  is($io9->raw_seek(-1, SEEK_CUR), 8, "seek relative");
-  is($io9->raw_seek(-5, SEEK_END), 4, "seek relative to end");
-  is($io9->raw_seek(-10, SEEK_CUR), -1, "seek failure");
-  undef $io9;
-}
-{
-  my $io = Imager::IO->new_bufchain();
-  is(ref $io, "Imager::IO", "check class");
-  is($io->raw_write("testdata"), 8, "check write");
-  is($io->raw_seek(-8, SEEK_CUR), 0, "seek relative");
-  my $work;
-  is($io->raw_read($work, 8), 8, "check read");
-  is($work, "testdata", "check data read");
-  is($io->raw_seek(-3, SEEK_END), 5, "seek end relative");
-  is($io->raw_read($work, 5), 3, "short read");
-  is($work, "ata", "check read data");
-  is($io->raw_seek(4, SEEK_SET), 4, "absolute seek to write some");
-  is($io->raw_write("testdata"), 8, "write");
-  is($io->raw_seek(0, SEEK_CUR), 12, "check size");
-  $io->raw_close();
-  
-  # grab the data
-  my $data = Imager::io_slurp($io);
-  is($data, "testtestdata", "check we have the right data");
-}
-
-{ # callback failure checks
-  my $fail_io = Imager::io_new_cb(\&fail_write, \&fail_read, \&fail_seek, undef, 1);
-  # scalar context
-  my $buffer;
-  my $read_result = $fail_io->raw_read($buffer, 10);
-  is($read_result, undef, "read failure undef in scalar context");
-  my @read_result = $fail_io->raw_read($buffer, 10);
-  is(@read_result, 0, "empty list in list context");
-  $read_result = $fail_io->raw_read2(10);
-  is($read_result, undef, "raw_read2 failure (scalar)");
-  @read_result = $fail_io->raw_read2(10);
-  is(@read_result, 0, "raw_read2 failure (list)");
-
-  my $write_result = $fail_io->raw_write("test");
-  is($write_result, -1, "failed write");
-
-  my $seek_result = $fail_io->raw_seek(-1, SEEK_SET);
-  is($seek_result, -1, "failed seek");
-}
-
-{ # callback success checks
-  my $good_io = Imager::io_new_cb(\&good_write, \&good_read, \&good_seek, undef, 1);
-  # scalar context
-  my $buffer;
-  my $read_result = $good_io->raw_read($buffer, 10);
-  is($read_result, 8, "read success (scalar)");
-  is($buffer, "testdata", "check data");
-  my @read_result = $good_io->raw_read($buffer, 10);
-  is_deeply(\@read_result, [ 8 ], "read success (list)");
-  is($buffer, "testdata", "check data");
-  $read_result = $good_io->raw_read2(10);
-  is($read_result, "testdata", "read2 success (scalar)");
-  @read_result = $good_io->raw_read2(10);
-  is_deeply(\@read_result, [ "testdata" ], "read2 success (list)");
-}
-
-{ # end of file
-  my $eof_io = Imager::io_new_cb(undef, \&eof_read, undef, undef, 1);
-  my $buffer;
-  my $read_result = $eof_io->raw_read($buffer, 10);
-  is($read_result, 0, "read eof (scalar)");
-  is($buffer, '', "check data");
-  my @read_result = $eof_io->raw_read($buffer, 10);
-  is_deeply(\@read_result, [ 0 ], "read eof (list)");
-  is($buffer, '', "check data");
-}
-
-{ # no callbacks
-  my $none_io = Imager::io_new_cb(undef, undef, undef, undef, 0);
-  is($none_io->raw_write("test"), -1, "write with no writecb should fail");
-  my $buffer;
-  is($none_io->raw_read($buffer, 10), undef, "read with no readcb should fail");
-  is($none_io->raw_seek(0, SEEK_SET), -1, "seek with no seekcb should fail");
-}
-
-SKIP:
-{ # make sure we croak when trying to write a string with characters over 0xff
-  # the write callback shouldn't get called
-  skip("no native UTF8 support in this version of perl", 2)
-    unless $] >= 5.006;
-  my $io = Imager::io_new_cb(\&good_write, undef, undef, 1);
-  my $data = chr(0x100);
-  is(ord $data, 0x100, "make sure we got what we expected");
-  my $result = 
-    eval {
-      $io->raw_write($data);
-      1;
-    };
-  ok(!$result, "should have croaked")
-    and print "# $@\n";
-}
-
-{ # 0.52 left some debug code in a path that wasn't tested, make sure
-  # that path is tested
-  # http://rt.cpan.org/Ticket/Display.html?id=20705
-  my $io = Imager::io_new_cb
-    (
-     sub { 
-       print "# write $_[0]\n";
-       1 
-     }, 
-     sub { 
-       print "# read $_[0], $_[1]\n";
-       "x" x $_[1]
-     }, 
-     sub { print "# seek\n"; 0 }, 
-     sub { print "# close\n"; 1 });
-  my $buffer;
-  is($io->raw_read($buffer, 10), 10, "read 10");
-  is($buffer, "xxxxxxxxxx", "read value");
-  ok($io->raw_write("foo"), "write");
-  is($io->raw_close, 0, "close");
-}
-
-SKIP:
-{ # fd_seek write failure
-  -c "/dev/full"
-    or skip("No /dev/full", 3);
-  open my $fh, "> /dev/full"
-    or skip("Can't open /dev/full: $!", 3);
-  my $io = Imager::io_new_fd(fileno($fh));
-  ok($io, "make fd io for /dev/full");
-  Imager::i_clear_error();
-  is($io->raw_write("test"), -1, "fail to write");
-  my $msg = Imager->_error_as_msg;
-  like($msg, qr/^write\(\) failure: /, "check error message");
-  print "# $msg\n";
-
-  # /dev/full succeeds on seek on Linux
-
-  undef $io;
-}
-
-SKIP:
-{ # fd_seek seek failure
-  my $seekfail = "testout/t07seekfail.dat";
-  open my $fh, "> $seekfail"
-    or skip("Can't open $seekfail: $!", 3);
-  my $io = Imager::io_new_fd(fileno($fh));
-  ok($io, "make fd io for $seekfail");
-
-  Imager::i_clear_error();
-  is($io->raw_seek(-1, SEEK_SET), -1, "shouldn't be able to seek to -1");
-  my $msg = Imager->_error_as_msg;
-  like($msg, qr/^lseek\(\) failure: /, "check error message");
-  print "# $msg\n";
-
-  undef $io;
-  close $fh;
-  unlink $seekfail;
-}
-
-SKIP:
-{ # fd_seek read failure
-  open my $fh, "> testout/t07writeonly.txt"
-    or skip("Can't open testout/t07writeonly.txt: $!", 3);
-  my $io = Imager::io_new_fd(fileno($fh));
-  ok($io, "make fd io for write-only");
-
-  Imager::i_clear_error();
-  my $buf;
-  is($io->raw_read($buf, 10), undef,
-     "file open for write shouldn't be readable");
-  my $msg = Imager->_error_as_msg;
-  like($msg, qr/^read\(\) failure: /, "check error message");
-  print "# $msg\n";
-
-  undef $io;
-}
-
-SKIP:
-{ # fd_seek eof
-  open my $fh, "> testout/t07readeof.txt"
-    or skip("Can't open testout/t07readeof.txt: $!", 5);
-  binmode $fh;
-  print $fh "test";
-  close $fh;
-  open my $fhr, "< testout/t07readeof.txt",
-    or skip("Can't open testout/t07readeof.txt: $!", 5);
-  my $io = Imager::io_new_fd(fileno($fhr));
-  ok($io, "make fd io for read eof");
-
-  Imager::i_clear_error();
-  my $buf;
-  is($io->raw_read($buf, 10), 4,
-     "10 byte read on 4 byte file should return 4");
-  my $msg = Imager->_error_as_msg;
-  is($msg, "", "should be no error message")
-    or print STDERR "# read(4) message is: $msg\n";
-
-  Imager::i_clear_error();
-  $buf = '';
-  is($io->raw_read($buf, 10), 0,
-     "10 byte read at end of 4 byte file should return 0 (eof)");
-
-  $msg = Imager->_error_as_msg;
-  is($msg, "", "should be no error message")
-    or print STDERR "# read(4), eof message is: $msg\n";
-
-  undef $io;
-}
-
-{ # buffered I/O
-  my $data="P2\n2 2\n255\n 255 0\n0 255\n";
-  my $io = Imager::io_new_buffer($data);
-
-  my $c = $io->getc();
-
-  is($c, ord "P", "getc");
-  my $peekc = $io->peekc();
-
-  is($peekc, ord "2", "peekc");
-
-  my $peekn = $io->peekn(2);
-  is($peekn, "2\n", "peekn");
-
-  $c = $io->getc();
-  is($c, ord "2", "getc after peekc/peekn");
-
-  is($io->seek(0, SEEK_SET), "0", "seek");
-  is($io->getc, ord "P", "check we got back to the start");
-}
-
-{ # test closecb result is propagated
-  my $success_cb = sub { 1 };
-  my $failure_cb = sub { 0 };
-
-  {
-    my $io = Imager::io_new_cb(undef, $success_cb, undef, $success_cb);
-    is($io->close(), 0, "test successful close");
-  }
-  {
-    my $io = Imager::io_new_cb(undef, $success_cb, undef, $failure_cb);
-    is($io->close(), -1, "test failed close");
-  }
-}
-
-{ # buffered coverage/function tests
-  # some data to play with
-  my $base = pack "C*", map rand(26) + ord("a"), 0 .. 20_001;
-
-  { # buffered accessors
-    my $io = Imager::io_new_buffer($base);
-    ok($io->set_buffered(0), "set unbuffered");
-    ok(!$io->is_buffered, "verify unbuffered");
-    ok($io->set_buffered(1), "set buffered");
-    ok($io->is_buffered, "verify buffered");
-  }
-
-  { # initial i_io_read(), buffered
-    my $pos = 0;
-    my $ops = "";
-    my $work = $base;
-    my $read = sub {
-      my ($size) = @_;
-
-      my $req_size = $size;
-
-      if ($pos + $size > length $work) {
-       $size = length($work) - $pos;
-      }
-
-      my $result = substr($work, $pos, $size);
-      $pos += $size;
-      $ops .= "R$req_size>$size;";
-
-      print "# read $req_size>$size\n";
-
-      return $result;
-    };
-    my $write = sub {
-      my ($data) = @_;
-
-      substr($work, $pos, length($data), $data);
-
-      return 1;
-    };
-    {
-      my $io = Imager::io_new_cb(undef, $read, undef, undef);
-      my $buf;
-      is($io->read($buf, 1000), 1000, "read initial 1000");
-      is($buf, substr($base, 0, 1000), "check data read");
-      is($ops, "R8192>8192;", "check read op happened to buffer size");
-
-      undef $buf;
-      is($io->read($buf, 1001), 1001, "read another 1001");
-      is($buf, substr($base, 1000, 1001), "check data read");
-      is($ops, "R8192>8192;", "should be no further reads");
-
-      undef $buf;
-      is($io->read($buf, 40_000), length($base) - 2001,
-        "read the rest in one chunk");
-      is($buf, substr($base, 2001), "check the data read");
-      my $buffer_left = 8192 - 2001;
-      my $after_buffer = length($base) - 8192;
-      is($ops, "R8192>8192;R".(40_000 - $buffer_left).">$after_buffer;R21999>0;",
-        "check we tried to read the remainder");
-    }
-    {
-      # read after write errors
-      my $io = Imager::io_new_cb($write, $read, undef, undef);
-      is($io->write("test"), 4, "write 4 bytes, io in write mode");
-      is($io->read2(10), undef, "read should fail");
-      is($io->peekn(10), undef, "peekn should fail");
-      is($io->getc(), -1, "getc should fail");
-      is($io->peekc(), -1, "peekc should fail");
-    }
-  }
-
-  {
-    my $io = Imager::io_new_buffer($base);
-    print "# buffer fill check\n";
-    ok($io, "make memory io");
-    my $buf;
-    is($io->read($buf, 4096), 4096, "read 4k");
-    is($buf, substr($base, 0, 4096), "check data is correct");
-
-    # peek a bit
-    undef $buf;
-    is($io->peekn(5120), substr($base, 4096, 5120),
-       "peekn() 5120, which should exceed the buffer, and only read the left overs");
-  }
-
-  { # initial peekn
-    my $io = Imager::io_new_buffer($base);
-    is($io->peekn(10), substr($base, 0, 10),
-       "make sure initial peekn() is sane");
-    is($io->read2(10), substr($base, 0, 10),
-       "and that reading 10 gets the expected data");
-  }
-
-  { # oversize peekn
-    my $io = Imager::io_new_buffer($base);
-    is($io->peekn(10_000), substr($base, 0, 8192),
-       "peekn() larger than buffer should return buffer-size bytes");
-  }
-
-  { # small peekn then large peekn with a small I/O back end
-    # this might happen when reading from a socket
-    my $work = $base;
-    my $pos = 0;
-    my $ops = '';
-    my $reader = sub {
-      my ($size) = @_;
-
-      my $req_size = $size;
-      # do small reads, to trigger a possible bug
-      if ($size > 10) {
-       $size = 10;
-      }
-
-      if ($pos + $size > length $work) {
-       $size = length($work) - $pos;
-      }
-
-      my $result = substr($work, $pos, $size);
-      $pos += $size;
-      $ops .= "R$req_size>$size;";
-
-      print "# read $req_size>$size\n";
-
-      return $result;
-    };
-    my $io = Imager::io_new_cb(undef, $reader, undef, undef);
-    ok($io, "small reader io");
-    is($io->peekn(25), substr($base, 0, 25), "peek 25");
-    is($ops, "R8192>10;R8182>10;R8172>10;",
-       "check we got the raw calls expected");
-    is($io->peekn(65), substr($base, 0, 65), "peek 65");
-    is($ops, "R8192>10;R8182>10;R8172>10;R8162>10;R8152>10;R8142>10;R8132>10;",
-       "check we got the raw calls expected");
-  }
-  for my $buffered (1, 0) { # peekn followed by errors
-    my $buffered_desc = $buffered ? "buffered" : "unbuffered";
-    my $read = 0;
-    my $base = "abcdef";
-    my $pos = 0;
-    my $reader = sub {
-      my $size = shift;
-      my $req_size = $size;
-      if ($pos + $size > length $base) {
-       $size = length($base) - $pos;
-      }
-      # error instead of eof
-      if ($size == 0) {
-       print "# read $req_size>error\n";
-       return;
-      }
-      my $result = substr($base, $pos, $size);
-      $pos += $size;
-
-      print "# read $req_size>$size\n";
-
-      return $result;
-    };
-    my $io = Imager::io_new_cb(undef, $reader, undef, undef);
-    ok($io, "make $buffered_desc cb with error after 6 bytes");
-    is($io->peekn(5), "abcde",
-       "peekn until just before error ($buffered_desc)");
-    is($io->peekn(6), "abcdef", "peekn until error ($buffered_desc)");
-    is($io->peekn(7), "abcdef", "peekn past error ($buffered_desc)");
-    ok(!$io->error,
-       "should be no error indicator, since data buffered ($buffered_desc)");
-    ok(!$io->eof,
-       "should be no eof indicator, since data buffered ($buffered_desc)");
-
-    # consume it
-    is($io->read2(6), "abcdef", "consume the buffer ($buffered_desc)");
-    is($io->peekn(10), undef,
-       "peekn should get an error indicator ($buffered_desc)");
-    ok($io->error, "should be an error state ($buffered_desc)");
-    ok(!$io->eof, "but not eof ($buffered_desc)");
-  }
-  { # peekn on an empty file
-    my $io = Imager::io_new_buffer("");
-    is($io->peekn(10), "", "peekn on empty source");
-    ok($io->eof, "should be in eof state");
-    ok(!$io->error, "but not error");
-  }
-  { # peekn on error source
-    my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef);
-    is($io->peekn(10), undef, "peekn on empty source");
-    ok($io->error, "should be in error state");
-    ok(!$io->eof, "but not eof");
-  }
-  { # peekn on short source
-    my $io = Imager::io_new_buffer("abcdef");
-    is($io->peekn(4), "abcd", "peekn 4 on 6 byte source");
-    is($io->peekn(10), "abcdef", "followed by peekn 10 on 6 byte source");
-    is($io->peekn(10), "abcdef", "and again, now eof is set");
-  }
-  { # peekn(0)
-    Imager::i_clear_error();
-    my $io = Imager::io_new_buffer("abcdef");
-    is($io->peekn(0), undef, "peekn 0 on 6 byte source");
-    my $msg = Imager->_error_as_msg;
-    is($msg, "peekn size must be positive");
-  }
-  { # getc through a whole file (buffered)
-    my $io = Imager::io_new_buffer($base);
-    my $out = '';
-    while ((my $c = $io->getc()) != -1) {
-      $out .= chr($c);
-    }
-    is($out, $base, "getc should return the file byte by byte (buffered)");
-    is($io->getc, -1, "another getc after eof should fail too");
-    ok($io->eof, "should be marked eof");
-    ok(!$io->error, "shouldn't be marked in error");
-  }
-  { # getc through a whole file (unbuffered)
-    my $io = Imager::io_new_buffer($base);
-    $io->set_buffered(0);
-    my $out = '';
-    while ((my $c = $io->getc()) != -1) {
-      $out .= chr($c);
-    }
-    is($out, $base, "getc should return the file byte by byte (unbuffered)");
-    is($io->getc, -1, "another getc after eof should fail too");
-    ok($io->eof, "should be marked eof");
-    ok(!$io->error, "shouldn't be marked in error");
-  }
-  { # buffered getc with an error
-    my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef);
-    is($io->getc, -1, "buffered getc error");
-    ok($io->error, "io marked in error");
-    ok(!$io->eof, "but not eof");
-  }
-  { # unbuffered getc with an error
-    my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef);
-    $io->set_buffered(0);
-    is($io->getc, -1, "unbuffered getc error");
-    ok($io->error, "io marked in error");
-    ok(!$io->eof, "but not eof");
-  }
-  { # initial peekc - buffered
-    my $io = Imager::io_new_buffer($base);
-    my $c = $io->peekc;
-    is($c, ord($base), "buffered peekc matches");
-    is($io->peekc, $c, "duplicate peekc matchess");
-  }
-  { # initial peekc - unbuffered
-    my $io = Imager::io_new_buffer($base);
-    $io->set_buffered(0);
-    my $c = $io->peekc;
-    is($c, ord($base), "unbuffered peekc matches");
-    is($io->peekc, $c, "duplicate peekc matchess");
-  }
-  { # initial peekc eof - buffered
-    my $io = Imager::io_new_cb(undef, sub { "" }, undef, undef);
-    my $c = $io->peekc;
-    is($c, -1, "buffered eof peekc is -1");
-    is($io->peekc, $c, "duplicate matches");
-    ok($io->eof, "io marked eof");
-    ok(!$io->error, "but not error");
-  }
-  { # initial peekc eof - unbuffered
-    my $io = Imager::io_new_cb(undef, sub { "" }, undef, undef);
-    $io->set_buffered(0);
-    my $c = $io->peekc;
-    is($c, -1, "buffered eof peekc is -1");
-    is($io->peekc, $c, "duplicate matches");
-    ok($io->eof, "io marked eof");
-    ok(!$io->error, "but not error");
-  }
-  { # initial peekc error - buffered
-    my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef);
-    my $c = $io->peekc;
-    is($c, -1, "buffered error peekc is -1");
-    is($io->peekc, $c, "duplicate matches");
-    ok($io->error, "io marked error");
-    ok(!$io->eof, "but not eof");
-  }
-  { # initial peekc error - unbuffered
-    my $io = Imager::io_new_cb(undef, sub { return; }, undef, undef);
-    $io->set_buffered(0);
-    my $c = $io->peekc;
-    is($c, -1, "unbuffered error peekc is -1");
-    is($io->peekc, $c, "duplicate matches");
-    ok($io->error, "io marked error");
-    ok(!$io->eof, "but not eof");
-  }
-  { # initial putc
-    my $io = Imager::io_new_bufchain();
-    is($io->putc(ord "A"), ord "A", "initial putc buffered");
-    is($io->close, 0, "close it");
-    is(Imager::io_slurp($io), "A", "check it was written");
-  }
-  { # initial putc - unbuffered
-    my $io = Imager::io_new_bufchain();
-    $io->set_buffered(0);
-    is($io->putc(ord "A"), ord "A", "initial putc unbuffered");
-    is($io->close, 0, "close it");
-    is(Imager::io_slurp($io), "A", "check it was written");
-  }
-  { # putc unbuffered with error
-    my $io = Imager::io_new_cb(undef, undef, undef, undef);
-    $io->set_buffered(0);
-    is($io->putc(ord "A"), -1, "initial putc unbuffered error");
-    ok($io->error, "io in error");
-    is($io->putc(ord "B"), -1, "still in error");
-  }
-  { # writes while in read state
-    my $io = Imager::io_new_cb(sub { 1 }, sub { return "AA" }, undef, undef);
-    is($io->getc, ord "A", "read to setup read buffer");
-    is($io->putc(ord "B"), -1, "putc should fail");
-    is($io->write("test"), -1, "write should fail");
-  }
-  { # buffered putc error handling
-    # tests the check for error state in the buffered putc code
-    my $io = Imager::io_new_cb(undef, undef, undef, undef);
-    $io->putc(ord "A");
-    ok(!$io->flush, "flush should fail");
-    ok($io->error, "should be in error state");
-    is($io->putc(ord "B"), -1, "check for error");
-  }
-  { # buffered putc flush error handling
-    # test handling of flush failure and of the error state resulting
-    # from that
-    my $io = Imager::io_new_cb(undef, undef, undef, undef);
-    my $i = 0;
-    while (++$i < 100_000 && $io->putc(ord "A") == ord "A") {
-      # until we have to flush and fail doing do
-    }
-    is($i, 8193, "should have failed on 8193rd byte");
-    ok($io->error, "should be in error state");
-    is($io->putc(ord "B"), -1, "next putc should fail");
-  }
-  { # buffered write flush error handling
-    # test handling of flush failure and of the error state resulting
-    # from that
-    my $io = Imager::io_new_cb(undef, undef, undef, undef);
-    my $i = 0;
-    while (++$i < 100_000 && $io->write("A") == 1) {
-      # until we have to flush and fail doing do
-    }
-    is($i, 8193, "should have failed on 8193rd byte");
-    ok($io->error, "should be in error state");
-    is($io->write("B"), -1, "next write should fail");
-  }
-  { # buffered read error
-    my $io = Imager::io_new_cb(undef, undef, undef, undef);
-    is($io->read2(10), undef, "initial read returning error");
-    ok($io->error, "should be in error state");
-  }
-  { # unbuffered read error
-    my $io = Imager::io_new_cb(undef, undef, undef, undef);
-    $io->set_buffered(0);
-    is($io->read2(10), undef, "initial read returning error");
-    ok($io->error, "should be in error state");
-  }
-  { # unbuffered write error
-    my $count = 0;
-    my $io = Imager::io_new_cb(sub { return $count++; }, undef, undef, undef);
-    $io->set_buffered(0);
-    is($io->write("A"), -1, "unbuffered write failure");
-    ok($io->error, "should be in error state");
-    is($io->write("BC"), -1, "should still fail");
-  }
-  { # buffered write + large write
-    my $io = Imager::io_new_bufchain();
-    is($io->write(substr($base, 0, 4096)), 4096,
-       "should be buffered");
-    is($io->write(substr($base, 4096)), length($base) - 4096,
-       "large write, should fill buffer and fall back to direct write");
-    is($io->close, 0, "close it");
-    is(Imager::io_slurp($io), $base, "make sure the data is correct");
-  }
-  { # initial large write with failure
-    # tests error handling for the case where we bypass the buffer
-    # when the write is too large to fit
-    my $io = Imager::io_new_cb(undef, undef, undef, undef);
-    ok($io->flush, "flush with nothing buffered should succeed");
-    is($io->write($base), -1, "large write failure");
-    ok($io->error, "should be in error state");
-    is($io->close, -1, "should fail to close");
-  }
-  { # write that causes a flush then fills the buffer a bit
-    my $io = Imager::io_new_bufchain();
-    is($io->write(substr($base, 0, 6000)), 6000, "fill the buffer a bit");
-    is($io->write(substr($base, 6000, 4000)), 4000,
-       "cause it to flush and then fill some more");
-    is($io->write(substr($base, 10000)), length($base)-10000,
-       "write out the rest of our test data");
-    is($io->close, 0, "close the stream");
-    is(Imager::io_slurp($io), $base, "make sure the data is right");
-  }
-  { # failure on flush on close
-    my $io = Imager::io_new_cb(undef, undef, undef, undef);
-    is($io->putc(ord "A"), ord "A", "something in the buffer");
-    ok(!$io->error, "should be no error yet");
-    is($io->close, -1, "close should failure due to flush error");
-  }
-  { # seek failure
-    my $io = Imager::io_new_cb(undef, undef, undef, undef);
-    is($io->seek(0, SEEK_SET), -1, "seek failure");
-  }
-  { # read a little and seek
-    my $io = Imager::io_new_buffer($base);
-    is($io->getc, ord $base, "read one");
-    is($io->getc, ord substr($base, 1, 1), "read another");
-    is($io->seek(-1, SEEK_CUR), 1, "seek relative back to origin+1");
-    is($io->getc, ord substr($base, 1, 1), "read another again");
-  }
-  { # seek with failing flush
-    my $io = Imager::io_new_cb(undef, undef, undef, undef);
-    is($io->putc(ord "A"), ord "A", "write one");
-    ok(!$io->error, "not in error mode (yet)");
-    is($io->seek(0, SEEK_SET), -1, "seek failure due to flush");
-    ok($io->error, "in error mode");
-  }
-  { # gets()
-    my $data = "test1\ntest2\ntest3";
-    my $io = Imager::io_new_buffer($data);
-    is($io->gets(6), "test1\n", "gets(6)");
-    is($io->gets(5), "test2", "gets(5) (short for the line)");
-    is($io->gets(10), "\n", "gets(10) the rest of the line (the newline)");
-    is($io->gets(), "test3", "gets(default) unterminated line");
-  }
-  { # more gets()
-    my $data = "test1\ntest2\ntest3";
-    my $io = Imager::io_new_buffer($data);
-    is($io->gets(6, ord("1")), "test1", "gets(6) (line terminator 1)");
-    is($io->gets(6, ord("2")), "\ntest2", "gets(6) (line terminator 2)");
-    is($io->gets(6, ord("3")), "\ntest3", "gets(6) (line terminator 3)");
-    is($io->getc, -1, "should be eof");
-  }
-}
-
-{ # based on discussion on IRC, user was attempting to write a TIFF
-  # image file with only a write callback, but TIFF requires seek and
-  # read callbacks when writing.
-  # https://rt.cpan.org/Ticket/Display.html?id=76782
-  my $cb = Imager::io_new_cb(undef, undef, undef, undef);
-  {
-    Imager::i_clear_error();
-    my $data;
-    is($cb->read($data, 10), undef, "default read callback should fail");
-    is(Imager->_error_as_msg(), "read callback called but no readcb supplied",
-       "check error message");
-  }
-  {
-    Imager::i_clear_error();
-    is($cb->raw_write("abc"), -1, "default write callback should fail");
-    is(Imager->_error_as_msg(), "write callback called but no writecb supplied",
-       "check error message");
-  }
-  {
-    Imager::i_clear_error();
-    is($cb->seek(0, 0), -1, "default seek callback should fail");
-    is(Imager->_error_as_msg(), "seek callback called but no seekcb supplied",
-       "check error message");
-  }
-}
-
-SKIP:
-{
-  $Config{useperlio}
-    or skip "PerlIO::scalar requires perlio", 13;
-
-  my $foo;
-  open my $fh, "+<", \$foo;
-  my $io = Imager::IO->_new_perlio($fh);
-  ok($io, "perlio: make a I/O object for a perl scalar fh");
-  is($io->write("test"), 4, "perlio: check we can write");
-  is($io->seek(2, SEEK_SET), 2, "perlio: check we can seek");
-  is($io->write("more"), 4, "perlio: write some more");
-  is($io->seek(0, SEEK_SET), 0, "perlio: seek back to start");
-  my $data;
-  is($io->read($data, 10), 6, "perlio: read everything back");
-  is($data, "temore", "perlio: check we read back what we wrote");
-  is($io->close, 0, "perlio: close it");
-  is($foo, "temore", "perlio: check it got to the scalar properly");
-
-  my $io2 = Imager::IO->new_fh($fh);
-  ok($io2, "new_fh() can make an I/O layer object from a scalar fh");
-  close $fh;
-
-  my $im = Imager->new(xsize => 10, ysize => 10);
-  $foo = "";
-  open my $fh2, ">", \$foo;
-  ok($im->write(fh => $fh2, type => "pnm"), "can write image to scalar fh")
-    or print "# ", $im->errstr, "\n";
-
-  close $fh2;
-  open my $fh3, "<", \$foo;
-  my $im2 = Imager->new(fh => $fh3);
-  ok($im2, "read image from a scalar fh");
-  is_image($im, $im2, "check they match");
-}
-
-{
-  tie *FOO, "IO::Tied";
-  my $io = Imager::IO->new_fh(\*FOO);
-  ok($io, "tied: make a I/O object for a tied fh");
-  is($io->write("test"), 4, "tied: check we can write");
-  is($io->seek(2, SEEK_SET), 2, "tied: check we can seek");
-  is($io->write("more"), 4, "tied: write some more");
-  is($io->seek(0, SEEK_SET), 0, "tied: seek back to start");
-  my $data;
-  is($io->read($data, 10), 6, "tied: read everything back");
-  is($data, "temore", "tied: check we read back what we wrote");
-  is($io->close, 0, "tied: close it");
-  is(tied(*FOO)->[0], "temore", "tied: check it got to the output properly");
-}
-
-Imager->close_log;
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t07.ppm", "testout/t07iolayer.log";
-}
-
-sub eof_read {
-  my ($max_len) = @_;
-
-  return '';
-}
-
-sub good_read {
-  my ($max_len) = @_;
-
-  my $data = "testdata";
-  length $data <= $max_len or substr($data, $max_len) = '';
-
-  print "# good_read ($max_len) => $data\n";
-
-  return $data;
-}
-
-sub fail_write {
-  return;
-}
-
-sub fail_read {
-  return;
-}
-
-sub fail_seek {
-  return -1;
-}
-
-package IO::Tied;
-use base 'Tie::Handle';
-use IO::Seekable;
-
-sub TIEHANDLE {
-  return bless [ "", 0 ];
-}
-
-sub PRINT {
-  for my $entry (@_[1 .. $#_]) {
-    substr($_[0][0], $_[0][1], length $entry, $entry);
-    $_[0][1] += length $entry;
-  }
-
-  return 1;
-}
-
-sub SEEK {
-  my ($self, $offset, $whence) = @_;
-
-  my $newpos;
-  if ($whence == SEEK_SET) {
-    $newpos = $offset;
-  }
-  elsif ($whence == SEEK_CUR) {
-    $newpos = $self->[1] + $offset;
-  }
-  elsif ($whence == SEEK_END) {
-    $newpos = length($self->[0]) + $newpos;
-  }
-  else {
-    return -1;
-  }
-
-  if ($newpos < 0) {
-    return 0;
-  }
-
-  $self->[1] = $newpos;
-
-  return 1;
-}
-
-sub TELL {
-  return $_[0][1];
-}
-
-sub READ {
-  my $self = shift;
-  my $outlen = $_[1];
-  my $offset = @_ > 2 ? $_[2] : 0;
-  if ($self->[1] + $outlen > length $self->[0]) {
-    $outlen = length($self->[0]) - $self->[1];
-    $outlen <= 0
-      and return "";
-  }
-  defined $_[0] or $_[0] = "";
-  substr($_[0], $offset, $outlen) = substr($self->[0], $self->[1], $outlen);
-  $self->[1] += $outlen;
-
-  return $outlen;
-}
diff --git a/t/t080log.t b/t/t080log.t
deleted file mode 100644 (file)
index 2aa7219..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-#!perl -w
-use strict;
-
-# avoiding this prologue would be nice, but it seems to be unavoidable,
-# see "It is also important to note ..." in perldoc threads
-use Config;
-my $loaded_threads;
-BEGIN {
-  if ($Config{useithreads} && $] > 5.008007) {
-    $loaded_threads =
-      eval {
-       require threads;
-       threads->import;
-       1;
-      };
-  }
-}
-use Test::More;
-
-$Config{useithreads}
-  or plan skip_all => "can't test Imager's threads support with no threads";
-$] > 5.008007
-  or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support";
-$loaded_threads
-  or plan skip_all => "couldn't load threads";
-
-$INC{"Devel/Cover.pm"}
-  and plan skip_all => "threads and Devel::Cover don't get along";
-
-use Imager;
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t080log1.log")
-  or plan skip_all => "Cannot open log file: " . Imager->errstr;
-
-plan tests => 3;
-
-Imager->log("main thread a\n");
-
-my $t1 = threads->create
-  (
-   sub {
-     Imager->log("child thread a\n");
-     Imager->open_log(log => "testout/t080log2.log")
-       or die "Cannot open second log file: ", Imager->errstr;
-     Imager->log("child thread b\n");
-     sleep(1);
-     Imager->log("child thread c\n");
-     sleep(1);
-     1;
-   }
-   );
-
-Imager->log("main thread b\n");
-sleep(1);
-Imager->log("main thread c\n");
-ok($t1->join, "join child thread");
-Imager->log("main thread d\n");
-Imager->close_log();
-
-my %log1 = parse_log("testout/t080log1.log");
-my %log2 = parse_log("testout/t080log2.log");
-
-my @log1 =
-  (
-   "main thread a",
-   "main thread b",
-   "child thread a",
-   "main thread c",
-   "main thread d",
-  );
-
-my @log2 =
-  (
-   "child thread b",
-   "child thread c",
-  );
-
-is_deeply(\%log1, { map {; $_ => 1 } @log1 },
-         "check messages in main thread log");
-is_deeply(\%log2, { map {; $_ => 1 } @log2 },
-         "check messages in child thread log");
-
-# grab the messages from the given log
-sub parse_log {
-  my ($filename) = @_;
-
-  open my $fh, "<", $filename
-    or die "Cannot open log file $filename: $!";
-
-  my %lines;
-  while (<$fh>) {
-    chomp;
-    my ($date, $time, $file_line, $level, $message) = split ' ', $_, 5;
-    $lines{$message} = 1;
-  }
-
-  delete $lines{"Imager - log started (level = 1)"};
-  delete $lines{"Imager $Imager::VERSION starting"};
-
-  return %lines;
-}
-
-END {
-  unlink "testout/t080log1.log", "testout/t080log2.log"
-    unless $ENV{IMAGER_KEEP_FILES};
-}
diff --git a/t/t081error.t b/t/t081error.t
deleted file mode 100644 (file)
index 4eacbd0..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#!perl -w
-use strict;
-
-# avoiding this prologue would be nice, but it seems to be unavoidable,
-# see "It is also important to note ..." in perldoc threads
-use Config;
-my $loaded_threads;
-BEGIN {
-  if ($Config{useithreads} && $] > 5.008007) {
-    $loaded_threads =
-      eval {
-       require threads;
-       threads->import;
-       1;
-      };
-  }
-}
-use Test::More;
-
-$Config{useithreads}
-  or plan skip_all => "can't test Imager's threads support with no threads";
-$] > 5.008007
-  or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support";
-$loaded_threads
-  or plan skip_all => "couldn't load threads";
-
-$INC{"Devel/Cover.pm"}
-  and plan skip_all => "threads and Devel::Cover don't get along";
-
-use Imager;
-
-# test that the error contexts are separate under threads
-
-plan tests => 11;
-
-Imager->open_log(log => "testout/t081error.log");
-
-Imager::i_clear_error();
-Imager::i_push_error(0, "main thread a");
-
-my @threads;
-for my $tid (1..5) {
-  my $t1 = threads->create
-    (
-     sub {
-       my $id = shift;
-       Imager::i_push_error(0, "$id: child thread a");
-       sleep(1+rand(4));
-       Imager::i_push_error(1, "$id: child thread b");
-
-       is_deeply([ Imager::i_errors() ],
-                [
-                 [ "$id: child thread b", 1 ],
-                 [ "$id: child thread a", 0 ],
-                ], "$id: check errors in child");
-       1;
-     },
-     $tid
-    );
-  push @threads, [ $tid, $t1 ];
-}
-
-Imager::i_push_error(1, "main thread b");
-
-for my $thread (@threads) {
-  my ($id, $t1) = @$thread;
-  ok($t1->join, "join child $id");
-}
-
-Imager::i_push_error(2, "main thread c");
-
-is_deeply([ Imager::i_errors() ],
-         [
-          [ "main thread c", 2 ],
-          [ "main thread b", 1 ],
-          [ "main thread a", 0 ],
-         ], "check errors in parent");
-
diff --git a/t/t082limit.t b/t/t082limit.t
deleted file mode 100644 (file)
index ac3d841..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-#!perl -w
-use strict;
-
-# avoiding this prologue would be nice, but it seems to be unavoidable,
-# see "It is also important to note ..." in perldoc threads
-use Config;
-my $loaded_threads;
-BEGIN {
-  if ($Config{useithreads} && $] > 5.008007) {
-    $loaded_threads =
-      eval {
-       require threads;
-       threads->import;
-       1;
-      };
-  }
-}
-use Test::More;
-
-$Config{useithreads}
-  or plan skip_all => "can't test Imager's threads support with no threads";
-$] > 5.008007
-  or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support";
-$loaded_threads
-  or plan skip_all => "couldn't load threads";
-
-$INC{"Devel/Cover.pm"}
-  and plan skip_all => "threads and Devel::Cover don't get along";
-
-use Imager;
-
-# test that image file limits are localized to a thread
-
-plan tests => 31;
-
-Imager->open_log(log => "testout/t082limit.log");
-
-ok(Imager->set_file_limits(width => 10, height => 10, bytes => 300),
-   "set limits to 10, 10, 300");
-
-ok(Imager->check_file_limits(width => 10, height => 10),
-   "successful check limits in parent");
-
-ok(!Imager->check_file_limits(width => 10, height => 10, sample_size => 2),
-   "failed check limits in parent");
-
-my @threads;
-for my $tid (1..5) {
-  my $t1 = threads->create
-    (
-     sub {
-       my $id = shift;
-       my $dlimit = $tid * 5;
-       my $blimit = $dlimit * $dlimit * 3;
-       ok(Imager->set_file_limits(width => $dlimit, height => $dlimit,
-                                 bytes => $blimit),
-         "$tid: set limits to $dlimit x $dlimit, $blimit bytes");
-       ok(Imager->check_file_limits(width => $dlimit, height => $dlimit),
-         "$tid: successful check $dlimit x $dlimit");
-       ok(!Imager->check_file_limits(width => $dlimit, height => $dlimit, sample_size => 2),
-         "$tid: failed check $dlimit x $dlimit, ssize 2");
-       is_deeply([ Imager->get_file_limits ], [ $dlimit, $dlimit, $blimit ],
-                "check limits are still $dlimit x $dlimit , $blimit bytes");
-     },
-     $tid
-    );
-  push @threads, [ $tid, $t1 ];
-}
-
-for my $thread (@threads) {
-  my ($id, $t1) = @$thread;
-  ok($t1->join, "join child $id");
-}
-
-ok(Imager->check_file_limits(width => 10, height => 10),
-   "test we still pass");
-ok(!Imager->check_file_limits(width => 10, height => 10, sample_size => 2),
-   "test we still fail");
-is_deeply([ Imager->get_file_limits ], [ 10, 10, 300 ],
-         "check original main thread limits still set");
diff --git a/t/t1000files.t b/t/t1000files.t
deleted file mode 100644 (file)
index 4b7764b..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-#!perl -w
-
-# This file is for testing file functionality that is independent of
-# the file format
-
-use strict;
-use Test::More tests => 89;
-use Imager;
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t1000files.log");
-
-SKIP:
-{
-  # Test that i_test_format_probe() doesn't pollute stdout
-
-  # Initally I tried to write this test using open to redirect files,
-  # but there was a buffering problem that made it so the data wasn't
-  # being written to the output file.  This external perl call avoids
-  # that problem
-
-  my $test_script = 'testout/t1000files_probe.pl';
-
-  # build a temp test script to use
-  ok(open(SCRIPT, "> $test_script"), "open test script")
-    or skip("no test script $test_script: $!", 2);
-  print SCRIPT <<'PERL';
-#!perl
-use Imager;
-use strict;
-my $file = shift or die "No file supplied";
-open FH, "< $file" or die "Cannot open file: $!";
-binmode FH;
-my $io = Imager::io_new_fd(fileno(FH));
-Imager::i_test_format_probe($io, -1);
-PERL
-  close SCRIPT;
-  my $perl = $^X;
-  $perl = qq/"$perl"/ if $perl =~ / /;
-  
-  print "# script: $test_script\n";
-  my $cmd = "$perl -Mblib $test_script t/t1000files.t";
-  print "# command: $cmd\n";
-
-  my $out = `$cmd`;
-  is($?, 0, "command successful");
-  is($out, '', "output should be empty");
-}
-
-# test the file limit functions
-# by default the limits are zero (unlimited)
-print "# image file limits\n";
-is_deeply([ Imager->get_file_limits() ], [0, 0, 0x40000000 ],
-         "check defaults");
-ok(Imager->set_file_limits(width=>100), "set only width");
-is_deeply([ Imager->get_file_limits() ], [100, 0, 0x40000000 ],
-         "check width set");
-ok(Imager->set_file_limits(height=>150, bytes=>10000),
-   "set height and bytes");
-is_deeply([ Imager->get_file_limits() ], [ 100, 150, 10000 ],
-         "check all values now set");
-ok(Imager->check_file_limits(width => 100, height => 30),
-   "check 100 x 30 (def channels, sample_size) ok")
-  or diag(Imager->errstr);
-ok(Imager->check_file_limits(width => 100, height => 100, channels => 1),
-   "check 100 x 100 x 1 (def sample_size) ok")
-  or diag(Imager->errstr);
-ok(Imager->check_file_limits(width => 100, height => 100, channels => 1),
-   "check 100 x 100 x 1 (def sample_size) ok")
-  or diag(Imager->errstr);
-ok(!Imager->check_file_limits(width => 100, height => 100, channels => 1, sample_size => "float"),
-   "check 100 x 100 x 1 x float should fail");
-ok(!Imager->check_file_limits(width => 100, height => 100, channels => 0),
-   "0 channels should fail");
-is(Imager->errstr, "file size limit - channels 0 out of range",
-   "check error message");
-ok(!Imager->check_file_limits(width => 0, height => 100),
-   "0 width should fail");
-is(Imager->errstr, "file size limit - image width of 0 is not positive",
-   "check error message");
-ok(!Imager->check_file_limits(width => 100, height => 0),
-   "0 height should fail");
-is(Imager->errstr, "file size limit - image height of 0 is not positive",
-   "check error message");
-ok(!Imager->check_file_limits(width => 10, height => 10, sample_size => 0),
-   "0 sample_size should fail");
-is(Imager->errstr, "file size limit - sample_size 0 out of range",
-   "check error message");
-ok(!Imager->check_file_limits(width => 10, height => 10, sample_size => 1000),
-   "1000 sample_size should fail");
-is(Imager->errstr, "file size limit - sample_size 1000 out of range",
-   "check error message");
-ok(Imager->set_file_limits(reset=>1, height => 99),
-   "set height and reset");
-is_deeply([ Imager->get_file_limits() ], [ 0, 99, 0x40000000 ],
-         "check only height is set");
-ok(Imager->set_file_limits(reset=>1),
-   "just reset");
-is_deeply([ Imager->get_file_limits() ], [ 0, 0, 0x40000000 ],
-         "check all are reset");
-
-# bad parameters
-is_deeply([ Imager->check_file_limits() ], [],
-         "missing size paramaters");
-is(Imager->errstr, "check_file_limits: width must be defined",
-   "check message");
-is_deeply([ Imager->check_file_limits(width => 100.5) ], [],
-         "non-integer parameter");
-is(Imager->errstr, "check_file_limits: width must be a positive integer",
-   "check message");
-
-# test error handling for loading file handers
-{
-  # first, no module at all
-  {
-    my $data = "abc";
-    ok(!Imager->new(data => $data, filetype => "unknown"),
-       "try to read an unknown file type");
-   like(Imager->errstr, qr(^format 'unknown' not supported - formats .* - Can't locate Imager/File/UNKNOWN.pm or Imager/File/UNKNOWNReader.pm$),
-       "check error message");
-  }
-  {
-    my $data;
-    my $im = Imager->new(xsize => 10, ysize => 10);
-    ok(!$im->write(data => \$data, type => "unknown"),
-       "try to write an unknown file type");
-   like($im->errstr, qr(^format 'unknown' not supported - formats .* - Can't locate Imager/File/UNKNOWN.pm or Imager/File/UNKNOWNWriter.pm$),
-       "check error message");
-  }
-  push @INC, "t/t1000lib";
-  {
-    my $data = "abc";
-    ok(!Imager->new(data => $data, filetype => "bad"),
-       "try to read an bad (other load failure) file type");
-   like(Imager->errstr, qr(^format 'bad' not supported - formats .* available for reading - This module fails to load loading Imager/File/BAD.pm$),
-       "check error message");
-  }
-  {
-    my $data;
-    my $im = Imager->new(xsize => 10, ysize => 10);
-    ok(!$im->write(data => \$data, type => "bad"),
-       "try to write an bad file type");
-   like($im->errstr, qr(^format 'bad' not supported - formats .* available for writing - This module fails to load loading Imager/File/BAD.pm$),
-       "check error message");
-  }
-}
-
-{ # test empty image handling for write()/write_multi()
-  my $empty = Imager->new;
-  my $data;
-  ok(!$empty->write(data => \$data, type => "pnm"),
-     "fail to write an empty image");
-  is($empty->errstr, "write: empty input image", "check error message");
-  my $good = Imager->new(xsize => 1, ysize => 1);
-  ok(!Imager->write_multi({ data => \$data, type => "pnm" }, $good, $empty),
-     "fail to write_multi an empty image");
-  is(Imager->errstr, "write_multi: empty input image (image 2)");
-}
-
-# check file type probe
-probe_ok("49492A41", undef, "not quite tiff");
-probe_ok("4D4D0041", undef, "not quite tiff");
-probe_ok("49492A00", "tiff", "tiff intel");
-probe_ok("4D4D002A", "tiff", "tiff motorola");
-probe_ok("474946383961", "gif", "gif 89");
-probe_ok("474946383761", "gif", "gif 87");
-probe_ok(<<TGA, "tga", "TGA");
-00 00 0A 00 00 00 00 00 00 00 00 00 96 00 96 00
-18 20 FF 00 00 00 95 00 00 00 FF 00 00 00 95 00
-00 00 FF 00 00 00 95 00 00 00 FF 00 00 00 95 00
-00 00 FF 00 00 00 95 00 00 00 FF 00 00 00 95 00
-TGA
-
-probe_ok(<<TGA, "tga", "TGA 32-bit");
-00 00 0A 00 00 00 00 00 00 00 00 00 0A 00 0A 00
-20 08 84 00 00 00 00 84 FF FF FF FF 84 00 00 00
-00 84 FF FF FF FF 84 00 00 00 00 84 FF FF FF FF
-TGA
-
-probe_ok(<<ICO, "ico", "Windows Icon");
-00 00 01 00 02 00 20 20 10 00 00 00 00 00 E8 02
-00 00 26 00 00 00 20 20 00 00 00 00 00 00 A8 08
-00 00 0E 03 00 00 28 00 00 00 20 00 00 00 40 00
-ICO
-
-probe_ok(<<ICO, "cur", "Windows Cursor");
-00 00 02 00 02 00 20 20 10 00 00 00 00 00 E8 02
-00 00 26 00 00 00 20 20 00 00 00 00 00 00 A8 08
-00 00 0E 03 00 00 28 00 00 00 20 00 00 00 40 00
-ICO
-
-probe_ok(<<SGI, "sgi", "SGI RGB");
-01 DA 01 01 00 03 00 96 00 96 00 03 00 00 00 00 
-00 00 00 FF 00 00 00 00 6E 6F 20 6E 61 6D 65 00
-00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-SGI
-
-probe_ok(<<ILBM, "ilbm", "ILBM");
-46 4F 52 4D 00 00 60 7A 49 4C 42 4D 42 4D 48 44
-00 00 00 14 00 96 00 96 00 00 00 00 18 00 01 80
-00 00 0A 0A 00 96 00 96 42 4F 44 59 00 00 60 51
-ILBM
-
-probe_ok(<<XPM, "xpm", "XPM");
-2F 2A 20 58 50 4D 20 2A 2F 0A 73 74 61 74 69 63
-20 63 68 61 72 20 2A 6E 6F 6E 61 6D 65 5B 5D 20
-3D 20 7B 0A 2F 2A 20 77 69 64 74 68 20 68 65 69
-XPM
-
-probe_ok(<<PCX, "pcx", 'PCX');
-0A 05 01 08 00 00 00 00 95 00 95 00 96 00 96 00
-00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
-PCX
-
-probe_ok(<<FITS, "fits", "FITS");
-53 49 4D 50 4C 45 20 20 3D 20 20 20 20 20 20 20 
-20 20 20 20 20 20 20 20 20 20 20 20 20 54 20 20 
-20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 
-FITS
-
-probe_ok(<<PSD, "psd", "Photoshop");
-38 42 50 53 00 01 00 00 00 00 00 00 00 06 00 00
-00 3C 00 00 00 96 00 08 00 03 00 00 00 00 00 00
-0B E6 38 42 49 4D 03 ED 00 00 00 00 00 10 00 90
-PSD
-
-probe_ok(<<EPS, "eps", "Encapsulated Postscript");
-25 21 50 53 2D 41 64 6F 62 65 2D 32 2E 30 20 45
-50 53 46 2D 32 2E 30 0A 25 25 43 72 65 61 74 6F
-72 3A 20 70 6E 6D 74 6F 70 73 0A 25 25 54 69 74
-EPS
-
-probe_ok(<<UTAH, "utah", "Utah RLE");
-52 CC 00 00 00 00 0A 00 0A 00 0A 03 08 00 08 00 
-2F 00 48 49 53 54 4F 52 59 3D 70 6E 6D 74 6F 72 
-6C 65 20 6F 6E 20 54 68 75 20 4D 61 79 20 31 31 
-20 31 36 3A 33 35 3A 34 33 20 32 30 30 36 0A 09 
-UTAH
-
-probe_ok(<<XWD, "xwd", "X Window Dump");
-00 00 00 69 00 00 00 07 00 00 00 02 00 00 00 18
-00 00 01 E4 00 00 01 3C 00 00 00 00 00 00 00 00
-00 00 00 20 00 00 00 00 00 00 00 20 00 00 00 20
-00 00 07 90 00 00 00 04 00 FF 00 00 00 00 FF 00
-XWD
-
-probe_ok(<<GZIP, "gzip", "gzip compressed");
-1F 8B 08 08 C2 81 BD 44 02 03 49 6D 61 67 65 72
-2D 30 2E 35 31 5F 30 33 2E 74 61 72 00 EC 5B 09
-40 53 C7 BA 9E 24 AC 01 D9 44 04 44 08 8B B2 8A
-C9 C9 42 92 56 41 50 20 A0 02 41 41 01 17 48 80
-GZIP
-
-probe_ok(<<BZIP2, "bzip2", "bzip2 compressed");
-42 5A 68 39 31 41 59 26 53 59 0F D8 8C 09 00 03
-28 FF FF FF FF FB 7F FB 77 FF EF BF 6B 7F BE FF
-FF DF EE C8 0F FF F3 FF FF FF FC FF FB B1 FF FB
-F4 07 DF D0 03 B8 03 60 31 82 05 2A 6A 06 83 20
-BZIP2
-
-probe_ok(<<WEBP, "webp", "Google WEBP");
-52 49 46 46 2C 99 00 00 57 45 42 50 56 50 38 20
-20 99 00 00 70 7A 02 9D 01 2A E0 01 80 02 00 87
-08 85 85 88 85 84 88 88 83 AF E2 F7 64 1F 98 55
-1B 6A 70 F5 8A 45 09 95 0C 09 7E 25 D9 2E 46 44
-07 84 FB 01 FD 2C 8A 2F 97 CC ED DB 50 0F 11 3B
-WEBP
-
-probe_ok(<<JPEG2K, "jp2", "JPEG 2000");
-00 00 00 0C 6A 50 20 20 0D 0A 87 0A 00 00 00 14
-66 74 79 70 6A 70 32 20 00 00 00 00 6A 70 32 20
-00 00 00 2D 6A 70 32 68 00 00 00 16 69 68 64 72
-00 00 02 80 00 00 01 E0 00 03 07 07 00 00 00 00
-00 0F 63 6F 6C 72 01 00 00 00 00 00 10 00 00 00
-00 6A 70 32 63 FF 4F FF 51 00 2F 00 00 00 00 01
-JPEG2K
-
-{ # RT 72475
-  # check error messages from read/read_multi
-  my $data = "nothing useful";
-  my @mult_data = Imager->read_multi(data => $data);
-  is(@mult_data, 0, "read_multi with non-image input data should fail");
-  is(Imager->errstr,
-     "type parameter missing and it couldn't be determined from the file contents",
-     "check the error message");
-
-  my @mult_file = Imager->read_multi(file => "t/t1000files.t");
-  is(@mult_file, 0, "read_multi with non-image filename should fail");
-  is(Imager->errstr,
-     "type parameter missing and it couldn't be determined from the file contents or file name",
-     "check the error message");
-
-  my $im = Imager->new;
-  ok(!$im->read(data => $data), "read from non-image data should fail");
-  is($im->errstr,
-     "type parameter missing and it couldn't be determined from the file contents",
-     "check the error message");
-
-  ok(!$im->read(file => "t/t1000files.t"),
-     "read from non-image file should fail");
-  is($im->errstr,
-     "type parameter missing and it couldn't be determined from the file contents or file name",
-     "check the error message");
-}
-
-{
-  # test def_guess_type
-  my @tests =
-    (
-     pnm => "pnm",
-     GIF => "gif",
-     tif => "tiff",
-     TIFF => "tiff",
-     JPG => "jpeg",
-     rle => "utah",
-     bmp => "bmp",
-     dib => "bmp",
-     rgb => "sgi",
-     BW => "sgi",
-     TGA => "tga",
-     CUR => "cur",
-     ico => "ico",
-     ILBM => "ilbm",
-     pcx => "pcx",
-     psd => "psd",
-    );
-
-  while (my ($ext, $expect) = splice(@tests, 0, 2)) {
-    my $filename = "foo.$ext";
-    is(Imager::def_guess_type($filename), $expect,
-       "type for $filename should be $expect");
-  }
-}
-
-Imager->close_log;
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t1000files.log";
-}
-
-sub probe_ok {
-  my ($packed, $exp_type, $name) = @_;
-
-  my $builder = Test::Builder->new;
-  $packed =~ tr/ \r\n//d; # remove whitespace used for layout
-  my $data = pack("H*", $packed);
-
-  my $io = Imager::io_new_buffer($data);
-  my $result = Imager::i_test_format_probe($io, -1);
-
-  return $builder->is_eq($result, $exp_type, $name)
-}
diff --git a/t/t101nojpeg.t b/t/t101nojpeg.t
deleted file mode 100644 (file)
index 582d5bf..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!perl -w
-use strict;
-use Test::More;
-use Imager qw(:all);
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t101jpeg.log");
-
-$Imager::formats{"jpeg"}
-  and plan skip_all => "have jpeg support - this tests the lack of it";
-
-plan tests => 6;
-
-my $im = Imager->new;
-ok(!$im->read(file=>"testimg/base.jpg"), "should fail to read jpeg");
-cmp_ok($im->errstr, '=~', qr/format 'jpeg' not supported/, "check no jpeg message");
-$im = Imager->new(xsize=>2, ysize=>2);
-ok(!$im->write(file=>"testout/nojpeg.jpg"), "should fail to write jpeg");
-cmp_ok($im->errstr, '=~', qr/format 'jpeg' not supported/, "check no jpeg message");
-ok(!grep($_ eq 'jpeg', Imager->read_types), "check jpeg not in read types");
-ok(!grep($_ eq 'jpeg', Imager->write_types), "check jpeg not in write types");
-
-Imager->close_log;
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t101jpeg.log";
-}
diff --git a/t/t102nopng.t b/t/t102nopng.t
deleted file mode 100644 (file)
index 3509582..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!perl -w
-use strict;
-use Imager qw(:all);
-use Test::More;
-
-$Imager::formats{"png"}
-  and plan skip_all => "png available, and this tests the lack of it";
-
-plan tests => 6;
-
-my $im = Imager->new;
-ok(!$im->read(file=>"testimg/test.png"), "should fail to read png");
-cmp_ok($im->errstr, '=~', "format 'png' not supported", "check no png message");
-$im = Imager->new(xsize=>2, ysize=>2);
-ok(!$im->write(file=>"testout/nopng.png"), "should fail to write png");
-cmp_ok($im->errstr, '=~', "format 'png' not supported", "check no png message");
-ok(!grep($_ eq 'png', Imager->read_types), "check png not in read types");
-ok(!grep($_ eq 'png', Imager->write_types), "check png not in write types");
-
diff --git a/t/t103raw.t b/t/t103raw.t
deleted file mode 100644 (file)
index 52ab850..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 53;
-use Imager qw(:all);
-use Imager::Test qw/is_color3 is_color4 test_image test_image_mono/;
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t103raw.log");
-
-$| = 1;
-
-my $green=i_color_new(0,255,0,255);
-my $blue=i_color_new(0,0,255,255);
-my $red=i_color_new(255,0,0,255);
-
-my $img=Imager::ImgRaw::new(150,150,3);
-my $cmpimg=Imager::ImgRaw::new(150,150,3);
-
-i_box_filled($img,70,25,130,125,$green);
-i_box_filled($img,20,25,80,125,$blue);
-i_arc($img,75,75,30,0,361,$red);
-i_conv($img,[0.1, 0.2, 0.4, 0.2, 0.1]);
-
-my $timg = Imager::ImgRaw::new(20, 20, 4);
-my $trans = i_color_new(255, 0, 0, 127);
-i_box_filled($timg, 0, 0, 20, 20, $green);
-i_box_filled($timg, 2, 2, 18, 18, $trans);
-
-open(FH,">testout/t103.raw") || die "Cannot open testout/t103.raw for writing\n";
-binmode(FH);
-my $IO = Imager::io_new_fd( fileno(FH) );
-ok(i_writeraw_wiol($img, $IO), "write raw low") or
-  print "# Cannot write testout/t103.raw\n";
-close(FH);
-
-open(FH,"testout/t103.raw") || die "Cannot open testout/t103.raw\n";
-binmode(FH);
-$IO = Imager::io_new_fd( fileno(FH) );
-$cmpimg = i_readraw_wiol($IO, 150, 150, 3, 3, 0);
-ok($cmpimg, "read raw low")
-  or print "# Cannot read testout/t103.raw\n";
-close(FH);
-
-print "# raw average mean square pixel difference: ",sqrt(i_img_diff($img,$cmpimg))/150*150,"\n";
-
-# I could have kept the raw images for these tests in binary files in
-# testimg/, but I think keeping them as hex encoded data in here makes
-# it simpler to add more if necessary
-# Later we may change this to read from a scalar instead
-save_data('testout/t103_base.raw');
-save_data('testout/t103_3to4.raw');
-save_data('testout/t103_line_int.raw');
-save_data('testout/t103_img_int.raw');
-
-# load the base image
-open FH, "testout/t103_base.raw" 
-  or die "Cannot open testout/t103_base.raw: $!";
-binmode FH;
-$IO = Imager::io_new_fd( fileno(FH) );
-
-my $baseimg = i_readraw_wiol( $IO, 4, 4, 3, 3, 0);
-ok($baseimg, "read base raw image")
-  or die "Cannot read base raw image";
-close FH;
-
-# the actual read tests
-# each read_test() call does 2 tests:
-#  - check if the read succeeds
-#  - check if it matches $baseimg
-read_test('testout/t103_3to4.raw', 4, 4, 4, 3, 0, $baseimg);
-read_test('testout/t103_line_int.raw', 4, 4, 3, 3, 1, $baseimg);
-# intrl==2 is documented in raw.c but doesn't seem to be implemented
-#read_test('testout/t103_img_int.raw', 4, 4, 3, 3, 2, $baseimg, 7);
-
-# paletted images
-SKIP:
-{
-  my $palim = Imager::i_img_pal_new(20, 20, 3, 256);
-  ok($palim, "make paletted image")
-    or skip("couldn't make paletted image", 2);
-  my $redindex = Imager::i_addcolors($palim, $red);
-  my $blueindex = Imager::i_addcolors($palim, $blue);
-  for my $y (0..9) {
-    Imager::i_ppal($palim, 0, $y, ($redindex) x 20);
-  }
-  for my $y (10..19) {
-    Imager::i_ppal($palim, 0, $y, ($blueindex) x 20);
-  }
-  open FH, "> testout/t103_pal.raw"
-    or die "Cannot create testout/t103_pal.raw: $!";
-  binmode FH;
-  $IO = Imager::io_new_fd(fileno(FH));
-  ok(i_writeraw_wiol($palim, $IO), "write low paletted");
-  close FH;
-  
-  open FH, "testout/t103_pal.raw"
-    or die "Cannot open testout/t103_pal.raw: $!";
-  binmode FH;
-  my $data = do { local $/; <FH> };
-  is($data, "\x0" x 200 . "\x1" x 200, "compare paletted data written");
-  close FH;
-}
-
-# 16-bit image
-# we don't have 16-bit reads yet
-SKIP:
-{
-  my $img16 = Imager::i_img_16_new(150, 150, 3);
-  ok($img16, "make 16-bit/sample image")
-    or skip("couldn't make 16 bit/sample image", 1);
-  i_box_filled($img16,70,25,130,125,$green);
-  i_box_filled($img16,20,25,80,125,$blue);
-  i_arc($img16,75,75,30,0,361,$red);
-  i_conv($img16,[0.1, 0.2, 0.4, 0.2, 0.1]);
-  
-  open FH, "> testout/t103_16.raw" 
-    or die "Cannot create testout/t103_16.raw: $!";
-  binmode FH;
-  $IO = Imager::io_new_fd(fileno(FH));
-  ok(i_writeraw_wiol($img16, $IO), "write low 16 bit image");
-  close FH;
-}
-
-# try a simple virtual image
-SKIP:
-{
-  my $maskimg = Imager::i_img_masked_new($img, undef, 0, 0, 150, 150);
-  ok($maskimg, "make masked image")
-    or skip("couldn't make masked image", 3);
-
-  open FH, "> testout/t103_virt.raw" 
-    or die "Cannot create testout/t103_virt.raw: $!";
-  binmode FH;
-  $IO = Imager::io_new_fd(fileno(FH));
-  ok(i_writeraw_wiol($maskimg, $IO), "write virtual raw");
-  close FH;
-
-  open FH, "testout/t103_virt.raw"
-    or die "Cannot open testout/t103_virt.raw: $!";
-  binmode FH;
-  $IO = Imager::io_new_fd(fileno(FH));
-  my $cmpimgmask = i_readraw_wiol($IO, 150, 150, 3, 3, 0);
-  ok($cmpimgmask, "read result of masked write");
-  my $diff = i_img_diff($maskimg, $cmpimgmask);
-  print "# difference for virtual image $diff\n";
-  is($diff, 0, "compare masked to read");
-
-  # check that i_format is set correctly
-  my $index = Imager::i_tags_find($cmpimgmask, 'i_format', 0);
-  if ($index) {
-    my $value = Imager::i_tags_get($cmpimgmask, $index);
-    is($value, 'raw', "check i_format value");
-  }
-  else {
-    fail("couldn't find i_format tag");
-  }
-}
-
-{ # error handling checks
-  # should get an error writing to a open for read file
-  # make a empty file
-  open RAW, "> testout/t103_empty.raw"
-    or die "Cannot create testout/t103_empty.raw: $!";
-  close RAW;
-  open RAW, "< testout/t103_empty.raw"
-    or die "Cannot open testout/t103_empty.raw: $!";
-  my $im = Imager->new(xsize => 50, ysize=>50);
-  ok(!$im->write(fh => \*RAW, type => 'raw', buffered => 0),
-     "write to open for read handle");
-  cmp_ok($im->errstr, '=~', '^Could not write to file: write\(\) failure', 
-        "check error message");
-  close RAW;
-
-  # should get an error reading an empty file
-  ok(!$im->read(file => 'testout/t103_empty.raw', xsize => 50, ysize=>50, type=>'raw', interleave => 1),
-     'read an empty file');
-  is($im->errstr, 'premature end of file', "check message");
- SKIP:
-  {
-    # see 862083f7e40bc2a9e3b94aedce56c1336e7bdb25 in perl5 git
-    $] >= 5.010
-      or skip "5.8.x and earlier don't treat a read on a WRONLY file as an error", 2;
-    open RAW, "> testout/t103_empty.raw"
-      or die "Cannot create testout/t103_empty.raw: $!";
-    ok(!$im->read(fh => \*RAW, , xsize => 50, ysize=>50, type=>'raw', interleave => 1),
-       'read a file open for write');
-    cmp_ok($im->errstr, '=~', '^error reading file: read\(\) failure', "check message");
-  }
-}
-
-
-{
-  ok(grep($_ eq 'raw', Imager->read_types), "check raw in read types");
-  ok(grep($_ eq 'raw', Imager->write_types), "check raw in write types");
-}
-
-
-{ # OO no interleave warning
-  my $im = Imager->new;
-  my $msg;
-  local $SIG{__WARN__} = sub { $msg = "@_" };
-  ok($im->read(file => "testout/t103_line_int.raw", xsize => 4, ysize => 4,
-              type => "raw"),
-     "read without interleave parameter")
-    or print "# ", $im->errstr, "\n";
-  ok($msg, "should have warned");
-  like($msg, qr/interleave/, "check warning is ok");
-  # check we got the right value
-  is_color3($im->getpixel(x => 0, y => 0), 0x00, 0x11, 0x22,
-           "check the image was read correctly");
-
-  # check no warning if either is supplied
-  $im = Imager->new;
-  undef $msg;
-  ok($im->read(file => "testout/t103_base.raw", xsize => 4, ysize => 4, type => "raw", interleave => 0), 
-     "read with interleave 0");
-  is($msg, undef, "no warning");
-  is_color3($im->getpixel(x => 0, y => 0), 0x00, 0x11, 0x22,
-           "check read non-interleave");
-
-  $im = Imager->new;
-  undef $msg;
-  ok($im->read(file => "testout/t103_base.raw", xsize => 4, ysize => 4, type => "raw", raw_interleave => 0), 
-     "read with raw_interleave 0");
-  is($msg, undef, "no warning");
-  is_color3($im->getpixel(x => 1, y => 0), 0x01, 0x12, 0x23,
-           "check read non-interleave");
-
-  # make sure set to 1 is sane
-  $im = Imager->new;
-  undef $msg;
-  ok($im->read(file => "testout/t103_line_int.raw", xsize => 4, ysize => 4, type => "raw", raw_interleave => 1), 
-     "read with raw_interleave 1");
-  is($msg, undef, "no warning");
-  is_color3($im->getpixel(x => 2, y => 0), 0x02, 0x13, 0x24,
-           "check read interleave = 1");
-}
-
-{ # invalid interleave error handling
-  my $im = Imager->new;
-  ok(!$im->read(file => "testout/t103_base.raw", raw_interleave => 2, type => "raw", xsize => 4, ysize => 4),
-     "invalid interleave");
-  is($im->errstr, "raw_interleave must be 0 or 1", "check message");
-}
-
-{ # store/data channel behaviour
-  my $im = Imager->new;
-  ok($im->read(file => "testout/t103_3to4.raw", xsize => 4, ysize => 4, 
-              raw_datachannels => 4, raw_interleave => 0, type => "raw"),
-     "read 4 channel file as 3 channels")
-    or print "# ", $im->errstr, "\n";
-  is_color3($im->getpixel(x => 2, y => 1), 0x12, 0x23, 0x34,
-           "check read correctly");
-}
-
-{ # should fail to read with storechannels > 4
-  my $im = Imager->new;
-  ok(!$im->read(file => "testout/t103_line_int.raw", type => "raw",
-               raw_interleave => 1, xsize => 4, ysize => 4,
-               raw_storechannels => 5),
-     "read with large storechannels");
-  is($im->errstr, "raw_storechannels must be between 1 and 4", 
-     "check error message");
-}
-
-{ # should zero spare channels if storechannels > datachannels
-  my $im = Imager->new;
-  ok($im->read(file => "testout/t103_base.raw", type => "raw",
-               raw_interleave => 0, xsize => 4, ysize => 4,
-               raw_storechannels => 4),
-     "read with storechannels > datachannels");
-  is($im->getchannels, 4, "should have 4 channels");
-  is_color4($im->getpixel(x => 2, y => 1), 0x12, 0x23, 0x34, 0x00,
-           "check last channel zeroed");
-}
-
-{
-  my @ims = ( basic => test_image(), mono => test_image_mono() );
-  push @ims, masked => test_image()->masked();
-
-  my $fail_close = sub {
-    Imager::i_push_error(0, "synthetic close failure");
-    return 0;
-  };
-
-  while (my ($type, $im) = splice(@ims, 0, 2)) {
-    my $io = Imager::io_new_cb(sub { 1 }, undef, undef, $fail_close);
-    ok(!$im->write(io => $io, type => "raw"),
-       "write $type image with a failing close handler");
-    like($im->errstr, qr/synthetic close failure/,
-        "check error message");
-  }
-}
-
-Imager->close_log;
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t103raw.log";
-  unlink(qw(testout/t103_base.raw testout/t103_3to4.raw
-           testout/t103_line_int.raw testout/t103_img_int.raw))
-}
-
-sub read_test {
-  my ($in, $xsize, $ysize, $data, $store, $intrl, $base) = @_;
-  open FH, $in or die "Cannot open $in: $!";
-  binmode FH;
-  my $IO = Imager::io_new_fd( fileno(FH) );
-
-  my $img = i_readraw_wiol($IO, $xsize, $ysize, $data, $store, $intrl);
- SKIP:
-  {
-    ok($img, "read_test $in read")
-      or skip("couldn't read $in", 1);
-    is(i_img_diff($img, $baseimg), 0, "read_test $in compare");
-  }
-}
-
-sub save_data {
-  my $outname = shift;
-  my $data = load_data();
-  open FH, "> $outname" or die "Cannot create $outname: $!";
-  binmode FH;
-  print FH $data;
-  close FH;
-}
-
-sub load_data {
-  my $hex = '';
-  while (<DATA>) {
-    next if /^#/;
-    last if /^EOF/;
-    chomp;
-    $hex .= $_;
-  }
-  $hex =~ tr/ //d;
-  my $result = pack("H*", $hex);
-  #print unpack("H*", $result),"\n";
-  return $result;
-}
-
-# FIXME: may need tests for 1,2,4 channel images
-
-__DATA__
-# we keep some packed raw images here
-# we decode this in the code, ignoring lines starting with #, a subfile
-# ends with EOF, data is HEX encoded (spaces ignored)
-
-# basic 3 channel version of the image
-001122 011223 021324 031425
-102132 112233 122334 132435
-203142 213243 223344 233445
-304152 314253 324354 334455
-EOF
-
-# test image for reading a 4 channel image into a 3 channel image
-# 4 x 4 pixels
-00112233 01122334 02132435 03142536
-10213243 11223344 12233445 13243546
-20314253 21324354 22334455 23344556
-30415263 31425364 32435465 33445566
-EOF
-
-# test image for line based interlacing
-# 4 x 4 pixels
-# first line
-00 01 02 03
-11 12 13 14
-22 23 24 25
-
-# second line
-10 11 12 13
-21 22 23 24
-32 33 34 35
-
-# third line
-20 21 22 23
-31 32 33 34
-42 43 44 45
-
-# fourth line
-30 31 32 33
-41 42 43 44
-52 53 54 55
-
-EOF
-
-# test image for image based interlacing
-# first channel
-00 01 02 03
-10 11 12 13
-20 21 22 23
-30 31 32 33
-
-# second channel
-11 12 13 14
-21 22 23 24
-31 32 33 34
-41 42 43 44
-
-# third channel
-22 23 24 25
-32 33 34 35
-42 43 44 45
-52 53 54 55
-
-EOF
diff --git a/t/t104ppm.t b/t/t104ppm.t
deleted file mode 100644 (file)
index c85d7a9..0000000
+++ /dev/null
@@ -1,661 +0,0 @@
-#!perl -w
-use Imager ':all';
-use Test::More tests => 205;
-use strict;
-use Imager::Test qw(test_image_raw test_image_16 is_color3 is_color1 is_image test_image_named);
-
-$| = 1;
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t104ppm.log");
-
-my $green = i_color_new(0,255,0,255);
-my $blue  = i_color_new(0,0,255,255);
-my $red   = i_color_new(255,0,0,255);
-
-my @files;
-
-my $img    = test_image_raw();
-
-my $fh = openimage(">testout/t104.ppm");
-push @files, "t104.ppm";
-my $IO = Imager::io_new_fd(fileno($fh));
-ok(i_writeppm_wiol($img, $IO), "write pnm low")
-  or die "Cannot write testout/t104.ppm\n";
-close($fh);
-
-$IO = Imager::io_new_bufchain();
-ok(i_writeppm_wiol($img, $IO), "write to bufchain")
-  or die "Cannot write to bufchain";
-my $data = Imager::io_slurp($IO);
-
-$fh = openimage("testout/t104.ppm");
-$IO = Imager::io_new_fd( fileno($fh) );
-my $cmpimg = i_readpnm_wiol($IO,-1);
-ok($cmpimg, "read image we wrote")
-  or die "Cannot read testout/t104.ppm\n";
-close($fh);
-
-is(i_img_diff($img, $cmpimg), 0, "compare written and read images");
-
-my $rdata = slurp("testout/t104.ppm");
-is($data, $rdata, "check data read from file and bufchain data");
-
-# build a grayscale image
-my $gimg = Imager::ImgRaw::new(150, 150, 1);
-my $gray = i_color_new(128, 0, 0, 255);
-my $dgray = i_color_new(64, 0, 0, 255);
-my $white = i_color_new(255, 0, 0, 255);
-i_box_filled($gimg, 20, 20, 130, 130, $gray);
-i_box_filled($gimg, 40, 40, 110, 110, $dgray);
-i_arc($gimg, 75, 75, 30, 0, 361, $white);
-
-push @files, "t104_gray.pgm";
-open FH, "> testout/t104_gray.pgm" or die "Cannot create testout/t104_gray.pgm: $!\n";
-binmode FH;
-$IO = Imager::io_new_fd(fileno(FH));
-ok(i_writeppm_wiol($gimg, $IO), "write grayscale");
-close FH;
-
-open FH, "< testout/t104_gray.pgm" or die "Cannot open testout/t104_gray.pgm: $!\n";
-binmode FH;
-$IO = Imager::io_new_fd(fileno(FH));
-my $gcmpimg = i_readpnm_wiol($IO, -1);
-ok($gcmpimg, "read grayscale");
-is(i_img_diff($gimg, $gcmpimg), 0, 
-   "compare written and read greyscale images");
-
-my $ooim = Imager->new;
-ok($ooim->read(file=>"testimg/simple.pbm"), "read simple pbm, via OO")
-  or print "# ", $ooim->errstr, "\n";
-
-check_gray(Imager::i_get_pixel($ooim->{IMG}, 0, 0), 0);
-check_gray(Imager::i_get_pixel($ooim->{IMG}, 0, 1), 255);
-check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 0), 255);
-check_gray(Imager::i_get_pixel($ooim->{IMG}, 1, 1), 0);
-is($ooim->type, 'paletted', "check pbm read as paletted");
-is($ooim->tags(name=>'pnm_type'), 1, "check pnm_type tag");
-
-{
-  # https://rt.cpan.org/Ticket/Display.html?id=7465
-  # the pnm reader ignores the maxval that it reads from the pnm file
-  my $maxval = Imager->new;
-  ok($maxval->read(file=>"testimg/maxval.ppm"),
-     "read testimg/maxval.ppm");
-  
-  # this image contains three pixels, with each sample from 0 to 63
-  # the pixels are (63, 63, 63), (32, 32, 32) and (31, 31, 0)
-  
-  # check basic parameters
-  is($maxval->getchannels, 3, "channel count");
-  is($maxval->getwidth, 3, "width");
-  is($maxval->getheight, 1, "height");
-  
-  # check the pixels
-  ok(my ($white, $grey, $green) = $maxval->getpixel('x'=>[0,1,2], 'y'=>[0,0,0]), "fetch pixels");
-  is_color3($white, 255, 255, 255, "white pixel");
-  is_color3($grey,  130, 130, 130, "grey  pixel");
-  is_color3($green, 125, 125, 0,   "green pixel");
-  is($maxval->tags(name=>'pnm_type'), 6, "check pnm_type tag on maxval");
-
-  # and do the same for ASCII images
-  my $maxval_asc = Imager->new;
-  ok($maxval_asc->read(file=>"testimg/maxval_asc.ppm"),
-     "read testimg/maxval_asc.ppm");
-  
-  # this image contains three pixels, with each sample from 0 to 63
-  # the pixels are (63, 63, 63), (32, 32, 32) and (31, 31, 0)
-  
-  # check basic parameters
-  is($maxval_asc->getchannels, 3, "channel count");
-  is($maxval_asc->getwidth, 3, "width");
-  is($maxval_asc->getheight, 1, "height");
-
-  is($maxval->tags(name=>'pnm_type'), 6, "check pnm_type tag on maxval");
-  
-  # check the pixels
-  ok(my ($white_asc, $grey_asc, $green_asc) = $maxval_asc->getpixel('x'=>[0,1,2], 'y'=>[0,0,0]), "fetch pixels");
-  is_color3($white_asc, 255, 255, 255, "white asc pixel");
-  is_color3($grey_asc,  130, 130, 130, "grey  asc pixel");
-  is_color3($green_asc, 125, 125, 0,   "green asc pixel");
-}
-
-{ # previously we didn't validate maxval at all, make sure it's
-  # validated now
-  my $maxval0 = Imager->new;
-  ok(!$maxval0->read(file=>'testimg/maxval_0.ppm'),
-     "should fail to read maxval 0 image");
-  print "# ", $maxval0->errstr, "\n";
-  like($maxval0->errstr, qr/maxval is zero - invalid pnm file/,
-       "error expected from reading maxval_0.ppm");
-
-  my $maxval65536 = Imager->new;
-  ok(!$maxval65536->read(file=>'testimg/maxval_65536.ppm'),
-     "should fail reading maxval 65536 image");
-  print "# ",$maxval65536->errstr, "\n";
-  like($maxval65536->errstr, qr/maxval of 65536 is over 65535 - invalid pnm file/,
-       "error expected from reading maxval_65536.ppm");
-
-  # maxval of 256 is valid, and handled as of 0.56
-  my $maxval256 = Imager->new;
-  ok($maxval256->read(file=>'testimg/maxval_256.ppm'),
-     "should succeed reading maxval 256 image");
-  is_color3($maxval256->getpixel(x => 0, 'y' => 0),
-            0, 0, 0, "check black in maxval_256");
-  is_color3($maxval256->getpixel(x => 0, 'y' => 1),
-            255, 255, 255, "check white in maxval_256");
-  is($maxval256->bits, 16, "check bits/sample on maxval 256");
-
-  # make sure we handle maxval > 255 for ascii
-  my $maxval4095asc = Imager->new;
-  ok($maxval4095asc->read(file=>'testimg/maxval_4095_asc.ppm'),
-     "read maxval_4095_asc.ppm");
-  is($maxval4095asc->getchannels, 3, "channels");
-  is($maxval4095asc->getwidth, 3, "width");
-  is($maxval4095asc->getheight, 1, "height");
-  is($maxval4095asc->bits, 16, "check bits/sample on maxval 4095");
-
-  ok(my ($white, $grey, $green) = $maxval4095asc->getpixel('x'=>[0,1,2], 'y'=>[0,0,0]), "fetch pixels");
-  is_color3($white, 255, 255, 255, "white 4095 pixel");
-  is_color3($grey,  128, 128, 128, "grey  4095 pixel");
-  is_color3($green, 127, 127, 0,   "green 4095 pixel");
-}
-
-{ # check i_format is set when reading a pnm file
-  # doesn't really matter which file.
-  my $maxval = Imager->new;
-  ok($maxval->read(file=>"testimg/maxval.ppm"),
-      "read test file");
-  my ($type) = $maxval->tags(name=>'i_format');
-  is($type, 'pnm', "check i_format");
-}
-
-{ # check file limits are checked
-  my $limit_file = "testout/t104.ppm";
-  ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
-  my $im = Imager->new;
-  ok(!$im->read(file=>$limit_file),
-     "should fail read due to size limits");
-  print "# ",$im->errstr,"\n";
-  like($im->errstr, qr/image width/, "check message");
-
-  ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
-  ok(!$im->read(file=>$limit_file),
-     "should fail read due to size limits");
-  print "# ",$im->errstr,"\n";
-  like($im->errstr, qr/image height/, "check message");
-
-  ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
-  ok($im->read(file=>$limit_file),
-     "should succeed - just inside width limit");
-  ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
-  ok($im->read(file=>$limit_file),
-     "should succeed - just inside height limit");
-  
-  # 150 x 150 x 3 channel image uses 67500 bytes
-  ok(Imager->set_file_limits(reset=>1, bytes=>67499),
-     "set bytes limit 67499");
-  ok(!$im->read(file=>$limit_file),
-     "should fail - too many bytes");
-  print "# ",$im->errstr,"\n";
-  like($im->errstr, qr/storage size/, "check error message");
-  ok(Imager->set_file_limits(reset=>1, bytes=>67500),
-     "set bytes limit 67500");
-  ok($im->read(file=>$limit_file),
-     "should succeed - just inside bytes limit");
-  Imager->set_file_limits(reset=>1);
-}
-
-{
-  # check we correctly sync with the data stream
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/pgm.pgm', type => 'pnm'),
-     "read pgm.pgm")
-    or print "# cannot read pgm.pgm: ", $im->errstr, "\n";
-  print "# ", $im->getsamples('y' => 0), "\n";
-  is_color1($im->getpixel(x=>0, 'y' => 0), 254, "check top left");
-}
-
-{ # check error messages set correctly
-  my $im = Imager->new;
-  ok(!$im->read(file=>'t/t104ppm.t', type=>'pnm'),
-     'should fail to read script as an image file');
-  is($im->errstr, 'unable to read pnm image: bad header magic, not a PNM file',
-     "check error message");
-}
-
-{
-  # RT #30074
-  # give 4/2 channel images a background color when saving to pnm
-  my $im = Imager->new(xsize=>16, ysize=>16, channels=>4);
-  $im->box(filled => 1, xmin => 8, color => '#FFE0C0');
-  $im->box(filled => 1, color => NC(0, 192, 192, 128),
-          ymin => 8, xmax => 7);
-  push @files, "t104_alpha.ppm";
-  ok($im->write(file=>"testout/t104_alpha.ppm", type=>'pnm'),
-     "should succeed writing 4 channel image");
-  my $imread = Imager->new;
-  ok($imread->read(file => 'testout/t104_alpha.ppm'), "read it back")
-    or print "# ", $imread->errstr, "\n";
-  is_color3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, 
-           "check transparent became black");
-  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
-           "check color came through");
-  is_color3($imread->getpixel('x' => 0, 'y' => 15), 0, 96, 96,
-           "check translucent came through");
-  my $data;
-  ok($im->write(data => \$data, type => 'pnm', i_background => '#FF0000'),
-     "write with red background");
-  ok($imread->read(data => $data, type => 'pnm'),
-     "read it back");
-  is_color3($imread->getpixel('x' => 0, 'y' => 0), 255, 0, 0, 
-           "check transparent became red");
-  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
-           "check color came through");
-  is_color3($imread->getpixel('x' => 0, 'y' => 15), 127, 96, 96,
-           "check translucent came through");
-}
-
-{
-  # more RT #30074 - 16 bit images
-  my $im = Imager->new(xsize=>16, ysize=>16, channels=>4, bits => 16);
-  $im->box(filled => 1, xmin => 8, color => '#FFE0C0');
-  $im->box(filled => 1, color => NC(0, 192, 192, 128),
-          ymin => 8, xmax => 7);
-  push @files, "t104_alp16.ppm";
-  ok($im->write(file=>"testout/t104_alp16.ppm", type=>'pnm', 
-               pnm_write_wide_data => 1),
-     "should succeed writing 4 channel image");
-  my $imread = Imager->new;
-  ok($imread->read(file => 'testout/t104_alp16.ppm'), "read it back");
-  is($imread->bits, 16, "check we did produce a 16 bit image");
-  is_color3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, 
-           "check transparent became black");
-  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
-           "check color came through");
-  is_color3($imread->getpixel('x' => 0, 'y' => 15), 0, 96, 96,
-           "check translucent came through");
-  my $data;
-  ok($im->write(data => \$data, type => 'pnm', i_background => '#FF0000',
-               pnm_write_wide_data => 1),
-     "write with red background");
-  ok($imread->read(data => $data, type => 'pnm'),
-     "read it back");
-  is($imread->bits, 16, "check it's 16-bit");
-  is_color3($imread->getpixel('x' => 0, 'y' => 0), 255, 0, 0, 
-           "check transparent became red");
-  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
-           "check color came through");
-  is_color3($imread->getpixel('x' => 0, 'y' => 15), 127, 96, 96,
-           "check translucent came through");
-}
-
-# various bad input files
-print "# check error handling\n";
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/short_bin.ppm', type=>'pnm'),
-     "fail to read short bin ppm");
-  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/short_bin16.ppm', type=>'pnm'),
-     "fail to read short bin ppm (maxval 65535)");
-  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/short_bin.pgm', type=>'pnm'),
-     "fail to read short bin pgm");
-  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/short_bin16.pgm', type=>'pnm'),
-     "fail to read short bin pgm (maxval 65535)");
-  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/short_bin.pbm', type => 'pnm'),
-     "fail to read a short bin pbm");
-  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/short_asc.ppm', type => 'pnm'),
-     "fail to read a short asc ppm");
-  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/short_asc.pgm', type => 'pnm'),
-     "fail to read a short asc pgm");
-  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/short_asc.pbm', type => 'pnm'),
-     "fail to read a short asc pbm");
-  cmp_ok($im->errstr, '=~', 'short read - file truncated', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/bad_asc.ppm', type => 'pnm'),
-     "fail to read a bad asc ppm");
-  cmp_ok($im->errstr, '=~', 'invalid data for ascii pnm', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/bad_asc.pgm', type => 'pnm'),
-     "fail to read a bad asc pgm");
-  cmp_ok($im->errstr, '=~', 'invalid data for ascii pnm', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok(!$im->read(file => 'testimg/bad_asc.pbm', type => 'pnm'),
-     "fail to read a bad asc pbm");
-  cmp_ok($im->errstr, '=~', 'invalid data for ascii pnm', 
-         "check error message");
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/short_bin.ppm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read bin ppm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/short_bin16.ppm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read bin16 ppm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-  is($im->bits, 16, "check correct bits");
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/short_bin.pgm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read bin pgm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/short_bin16.pgm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read bin16 pgm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/short_bin.pbm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read bin pbm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/short_asc.ppm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read asc ppm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/short_asc.pgm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read asc pgm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/short_asc.pbm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read asc pbm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-}
-
-{
-  my @imgs = Imager->read_multi(file => 'testimg/multiple.ppm');
-  is( 0+@imgs, 3, "Read 3 images");
-  is( $imgs[0]->tags( name => 'pnm_type' ), 1, "Image 1 is type 1" );
-  is( $imgs[0]->getwidth, 2, " ... width=2" );
-  is( $imgs[0]->getheight, 2, " ... width=2" );
-  is( $imgs[1]->tags( name => 'pnm_type' ), 6, "Image 2 is type 6" );
-  is( $imgs[1]->getwidth, 164, " ... width=164" );
-  is( $imgs[1]->getheight, 180, " ... width=180" );
-  is( $imgs[2]->tags( name => 'pnm_type' ), 5, "Image 3 is type 5" );
-  is( $imgs[2]->getwidth, 2, " ... width=2" );
-  is( $imgs[2]->getheight, 2, " ... width=2" );
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/bad_asc.ppm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read bad asc ppm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/bad_asc.pgm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read bad asc pgm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-}
-
-{
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/bad_asc.pbm', type => 'pnm',
-                allow_incomplete => 1),
-     "partial read bad asc pbm");
-  is($im->tags(name => 'i_incomplete'), 1, "partial flag set");
-  is($im->tags(name => 'i_lines_read'), 1, "lines_read set");
-}
-
-{
-  print "# monochrome output\n";
-  my $im = Imager->new(xsize => 10, ysize => 10, channels => 1, type => 'paletted');
-  ok($im->addcolors(colors => [ '#000000', '#FFFFFF' ]),
-     "add black and white");
-  $im->box(filled => 1, xmax => 4, color => '#000000');
-  $im->box(filled => 1, xmin => 5, color => '#FFFFFF');
-  is($im->type, 'paletted', 'mono still paletted');
-  push @files, "t104_mono.pbm";
-  ok($im->write(file => 'testout/t104_mono.pbm', type => 'pnm'),
-     "save as pbm");
-
-  # check it
-  my $imread = Imager->new;
-  ok($imread->read(file => 'testout/t104_mono.pbm', type=>'pnm'),
-     "read it back in")
-    or print "# ", $imread->errstr, "\n";
-  is($imread->type, 'paletted', "check result is paletted");
-  is($imread->tags(name => 'pnm_type'), 4, "check type");
-  is_image($im, $imread, "check image matches");
-}
-
-{
-  print "# monochrome output - reversed palette\n";
-  my $im = Imager->new(xsize => 10, ysize => 10, channels => 1, type => 'paletted');
-  ok($im->addcolors(colors => [ '#FFFFFF', '#000000' ]),
-     "add white and black");
-  $im->box(filled => 1, xmax => 4, color => '#000000');
-  $im->box(filled => 1, xmin => 5, color => '#FFFFFF');
-  is($im->type, 'paletted', 'mono still paletted');
-  push @files, "t104_mono2.pbm";
-  ok($im->write(file => 'testout/t104_mono2.pbm', type => 'pnm'),
-     "save as pbm");
-
-  # check it
-  my $imread = Imager->new;
-  ok($imread->read(file => 'testout/t104_mono2.pbm', type=>'pnm'),
-     "read it back in")
-    or print "# ", $imread->errstr, "\n";
-  is($imread->type, 'paletted', "check result is paletted");
-  is($imread->tags(name => 'pnm_type'), 4, "check type");
-  is_image($im, $imread, "check image matches");
-}
-
-{
-  print "# 16-bit output\n";
-  my $data;
-  my $im = test_image_16();
-  
-  # without tag, it should do 8-bit output
-  ok($im->write(data => \$data, type => 'pnm'),
-     "write 16-bit image as 8-bit/sample ppm");
-  my $im8 = Imager->new;
-  ok($im8->read(data => $data), "read it back");
-  is($im8->tags(name => 'pnm_maxval'), 255, "check maxval");
-  is_image($im, $im8, "check image matches");
-
-  # try 16-bit output
-  $im->settag(name => 'pnm_write_wide_data', value => 1);
-  $data = '';
-  ok($im->write(data => \$data, type => 'pnm'),
-     "write 16-bit image as 16-bit/sample ppm");
-  push @files, "t104_16.ppm";
-  $im->write(file=>'testout/t104_16.ppm');
-  my $im16 = Imager->new;
-  ok($im16->read(data => $data), "read it back");
-  is($im16->tags(name => 'pnm_maxval'), 65535, "check maxval");
-  push @files, "t104_16b.ppm";
-  $im16->write(file=>'testout/t104_16b.ppm');
-  is_image($im, $im16, "check image matches");
-}
-
-{
-  ok(grep($_ eq 'pnm', Imager->read_types), "check pnm in read types");
-  ok(grep($_ eq 'pnm', Imager->write_types), "check pnm in write types");
-}
-
-{ # test new() loading an image
-  my $im = Imager->new(file => "testimg/penguin-base.ppm");
-  ok($im, "received an image");
-  is($im->getwidth, 164, "check width matches image");
-
-  # fail to load an image
-  my $im2 = Imager->new(file => "Imager.pm", filetype => "pnm");
-  ok(!$im2, "no image when file failed to load");
-  cmp_ok(Imager->errstr, '=~', "bad header magic, not a PNM file",
-        "check error message transferred");
-
-  # load from data
- SKIP:
-  {
-    ok(open(FH, "< testimg/penguin-base.ppm"), "open test file")
-      or skip("couldn't open data source", 4);
-    binmode FH;
-    my $imdata = do { local $/; <FH> };
-    close FH;
-    ok(length $imdata, "we got the data");
-    my $im3 = Imager->new(data => $imdata);
-    ok($im3, "read the file data");
-    is($im3->getwidth, 164, "check width matches image");
-  }
-}
-
-{ # image too large handling
-  {
-    ok(!Imager->new(file => "testimg/toowide.ppm", filetype => "pnm"),
-       "fail to read a too wide image");
-    is(Imager->errstr, "unable to read pnm image: could not read image width: integer overflow",
-       "check error message");
-  }
-  {
-    ok(!Imager->new(file => "testimg/tootall.ppm", filetype => "pnm"),
-       "fail to read a too wide image");
-    is(Imager->errstr, "unable to read pnm image: could not read image height: integer overflow",
-       "check error message");
-  }
-}
-
-{ # make sure close is checked for each image type
-  my $fail_close = sub {
-    Imager::i_push_error(0, "synthetic close failure");
-    return 0;
-  };
-
-  for my $type (qw(basic basic16 gray gray16 mono)) {
-    my $im = test_image_named($type);
-    my $io = Imager::io_new_cb(sub { 1 }, undef, undef, $fail_close);
-    ok(!$im->write(io => $io, type => "pnm"),
-       "write $type image with a failing close handler");
-    like($im->errstr, qr/synthetic close failure/,
-        "check error message");
-  }
-}
-
-Imager->close_log;
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t104ppm.log";
-  unlink map "testout/$_", @files;
-}
-
-sub openimage {
-  my $fname = shift;
-  local(*FH);
-  open(FH, $fname) or die "Cannot open $fname: $!\n";
-  binmode(FH);
-  return *FH;
-}
-
-sub slurp {
-  my $fh = openimage(shift);
-  local $/;
-  my $data = <$fh>;
-  close($fh);
-  return $data;
-}
-
-sub check_gray {
-  my ($c, $gray) = @_;
-
-  my ($g) = $c->rgba;
-  is($g, $gray, "compare gray");
-}
-
diff --git a/t/t105nogif.t b/t/t105nogif.t
deleted file mode 100644 (file)
index bf053e8..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#!perl -w
-use strict;
-$|=1;
-use Test::More;
-use Imager qw(:all);
-
-$Imager::formats{"gif"}
-  and plan skip_all => "gif support available and this tests the lack of it";
-
-plan tests => 12;
-
-my $im = Imager->new;
-ok(!$im->read(file=>"GIF/testimg/scale.gif"), "should fail to read gif");
-cmp_ok($im->errstr, '=~', "format 'gif' not supported",
-       "check no gif message");
-ok(!Imager->read_multi(file=>"GIF/testimg/scale.gif"), 
-   "should fail to read multi gif");
-cmp_ok($im->errstr, '=~', "format 'gif' not supported",
-       "check no gif message");
-
-$im = Imager->new(xsize=>2, ysize=>2);
-
-ok(!$im->write(file=>"testout/nogif.gif"), "should fail to write gif");
-ok(!-e "testout/nogif.gif", "shouldn't create the file");
-cmp_ok($im->errstr, '=~', "format 'gif' not supported",
-       "check no gif message");
-
-ok(!Imager->write_multi({file => "testout/nogif.gif"}, $im, $im),
-   "should fail to write multi gif");
-ok(!-e "testout/nogif.gif", "shouldn't create the file");
-cmp_ok($im->errstr, '=~', "format 'gif' not supported",
-       "check no gif message");
-
-ok(!grep($_ eq 'gif', Imager->read_types), "check gif not in read types");
-ok(!grep($_ eq 'gif', Imager->write_types), "check gif not in write types");
diff --git a/t/t106notiff.t b/t/t106notiff.t
deleted file mode 100644 (file)
index 0854640..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!perl -w
-use strict;
-use Test::More;
-use Imager qw(:all);
-
-$Imager::formats{"tiff"}
-  and plan skip_all => "tiff support available - this tests the lack of it";
-
-plan tests => 12;
-
-my $im = Imager->new;
-
-ok(!$im->read(file=>"TIFF/testimg/comp4.tif"), "should fail to read tif");
-cmp_ok($im->errstr, '=~', "format 'tiff' not supported",
-       "check no tiff message");
-
-ok(!$im->read_multi(file => "TIFF/testimg/comp4.tif"),
-   "should fail to read multi tiff");
-cmp_ok($im->errstr, '=~', "format 'tiff' not supported",
-       "check no tiff message");
-
-$im = Imager->new(xsize=>2, ysize=>2);
-
-ok(!$im->write(file=>"testout/notiff.tif"), "should fail to write tiff");
-cmp_ok($im->errstr, '=~', "format 'tiff' not supported",
-       "check no tiff message");
-ok(!-e "testout/notiff.tif", "file shouldn't be created");
-
-ok(!Imager->write_multi({file=>"testout/notiff.tif"}, $im, $im),
-   "should fail to write multi tiff");
-cmp_ok($im->errstr, '=~', "format 'tiff' not supported",
-       "check no tiff message");
-ok(!-e "testout/notiff.tif", "file shouldn't be created");
-
-ok(!grep($_ eq 'tiff', Imager->read_types), "check tiff not in read types");
-ok(!grep($_ eq 'tiff', Imager->write_types), "check tiff not in write types");
diff --git a/t/t107bmp.t b/t/t107bmp.t
deleted file mode 100644 (file)
index 356d776..0000000
+++ /dev/null
@@ -1,798 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 215;
-use Imager qw(:all);
-use Imager::Test qw(test_image_raw is_image is_color3 test_image);
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t107bmp.log");
-
-my @files;
-my $debug_writes = 0;
-
-my $base_diff = 0;
-# if you change this make sure you generate new compressed versions
-my $green=i_color_new(0,255,0,255);
-my $blue=i_color_new(0,0,255,255);
-my $red=i_color_new(255,0,0,255);
-
-my $img = test_image_raw();
-
-Imager::i_tags_add($img, 'i_xres', 0, '300', 0);
-Imager::i_tags_add($img, 'i_yres', 0, undef, 300);
-write_test($img, "testout/t107_24bit.bmp");
-push @files, "t107_24bit.bmp";
-# 'webmap' is noticably faster than the default
-my $im8 = Imager::i_img_to_pal($img, { make_colors=>'webmap', 
-                                      translate=>'errdiff'});
-write_test($im8, "testout/t107_8bit.bmp");
-push @files, "t107_8bit.bmp";
-# use a fixed palette so we get reproducible results for the compressed
-# version
-my @pal16 = map { NC($_) } 
-  qw(605844 966600 0148b2 00f800 bf0a33 5e009e
-     2ead1b 0000f8 004b01 fd0000 0e1695 000002);
-my $im4 = Imager::i_img_to_pal($img, { colors=>\@pal16, make_colors=>'none' });
-write_test($im4, "testout/t107_4bit.bmp");
-push @files, "t107_4bit.bmp";
-my $im1 = Imager::i_img_to_pal($img, { colors=>[ NC(0, 0, 0), NC(176, 160, 144) ],
-                              make_colors=>'none', translate=>'errdiff' });
-write_test($im1, "testout/t107_1bit.bmp");
-push @files, "t107_1bit.bmp";
-my $bi_rgb = 0;
-my $bi_rle8 = 1;
-my $bi_rle4 = 2;
-my $bi_bitfields = 3;
-read_test("testout/t107_24bit.bmp", $img, 
-          bmp_compression=>0, bmp_bit_count => 24);
-read_test("testout/t107_8bit.bmp", $im8, 
-          bmp_compression=>0, bmp_bit_count => 8);
-read_test("testout/t107_4bit.bmp", $im4, 
-          bmp_compression=>0, bmp_bit_count => 4);
-read_test("testout/t107_1bit.bmp", $im1, bmp_compression=>0, 
-          bmp_bit_count=>1);
-# the following might have slight differences
-$base_diff = i_img_diff($img, $im8) * 2;
-print "# base difference $base_diff\n";
-read_test("testimg/comp4.bmp", $im4, 
-          bmp_compression=>$bi_rle4, bmp_bit_count => 4);
-read_test("testimg/comp8.bmp", $im8, 
-          bmp_compression=>$bi_rle8, bmp_bit_count => 8);
-
-my $imoo = Imager->new;
-# read via OO
-ok($imoo->read(file=>'testout/t107_24bit.bmp'), "read via OO")
-  or print "# ",$imoo->errstr,"\n";
-
-ok($imoo->write(file=>'testout/t107_oo.bmp'), "write via OO")
-  or print "# ",$imoo->errstr,"\n";
-push @files, "t107_oo.bmp";
-
-# various invalid format tests
-# we have so many different test images to try to detect all the possible
-# failure paths in the code, adding these did detect real problems
-print "# catch various types of invalid bmp files\n";
-my @tests =
-  (
-   # entries in each array ref are:
-   #  - basename of an invalid BMP file
-   #  - error message that should be produced
-   #  - description of what is being tested
-   #  - possible flag to indicate testing only on 32-bit machines
-   [ 'badplanes.bmp', 'not a BMP file', "invalid planes value" ],
-   [ 'badbits.bmp', 'unknown bit count for BMP file (5)', 
-     'should fail to read invalid bits' ],
-
-   # 1-bit/pixel BMPs
-   [ 'badused1.bmp', 'out of range colors used (3)',
-     'out of range palette size (1-bit)' ],
-   [ 'badcomp1.bmp', 'unknown 1-bit BMP compression (1)',
-     'invalid compression value (1-bit)' ],
-   [ 'bad1wid0.bmp', 'file size limit - image width of 0 is not positive',
-     'width 0 (1-bit)' ],
-   [ 'bad4oflow.bmp', 
-     'file size limit - integer overflow calculating storage',
-     'overflow integers on 32-bit machines (1-bit)', '32bitonly' ],
-   [ 'short1.bmp', 'failed reading 1-bit bmp data', 
-     'short 1-bit' ],
-
-   # 4-bit/pixel BMPs
-   [ 'badused4a.bmp', 'out of range colors used (272)', 
-     'should fail to read invalid pal size (272) (4-bit)' ],
-   [ 'badused4b.bmp', 'out of range colors used (17)',
-     'should fail to read invalid pal size (17) (4-bit)' ],
-   [ 'badcomp4.bmp', 'unknown 4-bit BMP compression (1)',
-     'invalid compression value (4-bit)' ],
-   [ 'short4.bmp', 'failed reading 4-bit bmp data', 
-     'short uncompressed 4-bit' ],
-   [ 'short4rle.bmp', 'missing data during decompression', 
-     'short compressed 4-bit' ],
-   [ 'bad4wid0.bmp', 'file size limit - image width of 0 is not positive',
-     'width 0 (4-bit)' ],
-   [ 'bad4widbig.bmp', 'file size limit - image width of -2147483628 is not positive',
-     'width big (4-bit)' ],
-   [ 'bad4oflow.bmp', 'file size limit - integer overflow calculating storage',
-     'overflow integers on 32-bit machines (4-bit)', '32bitonly' ],
-
-   # 8-bit/pixel BMPs
-   [ 'bad8useda.bmp', 'out of range colors used (257)',
-     'should fail to read invalid pal size (8-bit)' ],
-   [ 'bad8comp.bmp', 'unknown 8-bit BMP compression (2)',
-     'invalid compression value (8-bit)' ],
-   [ 'short8.bmp', 'failed reading 8-bit bmp data', 
-     'short uncompressed 8-bit' ],
-   [ 'short8rle.bmp', 'missing data during decompression', 
-     'short compressed 8-bit' ],
-   [ 'bad8wid0.bmp', 'file size limit - image width of 0 is not positive',
-     'width 0 (8-bit)' ],
-   [ 'bad8oflow.bmp', 'file size limit - integer overflow calculating storage',
-     'overflow integers on 32-bit machines (8-bit)', '32bitonly' ],
-
-   # 24-bit/pixel BMPs
-   [ 'short24.bmp', 'failed reading image data',
-     'short 24-bit' ],
-   [ 'bad24wid0.bmp', 'file size limit - image width of 0 is not positive',
-     'width 0 (24-bit)' ],
-   [ 'bad24oflow.bmp', 'file size limit - integer overflow calculating storage',
-     'overflow integers on 32-bit machines (24-bit)', '32bitonly' ],
-   [ 'bad24comp.bmp', 'unknown 24-bit BMP compression (4)',
-     'bad compression (24-bit)' ],
-  );
-use Config;
-my $ptrsize = $Config{ptrsize};
-for my $test (@tests) {
-  my ($file, $error, $comment, $bit32only) = @$test;
- SKIP:
-  {
-    skip("only tested on 32-bit machines", 2)
-      if $bit32only && $ptrsize != 4;
-    ok(!$imoo->read(file=>"testimg/$file"), $comment);
-    print "# ", $imoo->errstr, "\n";
-    is($imoo->errstr, $error, "check error message");
-  }
-}
-
-# previously we didn't seek to the offbits position before reading
-# the image data, check we handle it correctly
-# in each case the first is an original image with a given number of
-# bits and the second is the same file with data inserted before the
-# image bits and the offset modified to suit
-my @comp =
-  (
-   [ 'winrgb2.bmp', 'winrgb2off.bmp', 1 ],
-   [ 'winrgb4.bmp', 'winrgb4off.bmp', 4 ],
-   [ 'winrgb8.bmp', 'winrgb8off.bmp', 8 ],
-   [ 'winrgb24.bmp', 'winrgb24off.bmp', 24 ],
-  );
-
-for my $comp (@comp) {
-  my ($base_file, $off_file, $bits) = @$comp;
-
-  my $base_im = Imager->new;
-  my $got_base = 
-    ok($base_im->read(file=>"testimg/$base_file"),
-        "read original")
-      or print "# ",$base_im->errstr,"\n";
-  my $off_im = Imager->new;
-  my $got_off =
-    ok($off_im->read(file=>"testimg/$off_file"),
-        "read offset file")
-      or print "# ",$off_im->errstr,"\n";
- SKIP:
-  {
-    skip("missed one file", 1)
-      unless $got_base && $got_off;
-    is(i_img_diff($base_im->{IMG}, $off_im->{IMG}), 0,
-        "compare base and offset image ($bits bits)");
-  }
-}
-
-{ # check file limits are checked
-  my $limit_file = "testout/t107_24bit.bmp";
-  ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
-  my $im = Imager->new;
-  ok(!$im->read(file=>$limit_file),
-     "should fail read due to size limits");
-  print "# ",$im->errstr,"\n";
-  like($im->errstr, qr/image width/, "check message");
-
-  ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
-  ok(!$im->read(file=>$limit_file),
-     "should fail read due to size limits");
-  print "# ",$im->errstr,"\n";
-  like($im->errstr, qr/image height/, "check message");
-
-  ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
-  ok($im->read(file=>$limit_file),
-     "should succeed - just inside width limit");
-  ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
-  ok($im->read(file=>$limit_file),
-     "should succeed - just inside height limit");
-  
-  # 150 x 150 x 3 channel image uses 67500 bytes
-  ok(Imager->set_file_limits(reset=>1, bytes=>67499),
-     "set bytes limit 67499");
-  ok(!$im->read(file=>$limit_file),
-     "should fail - too many bytes");
-  print "# ",$im->errstr,"\n";
-  like($im->errstr, qr/storage size/, "check error message");
-  ok(Imager->set_file_limits(reset=>1, bytes=>67500),
-     "set bytes limit 67500");
-  ok($im->read(file=>$limit_file),
-     "should succeed - just inside bytes limit");
-  Imager->set_file_limits(reset=>1);
-}
-
-{ # various short read failure tests, each entry has:
-  # source filename, size, expected error
-  # these have been selected based on code coverage, to check each
-  # failure path is checked, where practical
-  my @tests =
-    (
-     [ 
-      "file truncated inside header",
-      "winrgb2.bmp", 
-      20, "file too short to be a BMP file"
-     ],
-     [
-      "1-bit, truncated inside palette",
-      "winrgb2.bmp", 
-      56, "reading BMP palette" 
-     ],
-     [ 
-      "1-bit, truncated in offset region",
-      "winrgb2off.bmp", 64, "failed skipping to image data offset" 
-     ],
-     [ 
-      "1-bit, truncated in image data",
-      "winrgb2.bmp", 96, "failed reading 1-bit bmp data"
-     ],
-     [
-      "4-bit, truncated inside palette",
-      "winrgb4.bmp",
-      56, "reading BMP palette"
-     ],
-     [
-      "4-bit, truncated in offset region",
-      "winrgb4off.bmp", 120, "failed skipping to image data offset",
-     ],
-     [
-      "4-bit, truncate in image data",
-      "winrgb4.bmp", 120, "failed reading 4-bit bmp data"
-     ],
-     [
-      "4-bit RLE, truncate in uncompressed data",
-      "comp4.bmp", 0x229, "missing data during decompression"
-     ],
-     [
-      "8-bit, truncated in palette",
-      "winrgb8.bmp", 1060, "reading BMP palette"
-      ],
-     [
-      "8-bit, truncated in offset region",
-      "winrgb8off.bmp", 1080, "failed skipping to image data offset"
-     ],
-     [
-      "8-bit, truncated in image data",
-      "winrgb8.bmp", 1080, "failed reading 8-bit bmp data"
-     ],
-     [
-      "8-bit RLE, truncate in uncompressed data",
-      "comp8.bmp", 0x68C, "missing data during decompression"
-     ],
-     [
-      "24-bit, truncate in offset region",
-      "winrgb24off.bmp", 56, "failed skipping to image data offset",
-     ],
-     [
-      "24-bit, truncate in image data",
-      "winrgb24.bmp", 100, "failed reading image data",
-     ],
-    );
-
-  my $test_index = 0;
-  for my $test (@tests) {
-    my ($desc, $srcfile, $size, $error) = @$test;
-    my $im = Imager->new;
-    open IMDATA, "< testimg/$srcfile"
-      or die "$test_index - $desc: Cannot open testimg/$srcfile: $!";
-    binmode IMDATA;
-    my $data;
-    read(IMDATA, $data, $size) == $size
-      or die "$test_index - $desc: Could not read $size data from $srcfile";
-    close IMDATA;
-    ok(!$im->read(data => $data, type =>'bmp'),
-       "$test_index - $desc: Should fail to read");
-    is($im->errstr, $error, "$test_index - $desc: check message");
-    ++$test_index;
-  }
-}
-
-{ # various short read success tests, each entry has:
-  # source filename, size, expected tags
-  print "# allow_incomplete tests\n";
-  my @tests =
-    (
-     [ 
-      "1-bit",
-      "winrgb2.bmp", 96,
-      {
-       bmp_compression_name => 'BI_RGB',
-       bmp_compression => 0,
-       bmp_used_colors => 2,
-       i_lines_read => 8,
-      },
-     ],
-     [
-      "4-bit",
-      "winrgb4.bmp", 250,
-      {
-       bmp_compression_name => 'BI_RGB',
-       bmp_compression => 0,
-       bmp_used_colors => 16,
-       i_lines_read => 11,
-      },
-     ],
-     [
-      "4-bit RLE - uncompressed seq",
-      "comp4.bmp", 0x229, 
-      {
-       bmp_compression_name => 'BI_RLE4',
-       bmp_compression => 2,
-       bmp_used_colors => 16,
-       i_lines_read => 44,
-      },
-     ],
-     [
-      "4-bit RLE - start seq",
-      "comp4.bmp", 0x97, 
-      {
-       bmp_compression_name => 'BI_RLE4',
-       bmp_compression => 2,
-       bmp_used_colors => 16,
-       i_lines_read => 8,
-      },
-     ],
-     [
-      "8-bit",
-      "winrgb8.bmp", 1250,
-      {
-       bmp_compression_name => 'BI_RGB',
-       bmp_compression => 0,
-       bmp_used_colors => 256,
-       i_lines_read => 8,
-      },
-     ],
-     [
-      "8-bit RLE - uncompressed seq",
-      "comp8.bmp", 0x68C, 
-      {
-       bmp_compression_name => 'BI_RLE8',
-       bmp_compression => 1,
-       bmp_used_colors => 256,
-       i_lines_read => 27,
-      },
-     ],
-     [
-      "8-bit RLE - initial seq",
-      "comp8.bmp", 0x487, 
-      {
-       bmp_compression_name => 'BI_RLE8',
-       bmp_compression => 1,
-       bmp_used_colors => 256,
-       i_lines_read => 20,
-      },
-     ],
-     [
-      "24-bit",
-      "winrgb24.bmp", 800,
-      {
-       bmp_compression_name => 'BI_RGB',
-       bmp_compression => 0,
-       bmp_used_colors => 0,
-       i_lines_read => 12,
-      },
-     ],
-    );
-
-  my $test_index = 0;
-  for my $test (@tests) {
-    my ($desc, $srcfile, $size, $tags) = @$test;
-    my $im = Imager->new;
-    open IMDATA, "< testimg/$srcfile"
-      or die "$test_index - $desc: Cannot open testimg/$srcfile: $!";
-    binmode IMDATA;
-    my $data;
-    read(IMDATA, $data, $size) == $size
-      or die "$test_index - $desc: Could not read $size data from $srcfile";
-    close IMDATA;
-    ok($im->read(data => $data, type =>'bmp', allow_incomplete => 1),
-       "$test_index - $desc: Should read successfully");
-    # check standard tags are set
-    is($im->tags(name => 'i_format'), 'bmp',
-       "$test_index - $desc: i_format set");
-    is($im->tags(name => 'i_incomplete'), 1, 
-       "$test_index - $desc: i_incomplete set");
-    my %check_tags;
-    for my $key (keys %$tags) {
-      $check_tags{$key} = $im->tags(name => $key);
-    }
-    is_deeply(\%check_tags, $tags, "$test_index - $desc: check tags");
-    ++$test_index;
-  }
-}
-
-{ # check handling of reading images with negative height
-  # each entry is:
-  # source file, description
-  print "# check handling of negative height values\n";
-  my @tests =
-    (
-     [ "winrgb2.bmp", "1-bit, uncompressed" ],
-     [ "winrgb4.bmp", "4-bit, uncompressed" ],
-     [ "winrgb8.bmp", "8-bit, uncompressed" ],
-     [ "winrgb24.bmp", "24-bit, uncompressed" ],
-     [ "comp4.bmp", "4-bit, RLE" ],
-     [ "comp8.bmp", "8-bit, RLE" ],
-    );
-  my $test_index = 0;
-  for my $test (@tests) {
-    my ($file, $desc) = @$test;
-    open IMDATA, "< testimg/$file"
-      or die "$test_index - Cannot open $file: $!";
-    binmode IMDATA;
-    my $data = do { local $/; <IMDATA> };
-    close IMDATA;
-    my $im_orig = Imager->new;
-    $im_orig->read(data => $data)
-      or die "Cannot load original $file: ", $im_orig->errstr;
-    
-    # now negate the height
-    my $orig_height = unpack("V", substr($data, 0x16, 4));
-    my $neg_height = 0xFFFFFFFF & ~($orig_height - 1);
-    substr($data, 0x16, 4) = pack("V", $neg_height);
-
-    # and read the modified image
-    my $im = Imager->new;
-    ok($im->read(data => $data),
-       "$test_index - $desc: read negated height image")
-      or print "# ", $im->errstr, "\n";
-
-    # flip orig to match what we should get
-    $im_orig->flip(dir => 'v');
-
-    # check it out
-    is_image($im, $im_orig, "$test_index - $desc: check image");
-
-    ++$test_index;
-  }
-}
-
-{ print "# patched data read failure tests\n";
-  # like the "various invalid format" tests, these generate fail
-  # images from other images included with Imager without providing a
-  # full bmp source, saving on dist size and focusing on the changes needed
-  # to cause the failure
-  # each entry is: source file, patches, expected error, description
-  
-  my @tests =
-    (
-     # low image data offsets
-     [ 
-      "winrgb2.bmp", 
-      { 10 => "3d 00 00 00" }, 
-      "image data offset too small (61)",
-      "1-bit, small image offset"
-     ],
-     [ 
-      "winrgb4.bmp", 
-      { 10 => "75 00 00 00" }, 
-      "image data offset too small (117)",
-      "4-bit, small image offset"
-     ],
-     [ 
-      "winrgb8.bmp", 
-      { 10 => "35 04 00 00" }, 
-      "image data offset too small (1077)",
-      "8-bit, small image offset"
-     ],
-     [ 
-      "winrgb24.bmp", 
-      { 10 => "35 00 00 00" }, 
-      "image data offset too small (53)",
-      "24-bit, small image offset"
-     ],
-     # compression issues
-     [
-      "comp8.bmp",
-      { 0x436 => "97" },
-      "invalid data during decompression",
-      "8bit, RLE run beyond edge of image"
-     ],
-     [
-      # caused glibc malloc or valgrind to complain
-      "comp8.bmp",
-      { 0x436 => "94 00 00 03" },
-      "invalid data during decompression",
-      "8bit, literal run beyond edge of image"
-     ],
-     [
-      "comp4.bmp",
-      { 0x76 => "FF bb FF BB" },
-      "invalid data during decompression",
-      "4bit - RLE run beyond edge of image"
-     ],
-     [
-      "comp4.bmp",
-      { 0x76 => "94 bb 00 FF" },
-      "invalid data during decompression",
-      "4bit - literal run beyond edge of image"
-     ],
-    );
-  my $test_index = 0;
-  for my $test (@tests) {
-    my ($filename, $patches, $error, $desc) = @$test;
-
-    my $data = load_patched_file("testimg/$filename", $patches);
-    my $im = Imager->new;
-    ok(!$im->read(data => $data, type=>'bmp'),
-       "$test_index - $desc:should fail to read");
-    is($im->errstr, $error, "$test_index - $desc:check message");
-    ++$test_index;
-  }
-}
-
-{ # various write failure tests
-  # each entry is:
-  # source, limit, expected error, description
-  my @tests =
-    (
-     [ 
-      "winrgb2.bmp", 1, 
-      "cannot write bmp header: limit reached",
-      "1-bit, writing header" 
-     ],
-     [ 
-      "winrgb4.bmp", 1, 
-      "cannot write bmp header: limit reached",
-      "4-bit, writing header" 
-     ],
-     [ 
-      "winrgb8.bmp", 1, 
-      "cannot write bmp header: limit reached",
-      "8-bit, writing header" 
-     ],
-     [ 
-      "winrgb24.bmp", 1, 
-      "cannot write bmp header: limit reached",
-      "24-bit, writing header" 
-     ],
-     [ 
-      "winrgb2.bmp", 0x38, 
-      "cannot write palette entry: limit reached",
-      "1-bit, writing palette" 
-     ],
-     [ 
-      "winrgb4.bmp", 0x38, 
-      "cannot write palette entry: limit reached",
-      "4-bit, writing palette" 
-     ],
-     [ 
-      "winrgb8.bmp", 0x38, 
-      "cannot write palette entry: limit reached",
-      "8-bit, writing palette" 
-     ],
-     [ 
-      "winrgb2.bmp", 0x40, 
-      "writing 1 bit/pixel packed data: limit reached",
-      "1-bit, writing image data" 
-     ],
-     [ 
-      "winrgb4.bmp", 0x80, 
-      "writing 4 bit/pixel packed data: limit reached",
-      "4-bit, writing image data" 
-     ],
-     [ 
-      "winrgb8.bmp", 0x440, 
-      "writing 8 bit/pixel packed data: limit reached",
-      "8-bit, writing image data" 
-     ],
-     [ 
-      "winrgb24.bmp", 0x39, 
-      "writing image data: limit reached",
-      "24-bit, writing image data" 
-     ],
-    );
-  print "# write failure tests\n";
-  my $test_index = 0;
-  for my $test (@tests) {
-    my ($file, $limit, $error, $desc) = @$test;
-
-    my $im = Imager->new;
-    $im->read(file => "testimg/$file")
-      or die "Cannot read $file: ", $im->errstr;
-
-    my $io = Imager::io_new_cb(limited_write($limit), undef, undef, undef, 1);
-    $io->set_buffered(0);
-    print "# writing with limit of $limit\n";
-    ok(!$im->write(type => 'bmp', io => $io),
-       "$test_index - $desc: write should fail");
-    is($im->errstr, $error, "$test_index - $desc: check error message");
-
-    ++$test_index;
-  }
-}
-
-{
-  ok(grep($_ eq 'bmp', Imager->read_types), "check bmp in read types");
-  ok(grep($_ eq 'bmp', Imager->write_types), "check bmp in write types");
-}
-
-{
-  # RT #30075
-  # give 4/2 channel images a background color when saving to BMP
-  my $im = Imager->new(xsize=>16, ysize=>16, channels=>4);
-  $im->box(filled => 1, xmin => 8, color => '#FFE0C0');
-  $im->box(filled => 1, color => NC(0, 192, 192, 128),
-          ymin => 8, xmax => 7);
-  ok($im->write(file=>"testout/t107_alpha.bmp", type=>'bmp'),
-     "should succeed writing 4 channel image");
-  push @files, "t107_alpha.bmp";
-  my $imread = Imager->new;
-  ok($imread->read(file => 'testout/t107_alpha.bmp'), "read it back");
-  is_color3($imread->getpixel('x' => 0, 'y' => 0), 0, 0, 0, 
-           "check transparent became black");
-  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
-           "check color came through");
-  is_color3($imread->getpixel('x' => 0, 'y' => 15), 0, 96, 96,
-           "check translucent came through");
-  my $data;
-  ok($im->write(data => \$data, type => 'bmp', i_background => '#FF0000'),
-     "write with red background");
-  ok($imread->read(data => $data, type => 'bmp'),
-     "read it back");
-  is_color3($imread->getpixel('x' => 0, 'y' => 0), 255, 0, 0, 
-           "check transparent became red");
-  is_color3($imread->getpixel('x' => 8, 'y' => 0), 255, 224, 192,
-           "check color came through");
-  is_color3($imread->getpixel('x' => 0, 'y' => 15), 127, 96, 96,
-           "check translucent came through");
-}
-
-{ # RT 41406
-  my $data;
-  my $im = test_image();
-  ok($im->write(data => \$data, type => 'bmp'), "write using OO");
-  my $size = unpack("V", substr($data, 34, 4));
-  is($size, 67800, "check data size");
-}
-
-{ # check close failures are handled correctly
-  my $im = test_image();
-  my $fail_close = sub {
-    Imager::i_push_error(0, "synthetic close failure");
-    return 0;
-  };
-  ok(!$im->write(type => "bmp", callback => sub { 1 },
-                closecb => $fail_close),
-     "check failing close fails");
-    like($im->errstr, qr/synthetic close failure/,
-        "check error message");
-}
-
-Imager->close_log;
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink map "testout/$_", @files;
-  unlink "testout/t107bmp.log";
-}
-
-sub write_test {
-  my ($im, $filename) = @_;
-  local *FH;
-
-  if (open FH, "> $filename") {
-    binmode FH;
-    my $IO = Imager::io_new_fd(fileno(FH));
-    unless (ok(Imager::i_writebmp_wiol($im, $IO), $filename)) {
-      print "# ",Imager->_error_as_msg(),"\n";
-    }
-    undef $IO;
-    close FH;
-  }
-  else {
-    fail("could not open $filename: $!");
-  }
-}
-
-sub read_test {
-  my ($filename, $im, %tags) = @_;
-  local *FH;
-  
-  print "# read_test: $filename\n";
-
-  $tags{i_format} = "bmp";
-
-  if (open FH, "< $filename") {
-    binmode FH;
-    my $IO = Imager::io_new_fd(fileno(FH));
-    my $im_read = Imager::i_readbmp_wiol($IO);
-    if ($im_read) {
-      my $diff = i_img_diff($im, $im_read);
-      if ($diff > $base_diff) {
-       fail("image mismatch reading $filename");
-      }
-      else {
-        my $tags_ok = 1;
-        for my $tag (keys %tags) {
-          if (my $index = Imager::i_tags_find($im_read, $tag, 0)) {
-            my ($name, $value) = Imager::i_tags_get($im_read, $index);
-            my $exp_value = $tags{$tag};
-            print "#   tag $name = '$value' - expect '$exp_value'\n";
-            if ($exp_value =~ /\d/) {
-              if ($value != $tags{$tag}) {
-                print "# tag $tag value mismatch $tags{$tag} != $value\n";
-                $tags_ok = 0;
-              }
-            }
-            else {
-              if ($value ne $tags{$tag}) {
-                print "# tag $tag value mismatch $tags{$tag} != $value\n";
-                $tags_ok = 0;
-              }
-            }
-          }
-        }
-        ok($tags_ok, "reading $filename");
-        #  for my $i (0 .. Imager::i_tags_count($im_read)-1) {
-        #    my ($name, $value) = Imager::i_tags_get($im_read, $i);
-        #    print "# tag '$name' => '$value'\n";
-        #}
-      }
-    }
-    else {
-      fail("could not read $filename: ".Imager->_error_as_msg());
-    }
-    undef $IO;
-    close FH;
-  }
-  else {
-    fail("could not open $filename: $!");
-  }
-}
-
-sub limited_write {
-  my ($limit) = @_;
-
-  return
-     sub {
-       my ($data) = @_;
-       $limit -= length $data;
-       if ($limit >= 0) {
-         print "# write of ", length $data, " bytes successful ($limit left)\n" if $debug_writes;
-         return 1;
-       }
-       else {
-         print "# write of ", length $data, " bytes failed\n";
-         Imager::i_push_error(0, "limit reached");
-         return;
-       }
-     };
-}
-
-sub load_patched_file {
-  my ($filename, $patches) = @_;
-
-  open IMDATA, "< $filename"
-    or die "Cannot open $filename: $!";
-  binmode IMDATA;
-  my $data = do { local $/; <IMDATA> };
-  for my $offset (keys %$patches) {
-    (my $hdata = $patches->{$offset}) =~ tr/ //d;
-    my $pdata = pack("H*", $hdata);
-    substr($data, $offset, length $pdata) = $pdata;
-  }
-
-  return $data;
-}
diff --git a/t/t108tga.t b/t/t108tga.t
deleted file mode 100644 (file)
index cfdd664..0000000
+++ /dev/null
@@ -1,302 +0,0 @@
-#!perl -w
-use Imager qw(:all);
-use strict;
-use Test::More tests=>68;
-use Imager::Test qw(is_color4 is_image test_image);
-
--d "testout" or mkdir "testout";
-
-init_log("testout/t108tga.log",1);
-
-my $img = create_test_image();
-my $base_diff = 0;
-
-write_test($img, "testout/t108_24bit.tga", 0, 0, "");
-write_test($img, "testout/t108_24bit_rle.tga", 0, 1, "");
-write_test($img, "testout/t108_15bit.tga", 1, 1, "");
-write_test($img, "testout/t108_15bit_rle.tga", 1, 1, "");
-
-# 'webmap' is noticably faster than the default
-my $im8 = Imager::i_img_to_pal($img, { make_colors=>'webmap',
-                                      translate=>'errdiff'});
-
-write_test($im8, "testout/t108_8bit.tga", 0, 0, "");
-write_test($im8, "testout/t108_8bit_rle.tga", 0, 1, "");
-write_test($im8, "testout/t108_8_15bit.tga", 1, 0, "");
-write_test($im8, "testout/t108_8_15bit_rle.tga", 1, 1, "");
-
-
-# use a fixed palette so we get reproducible results for the compressed
-# version
-
-my @bit4 = map { NC($_) }
-  qw(605844 966600 0148b2 00f800 bf0a33 5e009e
-     2ead1b 0000f8 004b01 fd0000 0e1695 000002);
-
-my @bit1 = (NC(0, 0, 0), NC(176, 160, 144));
-
-my $im4 = Imager::i_img_to_pal($img, { colors=>\@bit4,
-                                      make_colors=>'none' });
-
-my $im1 = Imager::i_img_to_pal($img, { colors=>\@bit1,
-                                      make_colors=>'none',
-                                      translate=>'errdiff' });
-
-write_test($im4, "testout/t108_4bit.tga", 0, 1, "");
-write_test($im1, "testout/t108_1bit.tga", 0, 1, "This is a comment!");
-
-read_test("testout/t108_24bit.tga", $img);
-read_test("testout/t108_8bit.tga",  $im8);
-read_test("testout/t108_4bit.tga",  $im4);
-read_test("testout/t108_1bit.tga",  $im1);
-
-# the following might have slight differences
-
-$base_diff = i_img_diff($img, $im8) * 2;
-
-print "# base difference $base_diff\n";
-
-my $imoo = Imager->new;
-ok($imoo->read(file=>'testout/t108_24bit.tga'),
-   "OO read image")
-  or print "# ",$imoo->errstr,"\n";
-
-ok($imoo->write(file=>'testout/t108_oo.tga'),
-   "OO write image")
-  or print "# ",$imoo->errstr,"\n";
-
-my ($type) = $imoo->tags(name=>'i_format');
-is($type, 'tga', "check i_format tag");
-
-# in 0.44 and earlier, reading an image with an idstring of 128 or more
-# bytes would result in an allocation error, if the platform char type
-# was signed
-$imoo = Imager->new;
-ok($imoo->read(file=>'testimg/longid.tga'), "read long id image");
-my ($id) = $imoo->tags(name=>'tga_idstring');
-is($id, "X" x 128, "check tga_idstring tag");
-my ($bitspp) = $imoo->tags(name=>'tga_bitspp');
-is($bitspp, 24, "check tga_bitspp tag");
-my ($compressed) = $imoo->tags(name=>'compressed');
-is($compressed, 1, "check compressed tag");
-
-{ # check file limits are checked
-  my $limit_file = "testout/t108_24bit.tga";
-  ok(Imager->set_file_limits(reset=>1, width=>149), "set width limit 149");
-  my $im = Imager->new;
-  ok(!$im->read(file=>$limit_file),
-     "should fail read due to size limits");
-  print "# ",$im->errstr,"\n";
-  like($im->errstr, qr/image width/, "check message");
-  
-  ok(Imager->set_file_limits(reset=>1, height=>149), "set height limit 149");
-  ok(!$im->read(file=>$limit_file),
-     "should fail read due to size limits");
-  print "# ",$im->errstr,"\n";
-  like($im->errstr, qr/image height/, "check message");
-  
-  ok(Imager->set_file_limits(reset=>1, width=>150), "set width limit 150");
-  ok($im->read(file=>$limit_file),
-     "should succeed - just inside width limit");
-  ok(Imager->set_file_limits(reset=>1, height=>150), "set height limit 150");
-  ok($im->read(file=>$limit_file),
-     "should succeed - just inside height limit");
-  
-  # 150 x 150 x 3 channel image uses 67500 bytes
-  ok(Imager->set_file_limits(reset=>1, bytes=>67499),
-     "set bytes limit 67499");
-  ok(!$im->read(file=>$limit_file),
-     "should fail - too many bytes");
-  print "# ",$im->errstr,"\n";
-  like($im->errstr, qr/storage size/, "check error message");
-  ok(Imager->set_file_limits(reset=>1, bytes=>67500),
-     "set bytes limit 67500");
-  ok($im->read(file=>$limit_file),
-     "should succeed - just inside bytes limit");
-  Imager->set_file_limits(reset=>1);
-}
-
-{ # Issue # 18397
-  # the issue is for 4 channel images to jpeg, but 2 channel images have
-  # a similar problem on tga
-  my $im = Imager->new(xsize=>100, ysize=>100, channels => 2);
-  my $data;
-  ok(!$im->write(data => \$data, type=>'tga'),
-     "check failure of writing a 2 channel image");
-  is($im->errstr, "Cannot store 2 channel image in targa format",
-     "check the error message");
-}
-
-{
-  ok(grep($_ eq 'tga', Imager->read_types), "check tga in read types");
-  ok(grep($_ eq 'tga', Imager->write_types), "check tga in write types");
-}
-
-{ # Issue #32926
-  # a sample image was read as all transparent
-  # it had bitsperpixel = 16 and atribute channel set to 1, so it
-  # should have an alpha channel.
-  # So we'll do what the gimp does and treat a zero value as opaque.
-
-  my $im = Imager->new;
-  ok($im->read(file => 'testimg/alpha16.tga'),
-     "read 16-bit/pixel alpha image");
-  my $c1 = $im->getpixel('x' => 0, 'y' => 0);
-  is_color4($c1, 0, 0, 0, 0, "check transparent pixel");
-  my $c2 = $im->getpixel('x' => 19, 'y' => 0);
-  is_color4($c2, 255, 0, 0, 255, "check opaque pixel");
-
-  # since this has an effect on writing too, write,it, read it, check it
-  my $data;
-  ok($im->write(data => \$data, type => 'tga', wierdpack => 1),
-     "write 16-bit/pixel w/alpha");
-  my $im2 = Imager->new;
-  ok($im2->read(data => $data), "read it back");
-  is_image($im, $im2, "check they match");
-}
-
-{ # prior to the types re-work we treated the tga xsize/ysize as
-  # signed short, which is wrong
- SKIP:
-  {
-    my $im = Imager->new(xsize => 40960, ysize => 1);
-    my $data;
-    ok($im->write(data => \$data, type => "tga"),
-       "write a wide (but not too wide) image out");
-    my $im2 = Imager->new(data => $data);
-    ok($im2, "read it back in")
-      or skip("Couldn't read the wide image", 2);
-    is($im2->getwidth, 40960, "make sure the width survived the trip");
-    is($im2->getheight, 1, "make sure the height survived the trip");
-  }
-
- SKIP:
-  {
-    my $im = Imager->new(xsize => 1, ysize => 40960);
-    my $data;
-    ok($im->write(data => \$data, type => "tga"),
-       "write a tall (but not too tall) image out");
-    my $im2 = Imager->new(data => $data);
-    ok($im2, "read it back in")
-      or skip("Couldn't read the tall image", 2);
-    is($im2->getwidth, 1, "make sure the width survived the trip");
-    is($im2->getheight, 40960, "make sure the height survived the trip");
-  }
-}
-
-{
-  # TGA files are limited to 0xFFFF x 0xFFFF pixels
-  my $max_dim = 0xFFFF;
-  {
-    my $im = Imager->new(xsize => 1+$max_dim, ysize => 1);
-    my $data = '';
-    ok(!$im->write(data => \$data, type => "tga"),
-       "fail to write too wide an image");
-    is($im->errstr, "image too large for TGA",
-       "check error message");
-  }
- SKIP:
-  {
-    my $im = Imager->new(xsize => $max_dim, ysize => 1);
-    $im->box(fill => { hatch => "check4x4" });
-    my $data = '';
-    ok($im->write(data => \$data, type => "tga"),
-       "write image at width limit")
-      or print "# ", $im->errstr, "\n";
-    my $im2 = Imager->new(data => $data, ftype => "tga");
-    ok($im2, "read it ok")
-      or skip("cannot load the wide image", 1);
-    is($im->getwidth, $max_dim, "check width");
-    is($im->getheight, 1, "check height");
-  }
-  {
-    my $im = Imager->new(xsize => 1, ysize => 1+$max_dim);
-    my $data = '';
-    ok(!$im->write(data => \$data, type => "tga"),
-       "fail to write too tall an image");
-    is($im->errstr, "image too large for TGA",
-       "check error message");
-  }
- SKIP:
-  {
-    my $im = Imager->new(xsize => 1, ysize => $max_dim);
-    $im->box(fill => { hatch => "check2x2" });
-    my $data = '';
-    ok($im->write(data => \$data, type => "tga"),
-       "write image at width limit");
-    my $im2 = Imager->new(data => $data, ftype => "tga");
-    ok($im2, "read it ok")
-      or skip("cannot load the wide image", 1);
-    is($im->getwidth, 1, "check width");
-    is($im->getheight, $max_dim, "check height");
-  }
-}
-
-{ # check close failures are handled correctly
-  my $im = test_image();
-  my $fail_close = sub {
-    Imager::i_push_error(0, "synthetic close failure");
-    return 0;
-  };
-  ok(!$im->write(type => "tga", callback => sub { 1 },
-                closecb => $fail_close),
-     "check failing close fails");
-    like($im->errstr, qr/synthetic close failure/,
-        "check error message");
-}
-
-sub write_test {
-  my ($im, $filename, $wierdpack, $compress, $idstring) = @_;
-  local *FH;
-
-  if (open FH, "> $filename") {
-    binmode FH;
-    my $IO = Imager::io_new_fd(fileno(FH));
-    ok(Imager::i_writetga_wiol($im, $IO, $wierdpack, $compress, $idstring),
-       "write $filename")
-      or print "# ",Imager->_error_as_msg(),"\n";
-    undef $IO;
-    close FH;
-  } else {
-    fail("write $filename: open failed: $!");
-  }
-}
-
-
-sub read_test {
-  my ($filename, $im, %tags) = @_;
-  local *FH;
-
-  if (open FH, "< $filename") {
-    binmode FH;
-    my $IO = Imager::io_new_fd(fileno(FH));
-    my $im_read = Imager::i_readtga_wiol($IO,-1);
-    if ($im_read) {
-      my $diff = i_img_diff($im, $im_read);
-      cmp_ok($diff, '<=', $base_diff,
-            "check read image vs original");
-    } else {
-      fail("read $filename ".Imager->_error_as_msg());
-    }
-    undef $IO;
-    close FH;
-  } else {
-    fail("read $filename, open failure: $!");
-  }
-}
-
-sub create_test_image {
-
-  my $green  = i_color_new(0,255,0,255);
-  my $blue   = i_color_new(0,0,255,255);
-  my $red    = i_color_new(255,0,0,255);
-
-  my $img    = Imager::ImgRaw::new(150,150,3);
-
-  i_box_filled($img, 70, 25, 130, 125, $green);
-  i_box_filled($img, 20, 25,  80, 125, $blue);
-  i_arc($img, 75, 75, 30, 0, 361, $red);
-  i_conv($img, [0.1, 0.2, 0.4, 0.2, 0.1]);
-
-  return $img;
-}
diff --git a/t/t15color.t b/t/t15color.t
deleted file mode 100644 (file)
index edcb923..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-#!perl -w
-# Before `make install' is performed this script should be runnable with
-# `make test'. After `make install' it should work as `perl test.pl'
-
-######################### We start with some black magic to print on failure.
-
-# Change 1..1 below to 1..last_test_to_print .
-# (It may become useful if the test is moved to ./t subdirectory.)
-
-use Test::More tests => 70;
-
-use Imager;
-use Imager::Test qw(is_fcolor4);
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t15color.log");
-
-my $c1 = Imager::Color->new(100, 150, 200, 250);
-ok(test_col($c1, 100, 150, 200, 250), 'simple 4-arg');
-my $c2 = Imager::Color->new(100, 150, 200);
-ok(test_col($c2, 100, 150, 200, 255), 'simple 3-arg');
-my $c3 = Imager::Color->new("#6496C8");
-ok(test_col($c3, 100, 150, 200, 255), 'web color');
-# crashes in Imager-0.38pre8 and earlier
-my @foo;
-for (1..1000) {
-  push(@foo, Imager::Color->new("#FFFFFF"));
-}
-my $fail;
-for (@foo) {
-  Imager::Color::set_internal($_, 128, 128, 128, 128) == $_ or ++$fail;
-  Imager::Color::set_internal($_, 128, 128, 128, 128) == $_ or ++$fail;
-  test_col($_, 128, 128, 128, 128) or ++$fail;
-}
-ok(!$fail, 'consitency check');
-
-# test the new OO methods
-color_ok('r g b',, 100, 150, 200, 255, Imager::Color->new(r=>100, g=>150, b=>200));
-color_ok('red green blue', 101, 151, 201, 255, 
-         Imager::Color->new(red=>101, green=>151, blue=>201));
-color_ok('grey', 102, 255, 255, 255, Imager::Color->new(grey=>102));
-color_ok('gray', 103, 255, 255, 255, Imager::Color->new(gray=>103));
-SKIP:
-{
-  skip "no X rgb.txt found", 1 
-    unless grep -r, Imager::Color::_test_x_palettes();
-  color_ok('xname', 0, 0, 255, 255, Imager::Color->new(xname=>'blue'));
-}
-color_ok('gimp', 255, 250, 250, 255, 
-         Imager::Color->new(gimp=>'snow', palette=>'testimg/test_gimp_pal'));
-color_ok('h s v', 255, 255, 255, 255, Imager::Color->new(h=>0, 's'=>0, 'v'=>1.0));
-color_ok('h s v again', 255, 0, 0, 255, Imager::Color->new(h=>0, 's'=>1, v=>1));
-color_ok('web 6 digit', 128, 129, 130, 255, Imager::Color->new(web=>'#808182'));
-color_ok('web 3 digit', 0x11, 0x22, 0x33, 255, Imager::Color->new(web=>'#123'));
-color_ok('rgb arrayref', 255, 150, 121, 255, Imager::Color->new(rgb=>[ 255, 150, 121 ]));
-color_ok('rgba arrayref', 255, 150, 121, 128, 
-         Imager::Color->new(rgba=>[ 255, 150, 121, 128 ]));
-color_ok('hsv arrayref', 255, 0, 0, 255, Imager::Color->new(hsv=>[ 0, 1, 1 ]));
-color_ok('channel0-3', 129, 130, 131, 134, 
-         Imager::Color->new(channel0=>129, channel1=>130, channel2=>131,
-                            channel3=>134));
-color_ok('c0-3', 129, 130, 131, 134, 
-         Imager::Color->new(c0=>129, c1=>130, c2=>131, c3=>134));
-color_ok('channels arrayref', 200, 201, 203, 204, 
-         Imager::Color->new(channels=>[ 200, 201, 203, 204 ]));
-color_ok('name', 255, 250, 250, 255, 
-         Imager::Color->new(name=>'snow', palette=>'testimg/test_gimp_pal'));
-
-# test the internal HSV <=> RGB conversions
-# these values were generated using the GIMP
-# all but hue is 0..360, saturation and value from 0 to 1
-# rgb from 0 to 255
-my @hsv_vs_rgb =
-  (
-   { hsv => [ 0, 0.2, 0.1 ], rgb=> [ 25, 20, 20 ] },
-   { hsv => [ 0, 0.5, 1.0 ], rgb => [ 255, 127, 127 ] },
-   { hsv => [ 100, 0.5, 1.0 ], rgb => [ 170, 255, 127 ] },
-   { hsv => [ 100, 1.0, 1.0 ], rgb=> [ 85, 255, 0 ] },
-   { hsv => [ 335, 0.5, 0.5 ], rgb=> [127, 63, 90 ] },
-  );
-
-use Imager::Color::Float;
-my $test_num = 23;
-my $index = 0;
-for my $entry (@hsv_vs_rgb) {
-  print "# color index $index\n";
-  my $hsv = $entry->{hsv};
-  my $rgb = $entry->{rgb};
-  my $fhsvo = Imager::Color::Float->new($hsv->[0]/360.0, $hsv->[1], $hsv->[2]);
-  my $fc = Imager::Color::Float::i_hsv_to_rgb($fhsvo);
-  fcolor_close_enough("i_hsv_to_rgbf $index", $rgb->[0]/255, $rgb->[1]/255, 
-                      $rgb->[2]/255, $fc);
-  my $fc2 = Imager::Color::Float::i_rgb_to_hsv($fc);
-  fcolor_close_enough("i_rgbf_to_hsv $index", $hsv->[0]/360.0, $hsv->[1], $hsv->[2], 
-                      $fc2);
-
-  my $hsvo = Imager::Color->new($hsv->[0]*255/360.0, $hsv->[1] * 255, 
-                                $hsv->[2] * 255);
-  my $c = Imager::Color::i_hsv_to_rgb($hsvo);
-  color_close_enough("i_hsv_to_rgb $index", @$rgb, $c);
-  my $c2 = Imager::Color::i_rgb_to_hsv($c);
-  color_close_enough_hsv("i_rgb_to_hsv $index", $hsv->[0]*255/360.0, $hsv->[1] * 255, 
-                     $hsv->[2] * 255, $c2);
-  ++$index;
-}
-
-# check the built-ins table
-color_ok('builtin black', 0, 0, 0, 255, 
-       Imager::Color->new(builtin=>'black'));
-
-{
-  my $c1 = Imager::Color->new(255, 255, 255, 0);
-  my $c2 = Imager::Color->new(255, 255, 255, 255);
-  ok(!$c1->equals(other=>$c2), "not equal no ignore alpha");
-  ok(scalar($c1->equals(other=>$c2, ignore_alpha=>1)), 
-      "equal with ignore alpha");
-  ok($c1->equals(other=>$c1), "equal to itself");
-}
-
-{ # http://rt.cpan.org/NoAuth/Bug.html?id=13143
-  # Imager::Color->new(color_name) warning if HOME environment variable not set
-  local $ENV{HOME};
-  my @warnings;
-  local $SIG{__WARN__} = sub { push @warnings, "@_" };
-
-  # presumably no-one will name a color like this.
-  my $c1 = Imager::Color->new(gimp=>"ABCDEFGHIJKLMNOP");
-  is(@warnings, 0, "Should be no warnings")
-    or do { print "# $_" for @warnings };
-}
-
-{
-  # float color from hex triple
-  my $f3white = Imager::Color::Float->new("#FFFFFF");
-  is_fcolor4($f3white, 1.0, 1.0, 1.0, 1.0, "check color #FFFFFF");
-  my $f3black = Imager::Color::Float->new("#000000");
-  is_fcolor4($f3black, 0, 0, 0, 1.0, "check color #000000");
-  my $f3grey = Imager::Color::Float->new("#808080");
-  is_fcolor4($f3grey, 0x80/0xff, 0x80/0xff, 0x80/0xff, 1.0, "check color #808080");
-
-  my $f4white = Imager::Color::Float->new("#FFFFFF80");
-  is_fcolor4($f4white, 1.0, 1.0, 1.0, 0x80/0xff, "check color #FFFFFF80");
-}
-
-{
-  # fail to make a color
-  ok(!Imager::Color::Float->new("-unknown-"), "try to make float color -unknown-");
-}
-
-{
-  # set after creation
-  my $c = Imager::Color::Float->new(0, 0, 0);
-  is_fcolor4($c, 0, 0, 0, 1.0, "check simple init of float color");
-  ok($c->set(1.0, 0.5, 0.25, 1.0), "set() the color");
-  is_fcolor4($c, 1.0, 0.5, 0.25, 1.0, "check after set");
-
-  ok(!$c->set("-unknown-"), "set to unknown");
-}
-
-{
-  # test ->hsv
-  my $c = Imager::Color->new(255, 0, 0);
-  my($h,$s,$v) = $c->hsv;
-  is($h,0,'red hue');
-  is($s,1,'red saturation');
-  is($v,1,'red value');
-
-  $c = Imager::Color->new(0, 255, 0);
-  ($h,$s,$v) = $c->hsv;
-  is($h,120,'green hue');
-  is($s,1,'green saturation');
-  is($v,1,'green value');
-
-  $c = Imager::Color->new(0, 0, 255);
-  ($h,$s,$v) = $c->hsv;
-  is($h,240,'blue hue');
-  is($s,1,'blue saturation');
-  is($v,1,'blue value');
-
-  $c = Imager::Color->new(255, 255, 255);
-  ($h,$s,$v) = $c->hsv;
-  is($h,0,'white hue');
-  is($s,0,'white saturation');
-  is($v,1,'white value');
-
-  $c = Imager::Color->new(0, 0, 0);
-  ($h,$s,$v) = $c->hsv;
-  is($h,0,'black hue');
-  is($s,0,'black saturation');
-  is($v,0,'black value');
-}
-
-sub test_col {
-  my ($c, $r, $g, $b, $a) = @_;
-  unless ($c) {
-    print "# $Imager::ERRSTR\n";
-    return 0;
-  }
-  my ($cr, $cg, $cb, $ca) = $c->rgba;
-  return $r == $cr && $g == $cg && $b == $cb && $a == $ca;
-}
-
-sub color_close_enough {
-  my ($name, $r, $g, $b, $c) = @_;
-
-  my ($cr, $cg, $cb) = $c->rgba;
-  ok(abs($cr-$r) <= 5 && abs($cg-$g) <= 5 && abs($cb-$b) <= 5,
-    "$name - ($cr, $cg, $cb) <=> ($r, $g, $b)");
-}
-
-sub color_close_enough_hsv {
-  my ($name, $h, $s, $v, $c) = @_;
-
-  my ($ch, $cs, $cv) = $c->rgba;
-  if ($ch < 5 && $h > 250) {
-    $ch += 255;
-  }
-  elsif ($ch > 250 && $h < 5) {
-    $h += 255;
-  }
-  ok(abs($ch-$h) <= 5 && abs($cs-$s) <= 5 && abs($cv-$v) <= 5,
-    "$name - ($ch, $cs, $cv) <=> ($h, $s, $v)");
-}
-
-sub fcolor_close_enough {
-  my ($name, $r, $g, $b, $c) = @_;
-
-  my ($cr, $cg, $cb) = $c->rgba;
-  ok(abs($cr-$r) <= 0.01 && abs($cg-$g) <= 0.01 && abs($cb-$b) <= 0.01,
-    "$name - ($cr, $cg, $cb) <=> ($r, $g, $b)");
-}
-
-sub color_ok {
-  my ($name, $r, $g, $b, $a, $c) = @_;
-
-  unless (ok(test_col($c, $r, $g, $b, $a), $name)) {
-    print "# ($r,$g,$b,$a) != (".join(",", $c ? $c->rgba: ()).")\n";
-  }
-}
-
diff --git a/t/t16matrix.t b/t/t16matrix.t
deleted file mode 100644 (file)
index 4604602..0000000
+++ /dev/null
@@ -1,132 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 23;
-use Imager;
-
-BEGIN { use_ok('Imager::Matrix2d', ':handy') }
-
-my $id = Imager::Matrix2d->identity;
-
-ok(almost_equal($id, [ 1, 0, 0,
-                       0, 1, 0,
-                       0, 0, 1 ]), "identity matrix");
-my $trans = Imager::Matrix2d->translate('x'=>10, 'y'=>-11);
-ok(almost_equal($trans, [ 1, 0, 10,
-                          0, 1, -11,
-                          0, 0, 1 ]), "translate matrix");
-my $trans_x = Imager::Matrix2d->translate(x => 10);
-ok(almost_equal($trans_x, [ 1, 0, 10,
-                          0, 1, 0,
-                          0, 0, 1 ]), "translate just x");
-my $trans_y = Imager::Matrix2d->translate('y' => 11);
-ok(almost_equal($trans_y, [ 1, 0, 0,
-                          0, 1, 11,
-                          0, 0, 1 ]), "translate just y");
-
-my $rotate = Imager::Matrix2d->rotate(degrees=>90);
-ok(almost_equal($rotate, [ 0, -1, 0,
-                           1, 0,  0,
-                           0, 0,  1 ]), "rotate matrix");
-
-my $shear = Imager::Matrix2d->shear('x'=>0.2, 'y'=>0.3);
-ok(almost_equal($shear, [ 1,   0.2, 0,
-                          0.3, 1,   0,
-                          0,   0,   1 ]), "shear matrix");
-
-my $scale = Imager::Matrix2d->scale('x'=>1.2, 'y'=>0.8);
-ok(almost_equal($scale, [ 1.2, 0,   0,
-                          0,   0.8, 0,
-                          0,   0,   1 ]), "scale matrix");
-
-my $custom = Imager::Matrix2d->matrix(1, 0, 0, 0, 1, 0, 0, 0, 1);
-ok(almost_equal($custom, [ 1, 0, 0,
-                       0, 1, 0,
-                       0, 0, 1 ]), "custom matrix");
-
-my $trans_called;
-$rotate = Imager::Matrix2d::Test->rotate(degrees=>90, x=>50);
-ok($trans_called, "translate called on rotate with just x");
-
-$trans_called = 0;
-$rotate = Imager::Matrix2d::Test->rotate(degrees=>90, 'y'=>50);
-ok($trans_called, "translate called on rotate with just y");
-
-ok(!Imager::Matrix2d->matrix(), "bad custom matrix");
-is(Imager->errstr, "9 co-efficients required", "check error");
-
-{
-  my @half = ( 0.5, 0, 0,
-              0, 0.5, 0,
-              0, 0, 1 );
-  my @quart = ( 0, 0.25, 0,
-               1, 0, 0,
-               0, 0, 1 );
-  my $half_matrix = Imager::Matrix2d->matrix(@half);
-  my $quart_matrix = Imager::Matrix2d->matrix(@quart);
-  my $result = $half_matrix * $quart_matrix;
-  is_deeply($half_matrix * \@quart, $result, "mult by unblessed matrix");
-  is_deeply(\@half * $quart_matrix, $result, "mult with unblessed matrix");
-
-  my $half_three = Imager::Matrix2d->matrix(1.5, 0, 0, 0, 1.5, 0, 0, 0, 3);
-  is_deeply($half_matrix * 3, $half_three, "mult by three");
-  is_deeply(3 * $half_matrix, $half_three, "mult with three");
-
-  {
-    # check error handling - bad ref type
-    my $died = 
-      !eval {
-      my $foo = $half_matrix * +{};
-      1;
-    };
-    ok($died, "mult by hash ref died");
-    like($@, qr/multiply by array ref or number/, "check message");
-  }
-
-  {
-    # check error handling - bad array
-    $@ = '';
-    my $died = 
-      !eval {
-      my $foo = $half_matrix * [ 1 .. 8 ];
-      1;
-    };
-    ok($died, "mult by short array ref died");
-    like($@, qr/9 elements required in array ref/, "check message");
-  }
-
-  {
-    # check error handling - bad value
-    $@ = '';
-    my $died = 
-      !eval {
-      my $foo = $half_matrix * "abc";
-      1;
-    };
-    ok($died, "mult by bad scalar died");
-    like($@, qr/multiply by array ref or number/, "check message");
-  }
-  
-}
-
-
-sub almost_equal {
-  my ($m1, $m2) = @_;
-
-  for my $i (0..8) {
-    abs($m1->[$i] - $m2->[$i]) < 0.00001 or return undef;
-  }
-  return 1;
-}
-
-# this is used to ensure translate() is called correctly by rotate
-package Imager::Matrix2d::Test;
-use vars qw(@ISA);
-BEGIN { @ISA = qw(Imager::Matrix2d); }
-
-sub translate {
-  my ($class, %opts) = @_;
-
-  ++$trans_called;
-  return $class->SUPER::translate(%opts);
-}
-
diff --git a/t/t20fill.t b/t/t20fill.t
deleted file mode 100644 (file)
index c809f12..0000000
+++ /dev/null
@@ -1,736 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 165;
-
-use Imager ':handy';
-use Imager::Fill;
-use Imager::Color::Float;
-use Imager::Test qw(is_image is_color4 is_fcolor4 is_color3);
-use Config;
-
--d "testout" or mkdir "testout";
-
-Imager::init_log("testout/t20fill.log", 1);
-
-my $blue = NC(0,0,255);
-my $red = NC(255, 0, 0);
-my $redf = Imager::Color::Float->new(1, 0, 0);
-my $bluef = Imager::Color::Float->new(0, 0, 1);
-my $rsolid = Imager::i_new_fill_solid($blue, 0);
-ok($rsolid, "building solid fill");
-my $raw1 = Imager::ImgRaw::new(100, 100, 3);
-# use the normal filled box
-Imager::i_box_filled($raw1, 0, 0, 99, 99, $blue);
-my $raw2 = Imager::ImgRaw::new(100, 100, 3);
-Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rsolid);
-ok(1, "drawing with solid fill");
-my $diff = Imager::i_img_diff($raw1, $raw2);
-ok($diff == 0, "solid fill doesn't match");
-Imager::i_box_filled($raw1, 0, 0, 99, 99, $red);
-my $rsolid2 = Imager::i_new_fill_solidf($redf, 0);
-ok($rsolid2, "creating float solid fill");
-Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rsolid2);
-$diff = Imager::i_img_diff($raw1, $raw2);
-ok($diff == 0, "float solid fill doesn't match");
-
-# ok solid still works, let's try a hatch
-# hash1 is a 2x2 checkerboard
-my $rhatcha = Imager::i_new_fill_hatch($red, $blue, 0, 1, undef, 0, 0);
-my $rhatchb = Imager::i_new_fill_hatch($blue, $red, 0, 1, undef, 2, 0);
-ok($rhatcha && $rhatchb, "can't build hatched fill");
-
-# the offset should make these match
-Imager::i_box_cfill($raw1, 0, 0, 99, 99, $rhatcha);
-Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
-ok(1, "filling with hatch");
-$diff = Imager::i_img_diff($raw1, $raw2);
-ok($diff == 0, "hatch images different");
-$rhatchb = Imager::i_new_fill_hatch($blue, $red, 0, 1, undef, 4, 6);
-Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
-$diff = Imager::i_img_diff($raw1, $raw2);
-ok($diff == 0, "hatch images different");
-
-# I guess I was tired when I originally did this - make sure it keeps
-# acting the way it's meant to
-# I had originally expected these to match with the red and blue swapped
-$rhatchb = Imager::i_new_fill_hatch($red, $blue, 0, 1, undef, 2, 2);
-Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
-$diff = Imager::i_img_diff($raw1, $raw2);
-ok($diff == 0, "hatch images different");
-
-# this shouldn't match
-$rhatchb = Imager::i_new_fill_hatch($red, $blue, 0, 1, undef, 1, 1);
-Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rhatchb);
-$diff = Imager::i_img_diff($raw1, $raw2);
-ok($diff, "hatch images the same!");
-
-# custom hatch
-# the inverse of the 2x2 checkerboard
-my $hatch = pack("C8", 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC);
-my $rcustom = Imager::i_new_fill_hatch($blue, $red, 0, 0, $hatch, 0, 0);
-Imager::i_box_cfill($raw2, 0, 0, 99, 99, $rcustom);
-$diff = Imager::i_img_diff($raw1, $raw2);
-ok(!$diff, "custom hatch mismatch");
-
-{
-  # basic test of floating color hatch fills
-  # this will exercise the code that the gcc shipped with OS X 10.4
-  # forgets to generate
-  # the float version is called iff we're working with a non-8-bit image
-  # i_new_fill_hatchf() makes the same object as i_new_fill_hatch() but
-  # we test the other construction code path here
-  my $fraw1 = Imager::i_img_double_new(100, 100, 3);
-  my $fhatch1 = Imager::i_new_fill_hatchf($redf, $bluef, 0, 1, undef, 0, 0);
-  ok($fraw1, "making double image 1");
-  ok($fhatch1, "making float hatch 1");
-  Imager::i_box_cfill($fraw1, 0, 0, 99, 99, $fhatch1);
-  my $fraw2 = Imager::i_img_double_new(100, 100, 3);
-  my $fhatch2 = Imager::i_new_fill_hatchf($bluef, $redf, 0, 1, undef, 0, 2);
-  ok($fraw2, "making double image 2");
-  ok($fhatch2, "making float hatch 2");
-  Imager::i_box_cfill($fraw2, 0, 0, 99, 99, $fhatch2);
-
-  $diff = Imager::i_img_diff($fraw1, $fraw2);
-  ok(!$diff, "float custom hatch mismatch");
-  save($fraw1, "testout/t20hatchf1.ppm");
-  save($fraw2, "testout/t20hatchf2.ppm");
-}
-
-# test the oo interface
-my $im1 = Imager->new(xsize=>100, ysize=>100);
-my $im2 = Imager->new(xsize=>100, ysize=>100);
-
-my $solid = Imager::Fill->new(solid=>'#FF0000');
-ok($solid, "creating oo solid fill");
-ok($solid->{fill}, "bad oo solid fill");
-$im1->box(fill=>$solid);
-$im2->box(filled=>1, color=>$red);
-$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
-ok(!$diff, "oo solid fill");
-
-my $hatcha = Imager::Fill->new(hatch=>'check2x2');
-my $hatchb = Imager::Fill->new(hatch=>'check2x2', dx=>2);
-$im1->box(fill=>$hatcha);
-$im2->box(fill=>$hatchb);
-# should be different
-$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
-ok($diff, "offset checks the same!");
-$hatchb = Imager::Fill->new(hatch=>'check2x2', dx=>2, dy=>2);
-$im2->box(fill=>$hatchb);
-$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
-ok(!$diff, "offset into similar check should be the same");
-
-# test dymanic build of fill
-$im2->box(fill=>{hatch=>'check2x2', dx=>2, fg=>NC(255,255,255), 
-                 bg=>NC(0,0,0)});
-$diff = Imager::i_img_diff($im1->{IMG}, $im2->{IMG});
-ok(!$diff, "offset and flipped should be the same");
-
-# a simple demo
-my $im = Imager->new(xsize=>200, ysize=>200);
-
-$im->box(xmin=>10, ymin=>10, xmax=>190, ymax=>190,
-         fill=>{ hatch=>'check4x4',
-                 fg=>NC(128, 0, 0),
-                 bg=>NC(128, 64, 0) })
-  or print "# ",$im->errstr,"\n";
-$im->arc(r=>80, d1=>45, d2=>75, 
-           fill=>{ hatch=>'stipple2',
-                   combine=>1,
-                   fg=>[ 0, 0, 0, 255 ],
-                   bg=>{ rgba=>[255,255,255,160] } })
-  or print "# ",$im->errstr,"\n";
-$im->arc(r=>80, d1=>75, d2=>135,
-         fill=>{ fountain=>'radial', xa=>100, ya=>100, xb=>20, yb=>100 })
-  or print "# ",$im->errstr,"\n";
-$im->write(file=>'testout/t20_sample.ppm');
-
-# flood fill tests
-my $rffimg = Imager::ImgRaw::new(100, 100, 3);
-# build a H 
-Imager::i_box_filled($rffimg, 10, 10, 20, 90, $blue);
-Imager::i_box_filled($rffimg, 80, 10, 90, 90, $blue);
-Imager::i_box_filled($rffimg, 20, 45, 80, 55, $blue);
-my $black = Imager::Color->new(0, 0, 0);
-Imager::i_flood_fill($rffimg, 15, 15, $red);
-my $rffcmp = Imager::ImgRaw::new(100, 100, 3);
-# build a H 
-Imager::i_box_filled($rffcmp, 10, 10, 20, 90, $red);
-Imager::i_box_filled($rffcmp, 80, 10, 90, 90, $red);
-Imager::i_box_filled($rffcmp, 20, 45, 80, 55, $red);
-$diff = Imager::i_img_diff($rffimg, $rffcmp);
-ok(!$diff, "flood fill difference");
-
-my $ffim = Imager->new(xsize=>100, ysize=>100);
-my $yellow = Imager::Color->new(255, 255, 0);
-$ffim->box(xmin=>10, ymin=>10, xmax=>20, ymax=>90, color=>$blue, filled=>1);
-$ffim->box(xmin=>20, ymin=>45, xmax=>80, ymax=>55, color=>$blue, filled=>1);
-$ffim->box(xmin=>80, ymin=>10, xmax=>90, ymax=>90, color=>$blue, filled=>1);
-ok($ffim->flood_fill('x'=>50, 'y'=>50, color=>$red), "flood fill");
-$diff = Imager::i_img_diff($rffcmp, $ffim->{IMG});
-ok(!$diff, "oo flood fill difference");
-$ffim->flood_fill('x'=>50, 'y'=>50,
-                  fill=> {
-                          hatch => 'check2x2',
-                         fg => '0000FF',
-                         });
-#                  fill=>{
-#                         fountain=>'radial',
-#                         xa=>50, ya=>50,
-#                         xb=>10, yb=>10,
-#                        });
-$ffim->write(file=>'testout/t20_ooflood.ppm');
-
-my $copy = $ffim->copy;
-ok($ffim->flood_fill('x' => 50, 'y' => 50,
-                    color => $red, border => '000000'),
-   "border solid flood fill");
-is(Imager::i_img_diff($ffim->{IMG}, $rffcmp), 0, "compare");
-ok($ffim->flood_fill('x' => 50, 'y' => 50,
-                    fill => { hatch => 'check2x2', fg => '0000FF', },
-                    border => '000000'),
-   "border cfill fill");
-is(Imager::i_img_diff($ffim->{IMG}, $copy->{IMG}), 0,
-   "compare");
-
-# test combining modes
-my $fill = NC(192, 128, 128, 128);
-my $target = NC(64, 32, 64);
-my $trans_target = NC(64, 32, 64, 128);
-my %comb_tests =
-  (
-   none=>
-   { 
-    opaque => $fill,
-    trans => $fill,
-   },
-   normal=>
-   { 
-    opaque => NC(128, 80, 96),
-    trans => NC(150, 96, 107, 191),
-   },
-   multiply => 
-   { 
-    opaque => NC(56, 24, 48),
-    trans => NC(101, 58, 74, 192),
-   },
-   dissolve => 
-   { 
-    opaque => [ $target, NC(192, 128, 128, 255) ],
-    trans => [ $trans_target, NC(192, 128, 128, 255) ],
-   },
-   add => 
-   { 
-    opaque => NC(159, 96, 128),
-    trans => NC(128, 80, 96, 255),
-   },
-   subtract => 
-   { 
-    opaque => NC(0, 0, 0),
-    trans => NC(0, 0, 0, 255),
-   },
-   diff => 
-   { 
-    opaque => NC(96, 64, 64),
-    trans => NC(127, 85, 85, 192),
-   },
-   lighten => 
-   { 
-    opaque => NC(128, 80, 96), 
-    trans => NC(149, 95, 106, 192), 
-   },
-   darken => 
-   { 
-    opaque => $target,
-    trans => NC(106, 63, 85, 192),
-   },
-   # the following results are based on the results of the tests and
-   # are suspect for that reason (and were broken at one point <sigh>)
-   # but trying to work them out manually just makes my head hurt - TC
-   hue => 
-   { 
-    opaque => NC(64, 32, 47),
-    trans => NC(64, 32, 42, 128),
-   },
-   saturation => 
-   { 
-    opaque => NC(63, 37, 64),
-    trans => NC(64, 39, 64, 128),
-   },
-   value => 
-   { 
-    opaque => NC(127, 64, 128),
-    trans => NC(149, 75, 150, 128),
-   },
-   color => 
-   { 
-    opaque => NC(64, 37, 52),
-    trans => NC(64, 39, 50, 128),
-   },
-  );
-
-for my $comb (Imager::Fill->combines) {
-  my $test = $comb_tests{$comb};
-  my $fillobj = Imager::Fill->new(solid=>$fill, combine=>$comb);
-
-  for my $bits (qw(8 double)) {
-    {
-      my $targim = Imager->new(xsize=>4, ysize=>4, bits => $bits);
-      $targim->box(filled=>1, color=>$target);
-      $targim->box(fill=>$fillobj);
-      my $c = Imager::i_get_pixel($targim->{IMG}, 1, 1);
-      my $allowed = $test->{opaque};
-      $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
-      ok(scalar grep(color_close($_, $c), @$allowed), 
-        "opaque '$comb' $bits bits")
-       or print "# got:",join(",", $c->rgba),"  allowed: ", 
-         join("|", map { join(",", $_->rgba) } @$allowed),"\n";
-    }
-    
-    {
-      # make sure the alpha path in the combine function produces the same
-      # or at least as sane a result as the non-alpha path
-      my $targim = Imager->new(xsize=>4, ysize=>4, channels => 4, bits => $bits);
-      $targim->box(filled=>1, color=>$target);
-      $targim->box(fill=>$fillobj);
-      my $c = Imager::i_get_pixel($targim->{IMG}, 1, 1);
-      my $allowed = $test->{opaque};
-      $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
-      ok(scalar grep(color_close4($_, $c), @$allowed), 
-        "opaque '$comb' 4-channel $bits bits")
-       or print "# got:",join(",", $c->rgba),"  allowed: ", 
-         join("|", map { join(",", $_->rgba) } @$allowed),"\n";
-    }
-    
-    {
-      my $transim = Imager->new(xsize => 4, ysize => 4, channels => 4, bits => $bits);
-      $transim->box(filled=>1, color=>$trans_target);
-      $transim->box(fill => $fillobj);
-      my $c = $transim->getpixel(x => 1, 'y' => 1);
-      my $allowed = $test->{trans};
-      $allowed =~ /ARRAY/ or $allowed = [ $allowed ];
-      ok(scalar grep(color_close4($_, $c), @$allowed), 
-        "translucent '$comb' $bits bits")
-       or print "# got:",join(",", $c->rgba),"  allowed: ", 
-         join("|", map { join(",", $_->rgba) } @$allowed),"\n";
-    }
-  }
-}
-
-ok($ffim->arc(r=>45, color=>$blue, aa=>1), "aa circle");
-$ffim->write(file=>"testout/t20_aacircle.ppm");
-
-# image based fills
-my $green = NC(0, 255, 0);
-my $fillim = Imager->new(xsize=>40, ysize=>40, channels=>4);
-$fillim->box(filled=>1, xmin=>5, ymin=>5, xmax=>35, ymax=>35, 
-             color=>NC(0, 0, 255, 128));
-$fillim->arc(filled=>1, r=>10, color=>$green, aa=>1);
-my $ooim = Imager->new(xsize=>150, ysize=>150);
-$ooim->box(filled=>1, color=>$green, xmin=>70, ymin=>25, xmax=>130, ymax=>125);
-$ooim->box(filled=>1, color=>$blue, xmin=>20, ymin=>25, xmax=>80, ymax=>125);
-$ooim->arc(r=>30, color=>$red, aa=>1);
-
-my $oocopy = $ooim->copy();
-ok($oocopy->arc(fill=>{image=>$fillim, 
-                       combine=>'normal',
-                       xoff=>5}, r=>40),
-   "image based fill");
-$oocopy->write(file=>'testout/t20_image.ppm');
-
-# a more complex version
-use Imager::Matrix2d ':handy';
-$oocopy = $ooim->copy;
-ok($oocopy->arc(fill=>{
-                       image=>$fillim,
-                       combine=>'normal',
-                       matrix=>m2d_rotate(degrees=>30),
-                       xoff=>5
-                       }, r=>40),
-   "transformed image based fill");
-$oocopy->write(file=>'testout/t20_image_xform.ppm');
-
-ok(!$oocopy->arc(fill=>{ hatch=>"not really a hatch" }, r=>20),
-   "error handling of automatic fill conversion");
-ok($oocopy->errstr =~ /Unknown hatch type/,
-   "error message for automatic fill conversion");
-
-# previous box fills to float images, or using the fountain fill
-# got into a loop here
-
-SKIP:
-{
-  skip("can't test without alarm()", 1) unless $Config{d_alarm};
-  local $SIG{ALRM} = sub { die; };
-
-  eval {
-    alarm(2);
-    ok($ooim->box(xmin=>20, ymin=>20, xmax=>80, ymax=>40,
-                  fill=>{ fountain=>'linear', xa=>20, ya=>20, xb=>80, 
-                          yb=>20 }), "linear box fill");
-    alarm 0;
-  };
-  $@ and ok(0, "linear box fill $@");
-}
-
-# test that passing in a non-array ref returns an error
-{
-  my $fill = Imager::Fill->new(fountain=>'linear',
-                               xa => 20, ya=>20, xb=>20, yb=>40,
-                               segments=>"invalid");
-  ok(!$fill, "passing invalid segments produces an error");
-  cmp_ok(Imager->errstr, '=~', 'array reference',
-         "check the error message");
-}
-
-# test that colors in segments are converted
-{
-  my @segs =
-    (
-     [ 0.0, 0.5, 1.0, '000000', '#FFF', 0, 0 ],
-    );
-  my $fill = Imager::Fill->new(fountain=>'linear',
-                               xa => 0, ya=>20, xb=>49, yb=>20,
-                               segments=>\@segs);
-  ok($fill, "check that color names are converted")
-    or print "# ",Imager->errstr,"\n";
-  my $im = Imager->new(xsize=>50, ysize=>50);
-  $im->box(fill=>$fill);
-  my $left = $im->getpixel('x'=>0, 'y'=>20);
-  ok(color_close($left, Imager::Color->new(0,0,0)),
-     "check black converted correctly");
-  my $right = $im->getpixel('x'=>49, 'y'=>20);
-  ok(color_close($right, Imager::Color->new(255,255,255)),
-     "check white converted correctly");
-
-  # check that invalid colors handled correctly
-  
-  my @segs2 =
-    (
-     [ 0.0, 0.5, 1.0, '000000', 'FxFxFx', 0, 0 ],
-    );
-  my $fill2 = Imager::Fill->new(fountain=>'linear',
-                               xa => 0, ya=>20, xb=>49, yb=>20,
-                               segments=>\@segs2);
-  ok(!$fill2, "check handling of invalid color names");
-  cmp_ok(Imager->errstr, '=~', 'No color named', "check error message");
-}
-
-{ # RT #35278
-  # hatch fills on a grey scale image don't adapt colors
-  for my $bits (8, 'double') {
-    my $im_g = Imager->new(xsize => 10, ysize => 10, channels => 1, bits => $bits);
-    $im_g->box(filled => 1, color => 'FFFFFF');
-    my $fill = Imager::Fill->new
-      (
-       combine => 'normal', 
-       hatch => 'weave', 
-       fg => '000000', 
-       bg => 'FFFFFF'
-      );
-    $im_g->box(fill => $fill);
-    my $im_c = Imager->new(xsize => 10, ysize => 10, channels => 3, bits => $bits);
-    $im_c->box(filled => 1, color => 'FFFFFF');
-    $im_c->box(fill => $fill);
-    my $im_cg = $im_g->convert(preset => 'rgb');
-    is_image($im_c, $im_cg, "check hatch is the same between color and greyscale (bits $bits)");
-
-    # check the same for image fills
-    my $grey_fill = Imager::Fill->new
-      (
-       image => $im_g, 
-       combine => 'normal'
-      );
-    my $im_cfg = Imager->new(xsize => 20, ysize => 20, bits => $bits);
-    $im_cfg->box(filled => 1, color => '808080');
-    $im_cfg->box(fill => $grey_fill);
-    my $rgb_fill = Imager::Fill->new
-      (
-       image => $im_cg, 
-       combine => 'normal'
-      );
-    my $im_cfc = Imager->new(xsize => 20, ysize => 20, bits => $bits);
-    $im_cfc->box(filled => 1, color => '808080');
-    $im_cfc->box(fill => $rgb_fill);
-    is_image($im_cfg, $im_cfc, "check filling from grey image matches filling from rgb (bits = $bits)");
-
-    my $im_gfg = Imager->new(xsize => 20, ysize => 20, channels => 1, bits => $bits);
-    $im_gfg->box(filled => 1, color => '808080');
-    $im_gfg->box(fill => $grey_fill);
-    my $im_gfg_c = $im_gfg->convert(preset => 'rgb');
-    is_image($im_gfg_c, $im_cfg, "check grey filled with grey against base (bits = $bits)");
-
-    my $im_gfc = Imager->new(xsize => 20, ysize => 20, channels => 1, bits => $bits);
-    $im_gfc->box(filled => 1, color => '808080');
-    $im_gfc->box(fill => $rgb_fill);
-    my $im_gfc_c = $im_gfc->convert(preset => 'rgb');
-    is_image($im_gfc_c, $im_cfg, "check grey filled with color against base (bits = $bits)");
-  }
-}
-
-{ # alpha modifying fills
-  { # 8-bit/sample
-    my $base_img = Imager->new(xsize => 4, ysize => 2, channels => 4);
-    $base_img->setscanline
-      (
-       x => 0, 
-       y => 0, 
-       pixels => 
-       [
-       map Imager::Color->new($_),
-       qw/FF000020 00FF0080 00008040 FFFF00FF/,
-       ],
-      );
-    $base_img->setscanline
-      (
-       x => 0, 
-       y => 1, 
-       pixels => 
-       [
-       map Imager::Color->new($_),
-       qw/FFFF00FF FF000000 00FF0080 00008040/
-       ]
-      );
-    my $base_fill = Imager::Fill->new
-      (
-       image => $base_img,
-       combine => "normal",
-      );
-    ok($base_fill, "make the base image fill");
-    my $fill50 = Imager::Fill->new(type => "opacity", opacity => 0.5, other => $base_fill)
-      or print "# ", Imager->errstr, "\n";
-    ok($fill50, "make 50% alpha translation fill");
-
-    { # 4 channel image
-      my $out = Imager->new(xsize => 10, ysize => 10, channels => 4);
-      $out->box(fill => $fill50);
-      is_color4($out->getpixel(x => 0, y => 0),
-               255, 0, 0, 16, "check alpha output");
-      is_color4($out->getpixel(x => 2, y => 1),
-               0, 255, 0, 64, "check alpha output");
-      $out->box(filled => 1, color => "000000");
-      is_color4($out->getpixel(x => 0, y => 0),
-               0, 0, 0, 255, "check after clear");
-      $out->box(fill => $fill50);
-      is_color4($out->getpixel(x => 4, y => 2),
-               16, 0, 0, 255, "check drawn against background");
-      is_color4($out->getpixel(x => 6, y => 3),
-               0, 64, 0, 255, "check drawn against background");
-    }
-    { # 3 channel image
-      my $out = Imager->new(xsize => 10, ysize => 10, channels => 3);
-      $out->box(fill => $fill50);
-      is_color3($out->getpixel(x => 0, y => 0),
-               16, 0, 0, "check alpha output");
-      is_color3($out->getpixel(x => 2, y => 1),
-               0, 64, 0, "check alpha output");
-      is_color3($out->getpixel(x => 0, y => 1),
-               128, 128, 0, "check alpha output");
-    }
-  }
-  { # double/sample
-    use Imager::Color::Float;
-    my $base_img = Imager->new(xsize => 4, ysize => 2, channels => 4, bits => "double");
-    $base_img->setscanline
-      (
-       x => 0, 
-       y => 0, 
-       pixels => 
-       [
-       map Imager::Color::Float->new(@$_),
-       [ 1, 0, 0, 0.125 ],
-       [ 0, 1, 0, 0.5 ],
-       [ 0, 0, 0.5, 0.25 ],
-       [ 1, 1, 0, 1 ],
-       ],
-      );
-    $base_img->setscanline
-      (
-       x => 0, 
-       y => 1, 
-       pixels => 
-       [
-       map Imager::Color::Float->new(@$_),
-       [ 1, 1, 0, 1 ],
-       [ 1, 0, 0, 0 ],
-       [ 0, 1, 0, 0.5 ],
-       [ 0, 0, 0.5, 0.25 ],
-       ]
-      );
-    my $base_fill = Imager::Fill->new
-      (
-       image => $base_img,
-       combine => "normal",
-      );
-    ok($base_fill, "make the base image fill");
-    my $fill50 = Imager::Fill->new(type => "opacity", opacity => 0.5, other => $base_fill)
-      or print "# ", Imager->errstr, "\n";
-    ok($fill50, "make 50% alpha translation fill");
-    my $out = Imager->new(xsize => 10, ysize => 10, channels => 4, bits => "double");
-    $out->box(fill => $fill50);
-    is_fcolor4($out->getpixel(x => 0, y => 0, type => "float"),
-             1, 0, 0, 0.0625, "check alpha output at 0,0");
-    is_fcolor4($out->getpixel(x => 2, y => 1, type => "float"),
-             0, 1, 0, 0.25, "check alpha output at 2,1");
-    $out->box(filled => 1, color => "000000");
-    is_fcolor4($out->getpixel(x => 0, y => 0, type => "float"),
-             0, 0, 0, 1, "check after clear");
-    $out->box(fill => $fill50);
-    is_fcolor4($out->getpixel(x => 4, y => 2, type => "float"),
-             0.0625, 0, 0, 1, "check drawn against background at 4,2");
-    is_fcolor4($out->getpixel(x => 6, y => 3, type => "float"),
-             0, 0.25, 0, 1, "check drawn against background at 6,3");
-  }
-  ok(!Imager::Fill->new(type => "opacity"),
-     "should fail to make an opacity fill with no other fill object");
-  is(Imager->errstr, "'other' parameter required to create opacity fill",
-     "check error message");
-  ok(!Imager::Fill->new(type => "opacity", other => "xx"),
-     "should fail to make an opacity fill with a bad other parameter");
-  is(Imager->errstr, "'other' parameter must be an Imager::Fill object to create an opacity fill", 
-        "check error message");
-
-  # check auto conversion of hashes
-  ok(Imager::Fill->new(type => "opacity", other => { solid => "FF0000" }),
-     "check we auto-create fills")
-    or print "# ", Imager->errstr, "\n";
-
-  {
-    # fill with combine none was modifying the wrong channel for a
-    # no-alpha target image
-    my $fill = Imager::Fill->new(solid => "#FFF", combine => "none");
-    my $fill2 = Imager::Fill->new
-      (
-       type => "opacity", 
-       opacity => 0.5,
-       other => $fill
-      );
-    my $im = Imager->new(xsize => 1, ysize => 1);
-    ok($im->box(fill => $fill2), "fill with replacement opacity fill");
-    is_color3($im->getpixel(x => 0, y => 0), 255, 255, 255,
-             "check for correct colour");
-  }
-
-  {
-    require Imager::Fountain;
-    my $fount = Imager::Fountain->new;
-    $fount->add(c1 => "FFFFFF"); # simple white to black
-    # base fill is a fountain
-    my $base_fill = Imager::Fill->new
-      (
-       fountain => "linear",
-       segments => $fount,
-       xa => 0, 
-       ya => 0,
-       xb => 100,
-       yb => 100,
-      );
-    ok($base_fill, "made fountain fill base");
-    my $op_fill = Imager::Fill->new
-      (
-       type => "opacity",
-       other => $base_fill,
-       opacity => 0.5,
-      );
-    ok($op_fill, "made opacity fountain fill");
-    my $im = Imager->new(xsize => 100, ysize => 100);
-    ok($im->box(fill => $op_fill), "draw with it");
-  }
-}
-
-{ # RT 71309
-  my $fount = Imager::Fountain->simple(colors => [ '#804041', '#804041' ],
-                                      positions => [ 0, 1 ]);
-  my $im = Imager->new(xsize => 40, ysize => 40);
-  $im->box(filled => 1, color => '#804040');
-  my $fill = Imager::Fill->new
-    (
-     combine => 0,
-     fountain => "linear",
-     segments => $fount,
-     xa => 0, ya => 0,
-     xb => 40, yb => 40,
-    );
-  $im->polygon(fill => $fill,
-              points => 
-              [
-               [ 0, 0 ],
-               [ 40, 20 ],
-               [ 20, 40 ],
-              ]
-             );
-  # the bug magnified the differences between the source and destination
-  # color, blending between the background and fill colors here only allows
-  # for those 2 colors in the result.
-  # with the bug extra colors appeared along the edge of the polygon.
-  is($im->getcolorcount, 2, "only original and fill color");
-}
-
-SKIP:
-{
-  # the wrong image dimension was used for adjusting vs yoff,
-  # producing uncovered parts of the output image
-  my $tx = Imager->new(xsize => 30, ysize => 20);
-  ok($tx, "create texture image")
-    or diag "create texture image", Imager->errstr;
-  $tx or skip "no texture image", 7;
-  ok($tx->box(filled => 1, color => "ff0000"), "fill texture image")
-    or diag "fill texture image", $tx->errstr;
-  my $cmp = Imager->new(xsize => 100, ysize => 100);
-  ok($cmp, "create comparison image")
-    or diag "create comparison image: ", Imager->errstr;
-  $cmp or skip "no comparison image", 5;
-  ok($cmp->box(filled => 1, color => "FF0000"), "fill compare image")
-    or diag "fill compare image: ", $cmp->errstr;
-  my $im = Imager->new(xsize => 100, ysize => 100);
-  ok($im, "make test image")
-    or diag "make test image: ", Imager->errstr;
-  $im or skip "no test image", 3;
-  my $fill = Imager::Fill->new(image => $tx, yoff => 10);
-  ok($fill, "make xoff=10 image fill")
-    or diag "make fill: ", Imager->errstr;
-  $fill or skip "no fill", 2;
-  ok($im->box(fill => $fill), "fill test image")
-    or diag "fill test image: ", $im->errstr;
-  is_image($im, $cmp, "check test image");
-}
-
-sub color_close {
-  my ($c1, $c2) = @_;
-
-  my @c1 = $c1->rgba;
-  my @c2 = $c2->rgba;
-
-  for my $i (0..2) {
-    if (abs($c1[$i]-$c2[$i]) > 2) {
-      return 0;
-    }
-  }
-  return 1;
-}
-
-sub color_close4 {
-  my ($c1, $c2) = @_;
-
-  my @c1 = $c1->rgba;
-  my @c2 = $c2->rgba;
-
-  for my $i (0..3) {
-    if (abs($c1[$i]-$c2[$i]) > 2) {
-      return 0;
-    }
-  }
-  return 1;
-}
-
-# for use during testing
-sub save {
-  my ($im, $name) = @_;
-
-  open FH, "> $name" or die "Cannot create $name: $!";
-  binmode FH;
-  my $io = Imager::io_new_fd(fileno(FH));
-  Imager::i_writeppm_wiol($im, $io) or die "Cannot save to $name";
-  undef $io;
-  close FH;
-}
diff --git a/t/t21draw.t b/t/t21draw.t
deleted file mode 100644 (file)
index a1f7f2a..0000000
+++ /dev/null
@@ -1,353 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 256;
-use Imager ':all';
-use Imager::Test qw(is_color3 is_image);
-use constant PI => 3.14159265358979;
-
--d "testout" or mkdir "testout";
-
-init_log("testout/t21draw.log",1);
-
-my $redobj = NC(255, 0, 0);
-my $red = 'FF0000';
-my $greenobj = NC(0, 255, 0);
-my $green = [ 0, 255, 0 ];
-my $blueobj = NC(0, 0, 255);
-my $blue = { hue=>240, saturation=>1, value=>1 };
-my $white = '#FFFFFF';
-
-{
-  my $img = Imager->new(xsize=>100, ysize=>500);
-
-  ok($img->box(color=>$blueobj, xmin=>10, ymin=>10, xmax=>48, ymax=>18),
-     "box with color obj");
-  ok($img->box(color=>$blue, xmin=>10, ymin=>20, xmax=>48, ymax=>28),
-     "box with color");
-  ok($img->box(color=>$redobj, xmin=>10, ymin=>30, xmax=>28, ymax=>48, filled=>1),
-     "filled box with color obj");
-  ok($img->box(color=>$red, xmin=>30, ymin=>30, xmax=>48, ymax=>48, filled=>1),
-     "filled box with color");
-
-  ok($img->arc('x'=>75, 'y'=>25, r=>24, color=>$redobj),
-     "filled arc with colorobj");
-
-  ok($img->arc('x'=>75, 'y'=>25, r=>20, color=>$green),
-     "filled arc with colorobj");
-  ok($img->arc('x'=>75, 'y'=>25, r=>18, color=>$white, d1=>325, d2=>225),
-     "filled arc with color");
-
-  ok($img->arc('x'=>75, 'y'=>25, r=>18, color=>$blue, d1=>225, d2=>325),
-     "filled arc with color");
-  ok($img->arc('x'=>75, 'y'=>25, r=>15, color=>$green, aa=>1),
-     "filled arc with color");
-
-  ok($img->line(color=>$blueobj, x1=>5, y1=>55, x2=>35, y2=>95),
-     "line with colorobj");
-
-  # FIXME - neither the start nor end-point is set for a non-aa line
-  my $c = Imager::i_get_pixel($img->{IMG}, 5, 55);
-  ok(color_cmp($c, $blueobj) == 0, "# TODO start point not set");
-
-  ok($img->line(color=>$red, x1=>10, y1=>55, x2=>40, y2=>95, aa=>1),
-     "aa line with color");
-  ok($img->line(color=>$green, x1=>15, y1=>55, x2=>45, y2=>95, antialias=>1),
-     "antialias line with color");
-
-  ok($img->polyline(points=>[ [ 55, 55 ], [ 90, 60 ], [ 95, 95] ],
-                   color=>$redobj),
-     "polyline points with color obj");
-  ok($img->polyline('x'=>[ 55, 85, 90 ], 'y'=>[60, 65, 95], color=>$green, aa=>1),
-     "polyline xy with color aa");
-  ok($img->polyline('x'=>[ 55, 80, 85 ], 'y'=>[65, 70, 95], color=>$green, 
-                   antialias=>1),
-     "polyline xy with color antialias");
-
-  ok($img->setpixel('x'=>[35, 37, 39], 'y'=>[55, 57, 59], color=>$red),
-     "set array of pixels");
-  ok($img->setpixel('x'=>39, 'y'=>55, color=>$green),
-     "set single pixel");
-  use Imager::Color::Float;
-  my $flred = Imager::Color::Float->new(1, 0, 0, 0);
-  my $flgreen = Imager::Color::Float->new(0, 1, 0, 0);
-  ok($img->setpixel('x'=>[41, 43, 45], 'y'=>[55, 57, 59], color=>$flred),
-     "set array of float pixels");
-  ok($img->setpixel('x'=>45, 'y'=>55, color=>$flgreen),
-     "set single float pixel");
-  my @gp = $img->getpixel('x'=>[41, 43, 45], 'y'=>[55, 57, 59]);
-  ok(grep($_->isa('Imager::Color'), @gp) == 3, "check getpixel result type");
-  ok(grep(color_cmp($_, NC(255, 0, 0)) == 0, @gp) == 3, 
-     "check getpixel result colors");
-  my $gp = $img->getpixel('x'=>45, 'y'=>55);
-  ok($gp->isa('Imager::Color'), "check scalar getpixel type");
-  ok(color_cmp($gp, NC(0, 255, 0)) == 0, "check scalar getpixel color");
-  @gp = $img->getpixel('x'=>[35, 37, 39], 'y'=>[55, 57, 59], type=>'float');
-  ok(grep($_->isa('Imager::Color::Float'), @gp) == 3, 
-     "check getpixel float result type");
-  ok(grep(color_cmp($_, $flred) == 0, @gp) == 3,
-     "check getpixel float result type");
-  $gp = $img->getpixel('x'=>39, 'y'=>55, type=>'float');
-  ok($gp->isa('Imager::Color::Float'), "check scalar float getpixel type");
-  ok(color_cmp($gp, $flgreen) == 0, "check scalar float getpixel color");
-
-  # more complete arc tests
-  ok($img->arc(x=>25, 'y'=>125, r=>20, d1=>315, d2=>45, color=>$greenobj),
-     "color arc through angle 0");
-  # use diff combine here to make sure double writing is noticable
-  ok($img->arc(x=>75, 'y'=>125, r=>20, d1=>315, d2=>45,
-              fill => { solid=>$blueobj, combine => 'diff' }),
-     "fill arc through angle 0");
-  ok($img->arc(x=>25, 'y'=>175, r=>20, d1=>315, d2=>225, color=>$redobj),
-     "concave color arc");
-  angle_marker($img, 25, 175, 23, 315, 225);
-  ok($img->arc(x=>75, 'y'=>175, r=>20, d1=>315, d2=>225,
-              fill => { solid=>$greenobj, combine=>'diff' }),
-     "concave fill arc");
-  angle_marker($img, 75, 175, 23, 315, 225);
-  ok($img->arc(x=>25, y=>225, r=>20, d1=>135, d2=>45, color=>$redobj),
-     "another concave color arc");
-  angle_marker($img, 25, 225, 23, 45, 135);
-  ok($img->arc(x=>75, y=>225, r=>20, d1=>135, d2=>45, 
-              fill => { solid=>$blueobj, combine=>'diff' }),
-     "another concave fillarc");
-  angle_marker($img, 75, 225, 23, 45, 135);
-  ok($img->arc(x=>25, y=>275, r=>20, d1=>135, d2=>45, color=>$redobj, aa=>1),
-     "concave color arc aa");
-  ok($img->arc(x=>75, y=>275, r=>20, d1=>135, d2=>45, 
-              fill => { solid=>$blueobj, combine=>'diff' }, aa=>1),
-     "concave fill arc aa");
-
-  ok($img->circle(x=>25, y=>325, r=>20, color=>$redobj),
-     "color circle no aa");
-  ok($img->circle(x=>75, y=>325, r=>20, color=>$redobj, aa=>1),
-     "color circle aa");
-  ok($img->circle(x=>25, 'y'=>375, r=>20, 
-                 fill => { hatch=>'stipple', fg=>$blueobj, bg=>$redobj }),
-     "fill circle no aa");
-  ok($img->circle(x=>75, 'y'=>375, r=>20, aa=>1,
-                 fill => { hatch=>'stipple', fg=>$blueobj, bg=>$redobj }),
-     "fill circle aa");
-
-  ok($img->arc(x=>50, y=>450, r=>45, d1=>135, d2=>45, 
-              fill => { solid=>$blueobj, combine=>'diff' }),
-     "another concave fillarc");
-  angle_marker($img, 50, 450, 47, 45, 135);
-
-  ok($img->write(file=>'testout/t21draw.ppm'),
-     "saving output");
-}
-
-{
-  my $im = Imager->new(xsize => 400, ysize => 400);
-  ok($im->arc(x => 200, y => 202, r => 10, filled => 0),
-     "draw circle outline");
-  is_color3($im->getpixel(x => 200, y => 202), 0, 0, 0,
-           "check center not filled");
-  ok($im->arc(x => 198, y => 200, r => 13, filled => 0, color => "#f88"),
-     "draw circle outline");
-  is_color3($im->getpixel(x => 198, y => 200), 0, 0, 0,
-           "check center not filled");
-  ok($im->arc(x => 200, y => 200, r => 24, filled => 0, color => "#0ff"),
-     "draw circle outline");
-  my $r = 40;
-  while ($r < 180) {
-    ok($im->arc(x => 200, y => 200, r => $r, filled => 0, color => "#ff0"),
-       "draw circle outline r $r");
-    $r += 15;
-  }
-  ok($im->write(file => "testout/t21circout.ppm"),
-     "save arc outline");
-}
-
-{
-  my $im = Imager->new(xsize => 400, ysize => 400);
-  {
-    my $lc = Imager::Color->new(32, 32, 32);
-    my $an = 0;
-    while ($an < 360) {
-      my $an_r = $an * PI / 180;
-      my $ca = cos($an_r);
-      my $sa = sin($an_r);
-      $im->line(aa => 1, color => $lc,
-               x1 => 198 + 5 * $ca, y1 => 202 + 5 * $sa,
-               x2 => 198 + 190 * $ca, y2 => 202 + 190 * $sa);
-      $an += 5;
-    }
-  }
-  my $d1 = 0;
-  my $r = 20;
-  while ($d1 < 350) {
-    ok($im->arc(x => 198, y => 202, r => $r, d1 => $d1, d2 => $d1+300, filled => 0),
-       "draw arc outline r$r d1$d1 len 300");
-    ok($im->arc(x => 198, y => 202, r => $r+3, d1 => $d1, d2 => $d1+40, filled => 0, color => '#FFFF00'),
-       "draw arc outline r$r d1$d1 len 40");
-    $d1 += 15;
-    $r += 6;
-  }
-  is_color3($im->getpixel(x => 198, y => 202), 0, 0, 0,
-           "check center not filled");
-  ok($im->write(file => "testout/t21arcout.ppm"),
-     "save arc outline");
-}
-
-{
-  my $im = Imager->new(xsize => 400, ysize => 400);
-  ok($im->arc(x => 197, y => 201, r => 10, filled => 0, aa => 1, color => 'white'),
-     "draw circle outline");
-  is_color3($im->getpixel(x => 197, y => 201), 0, 0, 0,
-           "check center not filled");
-  ok($im->arc(x => 197, y => 205, r => 13, filled => 0, color => "#f88", aa => 1),
-     "draw circle outline");
-  is_color3($im->getpixel(x => 197, y => 205), 0, 0, 0,
-           "check center not filled");
-  ok($im->arc(x => 190, y => 215, r => 24, filled => 0, color => [0,0, 255, 128], aa => 1),
-     "draw circle outline");
-  my $r = 40;
-  while ($r < 190) {
-    ok($im->arc(x => 197, y => 201, r => $r, filled => 0, aa => 1, color => '#ff0'), "draw aa circle rad $r");
-    $r += 7;
-  }
-  ok($im->write(file => "testout/t21aacircout.ppm"),
-     "save arc outline");
-}
-
-{
-  my $im = Imager->new(xsize => 400, ysize => 400);
-  {
-    my $lc = Imager::Color->new(32, 32, 32);
-    my $an = 0;
-    while ($an < 360) {
-      my $an_r = $an * PI / 180;
-      my $ca = cos($an_r);
-      my $sa = sin($an_r);
-      $im->line(aa => 1, color => $lc,
-               x1 => 198 + 5 * $ca, y1 => 202 + 5 * $sa,
-               x2 => 198 + 190 * $ca, y2 => 202 + 190 * $sa);
-      $an += 5;
-    }
-  }
-  my $d1 = 0;
-  my $r = 20;
-  while ($d1 < 350) {
-    ok($im->arc(x => 198, y => 202, r => $r, d1 => $d1, d2 => $d1+300, filled => 0, aa => 1),
-       "draw aa arc outline r$r d1$d1 len 300");
-    ok($im->arc(x => 198, y => 202, r => $r+3, d1 => $d1, d2 => $d1+40, filled => 0, color => '#FFFF00', aa => 1),
-       "draw aa arc outline r$r d1$d1 len 40");
-    $d1 += 15;
-    $r += 6;
-  }
-  is_color3($im->getpixel(x => 198, y => 202), 0, 0, 0,
-           "check center not filled");
-  ok($im->write(file => "testout/t21aaarcout.ppm"),
-     "save arc outline");
-}
-
-{
-  my $im = Imager->new(xsize => 400, ysize => 400);
-
-  my $an = 0;
-  my $step = 15;
-  while ($an <= 360-$step) {
-    my $cx = int(200 + 20 * cos(($an+$step/2) * PI / 180));
-    my $cy = int(200 + 20 * sin(($an+$step/2) * PI / 180));
-
-    ok($im->arc(x => $cx, y => $cy, aa => 1, color => "#fff", 
-               d1 => $an, d2 => $an+$step, filled => 0, r => 170),
-      "angle starting from $an");
-    ok($im->arc(x => $cx+0.5, y => $cy+0.5, aa => 1, color => "#ff0", 
-               d1 => $an, d2 => $an+$step, r => 168),
-      "filled angle starting from $an");
-
-    $an += $step;
-  }
-  ok($im->write(file => "testout/t21aaarcs.ppm"),
-     "save arc outline");
-}
-
-{
-  # we document that drawing from d1 to d2 where d2 > d1 will draw an
-  # arc going through 360 degrees, test that
-  my $im = Imager->new(xsize => 200, ysize => 200);
-  ok($im->arc(x => 100, y => 100, aa => 0, filled => 0, color => '#fff',
-             d1 => 270, d2 => 90, r => 90), "draw non-aa arc through 0");
-  ok($im->arc(x => 100, y => 100, aa => 1, filled => 0, color => '#fff',
-             d1 => 270, d2 => 90, r => 80), "draw aa arc through 0");
-  ok($im->write(file => "testout/t21arc0.ppm"),
-     "save arc through 0");
-}
-
-{
-  # test drawing color defaults
-  {
-    my $im = Imager->new(xsize => 10, ysize => 10);
-    ok($im->box(), "default outline the image"); # should outline the image
-    is_color3($im->getpixel(x => 0, y => 0), 255, 255, 255,
-             "check outline default color TL");
-    is_color3($im->getpixel(x => 9, y => 5), 255, 255, 255,
-             "check outline default color MR");
-  }
-
-  {
-    my $im = Imager->new(xsize => 10, ysize => 10);
-    ok($im->box(filled => 1), "default fill the image"); # should fill the image
-    is_color3($im->getpixel(x => 0, y => 0), 255, 255, 255,
-             "check fill default color TL");
-    is_color3($im->getpixel(x => 5, y => 5), 255, 255, 255,
-             "check fill default color MM");
-  }
-}
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->box(), "can't draw box to empty image");
-  is($empty->errstr, "box: empty input image", "check error message");
-  ok(!$empty->arc(), "can't draw arc to empty image");
-  is($empty->errstr, "arc: empty input image", "check error message");
-  ok(!$empty->line(x1 => 0, y1 => 0, x2 => 10, y2 => 0),
-     "can't draw line to empty image");
-  is($empty->errstr, "line: empty input image", "check error message");
-  ok(!$empty->polyline(points => [ [ 0, 0 ], [ 10, 0 ] ]),
-     "can't draw polyline to empty image");
-  is($empty->errstr, "polyline: empty input image", "check error message");
-  ok(!$empty->polygon(points => [ [ 0, 0 ], [ 10, 0 ], [ 0, 10 ] ]),
-     "can't draw polygon to empty image");
-  is($empty->errstr, "polygon: empty input image", "check error message");
-  ok(!$empty->flood_fill(x => 0, y => 0), "can't flood fill to empty image");
-  is($empty->errstr, "flood_fill: empty input image", "check error message");
-}
-
-
-malloc_state();
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t21draw.ppm";
-  unlink "testout/t21circout.ppm";
-  unlink "testout/t21aacircout.ppm";
-  unlink "testout/t21arcout.ppm";
-  unlink "testout/t21aaarcout.ppm";
-  unlink "testout/t21aaarcs.ppm";
-  unlink "testout/t21arc0.ppm";
-}
-
-sub color_cmp {
-  my ($l, $r) = @_;
-  my @l = $l->rgba;
-  my @r = $r->rgba;
-  # print "# (",join(",", @l[0..2]),") <=> (",join(",", @r[0..2]),")\n";
-  return $l[0] <=> $r[0]
-    || $l[1] <=> $r[1]
-      || $l[2] <=> $r[2];
-}
-
-sub angle_marker {
-  my ($img, $x, $y, $radius, @angles) = @_;
-
-  for my $angle (@angles) {
-    my $x1 = int($x + $radius * cos($angle * PI / 180) + 0.5);
-    my $y1 = int($y + $radius * sin($angle * PI / 180) + 0.5);
-    my $x2 = int($x + (5+$radius) * cos($angle * PI / 180) + 0.5);
-    my $y2 = int($y + (5+$radius) * sin($angle * PI / 180) + 0.5);
-    
-    $img->line(x1=>$x1, y1=>$y1, x2=>$x2, y2=>$y2, color=>'#FFF');
-  }
-}
diff --git a/t/t22flood.t b/t/t22flood.t
deleted file mode 100644 (file)
index 767cbab..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 15;
-use Imager;
-use Imager::Test qw(is_image);
-
--d "testout" or mkdir "testout";
-
-{ # flood_fill wouldn't fill to the right if the area was just a
-  # single scan-line
-  my $im = Imager->new(xsize => 5, ysize => 3);
-  ok($im, "make flood_fill test image");
-  ok($im->line(x1 => 0, y1 => 1, x2 => 4, y2 => 1, color => "white"),
-     "create fill area");
-  ok($im->flood_fill(x => 3, y => 1, color => "blue"),
-     "fill it");
-  my $cmp = Imager->new(xsize => 5, ysize => 3);
-  ok($cmp, "make test image");
-  ok($cmp->line(x1 => 0, y1 => 1, x2 => 4, y2 => 1, color => "blue"),
-     "synthezied filled area");
-  is_image($im, $cmp, "flood_fill filled horizontal line");
-}
-
-SKIP:
-{ # flood_fill won't fill entire line below if line above is shorter
-  my $im = Imager->new(file => "testimg/filltest.ppm");
-  ok($im, "Load test image")
-    or skip("Couldn't load test image: " . Imager->errstr, 3);
-
-  # fill from first bad place
-  my $fill1 = $im->copy;
-  ok($fill1->flood_fill(x => 8, y => 2, color => "#000000"),
-     "fill from a top most spot");
-  my $cmp = Imager->new(xsize => $im->getwidth, ysize => $im->getheight);
-  is_image($fill1, $cmp, "check it filled the lot");
-  ok($fill1->write(file => "testout/t22fill1.ppm"), "save");
-
-  # second bad place
-  my $fill2 = $im->copy;
-  ok($fill2->flood_fill(x => 17, y => 3, color => "#000000"),
-     "fill from not quite top most spot");
-  is_image($fill2, $cmp, "check it filled the lot");
-  ok($fill2->write(file => "testout/t22fill2.ppm"), "save");
-}
-
-{ # verticals
-  my $im = vimage("FFFFFF");
-  my $cmp = vimage("FF0000");
-
-  ok($im->flood_fill(x => 4, y=> 8, color => "FF0000"),
-     "fill at bottom of vertical well");
-  is_image($im, $cmp, "check the result");
-}
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t22fill1.ppm";
-  unlink "testout/t22fill2.ppm";
-}
-
-# make a vertical test image
-sub vimage {
-  my $c = shift;
-
-  my $im = Imager->new(xsize => 10, ysize => 10);
-  $im->line(x1 => 1, y1 => 1, x2 => 8, y2 => 1, color => $c);
-  $im->line(x1 => 4, y1 => 2, x2 => 4, y2 => 8, color => $c);
-
-  return $im;
-}
diff --git a/t/t31font.t b/t/t31font.t
deleted file mode 100644 (file)
index 0d1bd65..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-#!perl -w
-use strict;
-use Imager;
-use Test::More tests => 14;
-
-unshift @INC, "t";
-
-ok(Imager::Font->register(type => "test",
-                         class=>"GoodTestFont",
-                         files => "\\.ppm\$"),
-   "register a test font");
-
-ok(Imager::Font->register(type => "bad",
-                         class => "BadTestFont",
-                         files => "\\.ppm\$"),
-   "register a bad test font");
-
-ok(!Imager::Font->register(), "no register parameters");
-like(Imager->errstr, qr/No type parameter/, "check message");
-
-ok(!Imager::Font->register(type => "bad1"), "no class parameter");
-like(Imager->errstr, qr/No class parameter/, "check message");
-
-ok(!Imager::Font->register(type => "bad2", class => "BadFont", files => "**"),
-   "bad files parameter");
-is(Imager->errstr, "files isn't a valid regexp", "check message");
-
-Imager::Font->priorities("bad", "test");
-
-# RT #62855
-# previously we'd select the first file matched font driver, even if
-# it wasn't available, then crash loading it.
-
-SKIP:
-{
-  my $good;
-  ok(eval {
-    $good = Imager::Font->new(file => "testimg/penguin-base.ppm");
-  }, "load good font avoiding RT 62855")
-    or skip("Failed to load", 1);
-  ok($good->isa("GoodTestFont"), "and it's the right type");
-}
-
-
-use Imager::Font::Test;
-
-# check string() and align_string() handle an empty image
-{
-  my $font = Imager::Font::Test->new;
-  my $empty = Imager->new;
-  ok(!$empty->string(text => "foo", x => 0, y => 10, size => 10, font => $font),
-     "can't draw text on an empty image");
-  is($empty->errstr, "string: empty input image",
-     "check error message");
-  ok(!$empty->align_string(text => "foo", x => 0, y => 10, size => 10, font => $font),
-     "can't draw text on an empty image");
-  is($empty->errstr, "align_string: empty input image",
-     "check error message");
-}
diff --git a/t/t35ttfont.t b/t/t35ttfont.t
deleted file mode 100644 (file)
index e9fd3b9..0000000
+++ /dev/null
@@ -1,334 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 97;
-
-$|=1;
-
-BEGIN { use_ok(Imager => ':all') }
-use Imager::Test qw(diff_text_with_nul is_color3 is_image);
-
--d "testout" or mkdir "testout";
-
-init_log("testout/t35ttfont.log",2);
-
-SKIP:
-{
-  skip("freetype 1.x unavailable or disabled", 96) 
-    unless $Imager::formats{"tt"};
-  print "# has tt\n";
-  
-  my $deffont = './fontfiles/dodge.ttf';
-  my $fontname=$ENV{'TTFONTTEST'} || $deffont;
-
-  if (!ok(-f $fontname, "check test font file exists")) {
-    print "# cannot find fontfile for truetype test $fontname\n";
-    skip('Cannot load test font', 89);
-  }
-
-  #i_init_fonts();
-  #     i_tt_set_aa(1);
-  
-  my $bgcolor = i_color_new(255,0,0,0);
-  my $overlay = Imager::ImgRaw::new(320,140,3);
-  i_box_filled($overlay, 0, 0, 319, 139, i_color_new(128, 128, 128));
-  
-  my $ttraw = Imager::i_tt_new($fontname);
-  ok($ttraw, "create font");
-
-  my @bbox = i_tt_bbox($ttraw,50.0,'XMCLH',0);
-  is(@bbox, 8, "bounding box");
-  print "#bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n";
-
-  ok(i_tt_cp($ttraw,$overlay,5,50,1,50.0,'XM CLH',6,1,0), "cp output");
-  ok(i_tt_cp($ttraw,$overlay,5,120,1,50.0,'XM CLH',6,0,0), "cp output (non AA)");
-  i_line($overlay,0,50,100,50,$bgcolor,1);
-
-  open(FH,">testout/t35ttfont.ppm") || die "cannot open testout/t35ttfont.ppm\n";
-  binmode(FH);
-  my $IO = Imager::io_new_fd( fileno(FH) );
-  ok(i_writeppm_wiol($overlay, $IO), "save t35ttfont.ppm");
-  close(FH);
-
-  $bgcolor=i_color_set($bgcolor,200,200,200,0);
-  my $backgr=Imager::ImgRaw::new(500,300,3);
-  
-  #     i_tt_set_aa(2);
-  
-  ok(i_tt_text($ttraw,$backgr,100,120,$bgcolor,50.0,'te st',5,1,0),
-      "normal output");
-  ok(i_tt_text($ttraw,$backgr,100,200,$bgcolor,50.0,'te st',5,0,0),
-      "normal output (non AA)");
-
-  my $ugly = Imager::i_tt_new("./fontfiles/ImUgly.ttf");
-  ok($ugly, "create ugly font");
-  # older versions were dropping the bottom of g and the right of a
-  ok(i_tt_text($ugly, $backgr,100, 80, $bgcolor, 14, 'g%g', 3, 1, 0), 
-     "draw g%g");
-  ok(i_tt_text($ugly, $backgr,150, 80, $bgcolor, 14, 'delta', 6, 1, 0),
-      "draw delta");
-  i_line($backgr,0,20,499,20,i_color_new(0,127,0,0),1);
-  ok(i_tt_text($ttraw, $backgr, 20, 20, $bgcolor, 14, 'abcdefghijklmnopqrstuvwxyz{|}', 29, 1, 0), "alphabet");
-  ok(i_tt_text($ttraw, $backgr, 20, 50, $bgcolor, 14, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 26, 1, 0), "ALPHABET");
-  
-  # UTF8 tests
-  # for perl < 5.6 we can hand-encode text
-  # the following is "A\x{2010}A"
-  # 
-  my $text = pack("C*", 0x41, 0xE2, 0x80, 0x90, 0x41);
-  my $alttext = "A-A";
-  
-  my @utf8box = i_tt_bbox($ttraw, 50.0, $text, 1);
-  is(@utf8box, 8, "utf8 bbox element count");
-  my @base = i_tt_bbox($ttraw, 50.0, $alttext, 0);
-  is(@base, 8, "alt bbox element count");
-  my $maxdiff = $fontname eq $deffont ? 0 : $base[2] / 3;
-  print "# (@utf8box vs @base)\n";
-  ok(abs($utf8box[2] - $base[2]) <= $maxdiff, 
-     "compare box sizes $utf8box[2] vs $base[2] (maxerror $maxdiff)");
-  
-  # hand-encoded UTF8 drawing
-  ok(i_tt_text($ttraw, $backgr, 200, 80, $bgcolor, 14, $text, length($text), 1, 1), "draw hand-encoded UTF8");
-
-  ok(i_tt_cp($ttraw, $backgr, 250, 80, 1, 14, $text, length($text), 1, 1), 
-      "cp hand-encoded UTF8");
-
-  # ok, try native perl UTF8 if available
- SKIP:
-  {
-    skip("perl too old to test native UTF8 support", 5) unless $] >= 5.006;
-
-    my $text;
-    # we need to do this in eval to prevent compile time errors in older
-    # versions
-    eval q{$text = "A\x{2010}A"}; # A, HYPHEN, A in our test font
-    #$text = "A".chr(0x2010)."A"; # this one works too
-    ok(i_tt_text($ttraw, $backgr, 300, 80, $bgcolor, 14, $text, 0, 1, 0),
-       "draw UTF8");
-    ok(i_tt_cp($ttraw, $backgr, 350, 80, 0, 14, $text, 0, 1, 0),
-       "cp UTF8");
-    @utf8box = i_tt_bbox($ttraw, 50.0, $text, 0);
-    is(@utf8box, 8, "native utf8 bbox element count");
-    ok(abs($utf8box[2] - $base[2]) <= $maxdiff, 
-       "compare box sizes native $utf8box[2] vs $base[2] (maxerror $maxdiff)");
-    eval q{$text = "A\x{0905}\x{0906}\x{0103}A"}; # Devanagari
-    ok(i_tt_text($ugly, $backgr, 100, 160, $bgcolor, 36, $text, 0, 1, 0),
-       "more complex output");
-  }
-
-  open(FH,">testout/t35ttfont2.ppm") || die "cannot open testout/t35ttfont.ppm\n";
-  binmode(FH);
-  $IO = Imager::io_new_fd( fileno(FH) );
-  ok(i_writeppm_wiol($backgr, $IO), "save t35ttfont2.ppm");
-  close(FH);
-  
-  my $exists_font = "fontfiles/ExistenceTest.ttf";
-  my $hcfont = Imager::Font->new(file=>$exists_font, type=>'tt');
- SKIP:
-  {
-    ok($hcfont, "loading existence test font")
-      or skip("could not load test font", 20);
-
-    # list interface
-    my @exists = $hcfont->has_chars(string=>'!A');
-    ok(@exists == 2, "check return count");
-    ok($exists[0], "we have an exclamation mark");
-    ok(!$exists[1], "we have no exclamation mark");
-    
-    # scalar interface
-    my $exists = $hcfont->has_chars(string=>'!A');
-    ok(length($exists) == 2, "check return length");
-    ok(ord(substr($exists, 0, 1)), "we have an exclamation mark");
-    ok(!ord(substr($exists, 1, 1)), "we have no upper-case A");
-    
-    my $face_name = Imager::i_tt_face_name($hcfont->{id});
-    print "# face $face_name\n";
-    is($face_name, 'ExistenceTest', "face name (function)");
-    $face_name = $hcfont->face_name;
-    is($face_name, 'ExistenceTest', "face name (OO)");
-    
-    # FT 1.x cheats and gives names even if the font doesn't have them
-    my @glyph_names = $hcfont->glyph_names(string=>"!J/");
-    is($glyph_names[0], 'exclam', "check exclam name OO");
-    ok(!defined($glyph_names[1]), "check for no J name OO");
-    is($glyph_names[2], 'slash', "check slash name OO");
-    
-    print "# ** name table of the test font **\n";
-    Imager::i_tt_dump_names($hcfont->{id});
-
-    # the test font is known to have a shorter advance width for that char
-    my @bbox = $hcfont->bounding_box(string=>"/", size=>100);
-    is(@bbox, 8, "should be 8 entries");
-    isnt($bbox[6], $bbox[2], "different advance width from pos width");
-    print "# @bbox\n";
-    my $bbox = $hcfont->bounding_box(string=>"/", size=>100);
-    isnt($bbox->pos_width, $bbox->advance_width, "OO check");
-
-    cmp_ok($bbox->right_bearing, '<', 0, "check right bearing");
-
-    cmp_ok($bbox->display_width, '>', $bbox->advance_width,
-           "check display width (roughly)");
-
-    # check with a char that fits inside the box
-    $bbox = $hcfont->bounding_box(string=>"!", size=>100);
-    print "# @$bbox\n";
-    print "# pos width ", $bbox->pos_width, "\n";
-    is($bbox->pos_width, $bbox->advance_width, 
-       "check backwards compatibility");
-    cmp_ok($bbox->left_bearing, '>', 0, "left bearing positive");
-    cmp_ok($bbox->right_bearing, '>', 0, "right bearing positive");
-    cmp_ok($bbox->display_width, '<', $bbox->advance_width,
-           "display smaller than advance");
-  }
-  undef $hcfont;
-  
-  my $name_font = "fontfiles/NameTest.ttf";
-  $hcfont = Imager::Font->new(file=>$name_font, type=>'tt');
- SKIP:
-  {
-    ok($hcfont, "loading name font")
-      or skip("could not load name font $name_font", 3);
-    # make sure a missing string parameter is handled correctly
-    eval {
-      $hcfont->glyph_names();
-    };
-    is($@, "", "correct error handling");
-    cmp_ok(Imager->errstr, '=~', qr/no string parameter/, "error message");
-    
-    my $text = pack("C*", 0xE2, 0x80, 0x90); # "\x{2010}" as utf-8
-    my @names = $hcfont->glyph_names(string=>$text, utf8=>1);
-    is($names[0], "hyphentwo", "check utf8 glyph name");
-  }
-
-  undef $hcfont;
-  
- SKIP:
-  { print "# alignment tests\n";
-    my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'tt');
-    ok($font, "loaded deffont OO")
-      or skip("could not load font:".Imager->errstr, 4);
-    my $im = Imager->new(xsize=>140, ysize=>150);
-    my %common = 
-      (
-       font=>$font, 
-       size=>40, 
-       aa=>1,
-      );
-    $im->line(x1=>0, y1=>40, x2=>139, y2=>40, color=>'blue');
-    $im->line(x1=>0, y1=>90, x2=>139, y2=>90, color=>'blue');
-    $im->line(x1=>0, y1=>110, x2=>139, y2=>110, color=>'blue');
-    for my $args ([ x=>5,   text=>"A", color=>"white" ],
-                  [ x=>40,  text=>"y", color=>"white" ],
-                  [ x=>75,  text=>"A", channel=>1 ],
-                  [ x=>110, text=>"y", channel=>1 ]) {
-      ok($im->string(%common, @$args, 'y'=>40), "A no alignment");
-      ok($im->string(%common, @$args, 'y'=>90, align=>1), "A align=1");
-      ok($im->string(%common, @$args, 'y'=>110, align=>0), "A align=0");
-    }
-    ok($im->write(file=>'testout/t35align.ppm'), "save align image");
-  }
-
-  { # Ticket #14804 Imager::Font->new() doesn't report error details
-    # when using freetype 1
-    # make sure we're using C locale for messages
-    use POSIX qw(setlocale LC_ALL);
-    setlocale(LC_ALL, "C");
-
-    my $font = Imager::Font->new(file=>'t/t35ttfont.t', type=>'tt');
-    ok(!$font, "font creation should have failed for invalid file");
-    cmp_ok(Imager->errstr, 'eq', 'Invalid file format.',
-         "test error message");
-
-    setlocale(LC_ALL, "");
-  }
-
-  { # check errstr set correctly
-    my $font = Imager::Font->new(file=>$fontname, type=>'tt',
-                               size => undef);
-    ok($font, "made size error test font");
-    my $im = Imager->new(xsize=>100, ysize=>100);
-    ok($im, "made size error test image");
-    ok(!$im->string(font=>$font, x=>10, 'y'=>50, string=>"Hello"),
-       "drawing should fail with no size");
-    is($im->errstr, "No font size provided", "check error message");
-
-    # try no string
-    ok(!$im->string(font=>$font, x=>10, 'y'=>50, size=>15),
-       "drawing should fail with no string");
-    is($im->errstr, "missing required parameter 'string'",
-       "check error message");
-  }
-
-  { # introduced in 0.46 - outputting just space crashes
-    my $im = Imager->new(xsize=>100, ysize=>100);
-    my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', size=>14);
-    ok($im->string(font=>$font, x=> 5, 'y' => 50, string=>' '),
-      "outputting just a space was crashing");
-  }
-
-  { # string output cut off at NUL ('\0')
-    # https://rt.cpan.org/Ticket/Display.html?id=21770 cont'd
-    my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'tt');
-    ok($font, "loaded imugly");
-
-    diff_text_with_nul("a\\0b vs a", "a\0b", "a", 
-                      font => $font, color => '#FFFFFF');
-    diff_text_with_nul("a\\0b vs a", "a\0b", "a", 
-                      font => $font, channel => 1);
-
-    # UTF8 encoded \x{2010}
-    my $dash = pack("C*", 0xE2, 0x80, 0x90);
-    diff_text_with_nul("utf8 dash\\0dash vs dash", "$dash\0$dash", $dash,
-                      font => $font, color => '#FFFFFF', utf8 => 1);
-    diff_text_with_nul("utf8 dash\\0dash vs dash", "$dash\0$dash", $dash,
-                      font => $font, channel => 1, utf8 => 1);
-  }
-
- SKIP:
-  { # RT 11972
-    # when rendering to a transparent image the coverage should be
-    # expressed in terms of the alpha channel rather than the color
-    my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'tt');
-    ok($font, "loaded fontfiles/ImUgly.ttf")
-      or skip("Could not load test font: ".Imager->errstr, 4);
-    my $im = Imager->new(xsize => 40, ysize => 20, channels => 4);
-    ok($im->string(string => "AB", size => 20, aa => 1, color => '#F00',
-                  x => 0, y => 15, font => $font),
-       "draw to transparent image");
-    #$im->write(file => "foo.png");
-    my $im_noalpha = $im->convert(preset => 'noalpha');
-    my $im_pal = $im->to_paletted(make_colors => 'mediancut');
-    my @colors = $im_pal->getcolors;
-    is(@colors, 2, "should be only 2 colors");
-    @colors = sort { ($a->rgba)[0] <=> ($b->rgba)[0] } @colors;
-    is_color3($colors[0], 0, 0, 0, "check we got black");
-    is_color3($colors[1], 255, 0, 0, "and red");
-  }
-
- SKIP:
-  { # RT 71564
-    my $noalpha = Imager::Color->new(255, 255, 255, 0);
-    my $font = Imager::Font->new(file=>'fontfiles/ImUgly.ttf', type=>'tt',
-                                color => $noalpha);
-    ok($font, "loaded fontfiles/ImUgly.ttf")
-      or skip("Could not load test font: ".Imager->errstr, 4);
-    {
-      my $im = Imager->new(xsize => 40, ysize => 20);
-      my $copy = $im->copy;
-      ok($im->string(string => "AB", size => 20, aa => 1,
-                    x => 0, y => 15, font => $font),
-        "draw with transparent color, aa");
-      is_image($im, $copy, "should draw nothing");
-    }
-    {
-      my $im = Imager->new(xsize => 40, ysize => 20);
-      my $copy = $im->copy;
-      ok($im->string(string => "AB", size => 20, aa => 0,
-                    x => 0, y => 15, font => $font),
-        "draw with transparent color, non-aa");
-      local $TODO = "RT 73359 - non-AA text isn't normal mode rendered";
-      is_image($im, $copy, "should draw nothing");
-    }
-  }
-
-  ok(1, "end of code");
-}
diff --git a/t/t36oofont.t b/t/t36oofont.t
deleted file mode 100644 (file)
index 2982681..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/perl -w
-use strict;
-
-#use lib qw(blib/lib blib/arch);
-
-# Before `make install' is performed this script should be runnable with
-# `make test'. After `make install' it should work as `perl test.pl'
-
-######################### We start with some black magic to print on failure.
-
-# Change 1..1 below to 1..last_test_to_print .
-# (It may become useful if the test is moved to ./t subdirectory.)
-use Test::More tests => 16;
-
-BEGIN { use_ok('Imager') };
-
-BEGIN {
-  require Imager::Test;
-  Imager::Test->import(qw(isnt_image));
-}
-
--d "testout" or mkdir "testout";
-
-Imager->open_log(log => "testout/t36oofont.log");
-
-my $fontname_tt=$ENV{'TTFONTTEST'}||'./fontfiles/dodge.ttf';
-
-my $green=Imager::Color->new(92,205,92,128);
-die $Imager::ERRSTR unless $green;
-my $red=Imager::Color->new(205, 92, 92, 255);
-die $Imager::ERRSTR unless $red;
-
-SKIP:
-{
-  $Imager::formats{"tt"} && -f $fontname_tt
-    or skip("FT1.x missing or disabled", 14);
-
-  my $img=Imager->new(xsize=>300, ysize=>100) or die "$Imager::ERRSTR\n";
-
-  my $font=Imager::Font->new(file=>$fontname_tt,size=>25)
-    or die $img->{ERRSTR};
-
-  ok(1, "create TT font object");
-
-  ok($img->string(font=>$font, text=>"XMCLH", 'x'=>100, 'y'=>100),
-      "draw text");
-
-  $img->line(x1=>0, x2=>300, y1=>50, y2=>50, color=>$green);
-
-  my $text="LLySja";
-  my @bbox=$font->bounding_box(string=>$text, 'x'=>0, 'y'=>50);
-
-  is(@bbox, 8, "bbox list size");
-
-  $img->box(box=>\@bbox, color=>$green);
-
-  $text = pack("C*", 0x41, 0xE2, 0x80, 0x90, 0x41);
-  ok($img->string(font=>$font, text=>$text, 'x'=>100, 'y'=>50, utf8=>1),
-      "draw hand-encoded UTF8 text");
-
- SKIP:
-  {
-    $] >= 5.006
-      or skip("perl too old for native utf8", 1);
-    eval q{$text = "A\x{2010}A"};
-    ok($img->string(font=>$font, text=>$text, 'x'=>200, 'y'=>50),
-       "draw native UTF8 text");
-  }
-
-  ok($img->write(file=>"testout/t36oofont2.ppm", type=>'pnm'),
-      "write t36oofont2.ppm")
-    or print "# ", $img->errstr,"\n";
-
-  ok($font->utf8, "make sure utf8 method returns true");
-
-  my $has_chars = $font->has_chars(string=>"\x01A");
-  is($has_chars, "\x00\x01", "has_chars scalar");
-  my @has_chars = $font->has_chars(string=>"\x01A");
-  ok(!$has_chars[0], "has_chars list 0");
-  ok($has_chars[1], "has_chars list 1");
-
-  { # RT 71469
-    my $font1 = Imager::Font->new(file => $fontname_tt, type => "tt");
-    my $font2 = Imager::Font::Truetype->new(file => $fontname_tt);
-
-    for my $font ($font1, $font2) {
-      print "# ", join(",", $font->{color}->rgba), "\n";
-
-      my $im = Imager->new(xsize => 20, ysize => 20, channels => 4);
-
-      ok($im->string(text => "T", font => $font, y => 15),
-        "draw with default color")
-       or print "# ", $im->errstr, "\n";
-      my $work = Imager->new(xsize => 20, ysize => 20);
-      my $cmp = $work->copy;
-      $work->rubthrough(src => $im);
-      isnt_image($work, $cmp, "make sure something was drawn");
-    }
-  }
-}
-
-ok(1, "end");
diff --git a/t/t37std.t b/t/t37std.t
deleted file mode 100644 (file)
index 73e28f5..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#!perl -w
-use strict;
-use Imager::Test qw(std_font_tests std_font_test_count);
-use Imager::Font;
-use Test::More;
-
-$Imager::formats{tt}
-       or plan skip_all => "No tt available";
-
-Imager->open_log(log => "testout/t37std.log");
-
-plan tests => std_font_test_count();
-
-my $font = Imager::Font->new(file => "fontfiles/dodge.ttf",
-                            type => "tt");
-my $name_font =
-  Imager::Font->new(file => "fontfiles/ImUgly.ttf",
-                   type => "tt");
-
-SKIP:
-{
-  $font
-    or skip "Cannot load font", std_font_test_count();
-  std_font_tests
-    ({
-      font => $font,
-      has_chars => [ 1, 1, 1 ],
-      glyph_name_font => $name_font,
-      glyph_names => [ qw(A uni2010 A) ],
-     });
-}
-
-Imager->close_log;
diff --git a/t/t40scale.t b/t/t40scale.t
deleted file mode 100644 (file)
index 6a99554..0000000
+++ /dev/null
@@ -1,255 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 232;
-
-BEGIN { use_ok(Imager=>':all') }
-use Imager::Test qw(is_image is_color4 is_image_similar);
-
--d "testout" or mkdir "testout";
-
-Imager::init('log'=>'testout/t40scale.log');
-my $img=Imager->new();
-
-ok($img->open(file=>'testimg/scale.ppm',type=>'pnm'),
-   "load test image") or print "# ",$img->errstr,"\n";
-
-my $scaleimg=$img->scale(scalefactor=>0.25)
-  or print "# ",$img->errstr,"\n";
-ok($scaleimg, "scale it (good mode)");
-
-ok($scaleimg->write(file=>'testout/t40scale1.ppm',type=>'pnm'),
-   "save scaled image") or print "# ",$img->errstr,"\n";
-
-$scaleimg=$img->scale(scalefactor=>0.25,qtype=>'preview');
-ok($scaleimg, "scale it (preview)") or print "# ",$img->errstr,"\n";
-
-ok($scaleimg->write(file=>'testout/t40scale2.ppm',type=>'pnm'),
-   "write preview scaled image")  or print "# ",$img->errstr,"\n";
-
-$scaleimg = $img->scale(scalefactor => 0.25, qtype => 'mixing');
-ok($scaleimg, "scale it (mixing)") or print "# ", $img->errstr, "\n";
-ok($scaleimg->write(file=>'testout/t40scale3.ppm', type=>'pnm'),
-   "write mixing scaled image") or print "# ", $img->errstr, "\n";
-
-{ # double image scaling with mixing, since it has code to handle it
-  my $dimg = Imager->new(xsize => $img->getwidth, ysize => $img->getheight,
-                         channels => $img->getchannels,
-                         bits => 'double');
-  ok($dimg, "create double/sample image");
-  $dimg->paste(src => $img);
-  $scaleimg = $dimg->scale(scalefactor => 0.25, qtype => 'mixing');
-  ok($scaleimg, "scale it (mixing, double)");
-  ok($scaleimg->write(file => 'testout/t40mixdbl.ppm', type => 'pnm'),
-     "write double/mixing scaled image");
-  is($scaleimg->bits, 'double', "got the right image type as output");
-
-  # hscale only, mixing
-  $scaleimg = $dimg->scale(xscalefactor => 0.33, yscalefactor => 1.0,
-                           qtype => 'mixing');
-  ok($scaleimg, "scale it (hscale, mixing, double)");
-  is($scaleimg->getheight, $dimg->getheight, "same height");
-  ok($scaleimg->write(file => 'testout/t40hscdmix.ppm', type => 'pnm'),
-     "save it");
-
-  # vscale only, mixing
-  $scaleimg = $dimg->scale(xscalefactor => 1.0, yscalefactor => 0.33,
-                           qtype => 'mixing');
-  ok($scaleimg, "scale it (vscale, mixing, double)");
-  is($scaleimg->getwidth, $dimg->getwidth, "same width");
-  ok($scaleimg->write(file => 'testout/t40vscdmix.ppm', type => 'pnm'),
-     "save it");
-}
-
-{
-  # check for a warning when scale() is called in void context
-  my $warning;
-  local $SIG{__WARN__} = 
-    sub { 
-      $warning = "@_";
-      my $printed = $warning;
-      $printed =~ s/\n$//;
-      $printed =~ s/\n/\n\#/g; 
-      print "# ",$printed, "\n";
-    };
-  $img->scale(scalefactor=>0.25);
-  cmp_ok($warning, '=~', qr/void/, "check warning");
-  cmp_ok($warning, '=~', qr/t40scale\.t/, "check filename");
-  $warning = '';
-  $img->scaleX(scalefactor=>0.25);
-  cmp_ok($warning, '=~', qr/void/, "check warning");
-  cmp_ok($warning, '=~', qr/t40scale\.t/, "check filename");
-  $warning = '';
-  $img->scaleY(scalefactor=>0.25);
-  cmp_ok($warning, '=~', qr/void/, "check warning");
-  cmp_ok($warning, '=~', qr/t40scale\.t/, "check filename");
-}
-{ # https://rt.cpan.org/Ticket/Display.html?id=7467
-  # segfault in Imager 0.43
-  # make sure scale() doesn't let us make an image zero pixels high or wide
-  # it does this by making the given axis as least 1 pixel high
-  my $out = $img->scale(scalefactor=>0.00001);
-  is($out->getwidth, 1, "min scale width");
-  is($out->getheight, 1, "min scale height");
-
-  $out = $img->scale(scalefactor=>0.00001, qtype => 'preview');
-  is($out->getwidth, 1, "min scale width (preview)");
-  is($out->getheight, 1, "min scale height (preview)");
-
-  $out = $img->scale(scalefactor=>0.00001, qtype => 'mixing');
-  is($out->getwidth, 1, "min scale width (mixing)");
-  is($out->getheight, 1, "min scale height (mixing)");
-}
-
-{ # error handling - NULL image
-  my $im = Imager->new;
-  ok(!$im->scale(scalefactor => 0.5), "try to scale empty image");
-  is($im->errstr, "scale: empty input image", "check error message");
-
-  # scaleX/scaleY
-  ok(!$im->scaleX(scalefactor => 0.5), "try to scaleX empty image");
-  is($im->errstr, "scaleX: empty input image", "check error message");
-  ok(!$im->scaleY(scalefactor => 0.5), "try to scaleY empty image");
-  is($im->errstr, "scaleY: empty input image", "check error message");
-}
-
-{ # invalid qtype value
-  my $im = Imager->new(xsize => 100, ysize => 100);
-  ok(!$im->scale(scalefactor => 0.5, qtype=>'unknown'), "unknown qtype");
-  is($im->errstr, "invalid value for qtype parameter", "check error message");
-  
-  # invalid type value
-  ok(!$im->scale(xpixels => 10, ypixels=>50, type=>"unknown"), "unknown type");
-  is($im->errstr, "invalid value for type parameter", "check error message");
-}
-
-SKIP:
-{ # Image::Math::Constrain support
-  eval "require Image::Math::Constrain;";
-  $@ and skip "optional module Image::Math::Constrain not installed", 3;
-  my $constrain = Image::Math::Constrain->new(20, 100);
-  my $im = Imager->new(xsize => 160, ysize => 96);
-  my $result = $im->scale(constrain => $constrain);
-  ok($result, "successful scale with Image::Math::Constrain");
-  is($result->getwidth, 20, "check result width");
-  is($result->getheight, 12, "check result height");
-}
-
-{ # scale size checks
-  my $im = Imager->new(xsize => 160, ysize => 96); # some random size
-
-  scale_test($im, 'scale', 80, 48, "48 x 48 def type",
-            xpixels => 48, ypixels => 48);
-  scale_test($im, 'scale', 80, 48, "48 x 48 max type",
-            xpixels => 48, ypixels => 48, type => 'max');
-  scale_test($im, 'scale', 80, 48, "80 x 80 min type",
-            xpixels => 80, ypixels => 80, type => 'min');
-  scale_test($im, 'scale', 80, 48, "no scale parameters (default to 0.5 scalefactor)");
-  scale_test($im, 'scale', 120, 72, "0.75 scalefactor",
-            scalefactor => 0.75);
-  scale_test($im, 'scale', 80, 48, "80 width",
-            xpixels => 80);
-  scale_test($im, 'scale', 120, 72, "72 height",
-            ypixels => 72);
-
-  # new scaling parameters in 0.54
-  scale_test($im, 'scale', 80, 48, "xscale 0.5",
-            xscalefactor => 0.5);
-  scale_test($im, 'scale', 80, 48, "yscale 0.5",
-            yscalefactor => 0.5);
-  scale_test($im, 'scale', 40, 48, "xscale 0.25 yscale 0.5",
-            xscalefactor => 0.25, yscalefactor => 0.5);
-  scale_test($im, 'scale', 160, 48, "xscale 1.0 yscale 0.5",
-            xscalefactor => 1.0, yscalefactor => 0.5);
-  scale_test($im, 'scale', 160, 48, "xpixels 160 ypixels 48 type nonprop",
-            xpixels => 160, ypixels => 48, type => 'nonprop');
-  scale_test($im, 'scale', 160, 96, "xpixels 160 ypixels 96",
-            xpixels => 160, ypixels => 96);
-  scale_test($im, 'scale', 80, 96, "xpixels 80 ypixels 96 type nonprop",
-            xpixels => 80, ypixels => 96, type => 'nonprop');
-
-  # scaleX
-  scale_test($im, 'scaleX', 80, 96, "defaults");
-  scale_test($im, 'scaleX', 40, 96, "0.25 scalefactor",
-             scalefactor => 0.25);
-  scale_test($im, 'scaleX', 120, 96, "pixels 120",
-             pixels => 120);
-
-  # scaleY
-  scale_test($im, 'scaleY', 160, 48, "defaults");
-  scale_test($im, 'scaleY', 160, 192, "2.0 scalefactor",
-             scalefactor => 2.0);
-  scale_test($im, 'scaleY', 160, 144, "pixels 144",
-             pixels => 144);
-}
-
-{ # check proper alpha handling for mixing
-  my $im = Imager->new(xsize => 40, ysize => 40, channels => 4);
-  $im->box(filled => 1, color => 'C0C0C0');
-  my $rot = $im->rotate(degrees => -4)
-    or die;
-  $rot = $rot->to_rgb16;
-  my $sc = $rot->scale(qtype => 'mixing', xpixels => 40);
-  my $out = Imager->new(xsize => $sc->getwidth, ysize => $sc->getheight);
-  $out->box(filled => 1, color => 'C0C0C0');
-  my $cmp = $out->copy;
-  $out->rubthrough(src => $sc);
-  is_image($out, $cmp, "check we get the right image after scaling (mixing)");
-
-  # we now set alpha=0 pixels to zero on scaling
-  is_color4($sc->getpixel('x' => 39, 'y' => 39), 0, 0, 0, 0,
-           "check we set alpha=0 pixels to zero on scaling");
-}
-
-{ # check proper alpha handling for default scaling
-  my $im = Imager->new(xsize => 40, ysize => 40, channels => 4);
-  $im->box(filled => 1, color => 'C0C0C0');
-  my $rot = $im->rotate(degrees => -4)
-    or die;
-  my $sc = $rot->scale(qtype => "normal", xpixels => 40);
-  my $out = Imager->new(xsize => $sc->getwidth, ysize => $sc->getheight);
-  $out->box(filled => 1, color => 'C0C0C0');
-  my $cmp = $out->copy;
-  $out->rubthrough(src => $sc);
-  is_image_similar($out, $cmp, 100, "check we get the right image after scaling (normal)");
-
-  # we now set alpha=0 pixels to zero on scaling
-  is_color4($sc->getpixel('x' => 39, 'y' => 39), 0, 0, 0, 0,
-           "check we set alpha=0 pixels to zero on scaling");
-}
-
-{ # scale_calculate
-  my $im = Imager->new(xsize => 100, ysize => 120);
-  is_deeply([ $im->scale_calculate(scalefactor => 0.5) ],
-           [ 0.5, 0.5, 50, 60 ],
-           "simple scale_calculate");
-  is_deeply([ Imager->scale_calculate(scalefactor => 0.5) ],
-           [], "failed scale_calculate");
-  is_deeply([ Imager->scale_calculate(width => 120, height => 150,
-                                     xpixels => 240) ],
-           [ 2.0, 2.0, 240, 300 ],
-           "class method scale_factor");
-}
-
-{ # passing a reference for scaling parameters should fail
-  # RT #35172
-  my $im = Imager->new(xsize => 100, ysize => 100);
-  ok(!$im->scale(xpixels => {}), "can't use a reference as a size");
-  cmp_ok($im->errstr, '=~', "xpixels parameter cannot be a reference",
-        "check error message");
-}
-
-sub scale_test {
-  my ($in, $method, $exp_width, $exp_height, $note, @parms) = @_;
-
-  print "# $note: @parms\n";
-  for my $qtype (qw(normal preview mixing)) {
-  SKIP:
-    {
-      my $scaled = $in->$method(@parms, qtype => $qtype);
-      ok($scaled, "$method $note qtype $qtype")
-       or skip("failed to scale", 2);
-      is($scaled->getwidth, $exp_width, "check width");
-      is($scaled->getheight, $exp_height, "check height");
-    }
-  }
-}
diff --git a/t/t50basicoo.t b/t/t50basicoo.t
deleted file mode 100644 (file)
index 994032f..0000000
+++ /dev/null
@@ -1,367 +0,0 @@
-#!perl -w
-######################### We start with some black magic to print on failure.
-
-# this used to do the check for the load of Imager, but I want to be able 
-# to count tests, which means I need to load Imager first
-# since many of the early tests already do this, we don't really need to
-
-use strict;
-use Imager;
-use IO::Seekable;
-
-my $buggy_giflib_file = "buggy_giflib.txt";
-
--d "testout" or mkdir "testout";
-
-Imager::init("log"=>"testout/t50basicoo.log");
-
-# single image/file types
-my @types = qw( jpeg png raw pnm gif tiff bmp tga );
-
-# multiple image/file formats
-my @mtypes = qw(tiff gif);
-
-my %hsh=%Imager::formats;
-
-my $test_num = 0;
-my $count;
-for my $type (@types) {
-  $count += 31 if $hsh{$type};
-}
-for my $type (@mtypes) {
-  $count += 7 if $hsh{$type};
-}
-
-print "1..$count\n";
-
-print "# avaliable formats:\n";
-for(keys %hsh) { print "# $_\n"; }
-
-#print Dumper(\%hsh);
-
-my $img = Imager->new();
-
-my %files;
-@files{@types} = ({ file => "JPEG/testimg/209_yonge.jpg"  },
-                 { file => "testimg/test.png"  },
-                 { file => "testimg/test.raw", xsize=>150, ysize=>150, type=>'raw', interleave => 0},
-                 { file => "testimg/penguin-base.ppm"  },
-                 { file => "GIF/testimg/expected.gif"  },
-                 { file => "TIFF/testimg/comp8.tif" },
-                  { file => "testimg/winrgb24.bmp" },
-                  { file => "testimg/test.tga" }, );
-my %writeopts =
-  (
-   gif=> { make_colors=>'webmap', translate=>'closest', gifquant=>'gen',
-         gif_delay=>20 },
-  );
-
-for my $type (@types) {
-  next unless $hsh{$type};
-  print "# type $type\n";
-  my %opts = %{$files{$type}};
-  my @a = map { "$_=>${opts{$_}}" } keys %opts;
-  print "#opening Format: $type, options: @a\n";
-  ok($img->read( %opts ), "reading from file", $img);
-  #or die "failed: ",$img->errstr,"\n";
-
-  my %mopts = %opts;
-  delete $mopts{file};
-
-  # read from a file handle
-  my $fh = IO::File->new($opts{file}, "r");
-  if (ok($fh, "opening $opts{file}")) {
-    binmode $fh;
-    my $fhimg = Imager->new;
-    if (ok($fhimg->read(fh=>$fh, %mopts), "read from fh")) {
-      ok($fh->seek(0, SEEK_SET), "seek after read");
-      if (ok($fhimg->read(fh=>$fh, %mopts, type=>$type), "read from fh")) {
-       ok(Imager::i_img_diff($img->{IMG}, $fhimg->{IMG}) == 0,
-          "image comparison after fh read");
-      }
-      else {
-       skip("no image to compare");
-      }
-      ok($fh->seek(0, SEEK_SET), "seek after read");
-    }
-
-    # read from a fd
-    my $fdimg = Imager->new;
-    if (ok($fdimg->read(fd=>fileno($fh), %mopts, type=>$type), "read from fd")) {
-      ok(Imager::i_img_diff($img->{IMG}, $fdimg->{IMG}) == 0,
-         "image comparistion after fd read");
-    }
-    else {
-      skip("no image to compare");
-    }
-    ok($fh->seek(0, SEEK_SET), "seek after fd read");
-    ok($fh->close, "close fh after reads");
-  }
-  else {
-    skip("couldn't open the damn file: $!", 7);
-  }
-
-  # read from a memory buffer
-  open DATA, "< $opts{file}"
-    or die "Cannot open $opts{file}: $!";
-  binmode DATA;
-  my $data = do { local $/; <DATA> };
-  close DATA;
-  my $bimg = Imager->new;
-  
-  if (ok($bimg->read(data=>$data, %mopts, type=>$type), "read from buffer", 
-        $img)) {
-    ok(Imager::i_img_diff($img->{IMG}, $bimg->{IMG}) == 0,
-       "comparing buffer read image");
-  }
-  else {
-    skip("nothing to compare");
-  }
-  
-  # read from callbacks, both with minimum and maximum reads
-  my $buf = $data;
-  my $seekpos = 0;
-  my $reader_min = 
-    sub { 
-      my ($size, $maxread) = @_;
-      my $out = substr($buf, $seekpos, $size);
-      $seekpos += length $out;
-      $out;
-    };
-  my $reader_max = 
-    sub { 
-      my ($size, $maxread) = @_;
-      my $out = substr($buf, $seekpos, $maxread);
-      $seekpos += length $out;
-      $out;
-    };
-  my $seeker =
-    sub {
-      my ($offset, $whence) = @_;
-      #print "io_seeker($offset, $whence)\n";
-      if ($whence == SEEK_SET) {
-       $seekpos = $offset;
-      }
-      elsif ($whence == SEEK_CUR) {
-       $seekpos += $offset;
-      }
-      else { # SEEK_END
-       $seekpos = length($buf) + $offset;
-      }
-      #print "-> $seekpos\n";
-      $seekpos;
-    };
-  my $cbimg = Imager->new;
-  ok($cbimg->read(callback=>$reader_min, seekcb=>$seeker, type=>$type, %mopts),
-     "read from callback min", $cbimg);
-  ok(Imager::i_img_diff($cbimg->{IMG}, $img->{IMG}) == 0,
-     "comparing mincb image");
-  $seekpos = 0;
-  ok($cbimg->read(callback=>$reader_max, seekcb=>$seeker, type=>$type, %mopts),
-     "read from callback max", $cbimg);
-  ok(Imager::i_img_diff($cbimg->{IMG}, $img->{IMG}) == 0,
-     "comparing maxcb image");
-}
-
-for my $type (@types) {
-  next unless $hsh{$type};
-
-  print "# write tests for $type\n";
-  # test writes
-  next unless $hsh{$type};
-  my $file = "testout/t50out.$type";
-  my $wimg = Imager->new;
-  # if this doesn't work, we're so screwed up anyway
-  
-  ok($wimg->read(file=>"testimg/penguin-base.ppm"),
-     "cannot read base file", $wimg);
-
-  # first to a file
-  print "# writing $type to a file\n";
-  my %extraopts;
-  %extraopts = %{$writeopts{$type}} if $writeopts{$type};
-  ok($wimg->write(file=>$file, %extraopts),
-     "writing $type to a file $file", $wimg);
-
-  print "# writing $type to a FH\n";
-  # to a FH
-  my $fh = IO::File->new($file, "w+")
-    or die "Could not create $file: $!";
-  binmode $fh;
-  ok($wimg->write(fh=>$fh, %extraopts, type=>$type),
-     "writing $type to a FH", $wimg);
-  ok($fh->seek(0, SEEK_END) > 0,
-     "seek after writing $type to a FH");
-  ok(print($fh "SUFFIX\n"),
-     "write to FH after writing $type");
-  ok($fh->close, "closing FH after writing $type");
-
-  if (ok(open(DATA, "< $file"), "opening data source")) {
-    binmode DATA;
-    my $data = do { local $/; <DATA> };
-    close DATA;
-
-    # writing to a buffer
-    print "# writing $type to a buffer\n";
-    my $buf = '';
-    ok($wimg->write(data=>\$buf, %extraopts, type=>$type),
-       "writing $type to a buffer", $wimg);
-    $buf .= "SUFFIX\n";
-    open DATA, "> testout/t50_buf.$type"
-      or die "Cannot create $type buffer file: $!";
-    binmode DATA;
-    print DATA $buf;
-    close DATA;
-    ok($data eq $buf, "comparing file data to buffer");
-
-    $buf = '';
-    my $seekpos = 0;
-    my $did_close;
-    my $writer = 
-      sub {
-       my ($what) = @_;
-       if ($seekpos > length $buf) {
-         $buf .= "\0" x ($seekpos - length $buf);
-       }
-       substr($buf, $seekpos, length $what) = $what;
-       $seekpos += length $what;
-       $did_close = 0; # the close must be last
-       1;
-      };
-    my $reader_min = 
-      sub { 
-       my ($size, $maxread) = @_;
-       my $out = substr($buf, $seekpos, $size);
-       $seekpos += length $out;
-       $out;
-      };
-    my $reader_max = 
-      sub { 
-       my ($size, $maxread) = @_;
-       my $out = substr($buf, $seekpos, $maxread);
-       $seekpos += length $out;
-       $out;
-      };
-    use IO::Seekable;
-    my $seeker =
-      sub {
-       my ($offset, $whence) = @_;
-       #print "io_seeker($offset, $whence)\n";
-       if ($whence == SEEK_SET) {
-         $seekpos = $offset;
-       }
-       elsif ($whence == SEEK_CUR) {
-         $seekpos += $offset;
-       }
-       else { # SEEK_END
-         $seekpos = length($buf) + $offset;
-       }
-       #print "-> $seekpos\n";
-       $seekpos;
-      };
-
-    my $closer = sub { ++$did_close; };
-
-    print "# writing $type via callbacks (mb=1)\n";
-    ok($wimg->write(writecb=>$writer, seekcb=>$seeker, closecb=>$closer,
-                   readcb=>$reader_min,
-                   %extraopts, type=>$type, maxbuffer=>1),
-       "writing $type to callback (mb=1)", $wimg);
-
-    ok($did_close, "checking closecb called");
-    $buf .= "SUFFIX\n";
-    ok($data eq $buf, "comparing callback output to file data");
-    print "# writing $type via callbacks (no mb)\n";
-    $buf = '';
-    $did_close = 0;
-    $seekpos = 0;
-    # we don't use the closecb here - used to make sure we don't get 
-    # a warning/error on an attempt to call an undef close sub
-    ok($wimg->write(writecb=>$writer, seekcb=>$seeker, readcb=>$reader_min,
-                   %extraopts, type=>$type),
-       "writing $type to callback (no mb)", $wimg);
-    $buf .= "SUFFIX\n";
-    ok($data eq $buf, "comparing callback output to file data");
-  }
-  else {
-    skip("couldn't open data source", 7);
-  }
-}
-
-my $img2 =  $img->crop(width=>50, height=>50);
-$img2 -> write(file=> 'testout/t50.ppm', type=>'pnm');
-
-undef($img);
-
-# multi image/file tests
-print "# multi-image write tests\n";
-for my $type (@mtypes) {
-  next unless $hsh{$type};
-  print "# $type\n";
-
-  my $file = "testout/t50out.$type";
-  my $wimg = Imager->new;
-
-  # if this doesn't work, we're so screwed up anyway
-  ok($wimg->read(file=>"testout/t50out.$type"),
-     "reading base file", $wimg);
-
-  ok(my $wimg2 = $wimg->copy, "copying base image", $wimg);
-  ok($wimg2->flip(dir=>'h'), "flipping base image", $wimg2);
-
-  my @out = ($wimg, $wimg2);
-  my %extraopts;
-  %extraopts = %{$writeopts{$type}} if $writeopts{$type};
-  ok(Imager->write_multi({ file=>"testout/t50_multi.$type", %extraopts },
-                         @out),
-     "writing multiple to a file", "Imager");
-
-  # make sure we get the same back
-  my @images = Imager->read_multi(file=>"testout/t50_multi.$type");
-  if (ok(@images == @out, "checking read image count")) {
-    for my $i (0 .. $#out) {
-      my $diff = Imager::i_img_diff($out[$i]{IMG}, $images[$i]{IMG});
-      print "# diff $diff\n";
-      ok($diff == 0, "comparing image $i");
-    }
-  }
-  else {
-    skip("wrong number of images read", 2);
-  }
-}
-
-
-Imager::malloc_state();
-
-#print "ok 2\n";
-
-sub ok {
-  my ($ok, $msg, $img, $why, $skipcount) = @_;
-
-  ++$test_num;
-  if ($ok) {
-    print "ok $test_num # $msg\n";
-    Imager::i_log_entry("ok $test_num # $msg\n", 0);
-  }
-  else {
-    my $err;
-    $err = $img->errstr if $img;
-    # VMS (if we ever support it) wants the whole line in one print
-    my $line = "not ok $test_num # line ".(caller)[2].": $msg";
-    $line .= ": $err" if $err;
-    print $line, "\n";
-    Imager::i_log_entry($line."\n", 0);
-  }
-  skip($why, $skipcount) if defined $why;
-  $ok;
-}
-
-sub skip {
-  my ($why, $skipcount) = @_;
-
-  $skipcount ||= 1;
-  for (1.. $skipcount) {
-    ++$test_num;
-    print "ok $test_num # skipped $why\n";
-  }
-}
diff --git a/t/t55trans.t b/t/t55trans.t
deleted file mode 100644 (file)
index 5554f2a..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#!perl -w
-use strict;
-use Test::More;
-use Imager;
-
-eval "use Affix::Infix2Postfix; 1;"
-  or plan skip_all => "No Affix::Infix2Postfix";
-
-plan tests => 8;
-
-#$Imager::DEBUG=1;
-
--d "testout" or mkdir "testout";
-
-Imager->open_log('log'=>'testout/t55trans.log');
-
-my $img=Imager->new();
-
-SKIP:
-{
-  ok($img, "make image object")
-    or skip("can't make image object", 5);
-
-  ok($img->open(file=>'testimg/scale.ppm',type=>'pnm'),
-     "read sample image")
-    or skip("couldn't load test image", 4);
-
- SKIP:
-  {
-    my $nimg=$img->transform(xexpr=>'x',yexpr=>'y+10*sin((x+y)/10)');
-    ok($nimg, "do transformation")
-      or skip ( "warning ".$img->errstr, 1 );
-
-    #  xopcodes=>[qw( x y Add)],yopcodes=>[qw( x y Sub)],parm=>[]
-
-    ok($nimg->write(type=>'pnm',file=>'testout/t55.ppm'), "save to file");
-  }
-
- SKIP:
-  {
-    my $nimg=$img->transform(xexpr=>'x+0.1*y+5*sin(y/10.0+1.57)',
-                            yexpr=>'y+10*sin((x+y-0.785)/10)');
-    ok($nimg, "more complex transform")
-      or skip("couldn't make image", 1);
-
-    ok($nimg->write(type=>'pnm',file=>'testout/t55b.ppm'), "save to file");
-  }
-}
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->transform(xexpr => "x", yexpr => "y"),
-     "fail to transform an empty image");
-  is($empty->errstr, "transform: empty input image",
-     "check error message");
-}
diff --git a/t/t56postfix.t b/t/t56postfix.t
deleted file mode 100644 (file)
index 6366e8b..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 6;
-BEGIN { use_ok('Imager::Expr') }
-
-SKIP:
-{
-  my $expr = Imager::Expr->new({rpnexpr=><<EXPR, variables=>[ qw(x y) ], constants=>{one=>1, two=>2}});
-x two * # see if comments work
-y one + 
-getp1
-EXPR
-  ok($expr, "compile postfix")
-    or print "# ", Imager::Expr->error, "\n";
-  $expr
-    or skip("Could not compile", 4);
-
-  # perform some basic validation on the code
-  my $code = $expr->dumpcode();
-  my @code = split /\n/, $code;
-  ok($code[-1] =~ /:\s+ret/, "ret at the end");
-  ok(grep(/:\s+mult.*x/, @code), "found mult");
-  ok(grep(/:\s+add.*y/, @code), "found add");
-  ok(grep(/:\s+getp1/, @code), "found getp1");
-}
diff --git a/t/t57infix.t b/t/t57infix.t
deleted file mode 100644 (file)
index 8c4d5d6..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 7;
-
-BEGIN { use_ok('Imager::Expr') }
-
-# only test this if Parse::RecDescent was loaded successfully
-SKIP:
-{
-  Imager::Expr->type_registered('expr')
-      or skip("Imager::Expr::Infix not available", 6);
-
-  my $opts = {expr=>'z=0.8;return hsv(x/w*360,y/h,z)', variables=>[ qw(x y) ], constants=>{h=>100,w=>100}};
-  my $expr = Imager::Expr->new($opts);
-  ok($expr, "make infix expression")
-    or skip("Could not make infix expression", 5);
-  my $code = $expr->dumpcode();
-  my @code = split /\n/,$code;
-  #print $code;
-  ok($code[-1] =~ /:\s+ret/, "final op a ret");
-  ok(grep(/:\s+mult.*360/, @code), "mult by 360 found");
-  # strength reduction converts these to mults
-  #print grep(/:\s+div.*x/, @code) ? "ok 5\n" : "not ok 5\n";
-  #print grep(/:\s+div.*y/, @code) ? "ok 6\n" : "not ok 6\n";
-  ok(grep(/:\s+mult.*x/, @code), "mult by x found");
-  ok(grep(/:\s+mult.*y/, @code), "mult by y found");
-  ok(grep(/:\s+hsv.*0\.8/, @code), "hsv op found");
-}
diff --git a/t/t58trans2.t b/t/t58trans2.t
deleted file mode 100644 (file)
index b9ebf58..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 40;
-BEGIN { use_ok('Imager'); }
-use Imager::Test qw(is_color3);
-
--d "testout" or mkdir "testout";
-
-Imager::init('log'=>'testout/t58trans2.log');
-
-my $im1 = Imager->new();
-$im1->open(file=>'testimg/penguin-base.ppm', type=>'pnm')
-        || die "Cannot read image";
-my $im2 = Imager->new();
-$im2->open(file=>'testimg/scale.ppm',type=>'pnm')
-       || die "Cannot read testimg/scale.ppm";
-
-# error handling
-my $opts = { rpnexpr=>'x x 10 / sin 10 * y + get1' };
-my $im3 = Imager::transform2($opts);
-ok(!$im3, "returned an image on error");
-ok(defined($Imager::ERRSTR), "No error message on failure");
-
-# image synthesis
-my $im4 = Imager::transform2({
-       width=>300, height=>300,
-       rpnexpr=>'x y cx cy distance !d y cy - x cx - atan2 !a @d 10 / @a + 3.1416 2 * % !a2 @a2 cy * 3.1416 / 1 @a2 sin 1 + 2 / hsv'});
-ok($im4, "synthesis failed");
-
-if ($im4) {
-  $im4->write(type=>'pnm', file=>'testout/t56a.ppm')
-    || die "Cannot write testout/t56a.ppm";
-}
-
-# image distortion
-my $im5 = Imager::transform2({
-       rpnexpr=>'x x 10 / sin 10 * y + getp1'
-}, $im1);
-ok($im5, "image distortion");
-if ($im5) {
-  $im5->write(type=>'pnm', file=>'testout/t56b.ppm')
-    || die "Cannot write testout/t56b.ppm";
-}
-
-# image combination
-$opts = {
-rpnexpr=>'x h / !rat x w2 % y h2 % getp2 !pat x y getp1 @rat * @pat 1 @rat - * +'
-};
-my $im6 = Imager::transform2($opts,$im1,$im2);
-ok($im6, "image combination");
-if ($im6) {
-  $im6->write(type=>'pnm', file=>'testout/t56c.ppm')
-    || die "Cannot write testout/t56c.ppm";
-}
-
-# alpha
-$opts = 
-  {
-   rpnexpr => '0 0 255 x y + w h + 2 - / 255 * rgba',
-   channels => 4,
-   width => 50,
-   height => 50,
-  };
-my $im8 = Imager::transform2($opts);
-ok($im8, "alpha output");
-my $c = $im8->getpixel(x=>0, 'y'=>0);
-is(($c->rgba)[3], 0, "zero alpha");
-$c = $im8->getpixel(x=>49, 'y'=>49);
-is(($c->rgba)[3], 255, "max alpha");
-
-$opts = { rpnexpr => 'x 1 + log 50 * y 1 + log 50 * getp1' };
-my $im9 = Imager::transform2($opts, $im1);
-ok($im9, "log function");
-if ($im9) {
-  $im9->write(type=>'pnm', file=>'testout/t56-9.ppm');
-}
-
-# op tests
-sub op_test($$$$$$);
-print "# op tests\n";
-op_test('7F0000', <<EOS, 0, 127, 0, 'value hsv getp1');
-120 1.0
-0 0 getp1 value
-hsv
-EOS
-op_test("7F0000", <<EOS, 255, 0, 0, 'hue');
-0 0 getp1 hue
-1.0 1.0 hsv
-EOS
-op_test("7F0000", <<EOS, 0, 255, 0, 'sat');
-120 0 0 getp1 sat 1.0 hsv
-EOS
-op_test("4060A0", <<'EOS', 128, 128, 128, "add mult sub rgb red green blue");
-0 0 getp1 !p @p red 2 * @p green 32 + @p blue 32 - rgb
-EOS
-op_test('806040', <<'EOS', 64, 64, 64, "div uminus");
-0 0 getp1 !p @p red 2 / @p green 32 uminus add @p blue rgb
-EOS
-op_test('40087f', <<'EOS', 8, 64, 31, 'pow mod');
-0 0 getp1 !p @p red 0.5 pow @p green 2 pow @p blue 32 mod rgb
-EOS
-op_test('202122', '0 0 getp1 4 *', 128, 132, 136, 'multp');
-op_test('404040', '0 0 getp1 1 2 3 rgb +', 65, 66, 67, 'addp');
-op_test('414243', '0 0 getp1 3 2 1 rgb -', 62, 64, 66, 'subp');
-op_test('808040', <<'EOS', 64, 64, 8, 'sin cos pi sqrt');
-0 0 getp1 !p pi 6 / sin @p red * 0.1 + pi 3 / cos @p green * 0.1 + 
-@p blue sqrt rgb
-EOS
-op_test('008080', <<'EOS', 0, 0, 0, 'atan2');
-0 0 0 0 getp1 !p @p red 128 / @p green 128 / atan2 hsv
-EOS
-op_test('000000', <<'EOS', 150, 150, 150, 'distance');
-0 100 120 10 distance !d @d @d @d rgb
-EOS
-op_test('000000', <<'EOS', 100, 100, 100, 'int');
-50.75 int 2 * !i @i @i @i rgb
-EOS
-op_test('000100', <<'EOS', 128, 0, 0, 'if');
-0 0 getp1 !p @p red 0 128 if @p green 0 128 if 0 rgb
-EOS
-op_test('FF0000', <<'EOS', 0, 255, 0, 'ifp');
-0 0 0 getp1 0 255 0 rgb ifp
-EOS
-op_test('000000', <<'EOS', 1, 0, 1, 'le lt gt');
-0 1 le 1 0 lt 1 0 gt rgb
-EOS
-op_test('000000', <<'EOS', 0, 1, 0, 'ge eq ne');
-0 1 ge 0 0 eq 0 0 ne rgb
-EOS
-op_test('000000', <<'EOS', 0, 1, 1, 'and or not');
-1 0 and 1 0 or 0 not rgb
-EOS
-op_test('000000', <<'EOS', 255, 0, 255, 'abs');
--255 abs 0 abs 255 abs rgb
-EOS
-op_test('000000', <<'EOS', 50, 82, 0, 'exp log');
-1 exp log 50 * 0.5 + 0.5 exp 50 * 0 rgb
-EOS
-op_test('800000', <<'EOS', 128, 0, 0, 'det');
-1 0 0 1 det 128 * 1 1 1 1 det 128 * 0 rgb
-EOS
-op_test('FF80C0', <<'EOS', 127, 0, 0, 'sat');
-0 0 getp1 sat 255 * 0.01 + 0 0 rgb
-EOS
-
-
-{
-  my $empty = Imager->new;
-  my $good = Imager->new(xsize => 1, ysize => 1);
-  ok(!Imager::transform2({ rpnexpr => "x y getp1" }, $good, $empty),
-     "can't transform an empty image");
-  is(Imager->errstr, "transform2: empty input image (input image 2)",
-     "check error message");
-}
-
-use Imager::Transform;
-
-# some simple tests
-print "# Imager::Transform\n";
-my @funcs = Imager::Transform->list;
-ok(@funcs, "funcs");
-
-my $tran = Imager::Transform->new($funcs[0]);
-ok($tran, "got tranform");
-ok($tran->describe() eq Imager::Transform->describe($funcs[0]),
-   "description");
-# look for a function that takes inputs (at least one does)
-my @needsinputs = grep Imager::Transform->new($_)->inputs, @funcs;
-# make sure they're 
-my @inputs = Imager::Transform->new($needsinputs[0])->inputs;
-ok($inputs[0]{desc}, "input description");
-# at some point I might want to test the actual transformations
-
-# check lower level error handling
-my $im7 = Imager::transform2({rpnexpr=>'x y getp2', width=>100, height=>100});
-ok(!$im7, "expected failure on accessing invalid image");
-print "# ", Imager->errstr, "\n";
-ok(Imager->errstr =~ /not enough images/, "didn't get expected error");
-
-sub op_test ($$$$$$) {
-  my ($in_color, $code, $r, $g, $b, $comment) = @_;
-
-  my $im = Imager->new(xsize => 1, ysize => 1);
-  $im->setpixel(x => 0, y => 0, color => $in_color);
- SKIP:
-  {
-    my $out = Imager::transform2({ rpnexpr => $code }, $im);
-    unless ($out) {
-      fail("$comment: could not compile $code - ".Imager->errstr);
-      return;
-    }
-    my $found = $out->getpixel(x => 0, y => 0);
-    is_color3($found, $r, $g, $b, $comment);
-  }
-}
diff --git a/t/t59assem.t b/t/t59assem.t
deleted file mode 100644 (file)
index 065cb3e..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 6;
-
-BEGIN { use_ok('Imager::Expr::Assem') }
-
-SKIP:
-{
-  my $expr = Imager::Expr->new
-    ({assem=><<EOS,
-       var count:n ; var p:p
-       count = 0
-       p = getp1 x y
-loop:
-# this is just a delay
-       count = add count 1
-       var temp:n
-       temp = lt count totalcount
-       jumpnz temp loop
-       ret p
-EOS
-      variables=>[qw(x y)],
-      constants=>{totalcount=>5}
-     });
-  ok($expr, "compile simple assembler")
-    or do {
-      print "# ", Imager::Expr->error, "\n";
-      skip("didn't compile", 4);
-    };
-  my $code = $expr->dumpcode();
-  my @code = split /\n/, $code;
-  ok($code[-1] =~ /:\s+ret/, "last op is a ret");
-  ok($code[0] =~ /:\s+set/, "first op is a set");
-  ok($code[1] =~ /:\s+getp1/, "next is a getp1");
-  ok($code[3] =~ /:\s+lt/, "found comparison");
-}
diff --git a/t/t61filters.t b/t/t61filters.t
deleted file mode 100644 (file)
index b6f8c30..0000000
+++ /dev/null
@@ -1,471 +0,0 @@
-#!perl -w
-use strict;
-use Imager qw(:handy);
-use Test::More tests => 122;
-
--d "testout" or mkdir "testout";
-
-Imager::init_log("testout/t61filters.log", 1);
-use Imager::Test qw(is_image_similar test_image is_image is_color4 is_fcolor4);
-# meant for testing the filters themselves
-
-my $imbase = test_image();
-
-my $im_other = Imager->new(xsize=>150, ysize=>150);
-$im_other->box(xmin=>30, ymin=>60, xmax=>120, ymax=>90, filled=>1);
-
-test($imbase, {type=>'autolevels'}, 'testout/t61_autolev.ppm');
-
-test($imbase, {type=>'contrast', intensity=>0.5}, 
-     'testout/t61_contrast.ppm');
-
-# this one's kind of cool
-test($imbase, {type=>'conv', coef=>[ 0.3, 1, 0.3, ], },
-     'testout/t61_conv_blur.ppm');
-
-{
-  my $work = $imbase->copy;
-  ok(!Imager::i_conv($work->{IMG}, []), "conv should fail with empty array");
-  ok(!$work->filter(type => 'conv', coef => []),
-     "check the conv OO intergave too");
-  is($work->errstr, "there must be at least one coefficient",
-     "check conv error message");
-}
-
-{
-  my $work8 = $imbase->copy;
-  ok(!$work8->filter(type => "conv", coef => "ABC"),
-     "coef not an array");
-}
-{
-  my $work8 = $imbase->copy;
-  ok(!$work8->filter(type => "conv", coef => [ -1, 2, -1 ]),
-     "should fail if sum of coef is 0");
-  is($work8->errstr, "sum of coefficients is zero", "check message");
-}
-
-{
-  my $work8 = $imbase->copy;
-  my $work16 = $imbase->to_rgb16;
-  my $coef = [ -0.2, 1, -0.2 ];
-  ok($work8->filter(type => "conv", coef => $coef),
-     "filter 8 bit image");
-  ok($work16->filter(type => "conv", , coef => $coef),
-     "filter 16 bit image");
-  is_image_similar($work8, $work16, 80000, "8 and 16 bit conv match");
-}
-
-{
-  my $gauss = test($imbase, {type=>'gaussian', stddev=>5 },
-                  'testout/t61_gaussian.ppm');
-
-  my $imbase16 = $imbase->to_rgb16;
-  my $gauss16 = test($imbase16,  {type=>'gaussian', stddev=>5 },
-                    'testout/t61_gaussian16.ppm');
-  is_image_similar($gauss, $gauss16, 250000, "8 and 16 gaussian match");
-}
-
-
-test($imbase, { type=>'gradgen', dist=>1,
-                   xo=>[ 10,  10, 120 ],
-                   yo=>[ 10, 140,  60 ],
-                   colors=> [ NC('#FF0000'), NC('#FFFF00'), NC('#00FFFF') ]},
-     'testout/t61_gradgen.ppm');
-
-test($imbase, {type=>'mosaic', size=>8}, 'testout/t61_mosaic.ppm');
-
-test($imbase, {type=>'hardinvert'}, 'testout/t61_hardinvert.ppm');
-
-{ # invert - 8 bit
-  my $im = Imager->new(xsize => 1, ysize => 1, channels => 4);
-  ok($im, "make test image for invert test");
-  ok($im->setpixel(x => 0, y => 0, color => "000010C0"),
-     "set a test pixel");
-  my $copy = $im->copy;
-  ok($im->filter(type => "hardinvert"), "hardinvert it");
-  is_color4($im->getpixel(x => 0, y => 0), 255, 255, 0xEF, 0xC0,
-           "check only colour inverted");
-  ok($copy->filter(type => "hardinvertall"), "hardinvertall copy");
-  is_color4($copy->getpixel(x => 0, y => 0), 255, 255, 0xEF, 0x3f,
-           "check all inverted");
-}
-
-{ # invert - double image
-  my $im = Imager->new(xsize => 1, ysize => 1, channels => 4, bits => "double");
-  ok($im, "make double test image for invert test");
-  ok($im->setpixel(x => 0, y => 0, color => Imager::Color::Float->new(0, 0, 0.125, 0.75)),
-     "set a test pixel");
-  my $copy = $im->copy;
-  ok($im->filter(type => "hardinvert"), "hardinvert it");
-  is_fcolor4($im->getpixel(x => 0, y => 0, type => "double"),
-            1.0, 1.0, 0.875, 0.75, 1e-5,
-            "check only colour inverted");
-  ok($copy->filter(type => "hardinvertall"), "hardinvertall copy");
-  is_fcolor4($copy->getpixel(x => 0, y => 0, type =>"double"),
-            1.0, 1.0, 0.875, 0.25, 1e-5,
-            "check all inverted");
-}
-
-test($imbase, {type=>'noise'}, 'testout/t61_noise.ppm');
-
-test($imbase, {type=>'radnoise'}, 'testout/t61_radnoise.ppm');
-
-test($imbase, {type=>'turbnoise'}, 'testout/t61_turbnoise.ppm');
-
-test($imbase, {type=>'bumpmap', bump=>$im_other, lightx=>30, lighty=>30},
-     'testout/t61_bumpmap.ppm');
-
-test($imbase, {type=>'bumpmap_complex', bump=>$im_other}, 'testout/t61_bumpmap_complex.ppm');
-
-test($imbase, {type=>'postlevels', levels=>3}, 'testout/t61_postlevels.ppm');
-
-test($imbase, {type=>'watermark', wmark=>$im_other },
-     'testout/t61_watermark.ppm');
-
-test($imbase, {type=>'fountain', xa=>75, ya=>75, xb=>85, yb=>30,
-               repeat=>'triangle', #ftype=>'radial', 
-               super_sample=>'circle', ssample_param => 16,
-              },
-     'testout/t61_fountain.ppm');
-use Imager::Fountain;
-
-my $f1 = Imager::Fountain->new;
-$f1->add(end=>0.2, c0=>NC(255, 0,0), c1=>NC(255, 255,0));
-$f1->add(start=>0.2, c0=>NC(255,255,0), c1=>NC(0,0,255,0));
-test($imbase, { type=>'fountain', xa=>20, ya=>130, xb=>130, yb=>20,
-                #repeat=>'triangle',
-                segments=>$f1
-              },
-     'testout/t61_fountain2.ppm');
-my $f2 = Imager::Fountain->new
-  ->add(end=>0.5, c0=>NC(255,0,0), c1=>NC(255,0,0), color=>'hueup')
-  ->add(start=>0.5, c0=>NC(255,0,0), c1=>NC(255,0,0), color=>'huedown');
-#use Data::Dumper;
-#print Dumper($f2);
-test($imbase, { type=>'fountain', xa=>20, ya=>130, xb=>130, yb=>20,
-                    segments=>$f2 },
-     'testout/t61_fount_hsv.ppm');
-my $f3 = Imager::Fountain->read(gimp=>'testimg/gimpgrad');
-ok($f3, "read gimpgrad");
-test($imbase, { type=>'fountain', xa=>75, ya=>75, xb=>90, yb=>15,
-                    segments=>$f3, super_sample=>'grid',
-                    ftype=>'radial_square', combine=>'color' },
-     'testout/t61_fount_gimp.ppm');
-{ # test new fountain with no parameters
-  my $warn = '';
-  local $SIG{__WARN__} = sub { $warn .= "@_" };
-  my $f4 = Imager::Fountain->read();
-  ok(!$f4, "read with no parameters does nothing");
-  like($warn, qr/Nothing to do!/, "check the warning");
-}
-{ # test with missing file
-  my $warn = '';
-  local $SIG{__WARN__} = sub { $warn .= "@_" };
-  my $f = Imager::Fountain->read(gimp => "no-such-file");
-  ok(!$f, "try to read a fountain defintion that doesn't exist");
-  is($warn, "", "should be no warning");
-  like(Imager->errstr, qr/^Cannot open no-such-file: /, "check message");
-}
-SKIP:
-{
-  my $fh = IO::File->new("testimg/gimpgrad", "r");
-  ok($fh, "opened gradient")
-    or skip "Couldn't open gradient: $!", 1;
-  my $f = Imager::Fountain->read(gimp => $fh);
-  ok($f, "read gradient from file handle");
-}
-{
-  # not a gradient
-  my $f = Imager::Fountain->read(gimp => "t/t61filters.t");
-  ok(!$f, "fail to read non-gradient");
-  is(Imager->errstr, "t/t61filters.t is not a GIMP gradient file",
-     "check error message");
-}
-{ # an invalid gradient file
-  my $f = Imager::Fountain->read(gimp => "testimg/gradbad.ggr");
-  ok(!$f, "fail to read bad gradient (bad seg count)");
-  is(Imager->errstr, "testimg/gradbad.ggr is missing the segment count",
-     "check error message");
-}
-{ # an invalid gradient file
-  my $f = Imager::Fountain->read(gimp => "testimg/gradbad2.ggr");
-  ok(!$f, "fail to read bad gradient (bad segment)");
-  is(Imager->errstr, "Bad segment definition",
-     "check error message");
-}
-test($imbase, { type=>'unsharpmask', stddev=>2.0 },
-     'testout/t61_unsharp.ppm');
-test($imbase, {type=>'conv', coef=>[ -1, 3, -1, ], },
-     'testout/t61_conv_sharp.ppm');
-
-test($imbase, { type=>'nearest_color', dist=>1,
-                   xo=>[ 10,  10, 120 ],
-                   yo=>[ 10, 140,  60 ],
-                   colors=> [ NC('#FF0000'), NC('#FFFF00'), NC('#00FFFF') ]},
-     'testout/t61_nearest.ppm');
-
-# Regression test: the checking of the segment type was incorrect
-# (the comparison was checking the wrong variable against the wrong value)
-my $f4 = [ [ 0, 0.5, 1, NC(0,0,0), NC(255,255,255), 5, 0 ] ];
-test($imbase, {type=>'fountain',  xa=>75, ya=>75, xb=>90, yb=>15,
-               segments=>$f4, super_sample=>'grid',
-               ftype=>'linear', combine=>'color' },
-     'testout/t61_regress_fount.ppm');
-my $im2 = $imbase->copy;
-$im2->box(xmin=>20, ymin=>20, xmax=>40, ymax=>40, color=>'FF0000', filled=>1);
-$im2->write(file=>'testout/t61_diff_base.ppm');
-my $im3 = Imager->new(xsize=>150, ysize=>150, channels=>3);
-$im3->box(xmin=>20, ymin=>20, xmax=>40, ymax=>40, color=>'FF0000', filled=>1);
-my $diff = $imbase->difference(other=>$im2);
-ok($diff, "got difference image");
-SKIP:
-{
-  skip(1, "missing comp or diff image") unless $im3 && $diff;
-
-  is(Imager::i_img_diff($im3->{IMG}, $diff->{IMG}), 0,
-     "compare test image and diff image");
-}
-
-# newer versions of gimp add a line to the gradient file
-my $name;
-my $f5 = Imager::Fountain->read(gimp=>'testimg/newgimpgrad.ggr',
-                                name => \$name);
-ok($f5, "read newer gimp gradient")
-  or print "# ",Imager->errstr,"\n";
-is($name, "imager test gradient", "check name read correctly");
-$f5 = Imager::Fountain->read(gimp=>'testimg/newgimpgrad.ggr');
-ok($f5, "check we handle case of no name reference correctly")
-  or print "# ",Imager->errstr,"\n";
-
-# test writing of gradients
-ok($f2->write(gimp=>'testout/t61grad1.ggr'), "save a gradient")
-  or print "# ",Imager->errstr,"\n";
-undef $name;
-my $f6 = Imager::Fountain->read(gimp=>'testout/t61grad1.ggr', 
-                                name=>\$name);
-ok($f6, "read what we wrote")
-  or print "# ",Imager->errstr,"\n";
-ok(!defined $name, "we didn't set the name, so shouldn't get one");
-
-# try with a name
-ok($f2->write(gimp=>'testout/t61grad2.ggr', name=>'test gradient'),
-   "write gradient with a name")
-  or print "# ",Imager->errstr,"\n";
-undef $name;
-my $f7 = Imager::Fountain->read(gimp=>'testout/t61grad2.ggr', name=>\$name);
-ok($f7, "read what we wrote")
-  or print "# ",Imager->errstr,"\n";
-is($name, "test gradient", "check the name matches");
-
-# we attempt to convert color names in segments to segments now
-{
-  my @segs =
-    (
-     [ 0.0, 0.5, 1.0, '000000', '#FFF', 0, 0 ],
-    );
-  my $im = Imager->new(xsize=>50, ysize=>50);
-  ok($im->filter(type=>'fountain', segments => \@segs,
-                 xa=>0, ya=>30, xb=>49, yb=>30), 
-     "fountain with color names instead of objects in segments");
-  my $left = $im->getpixel('x'=>0, 'y'=>20);
-  ok(color_close($left, Imager::Color->new(0,0,0)),
-     "check black converted correctly");
-  my $right = $im->getpixel('x'=>49, 'y'=>20);
-  ok(color_close($right, Imager::Color->new(255,255,255)),
-     "check white converted correctly");
-
-  # check that invalid color names are handled correctly
-  my @segs2 =
-    (
-     [ 0.0, 0.5, 1.0, '000000', 'FxFxFx', 0, 0 ],
-    );
-  ok(!$im->filter(type=>'fountain', segments => \@segs2,
-                  xa=>0, ya=>30, xb=>49, yb=>30), 
-     "fountain with invalid color name");
-  cmp_ok($im->errstr, '=~', 'No color named', "check error message");
-}
-
-{
-  # test simple gradient creation
-  my @colors = map Imager::Color->new($_), qw/white blue red/;
-  my $s = Imager::Fountain->simple(positions => [ 0, 0.3, 1.0 ],
-                                  colors => \@colors);
-  ok($s, "made simple gradient");
-  my $start = $s->[0];
-  is($start->[0], 0, "check start of first correct");
-  is_color4($start->[3], 255, 255, 255, 255, "check color at start");
-}
-{
-  # simple gradient error modes
-  {
-    my $warn = '';
-    local $SIG{__WARN__} = sub { $warn .= "@_" };
-    my $s = Imager::Fountain->simple();
-    ok(!$s, "no parameters to simple()");
-    like($warn, qr/Nothing to do/);
-  }
-  {
-    my $s = Imager::Fountain->simple(positions => [ 0, 1 ],
-                                    colors => [ NC(0, 0, 0) ]);
-    ok(!$s, "mismatch of positions and colors fails");
-    is(Imager->errstr, "positions and colors must be the same size",
-       "check message");
-  }
-  {
-    my $s = Imager::Fountain->simple(positions => [ 0 ],
-                                    colors => [ NC(0, 0, 0) ]);
-    ok(!$s, "not enough positions");
-    is(Imager->errstr, "not enough segments");
-  }
-}
-
-{
-  my $im = Imager->new(xsize=>100, ysize=>100);
-  # build the gradient the hard way - linear from black to white,
-  # then back again
-  my @simple =
-   (
-     [   0, 0.25, 0.5, 'black', 'white', 0, 0 ],
-     [ 0.5. 0.75, 1.0, 'white', 'black', 0, 0 ],
-   );
-  # across
-  my $linear = $im->filter(type   => "fountain",
-                           ftype  => 'linear',
-                           repeat => 'sawtooth',
-                           xa     => 0,
-                           ya     => $im->getheight / 2,
-                           xb     => $im->getwidth - 1,
-                           yb     => $im->getheight / 2);
-  ok($linear, "linear fountain sample");
-  # around
-  my $revolution = $im->filter(type   => "fountain",
-                               ftype  => 'revolution',
-                               xa     => $im->getwidth / 2,
-                               ya     => $im->getheight / 2,
-                               xb     => $im->getwidth / 2,
-                               yb     => 0);
-  ok($revolution, "revolution fountain sample");
-  # out from the middle
-  my $radial = $im->filter(type   => "fountain",
-                           ftype  => 'radial',
-                           xa     => $im->getwidth / 2,
-                           ya     => $im->getheight / 2,
-                           xb     => $im->getwidth / 2,
-                           yb     => 0);
-  ok($radial, "radial fountain sample");
-}
-
-{
-  # try a simple custom filter that uses the Perl image interface
-  sub perl_filt {
-    my %args = @_;
-
-    my $im = $args{imager};
-
-    my $channels = $args{channels};
-    unless (@$channels) {
-      $channels = [ reverse(0 .. $im->getchannels-1) ];
-    }
-    my @chans = @$channels;
-    push @chans, 0 while @chans < 4;
-
-    for my $y (0 .. $im->getheight-1) {
-      my $row = $im->getsamples(y => $y, channels => \@chans);
-      $im->setscanline(y => $y, pixels => $row);
-    }
-  }
-  Imager->register_filter(type => 'perl_test',
-                          callsub => \&perl_filt,
-                          defaults => { channels => [] },
-                          callseq => [ qw/imager channels/ ]);
-  test($imbase, { type => 'perl_test' }, 'testout/t61perl.ppm');
-}
-
-{ # check the difference method out
-  my $im1 = Imager->new(xsize => 3, ysize => 2);
-  $im1->box(filled => 1, color => '#FF0000');
-  my $im2 = $im1->copy;
-  $im1->setpixel(x => 1, 'y' => 0, color => '#FF00FF');
-  $im2->setpixel(x => 1, 'y' => 0, color => '#FF01FF');
-  $im1->setpixel(x => 2, 'y' => 0, color => '#FF00FF');
-  $im2->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
-
-  my $diff1 = $im1->difference(other => $im2);
-  my $cmp1 = Imager->new(xsize => 3, ysize => 2, channels => 4);
-  $cmp1->setpixel(x => 1, 'y' => 0, color => '#FF01FF');
-  $cmp1->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
-  is_image($diff1, $cmp1, "difference() - check image with mindist 0");
-
-  my $diff2 = $im1->difference(other => $im2, mindist => 1);
-  my $cmp2 = Imager->new(xsize => 3, ysize => 2, channels => 4);
-  $cmp2->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
-  is_image($diff2, $cmp2, "difference() - check image with mindist 1");
-}
-
-{
-  # and again with large samples
-  my $im1 = Imager->new(xsize => 3, ysize => 2, bits => 'double');
-  $im1->box(filled => 1, color => '#FF0000');
-  my $im2 = $im1->copy;
-  $im1->setpixel(x => 1, 'y' => 0, color => '#FF00FF');
-  $im2->setpixel(x => 1, 'y' => 0, color => '#FF01FF');
-  $im1->setpixel(x => 2, 'y' => 0, color => '#FF00FF');
-  $im2->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
-
-  my $diff1 = $im1->difference(other => $im2);
-  my $cmp1 = Imager->new(xsize => 3, ysize => 2, channels => 4);
-  $cmp1->setpixel(x => 1, 'y' => 0, color => '#FF01FF');
-  $cmp1->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
-  is_image($diff1, $cmp1, "difference() - check image with mindist 0 - large samples");
-
-  my $diff2 = $im1->difference(other => $im2, mindist => 1.1);
-  my $cmp2 = Imager->new(xsize => 3, ysize => 2, channels => 4);
-  $cmp2->setpixel(x => 2, 'y' => 0, color => '#FF02FF');
-  is_image($diff2, $cmp2, "difference() - check image with mindist 1.1 - large samples");
-}
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->filter(type => "hardinvert"), "can't filter an empty image");
-  is($empty->errstr, "filter: empty input image",
-     "check error message");
-  ok(!$empty->difference(other => $imbase), "can't difference empty image");
-  is($empty->errstr, "difference: empty input image",
-     "check error message");
-  ok(!$imbase->difference(other => $empty),
-     "can't difference against empty image");
-  is($imbase->errstr, "difference: empty input image (other image)",
-     "check error message");
-}
-
-sub test {
-  my ($in, $params, $out) = @_;
-
-  my $copy = $in->copy;
-  if (ok($copy->filter(%$params), $params->{type})) {
-    ok($copy->write(file=>$out), "write $params->{type}") 
-      or print "# ",$copy->errstr,"\n";
-  }
-  else {
-    diag($copy->errstr);
-  SKIP: 
-    {
-      skip("couldn't filter", 1);
-    }
-  }
-  $copy;
-}
-
-sub color_close {
-  my ($c1, $c2) = @_;
-
-  my @c1 = $c1->rgba;
-  my @c2 = $c2->rgba;
-
-  for my $i (0..2) {
-    if (abs($c1[$i]-$c2[$i]) > 2) {
-      return 0;
-    }
-  }
-  return 1;
-}
diff --git a/t/t62compose.t b/t/t62compose.t
deleted file mode 100644 (file)
index cbf8af3..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-#!perl -w
-use strict;
-use Imager qw(:handy);
-use Test::More tests => 120;
-use Imager::Test qw(is_image is_imaged);
-
--d "testout" or mkdir "testout";
-
-Imager::init_log("testout/t62compose.log", 1);
-
-my @files;
-
-my %types =
-  (
-   double =>
-   {
-    blue => NCF(0, 0, 1),
-    red =>  NCF(1, 0, 0),
-    green2 => NCF(0, 1, 0, 0.5),
-    green2_on_blue => NCF(0, 0.5, 0.5),
-    red3_on_blue => NCF(1/3, 0, 2/3),
-    green6_on_blue => NCF(0, 1/6, 5/6),
-    red2_on_blue => NCF(0.5, 0, 0.5),
-    green4_on_blue => NCF(0, 0.25, 0.75),
-    gray100 => NCF(1.0, 0, 0),
-    gray50 => NCF(0.5, 0, 0),
-    is_image => \&is_imaged,
-   },
-   8 =>
-   {
-    blue => NC(0, 0, 255),
-    red =>  NC(255, 0, 0),
-    green2 => NC(0, 255, 0, 128),
-    green2_on_blue => NC(0, 128, 127),
-    red3_on_blue => NC(85, 0, 170),
-    green6_on_blue => NC(0, 42, 213),
-    red2_on_blue => NC(128, 0, 127),
-    green4_on_blue => NC(0, 64, 191),
-    gray100 => NC(255, 0, 0),
-    gray50 => NC(128, 0, 0),
-    is_image => \&is_image,
-   },
-  );
-
-for my $type_id (sort keys %types) {
-  my $type = $types{$type_id};
-  my $blue = $type->{blue};
-  my $red = $type->{red};
-  my $green2 = $type->{green2};
-  my $green2_on_blue = $type->{green2_on_blue};
-  my $red3_on_blue = $type->{red3_on_blue};
-  my $green6_on_blue = $type->{green6_on_blue};
-  my $red2_on_blue = $type->{red2_on_blue};
-  my $green4_on_blue = $type->{green4_on_blue};
-  my $gray100 = $type->{gray100};
-  my $gray50 = $type->{gray50};
-  my $is_image = $type->{is_image};
-
-  print "# type $type_id\n";
-  my $targ = Imager->new(xsize => 100, ysize => 100, bits => $type_id);
-  $targ->box(color => $blue, filled => 1);
-  is($targ->type, "direct", "check target image type");
-  is($targ->bits, $type_id, "check target bits");
-
-  my $src = Imager->new(xsize => 40, ysize => 40, channels => 4, bits => $type_id);
-  $src->box(filled => 1, color => $red, xmax => 19, ymax => 19);
-  $src->box(filled => 1, xmin => 20, color => $green2);
-  save_to($src, "${type_id}_src");
-
-  my $mask_ones = Imager->new(channels => 1, xsize => 40, ysize => 40, bits => $type_id);
-  $mask_ones->box(filled => 1, color => NC(255, 255, 255));
-
-
-  # mask or full mask, should be the same
-  for my $mask_info ([ "nomask" ], [ "fullmask", mask => $mask_ones ]) {
-    my ($mask_type, @mask_extras) = @$mask_info;
-    print "# $mask_type\n";
-    {
-      my $cmp = $targ->copy;
-      $cmp->box(filled => 1, color => $red,
-               xmin=> 5, ymin => 10, xmax => 24, ymax => 29);
-      $cmp->box(filled => 1, color => $green2_on_blue,
-               xmin => 25, ymin => 10, xmax => 44, ymax => 49);
-      {
-       my $work = $targ->copy;
-       ok($work->compose(src => $src, tx => 5, ty => 10, @mask_extras),
-          "$mask_type - simple compose");
-       $is_image->($work, $cmp, "check match");
-       save_to($work, "${type_id}_${mask_type}_simple");
-      }
-      { # >1 opacity
-       my $work = $targ->copy;
-       ok($work->compose(src => $src, tx => 5, ty => 10, opacity => 2.0, @mask_extras),
-          "$mask_type - compose with opacity > 1.0 acts like opacity=1.0");
-       $is_image->($work, $cmp, "check match");
-      }
-      { # 0 opacity is a failure
-       my $work = $targ->copy;
-       ok(!$work->compose(src => $src, tx => 5, ty => 10, opacity => 0.0, @mask_extras),
-          "$mask_type - compose with opacity = 0 is an error");
-       is($work->errstr, "opacity must be positive", "check message");
-      }
-    }
-    { # compose at 1/3
-      my $work = $targ->copy;
-      ok($work->compose(src => $src, tx => 7, ty => 33, opacity => 1/3, @mask_extras),
-        "$mask_type - simple compose at 1/3");
-      my $cmp = $targ->copy;
-      $cmp->box(filled => 1, color => $red3_on_blue,
-               xmin => 7, ymin => 33, xmax => 26, ymax => 52);
-      $cmp->box(filled => 1, color => $green6_on_blue,
-               xmin => 27, ymin => 33, xmax => 46, ymax => 72);
-      $is_image->($work, $cmp, "check match");
-    }
-    { # targ off top left
-      my $work = $targ->copy;
-      ok($work->compose(src => $src, tx => -5, ty => -3, @mask_extras),
-        "$mask_type - compose off top left");
-      my $cmp = $targ->copy;
-      $cmp->box(filled => 1, color => $red,
-               xmin=> 0, ymin => 0, xmax => 14, ymax => 16);
-      $cmp->box(filled => 1, color => $green2_on_blue,
-               xmin => 15, ymin => 0, xmax => 34, ymax => 36);
-      $is_image->($work, $cmp, "check match");
-    }
-    { # targ off bottom right
-      my $work = $targ->copy;
-      ok($work->compose(src => $src, tx => 65, ty => 67, @mask_extras),
-        "$mask_type - targ off bottom right");
-      my $cmp = $targ->copy;
-      $cmp->box(filled => 1, color => $red,
-               xmin=> 65, ymin => 67, xmax => 84, ymax => 86);
-      $cmp->box(filled => 1, color => $green2_on_blue,
-               xmin => 85, ymin => 67, xmax => 99, ymax => 99);
-      $is_image->($work, $cmp, "check match");
-    }
-    { # src off top left
-      my $work = $targ->copy;
-      my @more_mask_extras;
-      if (@mask_extras) {
-       push @more_mask_extras,
-         (
-          mask_left => -5,
-          mask_top => -15,
-         );
-      }
-      ok($work->compose(src => $src, tx => 10, ty => 20,
-                       src_left => -5, src_top => -15,
-                       @mask_extras, @more_mask_extras),
-        "$mask_type - source off top left");
-      my $cmp = $targ->copy;
-      $cmp->box(filled => 1, color => $red,
-               xmin=> 15, ymin => 35, xmax => 34, ymax => 54);
-      $cmp->box(filled => 1, color => $green2_on_blue,
-             xmin => 35, ymin => 35, xmax => 54, ymax => 74);
-      $is_image->($work, $cmp, "check match");
-    }
-    {
-      # src off bottom right
-      my $work = $targ->copy;
-      ok($work->compose(src => $src, tx => 10, ty => 20,
-                       src_left => 10, src_top => 15,
-                       width => 40, height => 40, @mask_extras),
-        "$mask_type - source off bottom right");
-      my $cmp = $targ->copy;
-      $cmp->box(filled => 1, color => $red,
-               xmin=> 10, ymin => 20, xmax => 19, ymax => 24);
-      $cmp->box(filled => 1, color => $green2_on_blue,
-               xmin => 20, ymin => 20, xmax => 39, ymax => 44);
-      $is_image->($work, $cmp, "check match");
-    }
-    {
-      # simply out of bounds
-      my $work = $targ->copy;
-      ok(!$work->compose(src => $src, tx => 100, @mask_extras),
-        "$mask_type - off the right of the target");
-      $is_image->($work, $targ, "no changes");
-      ok(!$work->compose(src => $src, ty => 100, @mask_extras),
-        "$mask_type - off the bottom of the target");
-      $is_image->($work, $targ, "no changes");
-      ok(!$work->compose(src => $src, tx => -40, @mask_extras),
-        "$mask_type - off the left of the target");
-      $is_image->($work, $targ, "no changes");
-      ok(!$work->compose(src => $src, ty => -40, @mask_extras),
-        "$mask_type - off the top of the target");
-      $is_image->($work, $targ, "no changes");
-    }
-  }
-
-  # masked tests
-  my $mask = Imager->new(xsize => 40, ysize => 40, channels => 1, bits => $type_id);
-  $mask->box(filled => 1, xmax => 19, color => $gray100);
-  $mask->box(filled => 1, xmin => 20, ymax => 14, xmax => 34,
-            color => $gray50);
-  is($mask->bits, $type_id, "check mask bits");
-  {
-    my $work = $targ->copy;
-    ok($work->compose(src => $src, tx => 5, ty => 7,
-                     mask => $mask),
-       "simple draw masked");
-    my $cmp = $targ->copy;
-    $cmp->box(filled => 1, color => $red,
-             xmin => 5, ymin => 7, xmax => 24, ymax => 26);
-    $cmp->box(filled => 1, color => $green4_on_blue,
-             xmin => 25, ymin => 7, xmax => 39, ymax => 21);
-    $is_image->($work, $cmp, "check match");
-    save_to($work, "${type_id}_simp_masked");
-    save_to($work, "${type_id}_simp_masked_cmp");
-  }
-  {
-    my $work = $targ->copy;
-    ok($work->compose(src => $src, tx => 5, ty => 7,
-                     mask_left => 5, mask_top => 2, 
-                     mask => $mask),
-       "draw with mask offset");
-    my $cmp = $targ->copy;
-    $cmp->box(filled => 1, color => $red,
-             xmin => 5, ymin => 7, xmax => 19, ymax => 26);
-    $cmp->box(filled => 1, color => $red2_on_blue,
-             xmin => 20, ymin => 7, xmax => 24, ymax => 19);
-    $cmp->box(filled => 1, color => $green4_on_blue,
-             xmin => 25, ymin => 7, xmax => 34, ymax => 19);
-    $is_image->($work, $cmp, "check match");
-  }
-  {
-    my $work = $targ->copy;
-    ok($work->compose(src => $src, tx => 5, ty => 7,
-                     mask_left => -3, mask_top => -2, 
-                     mask => $mask),
-       "draw with negative mask offsets");
-    my $cmp = $targ->copy;
-    $cmp->box(filled => 1, color => $red,
-             xmin => 8, ymin => 9, xmax => 24, ymax => 26);
-    $cmp->box(filled => 1, color => $green2_on_blue,
-             xmin => 25, ymin => 9, xmax => 27, ymax => 46);
-    $cmp->box(filled => 1, color => $green4_on_blue,
-             xmin => 28, ymin => 9, xmax => 42, ymax => 23);
-    $is_image->($work, $cmp, "check match");
-  }
-}
-
-{
-  my $empty = Imager->new;
-  my $good = Imager->new(xsize => 1, ysize => 1);
-  ok(!$empty->compose(src => $good), "can't compose to empty image");
-  is($empty->errstr, "compose: empty input image",
-     "check error message");
-  ok(!$good->compose(src => $empty), "can't compose from empty image");
-  is($good->errstr, "compose: empty input image (for src)",
-     "check error message");
-  ok(!$good->compose(src => $good, mask => $empty),
-     "can't compose with empty mask");
-  is($good->errstr, "compose: empty input image (for mask)",
-     "check error message");
-}
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink @files;
-}
-
-sub save_to {
-  my ($im, $name) = @_;
-
-  my $type = $ENV{IMAGER_SAVE_TYPE} || "ppm";
-  $name = "testout/t62_$name.$type";
-  $im->write(file => $name,
-            pnm_write_wide_data => 1);
-  push @files, $name;
-}
diff --git a/t/t63combine.t b/t/t63combine.t
deleted file mode 100644 (file)
index 6cbe88b..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-#!perl -w
-use strict;
-use Imager;
-use Test::More tests => 31;
-use Imager::Test qw/test_image test_image_double is_image/;
-
-my $test_im = test_image;
-my $test_im_dbl = test_image_double;
-
-{
-  # split out channels and put it back together
-  my $red = Imager->combine(src => [ $test_im ]);
-  ok($red, "extracted the red channel");
-  is($red->getchannels, 1, "red should be a single channel");
-  my $green = Imager->combine(src => [ $test_im ], channels => [ 1 ]);
-  ok($green, "extracted the green channel");
-  is($green->getchannels, 1, "green should be a single channel");
-  my $blue = $test_im->convert(preset => "blue");
-  ok($blue, "extracted blue (via convert)");
-
-  # put them back together
-  my $combined = Imager->combine(src => [ $red, $green, $blue ]);
-  is($combined->getchannels, 3, "check we got a three channel image");
-  is_image($combined, $test_im, "presto! check it's the same");
-}
-
-{
-  # no src
-  ok(!Imager->combine(), "no src");
-  is(Imager->errstr, "src parameter missing", "check message");
-}
-
-{
-  # bad image error
-  my $im = Imager->new;
-  ok(!Imager->combine(src => [ $im ]), "empty image");
-  is(Imager->errstr, "combine: empty input image (src->[0])",
-     "check message");
-}
-
-{
-  # not an image
-  my $im = {};
-  ok(!Imager->combine(src => [ $im ]), "not an image");
-  is(Imager->errstr, "src must contain image objects", "check message");
-}
-
-{
-  # no images
-  ok(!Imager->combine(src => []), "no images");
-  is(Imager->errstr, "At least one image must be supplied",
-     "check message");
-}
-
-{
-  # too many images
-  ok(!Imager->combine(src => [ ($test_im) x 5 ]), "too many source images");
-  is(Imager->errstr, "Maximum of 4 channels, you supplied 5",
-     "check message");
-}
-
-{
-  # negative channel
-  ok(!Imager->combine(src => [ $test_im ], channels => [ -1 ]),
-     "negative channel");
-  is(Imager->errstr, "Channel numbers must be zero or positive",
-     "check message");
-}
-
-{
-  # channel too high
-  ok(!Imager->combine(src => [ $test_im ], channels => [ 3 ]),
-     "too high channel");
-  is(Imager->errstr, "Channel 3 for image 0 is too high (3 channels)",
-     "check message");
-}
-
-{
-  # make sure we get the higher of the bits
-  my $out = Imager->combine(src => [ $test_im, $test_im_dbl ]);
-  ok($out, "make from 8 and double/sample images");
-  is($out->bits, "double", "check output bits");
-}
-
-{
-  # check high-bit processing
-  # split out channels and put it back together
-  my $red = Imager->combine(src => [ $test_im_dbl ]);
-  ok($red, "extracted the red channel");
-  is($red->getchannels, 1, "red should be a single channel");
-  my $green = Imager->combine(src => [ $test_im_dbl ], channels => [ 1 ]);
-  ok($green, "extracted the green channel");
-  is($green->getchannels, 1, "green should be a single channel");
-  my $blue = $test_im_dbl->convert(preset => "blue");
-  ok($blue, "extracted blue (via convert)");
-
-  # put them back together
-  my $combined = Imager->combine(src => [ $red, $green, $blue ]);
-  is($combined->getchannels, 3, "check we got a three channel image");
-  is_image($combined, $test_im_dbl, "presto! check it's the same");
-  is($combined->bits, "double", "and we got a double image output");
-}
diff --git a/t/t64copyflip.t b/t/t64copyflip.t
deleted file mode 100644 (file)
index 38561cc..0000000
+++ /dev/null
@@ -1,285 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 95;
-use Imager;
-use Imager::Test qw(is_color3 is_image is_imaged test_image_double test_image isnt_image is_image_similar);
-
-#$Imager::DEBUG=1;
-
--d "testout" or mkdir "testout";
-
-Imager::init('log'=>'testout/t64copyflip.log');
-
-my $img=Imager->new() or die "unable to create image object\n";
-
-$img->open(file=>'testimg/scale.ppm',type=>'pnm');
-my $nimg = $img->copy();
-ok($nimg, "copy returned something");
-
-# test if ->copy() works
-
-my $diff = Imager::i_img_diff($img->{IMG}, $nimg->{IMG});
-is_image($img, $nimg, "copy matches source");
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->copy, "fail to copy an empty image");
-  is($empty->errstr, "copy: empty input image", "check error message");
-}
-
-# test if ->flip(dir=>'h')->flip(dir=>'h') doesn't alter the image
-$nimg->flip(dir=>"h")->flip(dir=>"h");
-is_image($nimg, $img, "double horiz flipped matches original");
-
-# test if ->flip(dir=>'v')->flip(dir=>'v') doesn't alter the image
-$nimg->flip(dir=>"v")->flip(dir=>"v");
-is_image($nimg, $img, "double vertically flipped image matches original");
-
-
-# test if ->flip(dir=>'h')->flip(dir=>'v') is same as ->flip(dir=>'hv')
-$nimg->flip(dir=>"v")->flip(dir=>"h")->flip(dir=>"hv");;
-is_image($img, $nimg, "check flip with hv matches flip v then flip h");
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->flip(dir => "v"), "fail to flip an empty image");
-  is($empty->errstr, "flip: empty input image", "check error message");
-}
-
-{
-  my $imsrc = test_image_double;
-  my $imcp = $imsrc->copy;
-  is_imaged($imsrc, $imcp, "copy double image");
-  $imcp->flip(dir=>"v")->flip(dir=>"v");
-  is_imaged($imsrc, $imcp, "flip v twice");
-  $imcp->flip(dir=>"h")->flip(dir=>"h");
-  is_imaged($imsrc, $imcp, "flip h twice");
-  $imcp->flip(dir=>"h")->flip(dir=>"v")->flip(dir=>"hv");
-  is_imaged($imsrc, $imcp, "flip h,v,hv twice");
-}
-
-{
-  my $impal = test_image()->to_paletted;
-  my $imcp = $impal->copy;
-  is($impal->type, "paletted", "check paletted test image is");
-  is($imcp->type, "paletted", "check copy test image is paletted");
-  ok($impal->flip(dir => "h"), "flip paletted h");
-  isnt_image($impal, $imcp, "check it changed");
-  ok($impal->flip(dir => "v"), "flip paletted v");
-  ok($impal->flip(dir => "hv"), "flip paletted hv");
-  is_image($impal, $imcp, "should be back to original image");
-  is($impal->type, "paletted", "and still paletted");
-}
-
-rot_test($img, 90, 4);
-rot_test($img, 180, 2);
-rot_test($img, 270, 4);
-rot_test($img, 0, 1);
-
-my $pimg = $img->to_paletted();
-rot_test($pimg, 90, 4);
-rot_test($pimg, 180, 2);
-rot_test($pimg, 270, 4);
-rot_test($pimg, 0, 1);
-
-my $timg = $img->rotate(right=>90)->rotate(right=>270);
-is(Imager::i_img_diff($img->{IMG}, $timg->{IMG}), 0,
-   "check rotate 90 then 270 matches original");
-$timg = $img->rotate(right=>90)->rotate(right=>180)->rotate(right=>90);
-is(Imager::i_img_diff($img->{IMG}, $timg->{IMG}), 0,
-     "check rotate 90 then 180 then 90 matches original");
-
-# this could use more tests
-my $rimg = $img->rotate(degrees=>10);
-ok($rimg, "rotation by 10 degrees gave us an image");
-if (!$rimg->write(file=>"testout/t64_rot10.ppm")) {
-  print "# Cannot save: ",$rimg->errstr,"\n";
-}
-
-# rotate with background
-$rimg = $img->rotate(degrees=>10, back=>Imager::Color->new(builtin=>'red'));
-ok($rimg, "rotate with background gave us an image");
-if (!$rimg->write(file=>"testout/t64_rot10_back.ppm")) {
-  print "# Cannot save: ",$rimg->errstr,"\n";
-}
-
-{
-  # rotate with text background
-  my $rimg = $img->rotate(degrees => 45, back => '#FF00FF');
-  ok($rimg, "rotate with background as text gave us an image");
-  
-  # check the color set correctly
-  my $c = $rimg->getpixel(x => 0, 'y' => 0);
-  is_deeply([ 255, 0, 255 ], [ ($c->rgba)[0, 1, 2] ],
-            "check background set correctly");
-
-  # check error handling for background color
-  $rimg = $img->rotate(degrees => 45, back => "some really unknown color");
-  ok(!$rimg, "should fail due to bad back color");
-  cmp_ok($img->errstr, '=~', "^No color named ", "check error message");
-}
-SKIP:
-{ # rotate in double mode
-  my $dimg = $img->to_rgb16;
-  my $rimg = $dimg->rotate(degrees => 10);
-  ok($rimg, "rotate 16-bit image gave us an image")
-    or skip("could not rotate", 3);
-  ok($rimg->write(file => "testout/t64_rotf10.ppm", pnm_write_wide_data => 1),
-     "save wide data rotated")
-    or diag($rimg->errstr);
-
-  # with a background color
-  my $rimgb = $dimg->rotate(degrees => 10, back => "#FF8000");
-  ok($rimgb, "rotate 16-bit image with back gave us an image")
-    or skip("could not rotate", 1);
-  ok($rimgb->write(file => "testout/t64_rotfb10.ppm", pnm_write_wide_data => 1),
-     "save wide data rotated")
-    or diag($rimgb->errstr);
-}
-{ # rotate in paletted mode
-  my $rimg = $pimg->rotate(degrees => 10);
-  ok($rimg, "rotated paletted image 10 degrees");
-  ok($rimg->write(file => "testout/t64_rotp10.ppm"),
-     "save paletted rotated")
-    or diag($rimg->errstr);
-}
-
-my $trimg = $img->matrix_transform(matrix=>[ 1.2, 0, 0,
-                                             0,   1, 0,
-                                             0,   0, 1]);
-ok($trimg, "matrix_transform() returned an image");
-$trimg->write(file=>"testout/t64_trans.ppm")
-  or print "# Cannot save: ",$trimg->errstr,"\n";
-
-$trimg = $img->matrix_transform(matrix=>[ 1.2, 0, 0,
-                                             0,   1, 0,
-                                             0,   0, 1],
-                                  back=>Imager::Color->new(builtin=>'blue'));
-ok($trimg, "matrix_transform() with back returned an image");
-
-$trimg->write(file=>"testout/t64_trans_back.ppm")
-  or print "# Cannot save: ",$trimg->errstr,"\n";
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->matrix_transform(matrix => [ 1, 0, 0,
-                                          0, 1, 0,
-                                          0, 0, 1 ]),
-     "can't transform an empty image");
-  is($empty->errstr, "matrix_transform: empty input image",
-     "check error message");
-}
-
-sub rot_test {
-  my ($src, $degrees, $count) = @_;
-
-  my $cimg = $src->copy();
-  my $in;
-  for (1..$count) {
-    $in = $cimg;
-    $cimg = $cimg->rotate(right=>$degrees)
-      or last;
-  }
- SKIP:
-  {
-    ok($cimg, "got a rotated image")
-      or skip("no image to check", 4);
-    my $diff = Imager::i_img_diff($src->{IMG}, $cimg->{IMG});
-    is($diff, 0, "check it matches source")
-      or skip("didn't match", 3);
-
-    # check that other parameters match
-    is($src->type, $cimg->type, "type check");
-    is($src->bits, $cimg->bits, "bits check");
-    is($src->getchannels, $cimg->getchannels, "channels check");
-  }
-}
-
-{ # http://rt.cpan.org/NoAuth/Bug.html?id=9672
-  my $warning;
-  local $SIG{__WARN__} = 
-    sub { 
-      $warning = "@_";
-      my $printed = $warning;
-      $printed =~ s/\n$//;
-      $printed =~ s/\n/\n\#/g; 
-      print "# ",$printed, "\n";
-    };
-  my $img = Imager->new(xsize=>10, ysize=>10);
-  $img->copy();
-  cmp_ok($warning, '=~', 'void', "correct warning");
-  cmp_ok($warning, '=~', 't64copyflip\\.t', "correct file");
-  $warning = '';
-  $img->rotate(degrees=>5);
-  cmp_ok($warning, '=~', 'void', "correct warning");
-  cmp_ok($warning, '=~', 't64copyflip\\.t', "correct file");
-  $warning = '';
-  $img->matrix_transform(matrix=>[1, 1, 1]);
-  cmp_ok($warning, '=~', 'void', "correct warning");
-  cmp_ok($warning, '=~', 't64copyflip\\.t', "correct file");
-}
-
-{
-  # 29936 - matrix_transform() should use fabs() instead of abs()
-  # range checking sz 
-
-  # this meant that when sz was < 1 (which it often is for these
-  # transformations), it treated the values out of range, producing a
-  # blank output image
-
-  my $src = Imager->new(xsize => 20, ysize => 20);
-  $src->box(filled => 1, color => 'FF0000');
-  my $out = $src->matrix_transform(matrix => [ 1, 0, 0,
-                                              0, 1, 0,
-                                              0, 0, 0.9999 ])
-    or print "# ", $src->errstr, "\n";
-  my $blank = Imager->new(xsize => 20, ysize => 20);
-  # they have to be different, surely that would be easy
-  my $diff = Imager::i_img_diff($out->{IMG}, $blank->{IMG});
-  ok($diff, "RT#29936 - check non-blank output");
-}
-
-{
-  my $im = Imager->new(xsize => 10, ysize => 10, channels => 4);
-  $im->box(filled => 1, color => 'FF0000');
-  my $back = Imager::Color->new(0, 0, 0, 0);
-  my $rot = $im->rotate(degrees => 10, back => $back);
-  # drop the alpha and make sure there's only 2 colors used
-  my $work = $rot->convert(preset => 'noalpha');
-  my $im_pal = $work->to_paletted(make_colors => 'mediancut');
-  my @colors = $im_pal->getcolors;
-  is(@colors, 2, "should be only 2 colors")
-    or do {
-      print "# ", join(",", $_->rgba), "\n" for @colors;
-    };
-  @colors = sort { ($a->rgba)[0] <=> ($b->rgba)[0] } @colors;
-  is_color3($colors[0], 0, 0, 0, "check we got black");
-  is_color3($colors[1], 255, 0, 0, "and red");
-}
-
-{ # RT #77063 rotate with degrees => 270 gives a black border
-  # so be a little less strict about rounding up
-  # I've also:
-  #  - improved calculation of the rotation matrix
-  #  - added rounding to interpolation for 1/3 channel images
-  my $im = test_image;
-  $im->box(color => "#00F");
-  my $right = $im->rotate(right => 270);
-  my $deg = $im->rotate(degrees => 270, back => "#FFF");
-  is($deg->getwidth, 150, "check degrees => 270 width");
-  is($deg->getheight, 150, "check degrees => 270 height");
-  ok($deg->write(file => "testout/t64rotdeg270.ppm"), "save it");
-  $right->write(file => "testout/t64rotright270.ppm");
-  is_image($deg, $right, "check right and degrees result the same");
-  #$deg = $deg->convert(preset => "addalpha");
-  # $right = $right->convert(preset => "addalpha");
-  # my $diff = $right->difference(other => $deg, mindist => 1);
-  # $diff->write(file => "testout/t64rotdiff.png");
-}
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->rotate(degrees => 90), "can't rotate an empty image");
-  is($empty->errstr, "rotate: empty input image",
-     "check error message");
-}
diff --git a/t/t65crop.t b/t/t65crop.t
deleted file mode 100644 (file)
index 3b19d98..0000000
+++ /dev/null
@@ -1,190 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 66;
-use Imager;
-use Imager::Test qw(test_image);
-
-#$Imager::DEBUG=1;
-
--d "testout" or mkdir "testout";
-
-Imager::init('log'=>'testout/t65crop.log');
-
-my $img=Imager->new() || die "unable to create image object\n";
-
-ok($img, "created image ph");
-
-SKIP:
-{
-  skip("couldn't load source image", 2)
-    unless ok($img->open(file=>'testimg/scale.ppm',type=>'pnm'), "loaded source");
-  my $nimg = $img->crop(top=>10, left=>10, bottom=>25, right=>25);
-  ok($nimg, "got an image");
-  ok($nimg->write(file=>"testout/t65.ppm"), "save to file");
-}
-
-{ # https://rt.cpan.org/Ticket/Display.html?id=7578
-  # make sure we get the right type of image on crop
-  my $src = Imager->new(xsize=>50, ysize=>50, channels=>2, bits=>16);
-  is($src->getchannels, 2, "check src channels");
-  is($src->bits, 16, "check src bits");
-  my $out = $src->crop(left=>10, right=>40, top=>10, bottom=>40);
-  is($out->getchannels, 2, "check out channels");
-  is($out->bits, 16, "check out bits");
-}
-{ # https://rt.cpan.org/Ticket/Display.html?id=7578
-  print "# try it for paletted too\n";
-  my $src = Imager->new(xsize=>50, ysize=>50, channels=>3, type=>'paletted');
-  # make sure color index zero is defined so there's something to copy
-  $src->addcolors(colors=>[Imager::Color->new(0,0,0)]);
-  is($src->type, 'paletted', "check source type");
-  my $out = $src->crop(left=>10, right=>40, top=>10, bottom=>40);
-  is($out->type, 'paletted', 'check output type');
-}
-
-{ # https://rt.cpan.org/Ticket/Display.html?id=7581
-  # crop() documentation says width/height takes precedence, but is unclear
-  # from looking at the existing code, setting width/height will go from
-  # the left of the image, even if left/top are provided, despite the
-  # sample in the docs
-  # Let's make sure that things happen as documented
-  my $src = test_image();
-  # make sure we get what we want
-  is($src->getwidth, 150, "src width");
-  is($src->getheight, 150, "src height");
-
-  # the test data is: 
-  #  - description
-  #  - hash ref containing args to crop()
-  #  - expected left, top, right, bottom values
-  # we call crop using the given arguments then call it using the 
-  # hopefully stable left/top/right/bottom/arguments
-  # this is kind of lame, but I don't want to include a rewritten
-  # crop in this file
-  my @tests = 
-    (
-     [ 
-      "basic",
-      { left=>10, top=>10, right=>70, bottom=>80 },
-      10, 10, 70, 80,
-     ],
-     [
-      "middle",
-      { width=>50, height=>50 },
-      50, 50, 100, 100,
-     ],
-     [
-      "lefttop",
-      { left=>20, width=>70, top=>30, height=>90 },
-      20, 30, 90, 120,
-     ],
-     [
-      "bottomright",
-      { right=>140, width=>50, bottom=>130, height=>60 },
-      90, 70, 140, 130,
-     ],
-     [
-      "acrossmiddle",
-      { top=>40, bottom=>110 },
-      0, 40, 150, 110,
-     ],
-     [
-      "downmiddle",
-      { left=>40, right=>110 },
-      40, 0, 110, 150,
-     ],
-     [
-      "rightside",
-      { left=>80, },
-      80, 0, 150, 150,
-     ],
-     [
-      "leftside",
-      { right=>40 },
-      0, 0, 40, 150,
-     ],
-     [
-      "topside",
-      { bottom=>40, },
-      0, 0, 150, 40,
-     ],
-     [
-      "bottomside",
-      { top=>90 },
-      0, 90, 150, 150,
-     ],
-     [
-      "overright",
-      { left=>100, right=>200 },
-      100, 0, 150, 150,
-     ],
-     [
-      "overtop",
-      { bottom=>50, height=>70 },
-      0, 0, 150, 50,
-     ],
-     [
-      "overleft",
-      { right=>30, width=>60 },
-      0, 0, 30, 150,
-     ],
-     [ 
-      "overbottom",
-      { top=>120, height=>60 },
-      0, 120, 150, 150,
-     ],
-    );
-  for my $test (@tests) {
-    my ($desc, $args, $left, $top, $right, $bottom) = @$test;
-    my $out = $src->crop(%$args);
-    ok($out, "got output for $desc");
-    my $cmp = $src->crop(left=>$left, top=>$top, right=>$right, bottom=>$bottom);
-    ok($cmp, "got cmp for $desc");
-    # make sure they're the same
-    my $diff = Imager::i_img_diff($out->{IMG}, $cmp->{IMG});
-    is($diff, 0, "difference should be 0 for $desc");
-  }
-}
-{ # https://rt.cpan.org/Ticket/Display.html?id=7581
-  # previously we didn't check that the result had some pixels
-  # make sure we do
-  my $src = test_image();
-  ok(!$src->crop(left=>50, right=>50), "nothing across");
-  cmp_ok($src->errstr, '=~', qr/resulting image would have no content/,
-        "and message");
-  ok(!$src->crop(top=>60, bottom=>60), "nothing down");
-  cmp_ok($src->errstr, '=~', qr/resulting image would have no content/,
-        "and message");
-}
-
-{ # http://rt.cpan.org/NoAuth/Bug.html?id=9672
-  my $warning;
-  local $SIG{__WARN__} = 
-    sub { 
-      $warning = "@_";
-      my $printed = $warning;
-      $printed =~ s/\n$//;
-      $printed =~ s/\n/\n\#/g; 
-      print "# ",$printed, "\n";
-    };
-  my $img = Imager->new(xsize=>10, ysize=>10);
-  $img->crop(left=>5);
-  cmp_ok($warning, '=~', 'void', "correct warning");
-  cmp_ok($warning, '=~', 't65crop\\.t', "correct file");
-}
-
-{
-    my $src = test_image();
-    ok(!$src->crop( top=>1000, bottom=>1500, left=>0, right=>100 ),
-                "outside of image" );
-    cmp_ok($src->errstr, '=~', qr/outside of the image/, "and message");
-    ok(!$src->crop( top=>100, bottom=>1500, left=>1000, right=>1500 ),
-                "outside of image" );
-    cmp_ok($src->errstr, '=~', qr/outside of the image/, "and message");
-}
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->crop(left => 10), "can't crop an empty image");
-  is($empty->errstr, "crop: empty input image", "check message");
-}
diff --git a/t/t66paste.t b/t/t66paste.t
deleted file mode 100644 (file)
index 8599823..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 60;
-
-use Imager;
-use Imager::Test qw(is_image);
-
-#$Imager::DEBUG=1;
-
--d "testout" or mkdir "testout";
-
-Imager::init('log'=>'testout/t66paste.log');
-
-# the original smoke tests
-my $img=Imager->new() || die "unable to create image object\n";
-
-ok($img->open(file=>'testimg/scale.ppm',type=>'pnm'), "load test img");
-
-my $nimg=Imager->new() or die "Unable to create image object\n";
-ok($nimg->open(file=>'testimg/scale.ppm',type=>'pnm'), "load test img again");
-
-ok($img->paste(img=>$nimg, top=>30, left=>30), "paste it")
-  or print "# ", $img->errstr, "\n";;
-
-ok($img->write(type=>'pnm',file=>'testout/t66.ppm'), "save it")
-  or print "# ", $img->errstr, "\n";
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->paste(src => $nimg), "paste into empty image");
-  is($empty->errstr, "paste: empty input image",
-     "check error message");
-
-  ok(!$img->paste(src => $empty), "paste from empty image");
-  is($img->errstr, "paste: empty input image (for src)",
-     "check error message");
-
-  ok(!$img->paste(), "no source image");
-  is($img->errstr, "no source image");
-}
-
-# more stringent tests
-{
-  my $src = Imager->new(xsize => 100, ysize => 110);
-  $src->box(filled=>1, color=>'FF0000');
-
-  $src->box(filled=>1, color=>'0000FF', xmin => 20, ymin=>20,
-            xmax=>79, ymax=>79);
-
-  my $targ = Imager->new(xsize => 100, ysize => 110);
-  $targ->box(filled=>1, color =>'00FFFF');
-  $targ->box(filled=>1, color=>'00FF00', xmin=>20, ymin=>20, xmax=>79,
-             ymax=>79);
-  my $work = $targ->copy;
-  ok($work->paste(src=>$src, left => 15, top => 10), "paste whole image");
-  # build comparison image
-  my $cmp = $targ->copy;
-  $cmp->box(filled=>1, xmin=>15, ymin => 10, color=>'FF0000');
-  $cmp->box(filled=>1, xmin=>35, ymin => 30, xmax=>94, ymax=>89, 
-            color=>'0000FF');
-
-  is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
-     "compare pasted and expected");
-
-  $work = $targ->copy;
-  ok($work->paste(src=>$src, left=>2, top=>7, src_minx => 10, src_miny => 15),
-     "paste from inside src");
-  $cmp = $targ->copy;
-  $cmp->box(filled=>1, xmin=>2, ymin=>7, xmax=>91, ymax=>101, color=>'FF0000');
-  $cmp->box(filled=>1, xmin=>12, ymin=>12, xmax=>71, ymax=>71, 
-            color=>'0000FF');
-  is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
-     "compare pasted and expected");
-
-  # paste part source
-  $work = $targ->copy;
-  ok($work->paste(src=>$src, left=>15, top=>20, 
-                  src_minx=>10, src_miny=>15, src_maxx=>80, src_maxy =>70),
-     "paste src cropped all sides");
-  $cmp = $targ->copy;
-  $cmp->box(filled=>1, xmin=>15, ymin=>20, xmax=>84, ymax=>74, 
-            color=>'FF0000');
-  $cmp->box(filled=>1, xmin=>25, ymin=>25, xmax=>84, ymax=>74,
-            color=>'0000FF');
-  is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
-     "compare pasted and expected");
-
-  # go by width instead
-  $work = $targ->copy;
-  ok($work->paste(src=>$src, left=>15, top=>20,
-                  src_minx=>10, src_miny => 15, width => 70, height => 55),
-     "same but specify width/height instead");
-  is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
-     "compare pasted and expected");
-
-  # use src_coords
-  $work = $targ->copy;
-  ok($work->paste(src=>$src, left => 15, top => 20,
-                  src_coords => [ 10, 15, 80, 70 ]),
-     "using src_coords");
-  is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
-     "compare pasted and expected");
-
-  {
-    # Issue #18712
-    # supplying just src_maxx would set the internal maxy to undef
-    # supplying just src_maxy would be ignored
-    # src_maxy (or it's derived value) was being bounds checked against 
-    # the image width instead of the image height
-    $work = $targ->copy;
-    my @warns;
-    local $SIG{__WARN__} = sub { push @warns, "@_"; print "# @_"; };
-    
-    ok($work->paste(src=>$src, left => 15, top => 20,
-                   src_maxx => 50),
-       "paste with just src_maxx");
-    ok(!@warns, "shouldn't warn");
-    my $cmp = $targ->copy;
-    $cmp->box(filled=>1, color => 'FF0000', xmin => 15, ymin => 20,
-             xmax => 64, ymax => 109);
-    $cmp->box(filled=>1, color => '0000FF', xmin => 35, ymin => 40,
-             xmax => 64, ymax => 99);
-    is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
-       "check correctly pasted");
-
-    $work = $targ->copy;
-    @warns = ();
-    ok($work->paste(src=>$src, left=>15, top=>20,
-                   src_maxy => 60),
-       "paste with just src_maxy");
-    ok(!@warns, "shouldn't warn");
-    $cmp = $targ->copy;
-    $cmp->box(filled => 1, color => 'FF0000', xmin => 15, ymin => 20,
-             xmax => 99, ymax => 79);
-    $cmp->box(filled => 1, color => '0000FF', xmin => 35, ymin => 40,
-             xmax => 94, ymax => 79);
-    is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
-       "check pasted correctly");
-
-    $work = $targ->copy;
-    @warns = ();
-    ok($work->paste(src=>$src, left=>15, top=>20,
-                   src_miny => 20, src_maxy => 105),
-       "paste with src_maxy > source width");
-
-    $cmp = $targ->copy;
-    $cmp->box(filled => 1, color => 'FF0000', xmin => 15, ymin => 20,
-             ymax => 104);
-    $cmp->box(filled => 1, color => '0000FF', xmin => 35, ymin => 20,
-             xmax => 94, ymax => 79);
-    is(Imager::i_img_diff($work->{IMG}, $cmp->{IMG}), 0,
-       "check pasted correctly");
-  }
-}
-
-{ # https://rt.cpan.org/Ticket/Display.html?id=30908
-  # we now adapt the source channels to the target
-  # check each combination works as expected
-
-  # various source images
-  my $src1 = Imager->new(xsize => 50, ysize => 50, channels => 1);
-  my $g_grey_full = Imager::Color->new(128, 255, 0, 0);
-  my $g_white_50 = Imager::Color->new(255, 128, 0, 0);
-  $src1->box(filled => 1, xmax => 24, color => $g_grey_full);
-
-  my $src2 = Imager->new(xsize => 50, ysize => 50, channels => 2);
-  $src2->box(filled => 1, xmax => 24, color => $g_grey_full);
-  $src2->box(filled => 1, xmin => 25, color => $g_white_50);
-
-  my $c_red_full = Imager::Color->new(255, 0, 0);
-  my $c_blue_full = Imager::Color->new(0, 0, 255);
-  my $src3 = Imager->new(xsize => 50, ysize => 50, channels => 3);
-  $src3->box(filled => 1, xmax => 24, color => $c_red_full);
-  $src3->box(filled => 1, xmin => 25, color => $c_blue_full);
-
-  my $c_green_50 = Imager::Color->new(0, 255, 0, 127);
-  my $src4 = Imager->new(xsize => 50, ysize => 50, channels => 4);
-  $src4->box(filled => 1, xmax => 24, color => $c_blue_full);
-  $src4->box(filled => 1, xmin => 25, color => $c_green_50);
-
-  my @left_box = ( box => [ 25, 25, 49, 74 ] );
-  my @right_box = ( box => [ 50, 25, 74, 74 ] );
-
-  { # 1 channel output
-    my $base = Imager->new(xsize => 100, ysize => 100, channels => 1);
-    $base->box(filled => 1, color => Imager::Color->new(64, 255, 0, 0));
-
-    my $work = $base->copy;
-    ok($work->paste(left => 25, top => 25, src => $src1), "paste 1 to 1");
-    my $comp = $base->copy;
-    $comp->box(filled => 1, color => $g_grey_full, @left_box);
-    $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(left => 25, top => 25, src => $src2), "paste 2 to 1");
-    $comp = $base->copy;
-    $comp->box(filled => 1, @left_box, color => $g_grey_full);
-    $comp->box(filled => 1, @right_box, color => [ 128, 0, 0, 0 ]);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(left => 25, top => 25, src => $src3), "paste 3 to 1");
-     $comp = $base->copy;
-    $comp->box(filled => 1, @left_box, color => [ 57, 255, 0, 0 ]);
-    $comp->box(filled => 1, @right_box, color => [ 18, 255, 0, 0 ]);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(left => 25, top => 25, src => $src4), "paste 4 to 1");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
-    $comp->box(filled => 1, color => [ 90, 255, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-  }
-
-  { # 2 channel output
-    my $base = Imager->new(xsize => 100, ysize => 100, channels => 2);
-    $base->box(filled => 1, color => [ 128, 128, 0, 0 ]);
-    
-    my $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src1), "paste 1 to 2");
-    my $comp = $base->copy;
-    $comp->box(filled => 1, color => $g_grey_full, @left_box);
-    $comp->box(filled => 1, color => [ 0, 255, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src2), "paste 2 to 2");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => $g_grey_full, @left_box);
-    $comp->box(filled => 1, color => $g_white_50, @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src3), "paste 3 to 2");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 57, 255, 0, 0 ], @left_box);
-    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src4), "paste 4 to 2");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
-    $comp->box(filled => 1, color => [ 180, 127, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-  }
-
-  { # 3 channel output
-    my $base = Imager->new(xsize => 100, ysize => 100, channels => 3);
-    $base->box(filled => 1, color => [ 128, 255, 0, 0 ]);
-    
-    my $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src1), "paste 1 to 3");
-    my $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
-    $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src2), "paste 2 to 3");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
-    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src3), "paste 3 to 3");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
-    $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src4), "paste 4 to 3");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 0, 0, 255 ], @left_box);
-    $comp->box(filled => 1, color => [ 0, 127, 0 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-  }
-
-  { # 4 channel output
-    my $base = Imager->new(xsize => 100, ysize => 100, channels => 4);
-    $base->box(filled => 1, color => [ 128, 255, 64, 128 ]);
-    
-    my $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src1), "paste 1 to 4");
-    my $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
-    $comp->box(filled => 1, color => [ 0, 0, 0, 255 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src2), "paste 2 to 4");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
-    $comp->box(filled => 1, color => [ 255, 255, 255, 128 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src3), "paste 3 to 4");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
-    $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-
-    $work = $base->copy;
-    ok($work->paste(top => 25, left => 25, src => $src4), "paste 4 to 4");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => $c_blue_full, @left_box);
-    $comp->box(filled => 1, color => $c_green_50, @right_box);
-    is_image($work, $comp, "compare paste target to expected");
-  }
-}
diff --git a/t/t67convert.t b/t/t67convert.t
deleted file mode 100644 (file)
index a4517cc..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-#!perl -w
-use strict;
-use Imager qw(:all :handy);
-use Test::More tests => 31;
-use Imager::Test qw(test_colorf_gpix is_fcolor1 is_fcolor3);
-
--d "testout" or mkdir "testout";
-
-Imager::init("log"=>'testout/t67convert.log');
-
-my $imbase = Imager::ImgRaw::new(200,300,3);
-
-# first a basic test, make sure the basic things happen ok
-# make a 1 channel image from the above (black) image
-# but with 1 as the 'extra' value
-SKIP:
-{
-  my $im_white = i_convert($imbase, [ [ 0, 0, 0, 1 ] ]);
-  skip("convert to white failed", 3)
-    unless ok($im_white, "convert to white");
-
-  my ($w, $h, $ch) = i_img_info($im_white);
-
-  # the output image should now have one channel
-  is($ch, 1, "one channel image now");
-  # should have the same width and height
-  ok($w == 200 && $h == 300, "check converted size is the same");
-
-  # should be a white image now, let's check
-  my $c = Imager::i_get_pixel($im_white, 20, 20);
-  my @c = $c->rgba;
-  print "# @c\n";
-  is($c[0], 255, "check image is white");
-}
-
-# test the highlevel interface
-# currently this requires visual inspection of the output files
-my $im = Imager->new;
-SKIP:
-{
-  skip("could not load scale.ppm", 3)
-    unless $im->read(file=>'testimg/scale.ppm');
-  my $out = $im->convert(preset=>'gray');
-  ok($out, "convert preset gray");
-  ok($out->write(file=>'testout/t67_gray.ppm', type=>'pnm'),
-    "save grey image");
-  $out = $im->convert(preset=>'blue');
-  ok($out, "convert preset blue");
-
-  ok($out->write(file=>'testout/t67_blue.ppm', type=>'pnm'),
-     "save blue image");
-}
-
-# test against 16-bit/sample images
-{
- SKIP:
-  {
-    my $imbase16 = Imager::i_img_16_new(200, 200, 3);
-
-    my $im16targ = i_convert($imbase16, [ [ 0, 0, 0, 1 ],
-                                         [ 0, 0, 0, 0 ],
-                                         [ 0, 0, 0, 0 ] ]);
-    ok($im16targ, "convert 16/bit sample image")
-      or skip("could not convert 16-bit image", 2);
-
-    # image should still be 16-bit
-    is(Imager::i_img_bits($im16targ), 16, "Image still 16-bit/sample");
-
-    # make sure that it's roughly red
-    test_colorf_gpix($im16targ, 0, 0, NCF(1, 0, 0), 0.001, "image roughly red");
-  }
- SKIP:
-  {
-    my $imbase16 = Imager->new(xsize => 10, ysize => 10, bits => 16);
-    ok($imbase16->setpixel
-       (x => 5, y => 2, color => Imager::Color::Float->new(0.1, 0.2, 0.3)),
-       "set a sample pixel");
-    my $c1 = $imbase16->getpixel(x => 5, y => 2, type => "float");
-    is_fcolor3($c1, 0.1, 0.2, 0.3, "check it was set")
-      or print "#", join(",", $c1->rgba), "\n";
-    
-    my $targ16 = $imbase16->convert(matrix => [ [ 0.05, 0.15, 0.01, 0.5 ] ]);
-    ok($targ16, "convert another 16/bit sample image")
-      or skip("could not convert", 3);
-    is($targ16->getchannels, 1, "convert should be 1 channel");
-    is($targ16->bits, 16, "and 16-bits");
-    my $c = $targ16->getpixel(x => 5, y => 2, type => "float");
-    is_fcolor1($c, 0.538, 1/32768, "check grey value");
-  }
-}
-
-# test against palette based images
-my $impal = Imager::i_img_pal_new(200, 300, 3, 256);
-my $black = NC(0, 0, 0);
-my $blackindex = Imager::i_addcolors($impal, $black);
-ok($blackindex, "add black to paletted");
-for my $y (0..299) {
-  Imager::i_ppal($impal, 0, $y, ($blackindex) x 200);
-}
-
-SKIP:
-{
-  my $impalout = i_convert($impal, [ [ 0, 0, 0, 0 ],
-                                    [ 0, 0, 0, 1 ],
-                                    [ 0, 0, 0, 0 ] ]);
-  skip("could not convert paletted", 3)
-    unless ok($impalout, "convert paletted");
-  is(Imager::i_img_type($impalout), 1, "image still paletted");
-  is(Imager::i_colorcount($impalout), 1, "still only one colour");
-  my $c = Imager::i_getcolors($impalout, $blackindex);
-  ok($c, "get color from palette");
-  my @ch = $c->rgba;
-  print "# @ch\n";
-  ok($ch[0] == 0 && $ch[1] == 255 && $ch[2] == 0, 
-     "colour is as expected");
-}
-
-{ # http://rt.cpan.org/NoAuth/Bug.html?id=9672
-  # methods that return a new image should warn in void context
-  my $warning;
-  local $SIG{__WARN__} = 
-    sub { 
-      $warning = "@_";
-      my $printed = $warning;
-      $printed =~ s/\n$//;
-      $printed =~ s/\n/\n\#/g; 
-      print "# ",$printed, "\n";
-    };
-  my $img = Imager->new(xsize=>10, ysize=>10);
-  $img->convert(preset=>"grey");
-  cmp_ok($warning, '=~', 'void', "correct warning");
-  cmp_ok($warning, '=~', 't67convert\\.t', "correct file");
-}
-
-{ # http://rt.cpan.org/NoAuth/Bug.html?id=28492
-  # convert() doesn't preserve image sample size
-  my $im = Imager->new(xsize => 20, ysize => 20, channels => 3, 
-                      bits => 'double');
-  is($im->bits, 'double', 'check source bits');
-  my $conv = $im->convert(preset => 'grey');
-  is($conv->bits, 'double', 'make sure result has extra bits');
-}
-
-{ # http://rt.cpan.org/NoAuth/Bug.html?id=79922
-  # Segfault in convert with bad params
-  my $im = Imager->new(xsize => 10, ysize => 10);
-  ok(!$im->convert(matrix => [ 10, 10, 10 ]),
-     "this would crash");
-  is($im->errstr, "convert: invalid matrix: element 0 is not an array ref",
-     "check the error message");
-}
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->convert(preset => "addalpha"), "can't convert an empty image");
-  is($empty->errstr, "convert: empty input image", "check error message");
-}
diff --git a/t/t68map.t b/t/t68map.t
deleted file mode 100644 (file)
index b91f43d..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 8;
-
--d "testout" or mkdir "testout";
-
-Imager::init("log"=>'testout/t68map.log');
-
-use Imager qw(:all :handy);
-
-my $imbase = Imager::ImgRaw::new(200,300,3);
-
-
-my @map1 = map { int($_/2) } 0..255;
-my @map2 = map { 255-int($_/2) } 0..255;
-my @map3 = 0..255;
-my @maps = 0..24;
-my @mapl = 0..400;
-
-my $tst = 1;
-
-ok(i_map($imbase, [ [],     [],     \@map1 ]), "map1 in ch 3");
-ok(i_map($imbase, [ \@map1, \@map1, \@map1 ]), "map1 in ch1-3");
-
-ok(i_map($imbase, [ \@map1, \@map2, \@map3 ]), "map1-3 in ch 1-3");
-
-ok(i_map($imbase, [ \@maps, \@mapl, \@map3 ]), "incomplete maps");
-
-# test the highlevel interface
-# currently this requires visual inspection of the output files
-
-SKIP: {
-  my $im = Imager->new;
-  $im->read(file=>'testimg/scale.ppm')
-    or skip "Cannot load test image testimg/scale.ppm", 2;
-
-  ok( $im->map(red=>\@map1, green=>\@map2, blue=>\@map3),
-      "test OO interface (maps by color)");
-  ok( $im->map(maps=>[\@map1, [], \@map2]),
-      "test OO interface (maps by maps)");
-}
-
-{
-  my $empty = Imager->new;
-  ok(!$empty->map(maps => [ \@map1, \@map2, \@map3 ]),
-     "can't map an empty image");
-  is($empty->errstr, "map: empty input image", "check error message");
-}
diff --git a/t/t69rubthru.t b/t/t69rubthru.t
deleted file mode 100644 (file)
index a1fa3d3..0000000
+++ /dev/null
@@ -1,327 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 76;
-use Imager qw(:all :handy);
-use Imager::Test qw(is_image);
-
--d "testout" or mkdir "testout";
-
-init_log("testout/t69rubthru.log", 1);
-
-my $src_height = 80;
-my $src_width = 80;
-
-# raw interface
-my $targ = Imager::ImgRaw::new(100, 100, 3);
-my $src = Imager::ImgRaw::new($src_height, $src_width, 4);
-my $halfred = NC(255, 0, 0, 128);
-i_box_filled($src, 20, 20, 60, 60, $halfred);
-ok(i_rubthru($targ, $src, 10, 10, 0, 0, $src_width, $src_height),
-   "low level rubthrough");
-my $c = Imager::i_get_pixel($targ, 10, 10);
-ok($c, "get pixel at (10, 10)");
-ok(color_cmp($c, NC(0, 0, 0)) == 0, "check for correct color");
-$c = Imager::i_get_pixel($targ, 30, 30);
-ok($c, "get pixel at (30, 30)");
-ok(color_cmp($c, NC(128, 0, 0)) == 0, "check color");
-
-my $black = NC(0, 0, 0);
-# reset the target and try a grey+alpha source
-i_box_filled($targ, 0, 0, 100, 100, $black);
-my $gsrc = Imager::ImgRaw::new($src_width, $src_height, 2);
-my $halfwhite = NC(255, 128, 0);
-i_box_filled($gsrc, 20, 20, 60, 60, $halfwhite);
-ok(i_rubthru($targ, $gsrc, 10, 10, 0, 0, $src_width, $src_height),
-   "low level with grey/alpha source");
-$c = Imager::i_get_pixel($targ, 15, 15);
-ok($c, "get at (15, 15)");
-ok(color_cmp($c, NC(0, 0, 0)) == 0, "check color");
-$c = Imager::i_get_pixel($targ, 30, 30);
-ok($c, "get pixel at (30, 30)");
-ok(color_cmp($c, NC(128, 128, 128)) == 0, "check color");
-
-# try grey target and grey alpha source
-my $gtarg = Imager::ImgRaw::new(100, 100, 1);
-ok(i_rubthru($gtarg, $gsrc, 10, 10, 0, 0, $src_width, $src_height), 
-   "low level with grey target and gray/alpha source");
-$c = Imager::i_get_pixel($gtarg, 10, 10);
-ok($c, "get pixel at 10, 10");
-is(($c->rgba)[0], 0, "check grey level");
-is((Imager::i_get_pixel($gtarg, 30, 30)->rgba)[0], 128,
-   "check grey level at 30, 30");
-
-# simple test for 16-bit/sample images
-my $targ16 = Imager::i_img_16_new(100, 100, 3);
-ok(i_rubthru($targ16, $src, 10, 10, 0, 0, $src_width, $src_height),
-   "smoke test vs 16-bit/sample image");
-$c = Imager::i_get_pixel($targ16, 30, 30);
-ok($c, "get pixel at 30, 30");
-ok(color_cmp($c, NC(128, 0, 0)) == 0, "check color");
-
-# check the OO interface
-my $ootarg = Imager->new(xsize=>100, ysize=>100);
-my $oosrc = Imager->new(xsize=>80, ysize=>80, channels=>4);
-$oosrc->box(color=>$halfred, xmin=>20, ymin=>20, xmax=>60, ymax=>60,
-            filled=>1);
-ok($ootarg->rubthrough(src=>$oosrc, tx=>10, ty=>10),
-   "oo rubthrough");
-ok(color_cmp(Imager::i_get_pixel($ootarg->{IMG}, 10, 10), NC(0, 0, 0)) == 0,
-   "check pixel at 10, 10");
-ok(color_cmp(Imager::i_get_pixel($ootarg->{IMG}, 30, 30), NC(128, 0, 0)) == 0,
-   "check pixel at 30, 30");
-
-my $oogtarg = Imager->new(xsize=>100, ysize=>100, channels=>1);
-
-{ # check empty image errors
-  my $empty = Imager->new;
-  ok(!$empty->rubthrough(src => $oosrc), "check empty target");
-  is($empty->errstr, 'rubthrough: empty input image', "check error message");
-  ok(!$oogtarg->rubthrough(src=>$empty), "check empty source");
-  is($oogtarg->errstr, 'rubthrough: empty input image (for src)',
-     "check error message");
-}
-
-{
-  # alpha source and target
-  for my $method (qw/rubthrough compose/) {
-
-    my $src = Imager->new(xsize => 10, ysize => 1, channels => 4);
-    my $targ = Imager->new(xsize => 10, ysize => 2, channels => 4);
-
-    # simple initialization
-    $targ->setscanline('y' => 1, x => 1,
-                      pixels =>
-                      [
-                       NC(255, 128, 0, 255),
-                       NC(255, 128, 0, 128),
-                       NC(255, 128, 0, 0),
-                       NC(255, 128, 0, 255),
-                       NC(255, 128, 0, 128),
-                       NC(255, 128, 0, 0),
-                       NC(255, 128, 0, 255),
-                       NC(255, 128, 0, 128),
-                       NC(255, 128, 0, 0),
-                      ]);
-    $src->setscanline('y' => 0,
-                     pixels =>
-                     [
-                      NC(0, 128, 255, 0),
-                      NC(0, 128, 255, 0),
-                      NC(0, 128, 255, 0),
-                      NC(0, 128, 255, 128),
-                      NC(0, 128, 255, 128),
-                      NC(0, 128, 255, 128),
-                      NC(0, 128, 255, 255),
-                      NC(0, 128, 255, 255),
-                      NC(0, 128, 255, 255),
-                     ]);
-    ok($targ->$method(src => $src, combine => 'normal',
-                     tx => 1, ty => 1), "do 4 on 4 $method");
-    iscolora($targ->getpixel(x => 1, 'y' => 1), NC(255, 128, 0, 255),
-            "check at zero source coverage on full targ coverage");
-    iscolora($targ->getpixel(x => 2, 'y' => 1), NC(255, 128, 0, 128),
-            "check at zero source coverage on half targ coverage");
-    iscolora($targ->getpixel(x => 3, 'y' => 1), NC(255, 128, 0, 0),
-            "check at zero source coverage on zero targ coverage");
-    iscolora($targ->getpixel(x => 4, 'y' => 1), NC(127, 128, 128, 255),
-            "check at half source_coverage on full targ coverage");
-    iscolora($targ->getpixel(x => 5, 'y' => 1), NC(85, 128, 170, 191),
-            "check at half source coverage on half targ coverage");
-    iscolora($targ->getpixel(x => 6, 'y' => 1), NC(0, 128, 255, 128),
-            "check at half source coverage on zero targ coverage");
-    iscolora($targ->getpixel(x => 7, 'y' => 1), NC(0, 128, 255, 255),
-            "check at full source_coverage on full targ coverage");
-    iscolora($targ->getpixel(x => 8, 'y' => 1), NC(0, 128, 255, 255),
-            "check at full source coverage on half targ coverage");
-    iscolora($targ->getpixel(x => 9, 'y' => 1), NC(0, 128, 255, 255),
-            "check at full source coverage on zero targ coverage");
-  }
-}
-
-{ # https://rt.cpan.org/Ticket/Display.html?id=30908
-  # we now adapt the source channels to the target
-  # check each combination works as expected
-
-  # various source images
-  my $src1 = Imager->new(xsize => 50, ysize => 50, channels => 1);
-  my $g_grey_full = Imager::Color->new(128, 255, 0, 0);
-  my $g_white_50 = Imager::Color->new(255, 128, 0, 0);
-  $src1->box(filled => 1, xmax => 24, color => $g_grey_full);
-
-  my $src2 = Imager->new(xsize => 50, ysize => 50, channels => 2);
-  $src2->box(filled => 1, xmax => 24, color => $g_grey_full);
-  $src2->box(filled => 1, xmin => 25, color => $g_white_50);
-
-  my $c_red_full = Imager::Color->new(255, 0, 0);
-  my $c_blue_full = Imager::Color->new(0, 0, 255);
-  my $src3 = Imager->new(xsize => 50, ysize => 50, channels => 3);
-  $src3->box(filled => 1, xmax => 24, color => $c_red_full);
-  $src3->box(filled => 1, xmin => 25, color => $c_blue_full);
-
-  my $c_green_50 = Imager::Color->new(0, 255, 0, 127);
-  my $src4 = Imager->new(xsize => 50, ysize => 50, channels => 4);
-  $src4->box(filled => 1, xmax => 24, color => $c_blue_full);
-  $src4->box(filled => 1, xmin => 25, color => $c_green_50);
-
-  my @left_box = ( box => [ 25, 25, 49, 74 ] );
-  my @right_box = ( box => [ 50, 25, 74, 74 ] );
-
-  { # 1 channel output
-    my $base = Imager->new(xsize => 100, ysize => 100, channels => 1);
-    $base->box(filled => 1, color => Imager::Color->new(64, 255, 0, 0));
-
-    my $work = $base->copy;
-    ok($work->rubthrough(left => 25, top => 25, src => $src1), "rubthrough 1 to 1");
-    my $comp = $base->copy;
-    $comp->box(filled => 1, color => $g_grey_full, @left_box);
-    $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(left => 25, top => 25, src => $src2), "rubthrough 2 to 1");
-    $comp = $base->copy;
-    $comp->box(filled => 1, @left_box, color => $g_grey_full);
-    $comp->box(filled => 1, @right_box, color => [ 159, 0, 0, 0 ]);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(left => 25, top => 25, src => $src3), "rubthrough 3 to 1");
-     $comp = $base->copy;
-    $comp->box(filled => 1, @left_box, color => [ 57, 255, 0, 0 ]);
-    $comp->box(filled => 1, @right_box, color => [ 18, 255, 0, 0 ]);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(left => 25, top => 25, src => $src4), "rubthrough 4 to 1");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
-    $comp->box(filled => 1, color => [ 121, 255, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-  }
-
-  { # 2 channel output
-    my $base = Imager->new(xsize => 100, ysize => 100, channels => 2);
-    $base->box(filled => 1, color => [ 128, 128, 0, 0 ]);
-    
-    my $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src1), "rubthrough 1 to 2");
-    my $comp = $base->copy;
-    $comp->box(filled => 1, color => $g_grey_full, @left_box);
-    $comp->box(filled => 1, color => [ 0, 255, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src2), "rubthrough 2 to 2");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => $g_grey_full, @left_box);
-    $comp->box(filled => 1, color => [ 213, 191, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src3), "rubthrough 3 to 2");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 57, 255, 0, 0 ], @left_box);
-    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src4), "rubthrough 4 to 2");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 18, 255, 0, 0 ], @left_box);
-    $comp->box(filled => 1, color => [ 162, 191, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-  }
-
-  { # 3 channel output
-    my $base = Imager->new(xsize => 100, ysize => 100, channels => 3);
-    $base->box(filled => 1, color => [ 128, 255, 0, 0 ]);
-    
-    my $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src1), "rubthrough 1 to 3");
-    my $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
-    $comp->box(filled => 1, color => [ 0, 0, 0, 0 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src2), "rubthrough 2 to 3");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
-    $comp->box(filled => 1, color => [ 191, 255, 128, 255 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src3), "rubthrough 3 to 3");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
-    $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src4), "rubthrough 4 to 3");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 0, 0, 255 ], @left_box);
-    $comp->box(filled => 1, color => [ 64, 255, 0 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-  }
-
-  { # 4 channel output
-    my $base = Imager->new(xsize => 100, ysize => 100, channels => 4);
-    $base->box(filled => 1, color => [ 128, 255, 64, 128 ]);
-    
-    my $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src1), "rubthrough 1 to 4");
-    my $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
-    $comp->box(filled => 1, color => [ 0, 0, 0, 255 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src2), "rubthrough 2 to 4");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 128, 128, 128, 255 ], @left_box);
-    $comp->box(filled => 1, color => [ 213, 255, 192, 191 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src3), "rubthrough 3 to 4");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => [ 255, 0, 0 ], @left_box);
-    $comp->box(filled => 1, color => [ 0, 0, 255 ], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-
-    $work = $base->copy;
-    ok($work->rubthrough(top => 25, left => 25, src => $src4), "rubthrough 4 to 4");
-    $comp = $base->copy;
-    $comp->box(filled => 1, color => $c_blue_full, @left_box);
-    $comp->box(filled => 1, color => [ 43, 255, 21, 191], @right_box);
-    is_image($work, $comp, "compare rubthrough target to expected");
-  }
-}
-
-sub color_cmp {
-  my ($l, $r) = @_;
-  my @l = $l->rgba;
-  my @r = $r->rgba;
-  print "# (",join(",", @l[0..2]),") <=> (",join(",", @r[0..2]),")\n";
-  return $l[0] <=> $r[0]
-    || $l[1] <=> $r[1]
-      || $l[2] <=> $r[2];
-}
-
-sub iscolora {
-  my ($c1, $c2, $msg) = @_;
-
-  my $builder = Test::Builder->new;
-  my @c1 = $c1->rgba;
-  my @c2 = $c2->rgba;
-  if (!$builder->ok($c1[0] == $c2[0] && $c1[1] == $c2[1] && $c1[2] == $c2[2]
-                    && $c1[3] == $c2[3],
-                    $msg)) {
-    $builder->diag(<<DIAG);
-      got color: [ @c1 ]
- expected color: [ @c2 ]
-DIAG
-  }
-}
-
diff --git a/t/t75polyaa.t b/t/t75polyaa.t
deleted file mode 100644 (file)
index ace2e64..0000000
+++ /dev/null
@@ -1,279 +0,0 @@
-#!perl -w
-
-use strict;
-use Test::More tests => 18;
-
-use Imager qw/NC/;
-use Imager::Test qw(is_image is_color3);
-
-sub PI () { 3.14159265358979323846 }
-
--d "testout" or mkdir "testout";
-
-Imager::init_log("testout/t75aapolyaa.log",1);
-
-my $red   = Imager::Color->new(255,0,0);
-my $green = Imager::Color->new(0,255,0);
-my $blue  = Imager::Color->new(0,0,255);
-my $white = Imager::Color->new(255,255,255);
-
-{ # artifacts with multiple vertical lobes
-  # https://rt.cpan.org/Ticket/Display.html?id=43518
-  # previously this would have a full coverage pixel at (0,0) caused
-  # by the (20,0.5) point in the right lobe
-
-  my @pts = 
-    (
-     [ 0.5, -9 ],
-     [ 10, -9 ],
-     [ 10, 11 ],
-     [ 15, 11 ],
-     [ 15, -9 ],
-     [ 17, -9 ],
-     [ 20, 0.5 ],
-     [ 17, 11 ],
-     [ 0.5, 11 ],
-    );
-  my $im = Imager->new(xsize => 10, ysize => 2);
-  ok($im->polygon(points => \@pts,
-                 color => $white),
-     "draw with inside point");
-  ok($im->write(file => "testout/t75inside.ppm"), "save to file");
-  # both scanlines should be the same
-  my $line0 = $im->crop(top => 0, height => 1);
-  my $line1 = $im->crop(top => 1, height => 1);
-  is_image($line0, $line1, "both scanlines should be the same");
-}
-
-{ # check vertical edges are consistent
-  my $im = Imager->new(xsize => 10, ysize => 10);
-  ok($im->polygon(points => [ [ 0.5, 0 ], [ 9.25, 0 ], 
-                             [ 9.25, 10 ], [ 0.5, 10 ] ],
-                 color => $white,
-                 aa => 1), 
-     "draw polygon with mid pixel vertical edges")
-    or diag $im->errstr;
-  my @line0 = $im->getscanline(y => 0);
-  my $im2 = Imager->new(xsize => 10, ysize => 10);
-  for my $y (0..9) {
-    $im2->setscanline(y => $y, pixels => \@line0);
-  }
-  is_image($im, $im2, "all scan lines should be the same");
-  is_color3($line0[0], 128, 128, 128, "(0,0) should be 50% coverage");
-  is_color3($line0[9], 64, 64, 64, "(9,0) should be 25% coverage");
-}
-
-{ # check horizontal edges are consistent
-  my $im = Imager->new(xsize => 10, ysize => 10);
-  ok($im->polygon(points => [ [ 0, 0.5 ], [ 0, 9.25 ],
-                             [ 10, 9.25 ], [ 10, 0.5 ] ],
-                 color => $white,
-                 aa => 1),
-     "draw polygon with mid-pixel horizontal edges");
-  is_deeply([ $im->getsamples(y => 0, channels => [ 0 ]) ],
-           [ (128) x 10 ],
-           "all of line 0 should be 50% coverage");
-  is_deeply([ $im->getsamples(y => 9, channels => [ 0 ]) ],
-           [ (64) x 10 ],
-           "all of line 9 should be 25% coverage");
-}
-
-{
-  my $img = Imager->new(xsize=>20, ysize=>10);
-  my @data = translate(5.5,5,
-                      rotate(0,
-                             scale(5, 5,
-                                   get_polygon(n_gon => 5)
-                                  )
-                            )
-                     );
-  
-  
-  my ($x, $y) = array_to_refpair(@data);
-  ok(Imager::i_poly_aa($img->{IMG}, $x, $y, $white), "primitive poly");
-
-  ok($img->write(file=>"testout/t75.ppm"), "write to file")
-    or diag $img->errstr;
-
-  my $zoom = make_zoom($img, 8, \@data, $red);
-  ok($zoom, "make zoom of primitive");
-  $zoom->write(file=>"testout/t75zoom.ppm") or die $zoom->errstr;
-}
-
-{
-  my $img = Imager->new(xsize=>300, ysize=>100);
-
-  my $good = 1;
-  for my $n (0..55) {
-    my @data = translate(20+20*($n%14),18+20*int($n/14),
-                        rotate(15*$n/PI,
-                               scale(15, 15,
-                                     get_polygon('box')
-                                    )
-                              )
-                       );
-    my ($x, $y) = array_to_refpair(@data);
-    Imager::i_poly_aa($img->{IMG}, $x, $y, NC(rand(255), rand(255), rand(255)))
-       or $good = 0;
-  }
-  
-  $img->write(file=>"testout/t75big.ppm") or die $img->errstr;
-
-  ok($good, "primitive squares");
-}
-
-{
-  my $img = Imager->new(xsize => 300, ysize => 300);
-  ok($img -> polygon(color=>$white,
-                 points => [
-                            translate(150,150,
-                                      rotate(45*PI/180,
-                                             scale(70,70,
-                                                   get_polygon('wavycircle', 32*8, sub { 1.2+1*cos(4*$_) }))))
-                           ],
-                ), "method call")
-    or diag $img->errstr();
-
-  $img->write(file=>"testout/t75wave.ppm") or die $img->errstr;
-}
-
-{
-  my $img = Imager->new(xsize=>10,ysize=>6);
-  my @data = translate(165,5,
-                      scale(80,80,
-                            get_polygon('wavycircle', 32*8, sub { 1+1*cos(4*$_) })));
-  
-  ok($img -> polygon(color=>$white,
-               points => [
-                          translate(165,5,
-                                    scale(80,80,
-                                          get_polygon('wavycircle', 32*8, sub { 1+1*cos(4*$_) })))
-                         ],
-                ), "bug check")
-    or diag $img->errstr();
-
-  make_zoom($img,20,\@data, $blue)->write(file=>"testout/t75wavebug.ppm") or die $img->errstr;
-
-}
-
-{
-  my $img = Imager->new(xsize=>300, ysize=>300);
-  ok($img->polygon(fill=>{ hatch=>'cross1', fg=>'00FF00', bg=>'0000FF', dx=>3 },
-              points => [
-                         translate(150,150,
-                                   scale(70,70,
-                                         get_polygon('wavycircle', 32*8, sub { 1+1*cos(4*$_) })))
-                        ],
-             ), "poly filled with hatch")
-    or diag $img->errstr();
-  $img->write(file=>"testout/t75wave_fill.ppm") or die $img->errstr;
-}
-
-{
-  my $img = Imager->new(xsize=>300, ysize=>300, bits=>16);
-  ok($img->polygon(fill=>{ hatch=>'cross1', fg=>'00FF00', bg=>'0000FF' },
-              points => [
-                         translate(150,150,
-                                   scale(70,70,
-                                         get_polygon('wavycircle', 32*8, sub { 1+1*cos(5*$_) })))
-                        ],
-             ), "hatched to 16-bit image")
-    or diag $img->errstr();
-  $img->write(file=>"testout/t75wave_fill16.ppm") or die $img->errstr;
-}
-
-Imager::malloc_state();
-
-
-#initialized in a BEGIN, later
-my %primitives;
-my %polygens;
-
-sub get_polygon {
-  my $name = shift;
-  if (exists $primitives{$name}) {
-    return @{$primitives{$name}};
-  }
-
-  if (exists $polygens{$name}) {
-    return $polygens{$name}->(@_);
-  }
-
-  die "polygon spec: $name unknown\n";
-}
-
-
-sub make_zoom {
-  my ($img, $sc, $polydata, $linecolor) = @_;
-
-  # scale with nearest neighboor sampling
-  my $timg = $img->scale(scalefactor=>$sc, qtype=>'preview');
-
-  # draw the grid
-  for(my $lx=0; $lx<$timg->getwidth(); $lx+=$sc) {
-    $timg->line(color=>$green, x1=>$lx, x2=>$lx, y1=>0, y2=>$timg->getheight(), antialias=>0);
-  }
-
-  for(my $ly=0; $ly<$timg->getheight(); $ly+=$sc) {
-    $timg->line(color=>$green, y1=>$ly, y2=>$ly, x1=>0, x2=>$timg->getwidth(), antialias=>0);
-  }
-  my @data = scale($sc, $sc, @$polydata);
-  push(@data, $data[0]);
-  my ($x, $y) = array_to_refpair(@data);
-
-  $timg->polyline(color=>$linecolor, 'x'=>$x, 'y'=>$y, antialias=>0);
-  return $timg;
-}
-
-# utility functions to manipulate point data
-
-sub scale {
-  my ($x, $y, @data) = @_;
-  return map { [ $_->[0]*$x , $_->[1]*$y ] } @data;
-}
-
-sub translate {
-  my ($x, $y, @data) = @_;
-  map { [ $_->[0]+$x , $_->[1]+$y ] } @data;
-}
-
-sub rotate {
-  my ($rad, @data) = @_;
-  map { [ $_->[0]*cos($rad)+$_->[1]*sin($rad) , $_->[1]*cos($rad)-$_->[0]*sin($rad) ] } @data;
-}
-
-sub array_to_refpair {
-  my (@x, @y);
-  for (@_) {
-    push(@x, $_->[0]);
-    push(@y, $_->[1]);
-  }
-  return \@x, \@y;
-}
-
-
-
-BEGIN {
-%primitives = (
-              box => [ [-0.5,-0.5], [0.5,-0.5], [0.5,0.5], [-0.5,0.5] ],
-              triangle => [ [0,0], [1,0], [1,1] ],
-             );
-
-%polygens = (
-            wavycircle => sub {
-              my $numv = shift;
-              my $radfunc = shift;
-              my @radians = map { $_*2*PI/$numv } 0..$numv-1;
-              my @radius  = map { $radfunc->($_) } @radians;
-              map {
-                [ $radius[$_] * cos($radians[$_]), $radius[$_] * sin($radians[$_]) ]
-              } 0..$#radians;
-            },
-            n_gon => sub {
-              my $N = shift;
-              map {
-                [ cos($_*2*PI/$N), sin($_*2*PI/$N) ]
-              } 0..$N-1;
-            },
-);
-}
diff --git a/t/t80texttools.t b/t/t80texttools.t
deleted file mode 100644 (file)
index b685c7c..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 13;
-
-BEGIN { use_ok('Imager') }
-
--d "testout" or mkdir "testout";
-
-require_ok('Imager::Font::Wrap');
-
-my $img = Imager->new(xsize=>400, ysize=>400);
-
-my $text = <<EOS;
-This is a test of text wrapping. This is a test of text wrapping. This =
-is a test of text wrapping. This is a test of text wrapping. This is a =
-test of text wrapping. This is a test of text wrapping. This is a test =
-of text wrapping. This is a test of text wrapping. This is a test of =
-text wrapping. XX.
-
-Xxxxxxxxxxxxxxxxxxxxxxxxxxxwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww xxxx.
-
-This is a test of text wrapping. This is a test of text wrapping. This =
-is a test of text wrapping. This is a test of text wrapping. This is a =
-test of text wrapping. This is a test of text wrapping. This is a test =
-of text wrapping. This is a test of text wrapping. This is a test of =
-text wrapping. This is a test of text wrapping. This is a test of text =
-wrapping. This is a test of text wrapping. This is a test of text =
-wrapping. This is a test of text wrapping. This is a test of text =
-wrapping. This is a test of text wrapping. This is a test of text =
-wrapping. XX.
-EOS
-
-$text =~ s/=\n//g;
-
-my $fontfile = $ENV{WRAPTESTFONT} || $ENV{TTFONTTEST} || "fontfiles/ImUgly.ttf";
-
-my $font = Imager::Font->new(file=>$fontfile);
-
-SKIP:
-{
-  $Imager::formats{'tt'} || $Imager::formats{'ft2'}
-      or skip("Need Freetype 1.x or 2.x to test", 11);
-
-  ok($font, "loading font")
-    or skip("Could not load test font", 8);
-
-  Imager::Font->priorities(qw(t1 ft2 tt));
-  ok(scalar Imager::Font::Wrap->wrap_text(string => $text,
-                                font=>$font,
-                                image=>$img,
-                                size=>13,
-                                width => 380, aa=>1,
-                                x=>10, 'y'=>10,
-                                justify=>'fill',
-                                color=>'FFFFFF'),
-      "basic test");
-  ok($img->write(file=>'testout/t80wrapped.ppm'), "save to file");
-  ok(scalar Imager::Font::Wrap->wrap_text(string => $text,
-                                font=>$font,
-                                image=>undef,
-                                size=>13,
-                                width => 380,
-                                x=>10, 'y'=>10,
-                                justify=>'left',
-                                color=>'FFFFFF'),
-      "no image test");
-  my $bbox = $font->bounding_box(string=>"Xx", size=>13);
-  ok($bbox, "get height for check");
-
-  my $used;
-  ok(scalar Imager::Font::Wrap->wrap_text
-      (string=>$text, font=>$font, image=>undef, size=>13, width=>380,
-       savepos=> \$used, height => $bbox->font_height), "savepos call");
-  ok($used > 20 && $used < length($text), "savepos value");
-  print "# $used\n";
-  my @box = Imager::Font::Wrap->wrap_text
-    (string=>substr($text, 0, $used), font=>$font, image=>undef, size=>13,
-     width=>380);
-
-  ok(@box == 4, "bounds list count");
-  print "# @box\n";
-  ok($box[3] == $bbox->font_height, "check height");
-
-  { # regression
-    # http://rt.cpan.org/Ticket/Display.html?id=29771
-    # the length of the trailing line wasn't included in the text consumed
-    my $used;
-    ok(scalar Imager::Font::Wrap->wrap_text
-       ( string => "test", font => $font, image => undef, size => 12,
-        width => 200, savepos => \$used, height => $bbox->font_height),
-       "regression 29771 - call wrap_text");
-    is($used, 4, "all text should be consumed");
-  }
-}
diff --git a/t/t81hlines.t b/t/t81hlines.t
deleted file mode 100644 (file)
index 594f010..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-#!perl -w
-use strict;
-use Test::More;
-use Imager;
-
-# this script tests an internal set of functions for Imager, they 
-# aren't intended to be used at the perl level.
-# these functions aren't present in all Imager builds
-
-unless (Imager::Internal::Hlines::testing()) {
-  plan skip_all => 'Imager not built to run this test';
-}
-
-plan tests => 15;
-
-my $hline = Imager::Internal::Hlines::new(0, 100, 0, 100);
-my $base_text = 'start_y: 0 limit_y: 100 start_x: 0 limit_x: 100';
-ok($hline, "made hline");
-is($hline->dump, "$base_text\n", "check values");
-$hline->add(5, -5, 7);
-is($hline->dump, <<EOS, "check (-5, 7) added");
-$base_text
- 5 (1): [0, 2)
-EOS
-$hline->add(5, 8, 4);
-is($hline->dump, <<EOS, "check (8, 4) added");
-$base_text
- 5 (2): [0, 2) [8, 12)
-EOS
-$hline->add(5, 3, 3);
-is($hline->dump, <<EOS, "check (3, 3) added");
-$base_text
- 5 (3): [0, 2) [3, 6) [8, 12)
-EOS
-$hline->add(5, 2, 6);
-is($hline->dump, <<EOS, "check (2, 6) added");
-$base_text
- 5 (1): [0, 12)
-EOS
-# adding out of range should do nothing
-my $current = <<EOS;
-$base_text
- 5 (1): [0, 12)
-EOS
-$hline->add(6, -5, 5);
-is($hline->dump, $current, "check (6, -5, 5) not added");
-$hline->add(6, 100, 5);
-is($hline->dump, $current, "check (6, 100, 5) not added");
-$hline->add(-1, 5, 2);
-is($hline->dump, $current, "check (-1, 5, 2) not added");
-$hline->add(100, 5, 2);
-is($hline->dump, $current, "check (10, 5, 2) not added");
-
-# overlapped add check
-$hline->add(6, 2, 6);
-$hline->add(6, 3, 4);
-is($hline->dump, <<EOS, "check internal overlap merged");
-$base_text
- 5 (1): [0, 12)
- 6 (1): [2, 8)
-EOS
-
-# white box test: try to force reallocation of an entry
-for my $i (0..20) {
-  $hline->add(7, $i*2, 1);
-}
-is($hline->dump, <<EOS, "lots of segments");
-$base_text
- 5 (1): [0, 12)
- 6 (1): [2, 8)
- 7 (21): [0, 1) [2, 3) [4, 5) [6, 7) [8, 9) [10, 11) [12, 13) [14, 15) [16, 17) [18, 19) [20, 21) [22, 23) [24, 25) [26, 27) [28, 29) [30, 31) [32, 33) [34, 35) [36, 37) [38, 39) [40, 41)
-EOS
-# now merge them
-$hline->add(7, 1, 39);
-is($hline->dump, <<EOS, "merge lots of segments");
-$base_text
- 5 (1): [0, 12)
- 6 (1): [2, 8)
- 7 (1): [0, 41)
-EOS
-
-# clean object
-$hline = Imager::Internal::Hlines::new(50, 50, 50, 50);
-$base_text = 'start_y: 50 limit_y: 100 start_x: 50 limit_x: 100';
-
-# left merge
-$hline->add(51, 45, 10);
-$hline->add(51, 55, 4);
-is($hline->dump, <<EOS, "left merge");
-$base_text
- 51 (1): [50, 59)
-EOS
-
-# right merge
-$hline->add(52, 90, 5);
-$hline->add(52, 87, 5);
-is($hline->dump, <<EOS, "right merge");
-$base_text
- 51 (1): [50, 59)
- 52 (1): [87, 95)
-EOS
-
-undef $hline;
diff --git a/t/t82inline.t b/t/t82inline.t
deleted file mode 100644 (file)
index 16d231a..0000000
+++ /dev/null
@@ -1,671 +0,0 @@
-#!perl -w
-#
-# this tests both the Inline interface and the API
-use strict;
-use Test::More;
-use Imager::Test qw(is_color3 is_color4);
-eval "require Inline::C;";
-plan skip_all => "Inline required for testing API" if $@;
-
-eval "require Parse::RecDescent;";
-plan skip_all => "Could not load Parse::RecDescent" if $@;
-
-use Cwd 'getcwd';
-plan skip_all => "Inline won't work in directories with spaces"
-  if getcwd() =~ / /;
-
-plan skip_all => "perl 5.005_04, 5.005_05 too buggy"
-  if $] =~ /^5\.005_0[45]$/;
-
--d "testout" or mkdir "testout";
-
-print STDERR "Inline version $Inline::VERSION\n";
-
-plan tests => 117;
-require Inline;
-Inline->import(with => 'Imager');
-Inline->import("FORCE"); # force rebuild
-#Inline->import(C => Config => OPTIMIZE => "-g");
-
-Inline->bind(C => <<'EOS');
-#include <math.h>
-
-int pixel_count(Imager::ImgRaw im) {
-  return im->xsize * im->ysize;
-}
-
-int count_color(Imager::ImgRaw im, Imager::Color c) {
-  int count = 0, x, y, chan;
-  i_color read_c;
-
-  for (x = 0; x < im->xsize; ++x) {
-    for (y = 0; y < im->ysize; ++y) {
-      int match = 1;
-      i_gpix(im, x, y, &read_c);
-      for (chan = 0; chan < im->channels; ++chan) {
-        if (read_c.channel[chan] != c->channel[chan]) {
-          match = 0;
-          break;
-        }
-      }
-      if (match)
-        ++count;
-    }
-  }
-
-  return count;
-}
-
-Imager make_10x10() {
-  i_img *im = i_img_8_new(10, 10, 3);
-  i_color c;
-  c.channel[0] = c.channel[1] = c.channel[2] = 255;
-  i_box_filled(im, 0, 0, im->xsize-1, im->ysize-1, &c);
-
-  return im;
-}
-
-/* tests that all of the APIs are visible - most of them anyway */
-Imager do_lots(Imager src) {
-  i_img *im = i_img_8_new(100, 100, 3);
-  i_img *fill_im = i_img_8_new(5, 5, 3);
-  i_img *testim;
-  i_color red, blue, green, black, temp_color;
-  i_fcolor redf, bluef;
-  i_fill_t *hatch, *fhatch_fill;
-  i_fill_t *im_fill;
-  i_fill_t *solid_fill, *fsolid_fill;
-  i_fill_t *fount_fill;
-  void *block;
-  double matrix[9] = /* 30 degree rotation */
-    {
-      0.866025,  -0.5,      0, 
-      0.5,       0.866025,  0, 
-      0,         0,         1,      
-    };
-  i_fountain_seg fseg;
-  i_img_tags tags;
-  int entry;
-  double temp_double;
-
-  red.channel[0] = 255; red.channel[1] = 0; red.channel[2] = 0;
-  red.channel[3] = 255;
-  blue.channel[0] = 0; blue.channel[1] = 0; blue.channel[2] = 255;
-  blue.channel[3] = 255;
-  green.channel[0] = 0; green.channel[1] = 255; green.channel[2] = 0;
-  green.channel[3] = 255;
-  black.channel[0] = black.channel[1] = black.channel[2] = 0;
-  black.channel[3] = 255;
-  hatch = i_new_fill_hatch(&red, &blue, 0, 1, NULL, 0, 0);
-
-  i_box(im, 0, 0, 9, 9, &red);
-  i_box_filled(im, 10, 0, 19, 9, &blue);
-  i_box_cfill(im, 20, 0, 29, 9, hatch);
-
-  /* make an image fill, and try it */
-  i_box_cfill(fill_im, 0, 0, 4, 4, hatch);
-  im_fill = i_new_fill_image(fill_im, matrix, 2, 2, 0);
-
-  i_box_cfill(im, 30, 0, 39, 9, im_fill);
-
-  /* make a solid fill and try it */
-  solid_fill = i_new_fill_solid(&red, 0);
-  i_box_cfill(im, 40, 0, 49, 9, solid_fill);
-
-  /* floating fills */
-  redf.channel[0] = 1.0; redf.channel[1] = 0; redf.channel[2] = 0;
-  redf.channel[3] = 1.0;
-  bluef.channel[0] = 0; bluef.channel[1] = 0; bluef.channel[2] = 1.0;
-  bluef.channel[3] = 1.0;
-
-  fsolid_fill = i_new_fill_solidf(&redf, 0);
-  i_box_cfill(im, 50, 0, 59, 9, fsolid_fill);
-  fhatch_fill = i_new_fill_hatchf(&redf, &bluef, 0, 2, NULL, 0, 0);
-  i_box_cfill(im, 60, 0, 69, 9, fhatch_fill);
-
-  /* fountain fill */
-  fseg.start = 0;
-  fseg.middle = 0.5;
-  fseg.end = 1.0;
-  fseg.c[0] = redf;
-  fseg.c[1] = bluef;
-  fseg.type = i_fst_linear;
-  fseg.color = i_fc_hue_down;
-  fount_fill = i_new_fill_fount(70, 0, 80, 0, i_ft_linear, i_fr_triangle, 0, i_fts_none, 1, 1, &fseg);
-
-  i_box_cfill(im, 70, 0, 79, 9, fount_fill);
-
-  i_line(im, 0, 10, 10, 15, &blue, 1);
-  i_line_aa(im, 0, 19, 10, 15, &red, 1);
-  
-  i_arc(im, 15, 15, 4, 45, 160, &blue);
-  i_arc_aa(im, 25, 15, 4, 75, 280, &red);
-  i_arc_cfill(im, 35, 15, 4, 0, 215, hatch);
-  i_arc_aa_cfill(im, 45, 15, 4, 30, 210, hatch);
-  i_circle_aa(im, 55, 15, 4, &red);
-  
-  i_box(im, 61, 11, 68, 18, &red);
-  i_flood_fill(im, 65, 15, &blue);
-  i_box(im, 71, 11, 78, 18, &red);
-  i_flood_cfill(im, 75, 15, hatch);
-
-  i_box_filled(im, 1, 21, 9, 24, &red);
-  i_box_filled(im, 1, 25, 9, 29, &blue);
-  i_flood_fill_border(im, 5, 25, &green, &black);
-
-  i_box_filled(im, 11, 21, 19, 24, &red);
-  i_box_filled(im, 11, 25, 19, 29, &blue);
-  i_flood_cfill_border(im, 15, 25, hatch, &black);
-
-  i_fill_destroy(fount_fill);
-  i_fill_destroy(fhatch_fill);
-  i_fill_destroy(solid_fill);
-  i_fill_destroy(fsolid_fill);
-  i_fill_destroy(hatch);
-  i_fill_destroy(im_fill);
-  i_img_destroy(fill_im);
-
-  /* make sure we can make each image type */
-  testim = i_img_16_new(100, 100, 3);
-  i_img_destroy(testim);
-  testim = i_img_double_new(100, 100, 3);
-  i_img_destroy(testim);
-  testim = i_img_pal_new(100, 100, 3, 256);
-  i_img_destroy(testim);
-  testim = i_sametype(im, 50, 50);
-  i_img_destroy(testim);
-  testim = i_sametype_chans(im, 50, 50, 4);
-  i_img_destroy(testim);
-
-  i_clear_error();
-  i_push_error(0, "Hello");
-  i_push_errorf(0, "%s", "World");
-
-  /* make sure tags create/destroy work */
-  i_tags_new(&tags);
-  i_tags_destroy(&tags);  
-
-  block = mymalloc(20);
-  block = myrealloc(block, 50);
-  myfree(block);
-
-  i_tags_set(&im->tags, "lots_string", "foo", -1);
-  i_tags_setn(&im->tags, "lots_number", 101);
-
-  if (!i_tags_find(&im->tags, "lots_number", 0, &entry)) {
-    i_push_error(0, "lots_number tag not found");
-    i_img_destroy(im);
-    return NULL;
-  }
-  i_tags_delete(&im->tags, entry);
-
-  /* these won't delete anything, but it makes sure the macros and function
-     pointers are correct */
-  i_tags_delbyname(&im->tags, "unknown");
-  i_tags_delbycode(&im->tags, 501);
-  i_tags_set_float(&im->tags, "lots_float", 0, 3.14);
-  if (!i_tags_get_float(&im->tags, "lots_float", 0, &temp_double)) {
-    i_push_error(0, "lots_float not found");
-    i_img_destroy(im);
-    return NULL;
-  }
-  if (fabs(temp_double - 3.14) > 0.001) {
-    i_push_errorf(0, "lots_float incorrect %g", temp_double);
-    i_img_destroy(im);
-    return NULL;
-  }
-  i_tags_set_float2(&im->tags, "lots_float2", 0, 100 * sqrt(2.0), 5);
-  if (!i_tags_get_int(&im->tags, "lots_float2", 0, &entry)) {
-    i_push_error(0, "lots_float2 not found as int");
-    i_img_destroy(im);
-    return NULL;
-  }
-  if (entry != 141) { 
-    i_push_errorf(0, "lots_float2 unexpected value %d", entry);
-    i_img_destroy(im);
-    return NULL;
-  }
-
-  i_tags_set_color(&im->tags, "lots_color", 0, &red);
-  if (!i_tags_get_color(&im->tags, "lots_color", 0, &temp_color)) {
-    i_push_error(0, "lots_color not found as color");
-    i_img_destroy(im);
-    return NULL;
-  }
-    
-  return im;
-}
-
-void
-io_fd(int fd) {
-  Imager::IO io = io_new_fd(fd);
-  i_io_write(io, "test", 4);
-  i_io_close(io);
-  io_glue_destroy(io);
-}
-
-int
-io_bufchain_test() {
-  Imager::IO io = io_new_bufchain();
-  unsigned char *result;
-  size_t size;
-  if (i_io_write(io, "test2", 5) != 5) {
-    fprintf(stderr, "write failed\n");
-    return 0;
-  }
-  if (!i_io_flush(io)) {
-    fprintf(stderr, "flush failed\n");
-    return 0;
-  }
-  if (i_io_close(io) != 0) {
-    fprintf(stderr, "close failed\n");
-    return 0;
-  }
-  size = io_slurp(io, &result);
-  if (size != 5) {
-    fprintf(stderr, "wrong size\n");
-    return 0;
-  }
-  if (memcmp(result, "test2", 5)) {
-    fprintf(stderr, "data mismatch\n");
-    return 0;
-  }
-  if (i_io_seek(io, 0, 0) != 0) {
-    fprintf(stderr, "seek failure\n");
-    return 0;
-  }
-  myfree(result);
-  io_glue_destroy(io);
-
-  return 1;
-}
-
-const char *
-io_buffer_test(SV *in) {
-  STRLEN len;
-  const char *in_str = SvPV(in, len);
-  static char buf[100];
-  Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
-  ssize_t read_size;
-
-  read_size = i_io_read(io, buf, sizeof(buf)-1);
-  io_glue_destroy(io);
-  if (read_size < 0 || read_size >= sizeof(buf)) {
-    return "";
-  }
-
-  buf[read_size] = '\0';
-
-  return buf;
-}
-
-const char *
-io_peekn_test(SV *in) {
-  STRLEN len;
-  const char *in_str = SvPV(in, len);
-  static char buf[100];
-  Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
-  ssize_t read_size;
-
-  read_size = i_io_peekn(io, buf, sizeof(buf)-1);
-  io_glue_destroy(io);
-  if (read_size < 0 || read_size >= sizeof(buf)) {
-    return "";
-  }
-
-  buf[read_size] = '\0';
-
-  return buf;
-}
-
-const char *
-io_gets_test(SV *in) {
-  STRLEN len;
-  const char *in_str = SvPV(in, len);
-  static char buf[100];
-  Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
-  ssize_t read_size;
-
-  read_size = i_io_gets(io, buf, sizeof(buf), 's');
-  io_glue_destroy(io);
-  if (read_size < 0 || read_size >= sizeof(buf)) {
-    return "";
-  }
-
-  return buf;
-}
-
-int
-io_getc_test(SV *in) {
-  STRLEN len;
-  const char *in_str = SvPV(in, len);
-  static char buf[100];
-  Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
-  int result;
-
-  result = i_io_getc(io);
-  io_glue_destroy(io);
-
-  return result;
-}
-
-int
-io_peekc_test(SV *in) {
-  STRLEN len;
-  const char *in_str = SvPV(in, len);
-  static char buf[100];
-  Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
-  int result;
-
-  i_io_set_buffered(io, 0);
-
-  result = i_io_peekc(io);
-  io_glue_destroy(io);
-
-  return result;
-}
-
-
-
-int
-test_render_color(Imager work_8) {
-  i_render *r8;
-  i_color c;
-  unsigned char render_coverage[3];
-
-  render_coverage[0] = 0;
-  render_coverage[1] = 128;
-  render_coverage[2] = 255;
-
-  r8 = i_render_new(work_8, 10);
-  c.channel[0] = 128;
-  c.channel[1] = 255;
-  c.channel[2] = 0;
-  c.channel[3] = 255;
-  i_render_color(r8, 0, 0, sizeof(render_coverage), render_coverage, &c);
-
-  c.channel[3] = 128;
-  i_render_color(r8, 0, 1, sizeof(render_coverage), render_coverage, &c);
-
-  c.channel[3] = 0;
-  i_render_color(r8, 0, 2, sizeof(render_coverage), render_coverage, &c);
-
-  i_render_delete(r8);
-
-  return 1;
-}
-
-int
-raw_psamp(Imager im, int chan_count) {
-  static i_sample_t samps[] = { 0, 127, 255 };
-
-  i_clear_error();
-  return i_psamp(im, 0, 1, 0, samps, NULL, chan_count);
-}
-
-int
-raw_psampf(Imager im, int chan_count) {
-  static i_fsample_t samps[] = { 0, 0.5, 1.0 };
-
-  i_clear_error();
-  return i_psampf(im, 0, 1, 0, samps, NULL, chan_count);
-}
-
-int
-test_mutex() {
-  i_mutex_t m;
-
-  m = i_mutex_new();
-  i_mutex_lock(m);
-  i_mutex_unlock(m);
-  i_mutex_destroy(m);
-
-  return 1;
-}
-
-int
-test_slots() {
-  im_slot_t slot = im_context_slot_new(NULL);
-
-  if (im_context_slot_get(aIMCTX, slot)) {
-    fprintf(stderr, "slots should default to NULL\n");
-    return 0;
-  }
-  if (!im_context_slot_set(aIMCTX, slot, &slot)) {
-    fprintf(stderr, "set slot failed\n");
-    return 0;
-  }
-
-  if (im_context_slot_get(aIMCTX, slot) != &slot) {
-    fprintf(stderr, "get slot didn't match\n");
-    return 0;
-  }
-
-  return 1;
-}
-
-EOS
-
-my $im = Imager->new(xsize=>50, ysize=>50);
-is(pixel_count($im), 2500, "pixel_count");
-
-my $black = Imager::Color->new(0,0,0);
-is(count_color($im, $black), 2500, "count_color black on black image");
-
-my $im2 = make_10x10();
-my $white = Imager::Color->new(255, 255, 255);
-is(count_color($im2, $white), 100, "check new image white count");
-ok($im2->box(filled=>1, xmin=>1, ymin=>1, xmax => 8, ymax=>8, color=>$black),
-   "try new image");
-is(count_color($im2, $black), 64, "check modified black count");
-is(count_color($im2, $white), 36, "check modified white count");
-
-my $im3 = do_lots($im2);
-ok($im3, "do_lots()")
-  or print "# ", Imager->_error_as_msg, "\n";
-ok($im3->write(file=>'testout/t82lots.ppm'), "write t82lots.ppm");
-
-{ # RT #24992
-  # the T_IMAGER_FULL_IMAGE typemap entry was returning a blessed
-  # hash with an extra ref, causing memory leaks
-
-  my $im = make_10x10();
-  my $im2 = Imager->new(xsize => 10, ysize => 10);
-  require B;
-  my $imb = B::svref_2object($im);
-  my $im2b = B::svref_2object($im2);
-  is ($imb->REFCNT, $im2b->REFCNT, 
-      "check refcnt of imager object hash between normal and typemap generated");
-}
-
-SKIP:
-{
-  use IO::File;
-  my $fd_filename = "testout/t82fd.txt";
-  {
-    my $fh = IO::File->new($fd_filename, "w")
-      or skip("Can't create file: $!", 1);
-    io_fd(fileno($fh));
-    $fh->close;
-  }
-  {
-    my $fh = IO::File->new($fd_filename, "r")
-      or skip("Can't open file: $!", 1);
-    my $data = <$fh>;
-    is($data, "test", "make sure data written to fd");
-  }
-  unlink $fd_filename;
-}
-
-ok(io_bufchain_test(), "check bufchain functions");
-
-is(io_buffer_test("test3"), "test3", "check io_new_buffer() and i_io_read");
-
-is(io_peekn_test("test5"), "test5", "check i_io_peekn");
-
-is(io_gets_test("test"), "tes", "check i_io_gets()");
-
-is(io_getc_test("ABC"), ord "A", "check i_io_getc(_imp)?");
-
-is(io_getc_test("XYZ"), ord "X", "check i_io_peekc(_imp)?");
-
-for my $bits (8, 16) {
-  print "# bits: $bits\n";
-
-  # the floating point processing is a little more accurate
-  my $bump = $bits == 16 ? 1 : 0;
-  {
-    my $im = Imager->new(xsize => 10, ysize => 10, channels => 3, bits => $bits);
-    ok($im->box(filled => 1, color => '#808080'), "fill work image with gray");
-    ok(test_render_color($im),
-       "call render_color on 3 channel image");
-    is_color3($im->getpixel(x => 0, y => 0), 128, 128, 128,
-             "check zero coverage, alpha 255 color, bits $bits");
-    is_color3($im->getpixel(x => 1, y => 0), 128, 191+$bump, 63+$bump,
-             "check 128 coverage, alpha 255 color, bits $bits");
-    is_color3($im->getpixel(x => 2, y => 0), 128, 255, 0,
-             "check 255 coverage, alpha 255 color, bits $bits");
-
-    is_color3($im->getpixel(x => 0, y => 1), 128, 128, 128,
-             "check zero coverage, alpha 128 color, bits $bits");
-    is_color3($im->getpixel(x => 1, y => 1), 128, 159+$bump, 95+$bump,
-             "check 128 coverage, alpha 128 color, bits $bits");
-    is_color3($im->getpixel(x => 2, y => 1), 128, 191+$bump, 63+$bump,
-             "check 255 coverage, alpha 128 color, bits $bits");
-
-    is_color3($im->getpixel(x => 0, y => 2), 128, 128, 128,
-             "check zero coverage, alpha 0 color, bits $bits");
-    is_color3($im->getpixel(x => 1, y => 2), 128, 128, 128,
-             "check 128 coverage, alpha 0 color, bits $bits");
-    is_color3($im->getpixel(x => 2, y => 2), 128, 128, 128,
-             "check 255 coverage, alpha 0 color, bits $bits");
-  }
-  {
-    my $im = Imager->new(xsize => 10, ysize => 10, channels => 4, bits => $bits);
-    ok($im->box(filled => 1, color => '#808080'), "fill work image with opaque gray");
-    ok(test_render_color($im),
-       "call render_color on 4 channel image");
-    is_color4($im->getpixel(x => 0, y => 0), 128, 128, 128, 255,
-             "check zero coverage, alpha 255 color, bits $bits");
-    is_color4($im->getpixel(x => 1, y => 0), 128, 191+$bump, 63+$bump, 255,
-             "check 128 coverage, alpha 255 color, bits $bits");
-    is_color4($im->getpixel(x => 2, y => 0), 128, 255, 0, 255,
-             "check 255 coverage, alpha 255 color, bits $bits");
-
-    is_color4($im->getpixel(x => 0, y => 1), 128, 128, 128, 255,
-             "check zero coverage, alpha 128 color, bits $bits");
-    is_color4($im->getpixel(x => 1, y => 1), 128, 159+$bump, 95+$bump, 255,
-             "check 128 coverage, alpha 128 color, bits $bits");
-    is_color4($im->getpixel(x => 2, y => 1), 128, 191+$bump, 63+$bump, 255,
-             "check 255 coverage, alpha 128 color, bits $bits");
-
-    is_color4($im->getpixel(x => 0, y => 2), 128, 128, 128, 255,
-             "check zero coverage, alpha 0 color, bits $bits");
-    is_color4($im->getpixel(x => 1, y => 2), 128, 128, 128, 255,
-             "check 128 coverage, alpha 0 color, bits $bits");
-    is_color4($im->getpixel(x => 2, y => 2), 128, 128, 128, 255,
-             "check 255 coverage, alpha 0 color, bits $bits");
-  }
-
-  {
-    my $im = Imager->new(xsize => 10, ysize => 10, channels => 4, bits => $bits);
-    ok($im->box(filled => 1, color => Imager::Color->new(128, 128, 128, 64)), "fill work image with translucent gray");
-    ok(test_render_color($im),
-       "call render_color on 4 channel image");
-    is_color4($im->getpixel(x => 0, y => 0), 128, 128, 128, 64,
-             "check zero coverage, alpha 255 color, bits $bits");
-    is_color4($im->getpixel(x => 1, y => 0), 128, 230, 25+$bump, 159+$bump,
-             "check 128 coverage, alpha 255 color, bits $bits");
-    is_color4($im->getpixel(x => 2, y => 0), 128, 255, 0, 255,
-             "check 255 coverage, alpha 255 color, bits $bits");
-
-    is_color4($im->getpixel(x => 0, y => 1), 128, 128, 128, 64,
-             "check zero coverage, alpha 128 color, bits $bits");
-    is_color4($im->getpixel(x => 1, y => 1), 129-$bump, 202-$bump, 55, 111+$bump,
-             "check 128 coverage, alpha 128 color, bits $bits");
-    is_color4($im->getpixel(x => 2, y => 1), 128, 230, 25+$bump, 159+$bump,
-             "check 255 coverage, alpha 128 color, bits $bits");
-
-    is_color4($im->getpixel(x => 0, y => 2), 128, 128, 128, 64,
-             "check zero coverage, alpha 0 color, bits $bits");
-    is_color4($im->getpixel(x => 1, y => 2), 128, 128, 128, 64,
-             "check 128 coverage, alpha 0 color, bits $bits");
-    is_color4($im->getpixel(x => 2, y => 2), 128, 128, 128, 64,
-             "check 255 coverage, alpha 0 color, bits $bits");
-  }
-}
-
-{
-  my $im = Imager->new(xsize => 10, ysize => 10);
-  is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail");
-  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psamp($im, 0), -1, "bad channel list (0) for psamp should fail");
-  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail");
-  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail");
-  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
-     "check message");
-}
-
-{
-  my $im = Imager->new(xsize => 10, ysize => 10, bits => 16);
-  is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail (16-bit)");
-  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psamp($im, 0), -1, "bad channel list (0) for psamp should fail (16-bit)");
-  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail (16-bit)");
-  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail (16-bit)");
-  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
-     "check message");
-}
-
-{
-  my $im = Imager->new(xsize => 10, ysize => 10, bits => 'double');
-  is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail (double)");
-  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psamp($im, 0), -1,, "bad channel list (0) for psamp should fail (double)");
-  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail (double)");
-  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail (double)");
-  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
-     "check message");
-}
-
-{
-  my $im = Imager->new(xsize => 10, ysize => 10, type => "paletted");
-  is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail (paletted)");
-  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psamp($im, 0), -1, "bad channel list (0) for psamp should fail (paletted)");
-  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail (paletted)");
-  is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
-     "check message");
-  is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail (paletted)");
-  is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
-     "check message");
-  is($im->type, "paletted", "make sure we kept the image type");
-}
-
-ok(test_mutex(), "call mutex APIs");
-
-ok(test_slots(), "call slot APIs");
-
-sub _get_error {
-  my @errors = Imager::i_errors();
-  return join(": ", map $_->[0], @errors);
-}
diff --git a/t/t83extutil.t b/t/t83extutil.t
deleted file mode 100644 (file)
index 15080f3..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 6;
-use File::Spec;
-
-{ # RT 37353
-  local @INC = @INC;
-
-  unshift @INC, File::Spec->catdir('blib', 'lib');
-  unshift @INC, File::Spec->catdir('blib', 'arch');
-  require Imager::ExtUtils;
-  my $path = Imager::ExtUtils->base_dir;
-  ok(File::Spec->file_name_is_absolute($path), "check dirs absolute")
-    or print "# $path\n";
-}
-
-{ # includes
-  my $includes = Imager::ExtUtils->includes;
-  ok($includes =~ s/^-I//, "has the -I");
-  ok(-e File::Spec->catfile($includes, "imext.h"), "found a header");
-}
-
-{ # typemap
-  my $typemap = Imager::ExtUtils->typemap;
-  ok($typemap, "got a typemap path");
-  ok(-f $typemap, "it exists");
-  open TYPEMAP, "< $typemap";
-  my $tm_content = do { local $/; <TYPEMAP>; };
-  close TYPEMAP;
-  cmp_ok($tm_content, '=~', "Imager::Color\\s+T_PTROBJ",
-        "it seems to be the right file");
-}
diff --git a/t/t84inlinectx.t b/t/t84inlinectx.t
deleted file mode 100644 (file)
index 90665f4..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#!perl -w
-#
-# this tests both the Inline interface and the API with IMAGER_NO_CONTEXT
-use strict;
-use Test::More;
-use Imager::Test qw(is_color3 is_color4);
-eval "require Inline::C;";
-plan skip_all => "Inline required for testing API" if $@;
-
-eval "require Parse::RecDescent;";
-plan skip_all => "Could not load Parse::RecDescent" if $@;
-
-use Cwd 'getcwd';
-plan skip_all => "Inline won't work in directories with spaces"
-  if getcwd() =~ / /;
-
-plan skip_all => "perl 5.005_04, 5.005_05 too buggy"
-  if $] =~ /^5\.005_0[45]$/;
-
--d "testout" or mkdir "testout";
-
-plan tests => 5;
-require Inline;
-Inline->import(C => Config => AUTO_INCLUDE => "#define IMAGER_NO_CONTEXT\n");
-Inline->import(with => 'Imager');
-Inline->import("FORCE"); # force rebuild
-#Inline->import(C => Config => OPTIMIZE => "-g");
-
-Inline->bind(C => <<'EOS');
-#include <math.h>
-
-Imager make_10x10() {
-  dIMCTX;
-  i_img *im = i_img_8_new(10, 10, 3);
-  i_color c;
-  c.channel[0] = c.channel[1] = c.channel[2] = 255;
-  i_box_filled(im, 0, 0, im->xsize-1, im->ysize-1, &c);
-
-  return im;
-}
-
-void error_dIMCTX() {
-  dIMCTX;
-  im_clear_error(aIMCTX);
-  im_push_error(aIMCTX, 0, "test1");
-  im_push_errorf(aIMCTX, 0, "test%d", 2);
-
-  im_log((aIMCTX, 0, "test logging\n"));
-}
-
-void error_dIMCTXim(Imager im) {
-  dIMCTXim(im);
-  im_clear_error(aIMCTX);
-  im_push_error(aIMCTX, 0, "test1");
-}
-
-int context_refs() {
-  dIMCTX;
-
-  im_context_refinc(aIMCTX, "context_refs");
-  im_context_refdec(aIMCTX, "context_refs");
-
-  return 1;
-}
-
-EOS
-
-Imager->open_log(log => "testout/t84inlinectx.log");
-
-my $im2 = make_10x10();
-ok($im2, "make an image");
-is_color3($im2->getpixel(x => 0, y => 0), 255, 255, 255,
-         "check the colors");
-error_dIMCTX();
-is(_get_error(), "test2: test1", "check dIMCTX");
-
-my $im = Imager->new(xsize => 1, ysize => 1);
-error_dIMCTXim($im);
-is(_get_error(), "test1", "check dIMCTXim");
-
-ok(context_refs(), "check refcount functions");
-
-Imager->close_log();
-
-unless ($ENV{IMAGER_KEEP_FILES}) {
-  unlink "testout/t84inlinectx.log";
-}
-
-sub _get_error {
-  my @errors = Imager::i_errors();
-  return join(": ", map $_->[0], @errors);
-}
diff --git a/t/t90cc.t b/t/t90cc.t
deleted file mode 100644 (file)
index c38453e..0000000
--- a/t/t90cc.t
+++ /dev/null
@@ -1,84 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 22;
-
-use Imager;
-
--d "testout" or mkdir "testout";
-
-Imager::init('log'=>'testout/t90cc.log');
-
-{
-  my $img=Imager->new();
-  ok($img->open(file=>'testimg/scale.ppm'), 'load test image')
-    or print "failed: ",$img->{ERRSTR},"\n";
-  
-  ok(defined($img->getcolorcount(maxcolors=>10000)), 'check color count is small enough');
-  print "# color count: ".$img->getcolorcount()."\n";
-  is($img->getcolorcount(), 86, 'expected number of colors');
-  is($img->getcolorcount(maxcolors => 50), undef, 'check overflow handling');
-}
-
-{
-  my $black = Imager::Color->new(0, 0, 0);
-  my $blue  = Imager::Color->new(0, 0, 255);
-  my $red   = Imager::Color->new(255, 0, 0);
-  
-  my $im    = Imager->new(xsize=>50, ysize=>50);
-  
-  my $count = $im->getcolorcount();
-  is ($count, 1, "getcolorcount is 1");
-  my @colour_usage = $im->getcolorusage();
-  is_deeply (\@colour_usage, [2500], "2500 are in black");
-  
-  $im->box(filled=>1, color=>$blue, xmin=>25);
-  
-  $count = $im->getcolorcount();
-  is ($count, 2, "getcolorcount is 2");
-  @colour_usage = $im->getcolorusage();
-  is_deeply(\@colour_usage, [1250, 1250] , "1250, 1250: Black and blue");
-  
-  $im->box(filled=>1, color=>$red, ymin=>25);
-  
-  $count = $im->getcolorcount();
-  is ($count, 3, "getcolorcount is 3");
-  @colour_usage = $im->getcolorusage();
-  is_deeply(\@colour_usage, [625, 625, 1250] , 
-           "625, 625, 1250: Black blue and red");
-  @colour_usage = $im->getcolorusage(maxcolors => 2);
-  is(@colour_usage, 0, 'test overflow check');
-  
-  my $colour_usage = $im->getcolorusagehash();
-  my $red_pack = pack("CCC", 255, 0, 0);
-  my $blue_pack = pack("CCC", 0, 0, 255);
-  my $black_pack = pack("CCC", 0, 0, 0);
-  is_deeply( $colour_usage, 
-            { $black_pack => 625, $blue_pack => 625, $red_pack => 1250 },
-            "625, 625, 1250: Black blue and red (hash)");
-  is($im->getcolorusagehash(maxcolors => 2), undef,
-     'test overflow check');
-
-  # test with a greyscale image
-  my $im_g = $im->convert(preset => 'grey');
-  # since the grey preset scales each source channel differently
-  # each of the original colors will be converted to different colors
-  is($im_g->getcolorcount, 3, '3 colors (grey)');
-  is_deeply([ $im_g->getcolorusage ], [ 625, 625, 1250 ], 
-           'color counts (grey)');
-  is_deeply({ "\x00" => 625, "\x12" => 625, "\x38" => 1250 },
-           $im_g->getcolorusagehash,
-           'color usage hash (grey)');
-}
-
-{
-  my $empty = Imager->new;
-  is($empty->getcolorcount, undef, "can't getcolorcount an empty image");
-  is($empty->errstr, "getcolorcount: empty input image",
-     "check error message");
-  is($empty->getcolorusagehash, undef, "can't getcolorusagehash an empty image");
-  is($empty->errstr, "getcolorusagehash: empty input image",
-     "check error message");
-  is($empty->getcolorusage, undef, "can't getcolorusage an empty image");
-  is($empty->errstr, "getcolorusage: empty input image",
-     "check error message");
-}
diff --git a/t/t91pod.t b/t/t91pod.t
deleted file mode 100644 (file)
index 45d6ef5..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-#!perl -w
-use strict;
-use Test::More;
-use ExtUtils::Manifest qw(maniread);
-eval "use Test::Pod 1.00;";
-plan skip_all => "Test::Pod 1.00 required for testing POD" if $@;
-my $manifest = maniread();
-my @pod = grep /\.(pm|pl|pod|PL)$/, keys %$manifest;
-plan tests => scalar(@pod);
-for my $file (@pod) {
-  pod_file_ok($file, "pod ok in $file");
-}
diff --git a/t/t92samples.t b/t/t92samples.t
deleted file mode 100644 (file)
index 7ca1598..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#!perl -w
-# packaging test - make sure we included the samples in the MANIFEST <sigh>
-use Test::More;
-use ExtUtils::Manifest qw(maniread);
-
-# first build a list of samples from samples/README
-open SAMPLES, "< samples/README"
-  or die "Cannot open samples/README: $!";
-my @sample_files;
-while (<SAMPLES>) {
-  chomp;
-  /^\w[\w.-]+\.\w+$/ and push @sample_files, $_;
-}
-
-close SAMPLES;
-
-plan tests => scalar(@sample_files);
-
-my $manifest = maniread();
-
-for my $filename (@sample_files) {
-  ok(exists($manifest->{"samples/$filename"}), 
-     "sample file $filename in manifest");
-}
diff --git a/t/t93podcover.t b/t/t93podcover.t
deleted file mode 100644 (file)
index 05efe11..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-#!perl -w
-use strict;
-use lib 't';
-use Test::More;
-use ExtUtils::Manifest qw(maniread);
-#sub Pod::Coverage::TRACE_ALL() { 1 }
-eval "use Test::Pod::Coverage 1.08;";
-# 1.08 required for coverage_class support
-plan skip_all => "Test::Pod::Coverage 1.08 required for POD coverage" if $@;
-
-# scan for a list of files to get Imager method documentation from
-my $manifest = maniread();
-my @pods = ( 'Imager.pm', grep /\.pod$/, keys %$manifest );
-
-my @private = 
-  ( 
-   '^io?_',
-   '^DSO_',
-   '^Inline$',
-   '^yatf$',
-   '^malloc_state$',
-   '^init_log$',
-   '^polybezier$', # not ready for public consumption
-   '^border$', # I don't know what it is, expect it to go away
-  );
-my @trustme = ( '^open$',  );
-
-plan tests => 20;
-
-{
-  pod_coverage_ok('Imager', { also_private => \@private,
-                             pod_from => \@pods,
-                             trustme => \@trustme,
-                             coverage_class => 'Pod::Coverage::Imager' });
-  pod_coverage_ok('Imager::Font');
-  my @color_private = ( '^i_', '_internal$' );
-  pod_coverage_ok('Imager::Color', 
-                 { also_private => \@color_private });
-  pod_coverage_ok('Imager::Color::Float', 
-                 { also_private => \@color_private });
-  pod_coverage_ok('Imager::Color::Table');
-  pod_coverage_ok('Imager::ExtUtils');
-  pod_coverage_ok('Imager::Expr');
-  my $trust_parents = { coverage_class => 'Pod::Coverage::CountParents' };
-  pod_coverage_ok('Imager::Expr::Assem', $trust_parents);
-  pod_coverage_ok('Imager::Fill');
-  pod_coverage_ok('Imager::Font::BBox');
-  pod_coverage_ok('Imager::Font::Wrap');
-  pod_coverage_ok('Imager::Fountain');
-  pod_coverage_ok('Imager::Matrix2d');
-  pod_coverage_ok('Imager::Regops');
-  pod_coverage_ok('Imager::Transform');
-  pod_coverage_ok('Imager::Test');
-  pod_coverage_ok('Imager::IO',
-                 {
-                  pod_from => "lib/Imager/IO.pod",
-                  coverage_class => "Pod::Coverage::Imager",
-                  module => "Imager",
-                 });
-}
-
-{
-  # check all documented methods/functions are in the method index
-  my $coverage = 
-    Pod::Coverage::Imager->new(package => 'Imager',
-                              pod_from => \@pods,
-                              trustme => \@trustme,
-                              also_private => \@private);
-  my %methods = map { $_ => 1 } $coverage->covered;
-  open IMAGER, "< Imager.pm"
-    or die "Cannot open Imager.pm: $!";
-  while (<IMAGER>) {
-    last if /^=head1 METHOD INDEX/;
-  }
-  my @indexed;
-  my @unknown_indexed;
-  while (<IMAGER>) {
-    last if /^=\w/ && !/^=for\b/;
-
-    if (/^(\w+)\(/) {
-      push @indexed, $1;
-      unless (delete $methods{$1}) {
-       push @unknown_indexed, $1;
-      }
-    }
-  }
-
-  unless (is(keys %methods, 0, "all methods in method index")) {
-    diag "the following methods are documented but not in the index:";
-    diag $_ for sort keys %methods;
-  }
-  unless (is(@unknown_indexed, 0, "only methods in method index")) {
-    diag "the following names are in the method index but not documented";
-    diag $_ for sort @unknown_indexed;
-  }
-
-  sub dict_cmp_func;
-  is_deeply(\@indexed, [ sort dict_cmp_func @indexed ],
-           "check method index is alphabetically sorted");
-}
-
-sub dict_cmp_func {
-  (my $tmp_a = lc $a) =~ tr/_//d;
-  (my $tmp_b = lc $b) =~ tr/_//d;
-
-  $tmp_a cmp $tmp_b;
-}
diff --git a/t/t94kwalitee.t b/t/t94kwalitee.t
deleted file mode 100644 (file)
index b3e73dc..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#!perl -w
-# this is intended for various kwalitee tests
-use strict;
-use Test::More;
-use ExtUtils::Manifest qw(maniread);
-
-my $manifest = maniread;
-
-# work up counts first
-
-my @pl_files = grep /\.(p[lm]|PL|perl)$/, keys %$manifest;
-
-plan tests => scalar(@pl_files);
-
-for my $filename (@pl_files) {
-  open PL, "< $filename"
-    or die "Cannot open $filename: $!";
-  my $found_strict;
-  while (<PL>) {
-    if (/^use strict;/) {
-      ++$found_strict;
-      last;
-    }
-  }
-  close PL;
-  ok($found_strict, "file $filename has use strict");
-}
diff --git a/t/t95log.t b/t/t95log.t
deleted file mode 100644 (file)
index ce3323a..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#!perl -w
-use strict;
-use Imager;
-use Test::More tests => 6;
-
-my $log_name = "testout/t95log.log";
-
-my $log_message = "test message 12345";
-
-SKIP: {
-  skip("Logging not build", 3)
-    unless Imager::i_log_enabled();
-  ok(Imager->open_log(log => $log_name), "open log")
-    or diag("Open log: " . Imager->errstr);
-  ok(-f $log_name, "file is there");
-  Imager->log($log_message);
-  Imager->close_log();
-
-  my $data = '';
-  if (open LOG, "< $log_name") {
-    $data = do { local $/; <LOG> };
-    close LOG;
-  }
-  like($data, qr/\Q$log_message/, "check message made it to the log");
-}
-
-SKIP: {
-  skip("Logging built", 3)
-    if Imager::i_log_enabled();
-
-  ok(!Imager->open_log(log => $log_name), "should be no logfile");
-  is(Imager->errstr, "Logging disabled", "check error message");
-  ok(!-f $log_name, "file shouldn't be there");
-}
diff --git a/t/t98meta.t b/t/t98meta.t
deleted file mode 100644 (file)
index 079e84a..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!perl -w
-use strict;
-use Test::More;
-plan skip_all => "Only run as part of the dist"
-  unless -f "META.yml";
-eval "use CPAN::Meta 2.110580;";
-plan skip_all => "CPAN::Meta required for testing META.yml"
-  if $@;
-plan skip_all => "Only if automated or author testing"
-  unless $ENV{AUTOMATED_TESTING} || -d "../.git";
-plan tests => 1;
-
-my $meta;
-unless (ok(eval {
-  $meta = CPAN::Meta->load_file("META.yml",
-                               { lazy_validation => 0 }) },
-          "loaded META.yml successfully")) {
-  diag($@);
-}
diff --git a/t/t99thread.t b/t/t99thread.t
deleted file mode 100644 (file)
index f03cd24..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-#!perl
-use strict;
-use Imager;
-use Imager::Color::Float;
-use Imager::Fill;
-use Config;
-my $loaded_threads;
-BEGIN {
-  if ($Config{useithreads} && $] > 5.008007) {
-    $loaded_threads =
-      eval {
-       require threads;
-       threads->import;
-       1;
-      };
-  }
-}
-use Test::More;
-
-$Config{useithreads}
-  or plan skip_all => "can't test Imager's threads support with no threads";
-$] > 5.008007
-  or plan skip_all => "require a perl with CLONE_SKIP to test Imager's threads support";
-$loaded_threads
-  or plan skip_all => "couldn't load threads";
-
-$INC{"Devel/Cover.pm"}
-  and plan skip_all => "threads and Devel::Cover don't get along";
-
-# https://rt.cpan.org/Ticket/Display.html?id=65812
-# https://github.com/schwern/test-more/issues/labels/Test-Builder2#issue/100
-$Test::More::VERSION =~ /^2\.00_/
-  and plan skip_all => "threads are hosed in 2.00_06 and presumably all 2.00_*";
-
-plan tests => 13;
-
-my $thread = threads->create(sub { 1; });
-ok($thread->join, "join first thread");
-
-# these are all, or contain, XS allocated objects, if we don't handle
-# CLONE requests, or provide a CLONE_SKIP, we'll probably see a
-# double-free, one from the thread, and the other from the main line
-# of control.
-#
-# So make one of each
-
-my $im = Imager->new(xsize => 10, ysize => 10);
-my $c = Imager::Color->new(0, 0, 0); # make some sort of color
-ok($c, "made the color");
-my $cf = Imager::Color::Float->new(0, 0, 0);
-ok($cf, "made the float color");
-my $hl;
-SKIP:
-{
-  Imager::Internal::Hlines::testing()
-      or skip "no hlines visible to test", 1;
-  $hl = Imager::Internal::Hlines::new(0, 100, 0, 100);
-  ok($hl, "made the hlines");
-}
-my $io = Imager::io_new_bufchain();
-ok($io, "made the io");
-my $tt;
-SKIP:
-{
-  $Imager::formats{tt}
-    or skip("No TT font support", 1);
-  $tt = Imager::Font->new(type => "tt", file => "fontfiles/dodge.ttf");
-  ok($tt, "made the font");
-}
-my $ft2;
-SKIP:
-{
-  $Imager::formats{ft2}
-    or skip "No FT2 support", 1;
-  $ft2 = Imager::Font->new(type => "ft2", file => "fontfiles/dodge.ttf");
-  ok($ft2, "made ft2 font");
-}
-my $fill = Imager::Fill->new(solid => $c);
-ok($fill, "made the fill");
-
-my $t2 = threads->create
-  (
-   sub {
-     ok(!UNIVERSAL::isa($im->{IMG}, "Imager::ImgRaw"),
-       "the low level image object should become unblessed");
-     ok(!$im->_valid_image, "image no longer considered valid");
-     is($im->errstr, "images do not cross threads",
-       "check error message");
-     1;
-   }
-  );
-ok($t2->join, "join second thread");
-#print STDERR $im->{IMG}, "\n";
-ok(UNIVERSAL::isa($im->{IMG}, "Imager::ImgRaw"),
-   "but the object should be fine in the main thread");
-
diff --git a/t/tr18561.t b/t/tr18561.t
deleted file mode 100644 (file)
index fb7264f..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#!perl -w
-# regression test for RT issue 18561
-# 
-use strict;
-use Test::More tests => 1;
-eval {
-  use Imager;
-  
-  my $i = Imager->new(
-          xsize => 50,
-         ysize => 50,
-  );
-  
-  $i->setpixel(
-       x => 10,
-       y => 10,
-       color => [0, 0, 0],
-  );
-};
-ok(!$@, "shouldn't crash")
-  or print "# $@\n";
diff --git a/t/tr18561b.t b/t/tr18561b.t
deleted file mode 100644 (file)
index 6b93389..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!perl -w
-# variant on the code that produces 18561
-# the old _color() code could return floating colors in some cases
-# but in most cases the caller couldn't handle it
-use strict;
-use Test::More tests => 1;
-eval {
-  use Imager;
-  use Imager::Color::Float; # prevent the actual 18561 crash
-  my $i = Imager->new(
-         xsize => 50,
-         ysize => 50,
-  );
-  $i->line(x1 => 0, y1 => 0, x2 => 99, y2=>99, color => [ 0, 0, 0 ]);
-};
-ok(!$@, "shouldn't crash")
-  or print "# $@\n";
diff --git a/t/x107bmp.t b/t/x107bmp.t
deleted file mode 100644 (file)
index 07232d8..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-#!perl -w
-# Extra BMP tests not shipped
-use strict;
-use Test::More;
-use Imager::Test qw(is_image);
-use Imager;
-
-# test images from 
-my @tests =
-  (
-   [ "g01bg.bmp", "1-bit blue/green", 0 ],
-   [ "g01bw.bmp", "1-bit black and white", 0 ],
-   [ "g01p1.bmp", "1-bit single colour", 0 ],
-   [ "g01wb.bmp", "1-bit white and black", 0 ],
-   [ "g04.bmp", "4-bit", 0 ],
-   [ "g04p4.bmp", "4-bit gray", 0 ],
-   [ "g04rle.bmp", "4-bit rle", "currently broken" ],
-   [ "g08.bmp", "8-bit", 0 ],
-   [ "g08offs.bmp", "8-bit with image data offset", 0 ],
-   [ "g08os2.bmp", "8-bit OS/2", "OS/2 BMP not implemented" ],
-   [ "g08p256.bmp", "8-bit, no important", 0 ],
-   [ "g08p64.bmp", "8-bit, 64 greyscale entries", 0 ],
-   [ "g08pi256.bmp", "8-bit 256 important", 0 ],
-   [ "g08pi64.bmp", "8-bit 64 important", 0 ],
-   [ "g08res11.bmp", "8-bit, 100x100 dpi", 0 ],
-   [ "g08res21.bmp", "8-bit, 200x100 dpi", 0 ],
-   [ "g08res22.bmp", "8-bit, 200x200 dpi", 0 ],
-   [ "g08rle.bmp", "8-bit rle", 0 ],
-   [ "g08s0.bmp", "8-bit, bits size not given", 0 ],
-   [ "g08w124.bmp", "8-bit 124x61", 0 ],
-   [ "g08w125.bmp", "8-bit 125x62", 0 ],
-   [ "g08w126.bmp", "8-bit 126x63", 0 ],
-   [ "g16bf555.bmp", "16-bit bitfield 555", 0 ],
-   [ "g16bf565.bmp", "16-bit bitfield 565", 0 ],
-   [ "g16def555.bmp", "16-bit default 555", 0 ],
-   [ "g24.bmp", "24-bit", 0 ],
-   [ "g32bf.bmp", "32-bit bitfields", 0 ],
-   [ "g32def.bmp", "32-bit defaults", 0 ],
-   [ "test32bfv4.bmp", "32-bit bitfields, v4", "v4 BMP not implemented" ],
-   [ "test32v5.bmp", "32-bit, v5", "v5 BMP not implemented" ],
-   [ "test4os2v2.bmp", "4-bit OS/2", "OS/2 BMP not implemented" ],
-   [ "trans.bmp", "transparency", "alpha BMPs not implemented" ],
-   [ "width.bmp", "odd-width rle", "currently broken" ],
-  );
-
-Imager->open_log(log => "testout/x107bmp.log");
-
-plan tests => 3 * @tests;
-
-for my $test (@tests) {
-  my ($in, $note, $todo) = @$test;
-
-  my $im = Imager->new(file => "xtestimg/bmp/$in");
-  local $TODO = $todo;
-  ok($im, "load $in ($note)")
-    or diag "$in: ".Imager->errstr;
-  (my $alt = $in) =~ s/\.bmp$/.sgi/;
-
-  my $ref = Imager->new(file => "xtestimg/bmp/$alt");
-  {
-    local $TODO; # should always pass
-    ok($ref, "load reference image for $in")
-      or diag "$alt: ".Imager->errstr;
-    if ($ref->getchannels == 1) {
-      $ref = $ref->convert(preset => "rgb");
-    }
-  }
-  is_image($im, $ref, "compare $note");
-}
-
-Imager->close_log();
-
diff --git a/t/x11rubthru.t b/t/x11rubthru.t
deleted file mode 100644 (file)
index f71ad66..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#!perl -w
-use strict;
-use Imager;
-use Imager::Test qw(is_image);
-use Test::More;
-
-$Imager::formats{"tiff"}
-  or plan skip_all => "no tiff support";
-
--d "testout" or mkdir "testout";
-
-plan tests => 2;
-
-my $dest = Imager->new(xsize => 100, ysize => 100, channels => 4);
-$dest->box(filled => 1, color => '0000FF');
-my $src = Imager->new(xsize => 100, ysize => 100, channels => 4);
-$src->circle(color => 'FF0000', x => 50, y => 60, r => 40, aa => 1);
-ok($dest->rubthrough(src => $src, src_minx => 10, src_miny => 20, src_maxx => 90,
-              tx => 10, ty => 10), "rubthrough");
-ok($dest->write(file => "testout/x11rubthru.tif"), "save it");
-
diff --git a/t/x20spell.t b/t/x20spell.t
deleted file mode 100644 (file)
index d7368b9..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-#!perl -w
-use strict;
-use Test::More;
-use ExtUtils::Manifest qw(maniread);
-use File::Temp;
-eval "use Pod::Spell 1.01";
-plan skip_all => "Pod::Spell 1.01 required for spellchecking POD" if $@;
-my $manifest = maniread();
-my @pod = sort grep !/^inc/ && /\.(pm|pl|pod|PL)$/, keys %$manifest;
-plan tests => scalar(@pod);
-my @stopwords = qw/
-API
-Arnar
-BMP
-Blit
-CGI
-chromaticities
-CMYK
-CPAN
-FreeType
-GIF
-HSV
-Hrafnkelsson
-ICO
-IMAGER
-Imager
-Imager's
-JPEG
-POSIX
-PNG
-PNM
-RGB
-RGBA
-SGI
-sRGB
-TGA
-TIFF
-UTF-8
-Uncategorized
-bilevel
-const
-dpi
-eg
-gaussian
-ie
-infix
-invocant
-metadata
-multi-threaded
-mutex
-paletted
-postfix
-preload
-preloading
-preloads
-renderer
-tuple
-unary
-unseekable
-varargs
-/;
-
-local %Pod::Wordlist::Wordlist = %Pod::Wordlist::Wordlist;
-for my $stop (@stopwords) {
-  $Pod::Wordlist::Wordlist{$stop} = 1;
-}
-
-# see for example:
-#  https://bugs.launchpad.net/ubuntu/+source/aspell/+bug/71322
-$ENV{LANG} = "C";
-$ENV{LC_ALL} = "C";
-for my $file (@pod) {
-  my $check_fh = File::Temp->new;
-  my $check_filename = $check_fh->filename;
-  open POD, "< $file"
-    or die "Cannot open $file for spell check: $!\n";
-  Pod::Spell->new->parse_from_filehandle(\*POD, $check_fh);
-  close $check_fh;
-  
-  my @out = `aspell list <$check_filename`;
-  unless (ok(@out == 0, "spell check $file")) {
-    chomp @out;
-    diag $_ for @out;
-    print "#----\n";
-    open my $fh, "<", $check_filename;
-    while (<$fh>) {
-      chomp;
-      print "# $_\n";
-    }
-    print "#----\n";
-  }
-}
diff --git a/t/x30podlinkcheck.t b/t/x30podlinkcheck.t
deleted file mode 100644 (file)
index 776e14e..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-#!perl -w
-use strict;
-use Test::More;
-BEGIN {
-  eval 'use Pod::Parser 1.50;';
-  plan skip_all => "Pod::Parser 1.50 required for podlinkcheck" if $@;
-}
-use File::Find;
-use File::Spec::Functions qw(rel2abs abs2rel splitdir);
-
-# external stuff we refer to
-my @known =
-  qw(perl Affix::Infix2Postfix Parse::RecDescent GD Image::Magick Graphics::Magick CGI Image::ExifTool XSLoader DynaLoader Prima::Image IPA PDL);
-
-# also known since we supply them, but we don't always install them
-push @known, qw(Imager::Font::FT2 Imager::Font::W32 Imager::Font::T1
-   Imager::File::JPEG Imager::File::GIF Imager::File::PNG Imager::File::TIFF);
-
-my @pod; # files with pod
-
-my $base = rel2abs("blib/lib");
-
-my @files;
-find(sub {
-       -f && /\.(pod|pm)$/
-        and push @files, $File::Find::name;
-     }, $base);
-
-my %targets = map { $_ => {} } @known;
-my %item_in;
-
-for my $file (@files) {
-  my $parser = PodPreparse->new;
-
-  my $link = abs2rel($file, $base);
-  $link =~ s/\.(pod|pm|pl|PL)$//;
-  $link = join("::", splitdir($link));
-
-  $parser->{'targets'} = \%targets;
-  $parser->{'link'} = $link;
-  $parser->{'file'} = $file;
-  $parser->{item_in} = \%item_in;
-  $parser->parse_from_file($file);
-  if ($targets{$link}) {
-    push @pod, $file;
-  }
-}
-
-plan tests => scalar(@pod);
-
-for my $file (@pod) {
-  my $parser = PodLinkCheck->new;
-  $parser->{"targets"} = \%targets;
-  my $relfile = abs2rel($file, $base);
-  (my $link = $relfile) =~ s/\.(pod|pm|pl|PL)$//;
-  $link = join("::", splitdir($link));
-  $parser->{"file"} = $relfile;
-  $parser->{"link"} = $link;
-  my @errors;
-  $parser->{"errors"} = \@errors;
-  $parser->{item_in} = \%item_in;
-  $parser->parse_from_file($file);
-
-  unless (ok(!@errors, "check links in $relfile")) {
-    print STDERR "# $_\n" for @errors;
-  }
-}
-
-package PodPreparse;
-BEGIN { our @ISA = qw(Pod::Parser); }
-
-sub command {
-  my ($self, $cmd, $para) = @_;
-
-  my $targets = $self->{"targets"};
-  my $link = $self->{"link"};
-  $targets->{$link} ||= {};
-
-  if ($cmd =~ /^(head[1-5]|item)/) {
-    $para =~ s/X<.*?>//g;
-    $para =~ s/\s+$//;
-    $targets->{$link}{$para} = 1;
-    push @{$self->{item_in}{$para}}, $link;
-  }
-}
-
-sub verbatim {}
-
-sub textblock {}
-
-package PodLinkCheck;
-BEGIN { our @ISA = qw(Pod::Parser); }
-
-sub command {}
-
-sub verbatim {}
-
-sub textblock {
-  my ($self, $para, $line_num) = @_;
-
-  $self->parse_text
-    (
-     { -expand_seq => "sequence" },
-     $para, $line_num,
-    );
-}
-
-sub sequence {
-  my ($self, $seq) = @_;
-
-  if ($seq->cmd_name eq "L") {
-    my $raw = $seq->raw_text;
-    my $base_link = $seq->parse_tree->raw_text;
-    (my $link = $base_link) =~ s/.*\|//s;
-    $link =~ /^(https?|ftp|mailto):/
-      and return '';
-    my ($pod, $part) = split m(/), $link, 2;
-    $pod ||= $self->{link};
-    if ($part) {
-      $part =~ s/^\"//;
-      $part =~ s/"$//;
-    }
-    my $targets = $self->{targets};
-    my $errors = $self->{errors};
-    (undef, my $line) = $seq->file_line;
-
-    if (!$targets->{$pod}) {
-      push @$errors, "$line: No $pod found ($raw)";
-    }
-    elsif ($part && !$targets{$pod}{$part}) {
-      push @$errors, "$line: No item/section '$part' found in $pod ($raw)";
-      if ($self->{item_in}{$part}) {
-       push @$errors, "  $part can be found in:";
-       push @$errors, map "     $_", @{$self->{item_in}{$part}};
-      }
-    }
-  }
-
-  return $seq->raw_text;
-}
-
diff --git a/t/x40checklib.t b/t/x40checklib.t
deleted file mode 100644 (file)
index bf3f652..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-#!perl -w
-#
-# Each sub-module ships with our custom Devel::CheckLib, make sure
-# they all match
-use strict;
-use Test::More;
-
-my @subs = qw(FT2 GIF JPEG PNG T1 TIFF W32);
-
-plan tests => 1 + @subs;
-
-# load the base file
-
-my $base = load("inc/Devel/CheckLib.pm");
-
-ok($base, "Loaded base file");
-
-for my $sub (@subs) {
-  my $data = load("$sub/inc/Devel/CheckLib.pm");
-
-  # I'd normally use is() here, but it's excessively noisy when
-  # comparing this size of data
-  ok(defined($data) && $data eq $base, "check $sub");
-}
-
-sub load {
-  my ($filename) = @_;
-
-  if (open my $f, "<", $filename) {
-    my $data = do { local $/; <$f> };
-    close $f;
-
-    return $data;
-  }
-  else {
-    diag "Cannot load $filename: $!\n";
-    return;
-  }
-}
diff --git a/t/x90cmpversion.t b/t/x90cmpversion.t
deleted file mode 100644 (file)
index 202da2a..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#!perl -w
-use strict;
-use Test::More;
-use ExtUtils::MakeMaker;
-use ExtUtils::Manifest 'maniread';
-use File::Spec::Functions qw(devnull);
-
-my $last_tag = `git describe --abbrev=0`;
-chomp $last_tag;
-
-$last_tag
-  or plan skip_all => "Only usable in a git checkout";
-
-my $mani = maniread();
-
-my @subdirs = qw(PNG TIFF GIF JPEG W32 T1 FT2 ICO SGI Mandelbrot CountColor DynTest);
-
-my $subdir_re = "^(?:" . join("|", @subdirs) . ")/";
-
-my @pm_files = sort
-  grep /\.pm$/ && !/$subdir_re/ && !/^t\// && $_ ne 'Imager.pm', keys %$mani;
-
-plan tests => scalar(@subdirs) + scalar(@pm_files);
-
-for my $dir (@subdirs) {
-  my @changes = `git log --abbrev --oneline $last_tag..HEAD $dir`;
-  my @more_changes = `git status --porcelain $dir`;
- SKIP:
-  {
-    @changes || @more_changes
-      or skip "No changes for $dir", 1;
-    my $vfile = "$dir/$dir.pm";
-    my $current = eval { MM->parse_version($vfile) };
-    my $last_rel_content = get_file_from_git($vfile, $last_tag);
-    my $last = eval { MM->parse_version(\$last_rel_content) };
-    unless (isnt($current, $last, "$dir updated, $vfile version bump")) {
-      diag(@changes, @more_changes);
-    }
-  }
-}
-
-for my $file (@pm_files) {
-  my @changes = `git log --abbrev --oneline $last_tag..HEAD $file`;
-  my @more_changes = `git status --porcelain $file`;
- SKIP:
-  {
-    @changes || @more_changes
-      or skip "No changes for $file", 1;
-    my $current = eval { MM->parse_version($file) };
-    my $last_rel_content = get_file_from_git($file, $last_tag);
-    my $last = eval { MM->parse_version(\$last_rel_content) };
-    unless (isnt($current, $last, "$file updated, version bump")) {
-      diag(@changes, @more_changes);
-    }
-  }
-}
-
-sub get_file_from_git {
-    my ($file, $tag) = @_;
-    my $null = devnull();
-    local $/;
-    return scalar `git --no-pager show $tag:$file 2>$null`;
-}
diff --git a/t/x91manifest.t b/t/x91manifest.t
deleted file mode 100644 (file)
index b638fc3..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#!perl -w
-use strict;
-use ExtUtils::Manifest qw(maniread);
-use Test::More;
-use File::Spec;
-
-my @sub_dirs = qw(T1 FT2 W32 TIFF PNG GIF JPEG);
-
-plan tests => scalar @sub_dirs;
-
-my $base_mani = maniread();
-my @base_mani = keys %$base_mani;
-for my $sub_dir (@sub_dirs) {
-  my @expected = map { my $x = $_; $x =~ s(^$sub_dir/)(); $x }
-    grep /^$sub_dir\b/, @base_mani;
-  push @expected,
-    "MANIFEST", "MANIFEST.SKIP", "Changes", "inc/Devel/CheckLib.pm";
-  @expected = sort @expected;
-
-  my $found = maniread(File::Spec->catfile($sub_dir, "MANIFEST"));
-  my @found = sort keys %$found;
-  is_deeply(\@found, \@expected, "check sub-MANIFEST for $sub_dir");
-}
diff --git a/xt/x107bmp.t b/xt/x107bmp.t
new file mode 100644 (file)
index 0000000..07232d8
--- /dev/null
@@ -0,0 +1,72 @@
+#!perl -w
+# Extra BMP tests not shipped
+use strict;
+use Test::More;
+use Imager::Test qw(is_image);
+use Imager;
+
+# test images from 
+my @tests =
+  (
+   [ "g01bg.bmp", "1-bit blue/green", 0 ],
+   [ "g01bw.bmp", "1-bit black and white", 0 ],
+   [ "g01p1.bmp", "1-bit single colour", 0 ],
+   [ "g01wb.bmp", "1-bit white and black", 0 ],
+   [ "g04.bmp", "4-bit", 0 ],
+   [ "g04p4.bmp", "4-bit gray", 0 ],
+   [ "g04rle.bmp", "4-bit rle", "currently broken" ],
+   [ "g08.bmp", "8-bit", 0 ],
+   [ "g08offs.bmp", "8-bit with image data offset", 0 ],
+   [ "g08os2.bmp", "8-bit OS/2", "OS/2 BMP not implemented" ],
+   [ "g08p256.bmp", "8-bit, no important", 0 ],
+   [ "g08p64.bmp", "8-bit, 64 greyscale entries", 0 ],
+   [ "g08pi256.bmp", "8-bit 256 important", 0 ],
+   [ "g08pi64.bmp", "8-bit 64 important", 0 ],
+   [ "g08res11.bmp", "8-bit, 100x100 dpi", 0 ],
+   [ "g08res21.bmp", "8-bit, 200x100 dpi", 0 ],
+   [ "g08res22.bmp", "8-bit, 200x200 dpi", 0 ],
+   [ "g08rle.bmp", "8-bit rle", 0 ],
+   [ "g08s0.bmp", "8-bit, bits size not given", 0 ],
+   [ "g08w124.bmp", "8-bit 124x61", 0 ],
+   [ "g08w125.bmp", "8-bit 125x62", 0 ],
+   [ "g08w126.bmp", "8-bit 126x63", 0 ],
+   [ "g16bf555.bmp", "16-bit bitfield 555", 0 ],
+   [ "g16bf565.bmp", "16-bit bitfield 565", 0 ],
+   [ "g16def555.bmp", "16-bit default 555", 0 ],
+   [ "g24.bmp", "24-bit", 0 ],
+   [ "g32bf.bmp", "32-bit bitfields", 0 ],
+   [ "g32def.bmp", "32-bit defaults", 0 ],
+   [ "test32bfv4.bmp", "32-bit bitfields, v4", "v4 BMP not implemented" ],
+   [ "test32v5.bmp", "32-bit, v5", "v5 BMP not implemented" ],
+   [ "test4os2v2.bmp", "4-bit OS/2", "OS/2 BMP not implemented" ],
+   [ "trans.bmp", "transparency", "alpha BMPs not implemented" ],
+   [ "width.bmp", "odd-width rle", "currently broken" ],
+  );
+
+Imager->open_log(log => "testout/x107bmp.log");
+
+plan tests => 3 * @tests;
+
+for my $test (@tests) {
+  my ($in, $note, $todo) = @$test;
+
+  my $im = Imager->new(file => "xtestimg/bmp/$in");
+  local $TODO = $todo;
+  ok($im, "load $in ($note)")
+    or diag "$in: ".Imager->errstr;
+  (my $alt = $in) =~ s/\.bmp$/.sgi/;
+
+  my $ref = Imager->new(file => "xtestimg/bmp/$alt");
+  {
+    local $TODO; # should always pass
+    ok($ref, "load reference image for $in")
+      or diag "$alt: ".Imager->errstr;
+    if ($ref->getchannels == 1) {
+      $ref = $ref->convert(preset => "rgb");
+    }
+  }
+  is_image($im, $ref, "compare $note");
+}
+
+Imager->close_log();
+
diff --git a/xt/x11rubthru.t b/xt/x11rubthru.t
new file mode 100644 (file)
index 0000000..f71ad66
--- /dev/null
@@ -0,0 +1,21 @@
+#!perl -w
+use strict;
+use Imager;
+use Imager::Test qw(is_image);
+use Test::More;
+
+$Imager::formats{"tiff"}
+  or plan skip_all => "no tiff support";
+
+-d "testout" or mkdir "testout";
+
+plan tests => 2;
+
+my $dest = Imager->new(xsize => 100, ysize => 100, channels => 4);
+$dest->box(filled => 1, color => '0000FF');
+my $src = Imager->new(xsize => 100, ysize => 100, channels => 4);
+$src->circle(color => 'FF0000', x => 50, y => 60, r => 40, aa => 1);
+ok($dest->rubthrough(src => $src, src_minx => 10, src_miny => 20, src_maxx => 90,
+              tx => 10, ty => 10), "rubthrough");
+ok($dest->write(file => "testout/x11rubthru.tif"), "save it");
+
diff --git a/xt/x20spell.t b/xt/x20spell.t
new file mode 100644 (file)
index 0000000..d7368b9
--- /dev/null
@@ -0,0 +1,92 @@
+#!perl -w
+use strict;
+use Test::More;
+use ExtUtils::Manifest qw(maniread);
+use File::Temp;
+eval "use Pod::Spell 1.01";
+plan skip_all => "Pod::Spell 1.01 required for spellchecking POD" if $@;
+my $manifest = maniread();
+my @pod = sort grep !/^inc/ && /\.(pm|pl|pod|PL)$/, keys %$manifest;
+plan tests => scalar(@pod);
+my @stopwords = qw/
+API
+Arnar
+BMP
+Blit
+CGI
+chromaticities
+CMYK
+CPAN
+FreeType
+GIF
+HSV
+Hrafnkelsson
+ICO
+IMAGER
+Imager
+Imager's
+JPEG
+POSIX
+PNG
+PNM
+RGB
+RGBA
+SGI
+sRGB
+TGA
+TIFF
+UTF-8
+Uncategorized
+bilevel
+const
+dpi
+eg
+gaussian
+ie
+infix
+invocant
+metadata
+multi-threaded
+mutex
+paletted
+postfix
+preload
+preloading
+preloads
+renderer
+tuple
+unary
+unseekable
+varargs
+/;
+
+local %Pod::Wordlist::Wordlist = %Pod::Wordlist::Wordlist;
+for my $stop (@stopwords) {
+  $Pod::Wordlist::Wordlist{$stop} = 1;
+}
+
+# see for example:
+#  https://bugs.launchpad.net/ubuntu/+source/aspell/+bug/71322
+$ENV{LANG} = "C";
+$ENV{LC_ALL} = "C";
+for my $file (@pod) {
+  my $check_fh = File::Temp->new;
+  my $check_filename = $check_fh->filename;
+  open POD, "< $file"
+    or die "Cannot open $file for spell check: $!\n";
+  Pod::Spell->new->parse_from_filehandle(\*POD, $check_fh);
+  close $check_fh;
+  
+  my @out = `aspell list <$check_filename`;
+  unless (ok(@out == 0, "spell check $file")) {
+    chomp @out;
+    diag $_ for @out;
+    print "#----\n";
+    open my $fh, "<", $check_filename;
+    while (<$fh>) {
+      chomp;
+      print "# $_\n";
+    }
+    print "#----\n";
+  }
+}
diff --git a/xt/x30podlinkcheck.t b/xt/x30podlinkcheck.t
new file mode 100644 (file)
index 0000000..776e14e
--- /dev/null
@@ -0,0 +1,141 @@
+#!perl -w
+use strict;
+use Test::More;
+BEGIN {
+  eval 'use Pod::Parser 1.50;';
+  plan skip_all => "Pod::Parser 1.50 required for podlinkcheck" if $@;
+}
+use File::Find;
+use File::Spec::Functions qw(rel2abs abs2rel splitdir);
+
+# external stuff we refer to
+my @known =
+  qw(perl Affix::Infix2Postfix Parse::RecDescent GD Image::Magick Graphics::Magick CGI Image::ExifTool XSLoader DynaLoader Prima::Image IPA PDL);
+
+# also known since we supply them, but we don't always install them
+push @known, qw(Imager::Font::FT2 Imager::Font::W32 Imager::Font::T1
+   Imager::File::JPEG Imager::File::GIF Imager::File::PNG Imager::File::TIFF);
+
+my @pod; # files with pod
+
+my $base = rel2abs("blib/lib");
+
+my @files;
+find(sub {
+       -f && /\.(pod|pm)$/
+        and push @files, $File::Find::name;
+     }, $base);
+
+my %targets = map { $_ => {} } @known;
+my %item_in;
+
+for my $file (@files) {
+  my $parser = PodPreparse->new;
+
+  my $link = abs2rel($file, $base);
+  $link =~ s/\.(pod|pm|pl|PL)$//;
+  $link = join("::", splitdir($link));
+
+  $parser->{'targets'} = \%targets;
+  $parser->{'link'} = $link;
+  $parser->{'file'} = $file;
+  $parser->{item_in} = \%item_in;
+  $parser->parse_from_file($file);
+  if ($targets{$link}) {
+    push @pod, $file;
+  }
+}
+
+plan tests => scalar(@pod);
+
+for my $file (@pod) {
+  my $parser = PodLinkCheck->new;
+  $parser->{"targets"} = \%targets;
+  my $relfile = abs2rel($file, $base);
+  (my $link = $relfile) =~ s/\.(pod|pm|pl|PL)$//;
+  $link = join("::", splitdir($link));
+  $parser->{"file"} = $relfile;
+  $parser->{"link"} = $link;
+  my @errors;
+  $parser->{"errors"} = \@errors;
+  $parser->{item_in} = \%item_in;
+  $parser->parse_from_file($file);
+
+  unless (ok(!@errors, "check links in $relfile")) {
+    print STDERR "# $_\n" for @errors;
+  }
+}
+
+package PodPreparse;
+BEGIN { our @ISA = qw(Pod::Parser); }
+
+sub command {
+  my ($self, $cmd, $para) = @_;
+
+  my $targets = $self->{"targets"};
+  my $link = $self->{"link"};
+  $targets->{$link} ||= {};
+
+  if ($cmd =~ /^(head[1-5]|item)/) {
+    $para =~ s/X<.*?>//g;
+    $para =~ s/\s+$//;
+    $targets->{$link}{$para} = 1;
+    push @{$self->{item_in}{$para}}, $link;
+  }
+}
+
+sub verbatim {}
+
+sub textblock {}
+
+package PodLinkCheck;
+BEGIN { our @ISA = qw(Pod::Parser); }
+
+sub command {}
+
+sub verbatim {}
+
+sub textblock {
+  my ($self, $para, $line_num) = @_;
+
+  $self->parse_text
+    (
+     { -expand_seq => "sequence" },
+     $para, $line_num,
+    );
+}
+
+sub sequence {
+  my ($self, $seq) = @_;
+
+  if ($seq->cmd_name eq "L") {
+    my $raw = $seq->raw_text;
+    my $base_link = $seq->parse_tree->raw_text;
+    (my $link = $base_link) =~ s/.*\|//s;
+    $link =~ /^(https?|ftp|mailto):/
+      and return '';
+    my ($pod, $part) = split m(/), $link, 2;
+    $pod ||= $self->{link};
+    if ($part) {
+      $part =~ s/^\"//;
+      $part =~ s/"$//;
+    }
+    my $targets = $self->{targets};
+    my $errors = $self->{errors};
+    (undef, my $line) = $seq->file_line;
+
+    if (!$targets->{$pod}) {
+      push @$errors, "$line: No $pod found ($raw)";
+    }
+    elsif ($part && !$targets{$pod}{$part}) {
+      push @$errors, "$line: No item/section '$part' found in $pod ($raw)";
+      if ($self->{item_in}{$part}) {
+       push @$errors, "  $part can be found in:";
+       push @$errors, map "     $_", @{$self->{item_in}{$part}};
+      }
+    }
+  }
+
+  return $seq->raw_text;
+}
+
diff --git a/xt/x40checklib.t b/xt/x40checklib.t
new file mode 100644 (file)
index 0000000..bf3f652
--- /dev/null
@@ -0,0 +1,39 @@
+#!perl -w
+#
+# Each sub-module ships with our custom Devel::CheckLib, make sure
+# they all match
+use strict;
+use Test::More;
+
+my @subs = qw(FT2 GIF JPEG PNG T1 TIFF W32);
+
+plan tests => 1 + @subs;
+
+# load the base file
+
+my $base = load("inc/Devel/CheckLib.pm");
+
+ok($base, "Loaded base file");
+
+for my $sub (@subs) {
+  my $data = load("$sub/inc/Devel/CheckLib.pm");
+
+  # I'd normally use is() here, but it's excessively noisy when
+  # comparing this size of data
+  ok(defined($data) && $data eq $base, "check $sub");
+}
+
+sub load {
+  my ($filename) = @_;
+
+  if (open my $f, "<", $filename) {
+    my $data = do { local $/; <$f> };
+    close $f;
+
+    return $data;
+  }
+  else {
+    diag "Cannot load $filename: $!\n";
+    return;
+  }
+}
diff --git a/xt/x90cmpversion.t b/xt/x90cmpversion.t
new file mode 100644 (file)
index 0000000..202da2a
--- /dev/null
@@ -0,0 +1,63 @@
+#!perl -w
+use strict;
+use Test::More;
+use ExtUtils::MakeMaker;
+use ExtUtils::Manifest 'maniread';
+use File::Spec::Functions qw(devnull);
+
+my $last_tag = `git describe --abbrev=0`;
+chomp $last_tag;
+
+$last_tag
+  or plan skip_all => "Only usable in a git checkout";
+
+my $mani = maniread();
+
+my @subdirs = qw(PNG TIFF GIF JPEG W32 T1 FT2 ICO SGI Mandelbrot CountColor DynTest);
+
+my $subdir_re = "^(?:" . join("|", @subdirs) . ")/";
+
+my @pm_files = sort
+  grep /\.pm$/ && !/$subdir_re/ && !/^t\// && $_ ne 'Imager.pm', keys %$mani;
+
+plan tests => scalar(@subdirs) + scalar(@pm_files);
+
+for my $dir (@subdirs) {
+  my @changes = `git log --abbrev --oneline $last_tag..HEAD $dir`;
+  my @more_changes = `git status --porcelain $dir`;
+ SKIP:
+  {
+    @changes || @more_changes
+      or skip "No changes for $dir", 1;
+    my $vfile = "$dir/$dir.pm";
+    my $current = eval { MM->parse_version($vfile) };
+    my $last_rel_content = get_file_from_git($vfile, $last_tag);
+    my $last = eval { MM->parse_version(\$last_rel_content) };
+    unless (isnt($current, $last, "$dir updated, $vfile version bump")) {
+      diag(@changes, @more_changes);
+    }
+  }
+}
+
+for my $file (@pm_files) {
+  my @changes = `git log --abbrev --oneline $last_tag..HEAD $file`;
+  my @more_changes = `git status --porcelain $file`;
+ SKIP:
+  {
+    @changes || @more_changes
+      or skip "No changes for $file", 1;
+    my $current = eval { MM->parse_version($file) };
+    my $last_rel_content = get_file_from_git($file, $last_tag);
+    my $last = eval { MM->parse_version(\$last_rel_content) };
+    unless (isnt($current, $last, "$file updated, version bump")) {
+      diag(@changes, @more_changes);
+    }
+  }
+}
+
+sub get_file_from_git {
+    my ($file, $tag) = @_;
+    my $null = devnull();
+    local $/;
+    return scalar `git --no-pager show $tag:$file 2>$null`;
+}
diff --git a/xt/x91manifest.t b/xt/x91manifest.t
new file mode 100644 (file)
index 0000000..b638fc3
--- /dev/null
@@ -0,0 +1,23 @@
+#!perl -w
+use strict;
+use ExtUtils::Manifest qw(maniread);
+use Test::More;
+use File::Spec;
+
+my @sub_dirs = qw(T1 FT2 W32 TIFF PNG GIF JPEG);
+
+plan tests => scalar @sub_dirs;
+
+my $base_mani = maniread();
+my @base_mani = keys %$base_mani;
+for my $sub_dir (@sub_dirs) {
+  my @expected = map { my $x = $_; $x =~ s(^$sub_dir/)(); $x }
+    grep /^$sub_dir\b/, @base_mani;
+  push @expected,
+    "MANIFEST", "MANIFEST.SKIP", "Changes", "inc/Devel/CheckLib.pm";
+  @expected = sort @expected;
+
+  my $found = maniread(File::Spec->catfile($sub_dir, "MANIFEST"));
+  my @found = sort keys %$found;
+  is_deeply(\@found, \@expected, "check sub-MANIFEST for $sub_dir");
+}