]> git.imager.perl.org - imager.git/blob - t/450-api/100-inline.t
avoid a possible sign-extension for offsets/sizes in SGI
[imager.git] / t / 450-api / 100-inline.t
1 #!perl -w
2 #
3 # this tests both the Inline interface and the API
4 use strict;
5 use Test::More;
6 use Imager::Test qw(is_color3 is_color4);
7 eval "require Inline::C;";
8 plan skip_all => "Inline required for testing API" if $@;
9
10 eval "require Parse::RecDescent;";
11 plan skip_all => "Could not load Parse::RecDescent" if $@;
12
13 use Cwd 'getcwd';
14 plan skip_all => "Inline won't work in directories with spaces"
15   if getcwd() =~ / /;
16
17 plan skip_all => "perl 5.005_04, 5.005_05 too buggy"
18   if $] =~ /^5\.005_0[45]$/;
19
20 -d "testout" or mkdir "testout";
21
22 print STDERR "Inline version $Inline::VERSION\n";
23
24 plan tests => 120;
25 require Inline;
26 Inline->import(with => 'Imager');
27 Inline->import("FORCE"); # force rebuild
28 #Inline->import(C => Config => OPTIMIZE => "-g");
29
30 Inline->bind(C => <<'EOS');
31 #include <math.h>
32
33 int pixel_count(Imager::ImgRaw im) {
34   return im->xsize * im->ysize;
35 }
36
37 int count_color(Imager::ImgRaw im, Imager::Color c) {
38   int count = 0, x, y, chan;
39   i_color read_c;
40
41   for (x = 0; x < im->xsize; ++x) {
42     for (y = 0; y < im->ysize; ++y) {
43       int match = 1;
44       i_gpix(im, x, y, &read_c);
45       for (chan = 0; chan < im->channels; ++chan) {
46         if (read_c.channel[chan] != c->channel[chan]) {
47           match = 0;
48           break;
49         }
50       }
51       if (match)
52         ++count;
53     }
54   }
55
56   return count;
57 }
58
59 Imager make_10x10() {
60   i_img *im = i_img_8_new(10, 10, 3);
61   i_color c;
62   c.channel[0] = c.channel[1] = c.channel[2] = 255;
63   i_box_filled(im, 0, 0, im->xsize-1, im->ysize-1, &c);
64
65   return im;
66 }
67
68 /* tests that all of the APIs are visible - most of them anyway */
69 Imager do_lots(Imager src) {
70   i_img *im = i_img_8_new(100, 100, 3);
71   i_img *fill_im = i_img_8_new(5, 5, 3);
72   i_img *testim;
73   i_color red, blue, green, black, temp_color;
74   i_fcolor redf, bluef;
75   i_fill_t *hatch, *fhatch_fill;
76   i_fill_t *im_fill;
77   i_fill_t *solid_fill, *fsolid_fill;
78   i_fill_t *fount_fill;
79   void *block;
80   double matrix[9] = /* 30 degree rotation */
81     {
82       0.866025,  -0.5,      0, 
83       0.5,       0.866025,  0, 
84       0,         0,         1,      
85     };
86   i_fountain_seg fseg;
87   i_img_tags tags;
88   int entry;
89   double temp_double;
90
91   red.channel[0] = 255; red.channel[1] = 0; red.channel[2] = 0;
92   red.channel[3] = 255;
93   blue.channel[0] = 0; blue.channel[1] = 0; blue.channel[2] = 255;
94   blue.channel[3] = 255;
95   green.channel[0] = 0; green.channel[1] = 255; green.channel[2] = 0;
96   green.channel[3] = 255;
97   black.channel[0] = black.channel[1] = black.channel[2] = 0;
98   black.channel[3] = 255;
99   hatch = i_new_fill_hatch(&red, &blue, 0, 1, NULL, 0, 0);
100
101   i_box(im, 0, 0, 9, 9, &red);
102   i_box_filled(im, 10, 0, 19, 9, &blue);
103   i_box_cfill(im, 20, 0, 29, 9, hatch);
104
105   /* make an image fill, and try it */
106   i_box_cfill(fill_im, 0, 0, 4, 4, hatch);
107   im_fill = i_new_fill_image(fill_im, matrix, 2, 2, 0);
108
109   i_box_cfill(im, 30, 0, 39, 9, im_fill);
110
111   /* make a solid fill and try it */
112   solid_fill = i_new_fill_solid(&red, 0);
113   i_box_cfill(im, 40, 0, 49, 9, solid_fill);
114
115   /* floating fills */
116   redf.channel[0] = 1.0; redf.channel[1] = 0; redf.channel[2] = 0;
117   redf.channel[3] = 1.0;
118   bluef.channel[0] = 0; bluef.channel[1] = 0; bluef.channel[2] = 1.0;
119   bluef.channel[3] = 1.0;
120
121   fsolid_fill = i_new_fill_solidf(&redf, 0);
122   i_box_cfill(im, 50, 0, 59, 9, fsolid_fill);
123  
124   fhatch_fill = i_new_fill_hatchf(&redf, &bluef, 0, 2, NULL, 0, 0);
125   i_box_cfill(im, 60, 0, 69, 9, fhatch_fill);
126
127   /* fountain fill */
128   fseg.start = 0;
129   fseg.middle = 0.5;
130   fseg.end = 1.0;
131   fseg.c[0] = redf;
132   fseg.c[1] = bluef;
133   fseg.type = i_fst_linear;
134   fseg.color = i_fc_hue_down;
135   fount_fill = i_new_fill_fount(70, 0, 80, 0, i_ft_linear, i_fr_triangle, 0, i_fts_none, 1, 1, &fseg);
136
137   i_box_cfill(im, 70, 0, 79, 9, fount_fill);
138
139   i_line(im, 0, 10, 10, 15, &blue, 1);
140   i_line_aa(im, 0, 19, 10, 15, &red, 1);
141   
142   i_arc(im, 15, 15, 4, 45, 160, &blue);
143   i_arc_aa(im, 25, 15, 4, 75, 280, &red);
144   i_arc_cfill(im, 35, 15, 4, 0, 215, hatch);
145   i_arc_aa_cfill(im, 45, 15, 4, 30, 210, hatch);
146   i_circle_aa(im, 55, 15, 4, &red);
147   
148   i_box(im, 61, 11, 68, 18, &red);
149   i_flood_fill(im, 65, 15, &blue);
150   i_box(im, 71, 11, 78, 18, &red);
151   i_flood_cfill(im, 75, 15, hatch);
152
153   i_box_filled(im, 1, 21, 9, 24, &red);
154   i_box_filled(im, 1, 25, 9, 29, &blue);
155   i_flood_fill_border(im, 5, 25, &green, &black);
156
157   i_box_filled(im, 11, 21, 19, 24, &red);
158   i_box_filled(im, 11, 25, 19, 29, &blue);
159   i_flood_cfill_border(im, 15, 25, hatch, &black);
160
161   {
162      double x[3];
163      double y[3];
164      i_polygon_t poly;
165      x[0] = 55;
166      y[0] = 25;
167      x[1] = 55;
168      y[1] = 50;
169      x[2] = 70;
170      y[2] = 50;
171      i_poly_aa_m(im, 3, x, y, i_pfm_evenodd, &red);
172      x[2] = 40;
173      i_poly_aa_cfill_m(im, 3, x, y, i_pfm_evenodd, hatch);
174      y[0] = 65;
175      poly.x = x;
176      poly.y = y;
177      poly.count = 3;
178      i_poly_poly_aa(im, 1, &poly, i_pfm_nonzero, &green);
179      x[2] = 70;
180      i_poly_poly_aa_cfill(im, 1, &poly, i_pfm_nonzero, hatch);
181   }
182
183   i_fill_destroy(fount_fill);
184   i_fill_destroy(fhatch_fill);
185   i_fill_destroy(solid_fill);
186   i_fill_destroy(fsolid_fill);
187   i_fill_destroy(hatch);
188   i_fill_destroy(im_fill);
189   i_img_destroy(fill_im);
190
191   /* make sure we can make each image type */
192   testim = i_img_16_new(100, 100, 3);
193   i_img_destroy(testim);
194   testim = i_img_double_new(100, 100, 3);
195   i_img_destroy(testim);
196   testim = i_img_pal_new(100, 100, 3, 256);
197   i_img_destroy(testim);
198   testim = i_sametype(im, 50, 50);
199   i_img_destroy(testim);
200   testim = i_sametype_chans(im, 50, 50, 4);
201   i_img_destroy(testim);
202
203   i_clear_error();
204   i_push_error(0, "Hello");
205   i_push_errorf(0, "%s", "World");
206
207   /* make sure tags create/destroy work */
208   i_tags_new(&tags);
209   i_tags_destroy(&tags);  
210
211   block = mymalloc(20);
212   block = myrealloc(block, 50);
213   myfree(block);
214
215   i_tags_set(&im->tags, "lots_string", "foo", -1);
216   i_tags_setn(&im->tags, "lots_number", 101);
217
218   if (!i_tags_find(&im->tags, "lots_number", 0, &entry)) {
219     i_push_error(0, "lots_number tag not found");
220     i_img_destroy(im);
221     return NULL;
222   }
223   i_tags_delete(&im->tags, entry);
224
225   /* these won't delete anything, but it makes sure the macros and function
226      pointers are correct */
227   i_tags_delbyname(&im->tags, "unknown");
228   i_tags_delbycode(&im->tags, 501);
229   i_tags_set_float(&im->tags, "lots_float", 0, 3.14);
230   if (!i_tags_get_float(&im->tags, "lots_float", 0, &temp_double)) {
231     i_push_error(0, "lots_float not found");
232     i_img_destroy(im);
233     return NULL;
234   }
235   if (fabs(temp_double - 3.14) > 0.001) {
236     i_push_errorf(0, "lots_float incorrect %g", temp_double);
237     i_img_destroy(im);
238     return NULL;
239   }
240   i_tags_set_float2(&im->tags, "lots_float2", 0, 100 * sqrt(2.0), 5);
241   if (!i_tags_get_int(&im->tags, "lots_float2", 0, &entry)) {
242     i_push_error(0, "lots_float2 not found as int");
243     i_img_destroy(im);
244     return NULL;
245   }
246   if (entry != 141) { 
247     i_push_errorf(0, "lots_float2 unexpected value %d", entry);
248     i_img_destroy(im);
249     return NULL;
250   }
251
252   i_tags_set_color(&im->tags, "lots_color", 0, &red);
253   if (!i_tags_get_color(&im->tags, "lots_color", 0, &temp_color)) {
254     i_push_error(0, "lots_color not found as color");
255     i_img_destroy(im);
256     return NULL;
257   }
258
259   return im;
260 }
261
262 void
263 io_fd(int fd) {
264   Imager::IO io = io_new_fd(fd);
265   i_io_write(io, "test", 4);
266   i_io_close(io);
267   io_glue_destroy(io);
268 }
269
270 int
271 io_bufchain_test() {
272   Imager::IO io = io_new_bufchain();
273   unsigned char *result;
274   size_t size;
275   if (i_io_write(io, "test2", 5) != 5) {
276     fprintf(stderr, "write failed\n");
277     return 0;
278   }
279   if (!i_io_flush(io)) {
280     fprintf(stderr, "flush failed\n");
281     return 0;
282   }
283   if (i_io_close(io) != 0) {
284     fprintf(stderr, "close failed\n");
285     return 0;
286   }
287   size = io_slurp(io, &result);
288   if (size != 5) {
289     fprintf(stderr, "wrong size\n");
290     return 0;
291   }
292   if (memcmp(result, "test2", 5)) {
293     fprintf(stderr, "data mismatch\n");
294     return 0;
295   }
296   if (i_io_seek(io, 0, 0) != 0) {
297     fprintf(stderr, "seek failure\n");
298     return 0;
299   }
300   myfree(result);
301   io_glue_destroy(io);
302
303   return 1;
304 }
305
306 const char *
307 io_buffer_test(SV *in) {
308   STRLEN len;
309   const char *in_str = SvPV(in, len);
310   static char buf[100];
311   Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
312   ssize_t read_size;
313
314   read_size = i_io_read(io, buf, sizeof(buf)-1);
315   io_glue_destroy(io);
316   if (read_size < 0 || read_size >= sizeof(buf)) {
317     return "";
318   }
319
320   buf[read_size] = '\0';
321
322   return buf;
323 }
324
325 const char *
326 io_peekn_test(SV *in) {
327   STRLEN len;
328   const char *in_str = SvPV(in, len);
329   static char buf[100];
330   Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
331   ssize_t read_size;
332
333   read_size = i_io_peekn(io, buf, sizeof(buf)-1);
334   io_glue_destroy(io);
335   if (read_size < 0 || read_size >= sizeof(buf)) {
336     return "";
337   }
338
339   buf[read_size] = '\0';
340
341   return buf;
342 }
343
344 const char *
345 io_gets_test(SV *in) {
346   STRLEN len;
347   const char *in_str = SvPV(in, len);
348   static char buf[100];
349   Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
350   ssize_t read_size;
351
352   read_size = i_io_gets(io, buf, sizeof(buf), 's');
353   io_glue_destroy(io);
354   if (read_size < 0 || read_size >= sizeof(buf)) {
355     return "";
356   }
357
358   return buf;
359 }
360
361 int
362 io_getc_test(SV *in) {
363   STRLEN len;
364   const char *in_str = SvPV(in, len);
365   static char buf[100];
366   Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
367   int result;
368
369   result = i_io_getc(io);
370   io_glue_destroy(io);
371
372   return result;
373 }
374
375 int
376 io_peekc_test(SV *in) {
377   STRLEN len;
378   const char *in_str = SvPV(in, len);
379   static char buf[100];
380   Imager::IO io = io_new_buffer(in_str, len, NULL, NULL);
381   int result;
382
383   i_io_set_buffered(io, 0);
384
385   result = i_io_peekc(io);
386   io_glue_destroy(io);
387
388   return result;
389 }
390
391
392
393 int
394 test_render_color(Imager work_8) {
395   i_render *r8;
396   i_color c;
397   unsigned char render_coverage[3];
398
399   render_coverage[0] = 0;
400   render_coverage[1] = 128;
401   render_coverage[2] = 255;
402
403   r8 = i_render_new(work_8, 10);
404   c.channel[0] = 128;
405   c.channel[1] = 255;
406   c.channel[2] = 0;
407   c.channel[3] = 255;
408   i_render_color(r8, 0, 0, sizeof(render_coverage), render_coverage, &c);
409
410   c.channel[3] = 128;
411   i_render_color(r8, 0, 1, sizeof(render_coverage), render_coverage, &c);
412
413   c.channel[3] = 0;
414   i_render_color(r8, 0, 2, sizeof(render_coverage), render_coverage, &c);
415
416   i_render_delete(r8);
417
418   return 1;
419 }
420
421 int
422 raw_psamp(Imager im, int chan_count) {
423   static i_sample_t samps[] = { 0, 127, 255 };
424
425   i_clear_error();
426   return i_psamp(im, 0, 1, 0, samps, NULL, chan_count);
427 }
428
429 int
430 raw_psampf(Imager im, int chan_count) {
431   static i_fsample_t samps[] = { 0, 0.5, 1.0 };
432
433   i_clear_error();
434   return i_psampf(im, 0, 1, 0, samps, NULL, chan_count);
435 }
436
437 int
438 test_mutex() {
439   i_mutex_t m;
440
441   m = i_mutex_new();
442   i_mutex_lock(m);
443   i_mutex_unlock(m);
444   i_mutex_destroy(m);
445
446   return 1;
447 }
448
449 int
450 test_slots() {
451   im_slot_t slot = im_context_slot_new(NULL);
452
453   if (im_context_slot_get(aIMCTX, slot)) {
454     fprintf(stderr, "slots should default to NULL\n");
455     return 0;
456   }
457   if (!im_context_slot_set(aIMCTX, slot, &slot)) {
458     fprintf(stderr, "set slot failed\n");
459     return 0;
460   }
461
462   if (im_context_slot_get(aIMCTX, slot) != &slot) {
463     fprintf(stderr, "get slot didn't match\n");
464     return 0;
465   }
466
467   return 1;
468 }
469
470 int
471 color_channels(Imager im) {
472   return i_img_color_channels(im);
473 }
474
475 int
476 color_model(Imager im) {
477   return (int)i_img_color_model(im);
478 }
479
480 int
481 alpha_channel(Imager im) {
482   int channel;
483   if (!i_img_alpha_channel(im, &channel))
484     channel = -1;
485
486   return channel;
487 }
488
489 EOS
490
491 my $im = Imager->new(xsize=>50, ysize=>50);
492 is(pixel_count($im), 2500, "pixel_count");
493
494 my $black = Imager::Color->new(0,0,0);
495 is(count_color($im, $black), 2500, "count_color black on black image");
496
497 my $im2 = make_10x10();
498 my $white = Imager::Color->new(255, 255, 255);
499 is(count_color($im2, $white), 100, "check new image white count");
500 ok($im2->box(filled=>1, xmin=>1, ymin=>1, xmax => 8, ymax=>8, color=>$black),
501    "try new image");
502 is(count_color($im2, $black), 64, "check modified black count");
503 is(count_color($im2, $white), 36, "check modified white count");
504
505 my $im3 = do_lots($im2);
506 ok($im3, "do_lots()")
507   or print "# ", Imager->_error_as_msg, "\n";
508 ok($im3->write(file=>'testout/t82lots.ppm'), "write t82lots.ppm");
509
510 { # RT #24992
511   # the T_IMAGER_FULL_IMAGE typemap entry was returning a blessed
512   # hash with an extra ref, causing memory leaks
513
514   my $im = make_10x10();
515   my $im2 = Imager->new(xsize => 10, ysize => 10);
516   require B;
517   my $imb = B::svref_2object($im);
518   my $im2b = B::svref_2object($im2);
519   is ($imb->REFCNT, $im2b->REFCNT, 
520       "check refcnt of imager object hash between normal and typemap generated");
521 }
522
523 SKIP:
524 {
525   use IO::File;
526   my $fd_filename = "testout/t82fd.txt";
527   {
528     my $fh = IO::File->new($fd_filename, "w")
529       or skip("Can't create file: $!", 1);
530     io_fd(fileno($fh));
531     $fh->close;
532   }
533   {
534     my $fh = IO::File->new($fd_filename, "r")
535       or skip("Can't open file: $!", 1);
536     my $data = <$fh>;
537     is($data, "test", "make sure data written to fd");
538   }
539   unlink $fd_filename;
540 }
541
542 ok(io_bufchain_test(), "check bufchain functions");
543
544 is(io_buffer_test("test3"), "test3", "check io_new_buffer() and i_io_read");
545
546 is(io_peekn_test("test5"), "test5", "check i_io_peekn");
547
548 is(io_gets_test("test"), "tes", "check i_io_gets()");
549
550 is(io_getc_test("ABC"), ord "A", "check i_io_getc(_imp)?");
551
552 is(io_getc_test("XYZ"), ord "X", "check i_io_peekc(_imp)?");
553
554 for my $bits (8, 16) {
555   print "# bits: $bits\n";
556
557   # the floating point processing is a little more accurate
558   my $bump = $bits == 16 ? 1 : 0;
559   {
560     my $im = Imager->new(xsize => 10, ysize => 10, channels => 3, bits => $bits);
561     ok($im->box(filled => 1, color => '#808080'), "fill work image with gray");
562     ok(test_render_color($im),
563        "call render_color on 3 channel image");
564     is_color3($im->getpixel(x => 0, y => 0), 128, 128, 128,
565               "check zero coverage, alpha 255 color, bits $bits");
566     is_color3($im->getpixel(x => 1, y => 0), 128, 191+$bump, 63+$bump,
567               "check 128 coverage, alpha 255 color, bits $bits");
568     is_color3($im->getpixel(x => 2, y => 0), 128, 255, 0,
569               "check 255 coverage, alpha 255 color, bits $bits");
570
571     is_color3($im->getpixel(x => 0, y => 1), 128, 128, 128,
572               "check zero coverage, alpha 128 color, bits $bits");
573     is_color3($im->getpixel(x => 1, y => 1), 128, 159+$bump, 95+$bump,
574               "check 128 coverage, alpha 128 color, bits $bits");
575     is_color3($im->getpixel(x => 2, y => 1), 128, 191+$bump, 63+$bump,
576               "check 255 coverage, alpha 128 color, bits $bits");
577
578     is_color3($im->getpixel(x => 0, y => 2), 128, 128, 128,
579               "check zero coverage, alpha 0 color, bits $bits");
580     is_color3($im->getpixel(x => 1, y => 2), 128, 128, 128,
581               "check 128 coverage, alpha 0 color, bits $bits");
582     is_color3($im->getpixel(x => 2, y => 2), 128, 128, 128,
583               "check 255 coverage, alpha 0 color, bits $bits");
584   }
585   {
586     my $im = Imager->new(xsize => 10, ysize => 10, channels => 4, bits => $bits);
587     ok($im->box(filled => 1, color => '#808080'), "fill work image with opaque gray");
588     ok(test_render_color($im),
589        "call render_color on 4 channel image");
590     is_color4($im->getpixel(x => 0, y => 0), 128, 128, 128, 255,
591               "check zero coverage, alpha 255 color, bits $bits");
592     is_color4($im->getpixel(x => 1, y => 0), 128, 191+$bump, 63+$bump, 255,
593               "check 128 coverage, alpha 255 color, bits $bits");
594     is_color4($im->getpixel(x => 2, y => 0), 128, 255, 0, 255,
595               "check 255 coverage, alpha 255 color, bits $bits");
596
597     is_color4($im->getpixel(x => 0, y => 1), 128, 128, 128, 255,
598               "check zero coverage, alpha 128 color, bits $bits");
599     is_color4($im->getpixel(x => 1, y => 1), 128, 159+$bump, 95+$bump, 255,
600               "check 128 coverage, alpha 128 color, bits $bits");
601     is_color4($im->getpixel(x => 2, y => 1), 128, 191+$bump, 63+$bump, 255,
602               "check 255 coverage, alpha 128 color, bits $bits");
603
604     is_color4($im->getpixel(x => 0, y => 2), 128, 128, 128, 255,
605               "check zero coverage, alpha 0 color, bits $bits");
606     is_color4($im->getpixel(x => 1, y => 2), 128, 128, 128, 255,
607               "check 128 coverage, alpha 0 color, bits $bits");
608     is_color4($im->getpixel(x => 2, y => 2), 128, 128, 128, 255,
609               "check 255 coverage, alpha 0 color, bits $bits");
610   }
611
612   {
613     my $im = Imager->new(xsize => 10, ysize => 10, channels => 4, bits => $bits);
614     ok($im->box(filled => 1, color => Imager::Color->new(128, 128, 128, 64)), "fill work image with translucent gray");
615     ok(test_render_color($im),
616        "call render_color on 4 channel image");
617     is_color4($im->getpixel(x => 0, y => 0), 128, 128, 128, 64,
618               "check zero coverage, alpha 255 color, bits $bits");
619     is_color4($im->getpixel(x => 1, y => 0), 128, 230, 25+$bump, 159+$bump,
620               "check 128 coverage, alpha 255 color, bits $bits");
621     is_color4($im->getpixel(x => 2, y => 0), 128, 255, 0, 255,
622               "check 255 coverage, alpha 255 color, bits $bits");
623
624     is_color4($im->getpixel(x => 0, y => 1), 128, 128, 128, 64,
625               "check zero coverage, alpha 128 color, bits $bits");
626     is_color4($im->getpixel(x => 1, y => 1), 129-$bump, 202-$bump, 55, 111+$bump,
627               "check 128 coverage, alpha 128 color, bits $bits");
628     is_color4($im->getpixel(x => 2, y => 1), 128, 230, 25+$bump, 159+$bump,
629               "check 255 coverage, alpha 128 color, bits $bits");
630
631     is_color4($im->getpixel(x => 0, y => 2), 128, 128, 128, 64,
632               "check zero coverage, alpha 0 color, bits $bits");
633     is_color4($im->getpixel(x => 1, y => 2), 128, 128, 128, 64,
634               "check 128 coverage, alpha 0 color, bits $bits");
635     is_color4($im->getpixel(x => 2, y => 2), 128, 128, 128, 64,
636               "check 255 coverage, alpha 0 color, bits $bits");
637   }
638 }
639
640 {
641   my $im = Imager->new(xsize => 10, ysize => 10);
642   is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail");
643   is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
644      "check message");
645   is(raw_psamp($im, 0), -1, "bad channel list (0) for psamp should fail");
646   is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
647      "check message");
648   is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail");
649   is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
650      "check message");
651   is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail");
652   is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
653      "check message");
654 }
655
656 {
657   my $im = Imager->new(xsize => 10, ysize => 10, bits => 16);
658   is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail (16-bit)");
659   is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
660      "check message");
661   is(raw_psamp($im, 0), -1, "bad channel list (0) for psamp should fail (16-bit)");
662   is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
663      "check message");
664   is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail (16-bit)");
665   is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
666      "check message");
667   is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail (16-bit)");
668   is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
669      "check message");
670 }
671
672 {
673   my $im = Imager->new(xsize => 10, ysize => 10, bits => 'double');
674   is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail (double)");
675   is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
676      "check message");
677   is(raw_psamp($im, 0), -1,, "bad channel list (0) for psamp should fail (double)");
678   is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
679      "check message");
680   is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail (double)");
681   is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
682      "check message");
683   is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail (double)");
684   is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
685      "check message");
686 }
687
688 {
689   my $im = Imager->new(xsize => 10, ysize => 10, type => "paletted");
690   is(raw_psamp($im, 4), -1, "bad channel list (4) for psamp should fail (paletted)");
691   is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
692      "check message");
693   is(raw_psamp($im, 0), -1, "bad channel list (0) for psamp should fail (paletted)");
694   is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
695      "check message");
696   is(raw_psampf($im, 4), -1, "bad channel list (4) for psampf should fail (paletted)");
697   is(_get_error(), "chan_count 4 out of range, must be >0, <= channels",
698      "check message");
699   is(raw_psampf($im, 0), -1, "bad channel list (0) for psampf should fail (paletted)");
700   is(_get_error(), "chan_count 0 out of range, must be >0, <= channels",
701      "check message");
702   is($im->type, "paletted", "make sure we kept the image type");
703 }
704
705 {
706   my $rgb = Imager->new(xsize => 10, ysize => 10);
707   is(color_model($rgb), 3, "check i_img_color_model() api");
708   is(color_channels($rgb), 3, "check i_img_color_channels() api");
709   is(alpha_channel($rgb), -1, "check i_img_alpha_channel() api");
710 }
711
712 ok(test_mutex(), "call mutex APIs");
713
714 ok(test_slots(), "call slot APIs");
715
716 sub _get_error {
717   my @errors = Imager::i_errors();
718   return join(": ", map $_->[0], @errors);
719 }