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