]> git.imager.perl.org - imager.git/blobdiff - Imager.xs
check for the uninitialized gif89 bug in 4.2.0 and probe for the 5.0.1 api
[imager.git] / Imager.xs
index b0474ac3fd055004f54a72fd3d8859ca20b7f61a..f2e13576505630a68fd2c18a1f038591a27b3d28 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -29,6 +29,22 @@ extern "C" {
 
 #include "imperl.h"
 
 
 #include "imperl.h"
 
+/* used to represent channel lists parameters */
+typedef struct i_channel_list_tag {
+  int *channels;
+  int count;
+} i_channel_list;
+
+typedef struct {
+  size_t count;
+  const i_sample_t *samples;
+} i_sample_list;
+
+typedef struct {
+  size_t count;
+  const i_fsample_t *samples;
+} i_fsample_list;
+
 /*
 
 Allocate memory that will be discarded when mortals are discarded.
 /*
 
 Allocate memory that will be discarded when mortals are discarded.
@@ -135,6 +151,16 @@ i_log_entry(char *string, int level) {
   mm_log((level, "%s", string));
 }
 
   mm_log((level, "%s", string));
 }
 
+static SV *
+make_i_color_sv(pTHX_ const i_color *c) {
+  SV *sv;
+  i_color *col = mymalloc(sizeof(i_color));
+  *col = *c;
+  sv = sv_newmortal();
+  sv_setref_pv(sv, "Imager::Color", (void *)col);
+
+  return sv;
+}
 
 #define CBDATA_BUFSIZE 8192
 
 
 #define CBDATA_BUFSIZE 8192
 
@@ -155,8 +181,11 @@ call_reader(struct cbdata *cbd, void *buf, size_t size,
   SV *data;
   dSP;
 
   SV *data;
   dSP;
 
-  if (!SvOK(cbd->readcb))
+  if (!SvOK(cbd->readcb)) {
+    mm_log((1, "read callback called but no readcb supplied\n"));
+    i_push_error(0, "read callback called but no readcb supplied");
     return -1;
     return -1;
+  }
 
   ENTER;
   SAVETMPS;
 
   ENTER;
   SAVETMPS;
@@ -204,8 +233,11 @@ io_seeker(void *p, off_t offset, int whence) {
   off_t result;
   dSP;
 
   off_t result;
   dSP;
 
-  if (!SvOK(cbd->seekcb))
+  if (!SvOK(cbd->seekcb)) {
+    mm_log((1, "seek callback called but no seekcb supplied\n"));
+    i_push_error(0, "seek callback called but no seekcb supplied");
     return -1;
     return -1;
+  }
 
   ENTER;
   SAVETMPS;
 
   ENTER;
   SAVETMPS;
@@ -240,8 +272,11 @@ io_writer(void *p, void const *data, size_t size) {
   dSP;
   bool success;
 
   dSP;
   bool success;
 
-  if (!SvOK(cbd->writecb))
+  if (!SvOK(cbd->writecb)) {
+    mm_log((1, "write callback called but no writecb supplied\n"));
+    i_push_error(0, "write callback called but no writecb supplied");
     return -1;
     return -1;
+  }
 
   ENTER;
   SAVETMPS;
 
   ENTER;
   SAVETMPS;
@@ -325,6 +360,27 @@ do_io_new_buffer(pTHX_ SV *data_sv) {
   return io_new_buffer(data, length, my_SvREFCNT_dec, data_sv);
 }
 
   return io_new_buffer(data, length, my_SvREFCNT_dec, data_sv);
 }
 
+static const char *
+describe_sv(SV *sv) {
+  if (SvOK(sv)) {
+    if (SvROK(sv)) {
+      svtype type = SvTYPE(SvRV(sv));
+      switch (type) {
+      case SVt_PVCV: return "CV";
+      case SVt_PVGV: return "GV";
+      case SVt_PVLV: return "LV";
+      default: return "some reference";
+      }
+    }
+    else {
+      return "non-reference scalar";
+    }
+  }
+  else {
+    return "undef";
+  }
+}
+
 static i_io_glue_t *
 do_io_new_cb(pTHX_ SV *writecb, SV *readcb, SV *seekcb, SV *closecb) {
   struct cbdata *cbd;
 static i_io_glue_t *
 do_io_new_cb(pTHX_ SV *writecb, SV *readcb, SV *seekcb, SV *closecb) {
   struct cbdata *cbd;
@@ -335,6 +391,8 @@ do_io_new_cb(pTHX_ SV *writecb, SV *readcb, SV *seekcb, SV *closecb) {
   cbd->seekcb = newSVsv(seekcb);
   cbd->closecb = newSVsv(closecb);
 
   cbd->seekcb = newSVsv(seekcb);
   cbd->closecb = newSVsv(closecb);
 
+  mm_log((1, "do_io_new_cb(writecb %p (%s), readcb %p (%s), seekcb %p (%s), closecb %p (%s))\n", writecb, describe_sv(writecb), readcb, describe_sv(readcb), seekcb, describe_sv(seekcb), closecb, describe_sv(closecb)));
+
   return io_new_cb(cbd, io_reader, io_writer, io_seeker, io_closer, 
                   io_destroyer);
 }
   return io_new_cb(cbd, io_reader, io_writer, io_seeker, io_closer, 
                   io_destroyer);
 }
@@ -368,6 +426,9 @@ static struct value_name make_color_names[] =
   { "mediancut", mc_median_cut, },
   { "mono", mc_mono, },
   { "monochrome", mc_mono, },
   { "mediancut", mc_median_cut, },
   { "mono", mc_mono, },
   { "monochrome", mc_mono, },
+  { "gray", mc_gray, },
+  { "gray4", mc_gray4, },
+  { "gray16", mc_gray16, },
 };
 
 static struct value_name translate_names[] =
 };
 
 static struct value_name translate_names[] =
@@ -774,6 +835,9 @@ static im_pl_ext_funcs im_perl_funcs =
 #define PERL_PL_SET_GLOBAL_CALLBACKS \
   sv_setiv(get_sv(PERL_PL_FUNCTION_TABLE_NAME, 1), PTR2IV(&im_perl_funcs));
 
 #define PERL_PL_SET_GLOBAL_CALLBACKS \
   sv_setiv(get_sv(PERL_PL_FUNCTION_TABLE_NAME, 1), PTR2IV(&im_perl_funcs));
 
+#define IIM_new i_img_8_new
+#define IIM_DESTROY i_img_destroy
+
 #ifdef IMEXIF_ENABLE
 #define i_exif_enabled() 1
 #else
 #ifdef IMEXIF_ENABLE
 #define i_exif_enabled() 1
 #else
@@ -995,6 +1059,14 @@ i_get_image_file_limits()
           PUSHs(sv_2mortal(newSVuv(bytes)));
         }
 
           PUSHs(sv_2mortal(newSVuv(bytes)));
         }
 
+bool
+i_int_check_image_file_limits(width, height, channels, sample_size)
+       i_img_dim width
+       i_img_dim height
+       int channels
+       size_t sample_size
+  PROTOTYPE: DISABLE
+
 MODULE = Imager                PACKAGE = Imager::IO    PREFIX = io_
 
 Imager::IO
 MODULE = Imager                PACKAGE = Imager::IO    PREFIX = io_
 
 Imager::IO
@@ -1337,22 +1409,6 @@ i_list_formats()
                      PUSHs(sv_2mortal(newSVpv(item,0)));
               }
 
                      PUSHs(sv_2mortal(newSVpv(item,0)));
               }
 
-Imager::ImgRaw
-i_img_new()
-
-Imager::ImgRaw
-i_img_empty(im,x,y)
-    Imager::ImgRaw     im
-               i_img_dim     x
-              i_img_dim     y
-
-Imager::ImgRaw
-i_img_empty_ch(im,x,y,ch)
-    Imager::ImgRaw     im
-               i_img_dim     x
-              i_img_dim     y
-              int     ch
-
 Imager::ImgRaw
 i_sametype(im, x, y)
     Imager::ImgRaw im
 Imager::ImgRaw
 i_sametype(im, x, y)
     Imager::ImgRaw im
@@ -1385,14 +1441,6 @@ i_log_entry(string,level)
 int
 i_log_enabled()
 
 int
 i_log_enabled()
 
-void
-i_img_exorcise(im)
-    Imager::ImgRaw     im
-
-void
-i_img_destroy(im)
-    Imager::ImgRaw     im
-
 void
 i_img_info(im)
     Imager::ImgRaw     im
 void
 i_img_info(im)
     Imager::ImgRaw     im
@@ -1987,6 +2035,10 @@ i_convert(src, avmain)
              if (len > inchan)
                inchan = len;
            }
              if (len > inchan)
                inchan = len;
            }
+           else {
+             i_push_errorf(0, "invalid matrix: element %d is not an array ref", j);
+             XSRETURN(0);
+           }
           }
           coeff = mymalloc(sizeof(double) * outchan * inchan);
          for (j = 0; j < outchan; ++j) {
           }
           coeff = mymalloc(sizeof(double) * outchan * inchan);
          for (j = 0; j < outchan; ++j) {
@@ -2411,7 +2463,7 @@ i_get_anonymous_color_histo(im, maxc = 0x40000000)
         XSRETURN(col_cnt);
 
 
         XSRETURN(col_cnt);
 
 
-Imager::ImgRaw
+void
 i_transform(im,opx,opy,parm)
     Imager::ImgRaw     im
              PREINIT:
 i_transform(im,opx,opy,parm)
     Imager::ImgRaw     im
              PREINIT:
@@ -2424,7 +2476,8 @@ i_transform(im,opx,opy,parm)
              AV* av;
              SV* sv1;
              int i;
              AV* av;
              SV* sv1;
              int i;
-             CODE:
+            i_img *result;
+             PPCODE:
              if (!SvROK(ST(1))) croak("Imager: Parameter 1 must be a reference to an array\n");
              if (!SvROK(ST(2))) croak("Imager: Parameter 2 must be a reference to an array\n");
              if (!SvROK(ST(3))) croak("Imager: Parameter 3 must be a reference to an array\n");
              if (!SvROK(ST(1))) croak("Imager: Parameter 1 must be a reference to an array\n");
              if (!SvROK(ST(2))) croak("Imager: Parameter 2 must be a reference to an array\n");
              if (!SvROK(ST(3))) croak("Imager: Parameter 3 must be a reference to an array\n");
@@ -2452,15 +2505,18 @@ i_transform(im,opx,opy,parm)
                sv1=(*(av_fetch(av,i,0)));
                parm[i]=(double)SvNV(sv1);
              }
                sv1=(*(av_fetch(av,i,0)));
                parm[i]=(double)SvNV(sv1);
              }
-             RETVAL=i_transform(im,opx,opxl,opy,opyl,parm,parmlen);
+             result=i_transform(im,opx,opxl,opy,opyl,parm,parmlen);
              myfree(parm);
              myfree(opy);
              myfree(opx);
              myfree(parm);
              myfree(opy);
              myfree(opx);
-             ST(0) = sv_newmortal();
-             if (RETVAL == 0) ST(0)=&PL_sv_undef;
-             else sv_setref_pv(ST(0), "Imager::ImgRaw", (void*)RETVAL);
+            if (result) {
+              SV *result_sv = sv_newmortal();
+              EXTEND(SP, 1);
+              sv_setref_pv(result_sv, "Imager::ImgRaw", (void*)result);
+              PUSHs(result_sv);
+            }
 
 
-Imager::ImgRaw
+void
 i_transform2(sv_width,sv_height,channels,sv_ops,av_n_regs,av_c_regs,av_in_imgs)
        SV *sv_width
        SV *sv_height
 i_transform2(sv_width,sv_height,channels,sv_ops,av_n_regs,av_c_regs,av_in_imgs)
        SV *sv_width
        SV *sv_height
@@ -2484,7 +2540,8 @@ i_transform2(sv_width,sv_height,channels,sv_ops,av_n_regs,av_c_regs,av_in_imgs)
              SV *sv1;
              IV tmp;
             int i;
              SV *sv1;
              IV tmp;
             int i;
-             CODE:
+            i_img *result;
+             PPCODE:
 
              in_imgs_count = av_len(av_in_imgs)+1;
             for (i = 0; i < in_imgs_count; ++i) {
 
              in_imgs_count = av_len(av_in_imgs)+1;
             for (i = 0; i < in_imgs_count; ++i) {
@@ -2539,16 +2596,19 @@ i_transform2(sv_width,sv_height,channels,sv_ops,av_n_regs,av_c_regs,av_in_imgs)
              c_regs = mymalloc(c_regs_count * sizeof(i_color));
              /* I don't bother initializing the colou?r registers */
 
              c_regs = mymalloc(c_regs_count * sizeof(i_color));
              /* I don't bother initializing the colou?r registers */
 
-            RETVAL=i_transform2(width, height, channels, ops, ops_count, 
+            result=i_transform2(width, height, channels, ops, ops_count, 
                                 n_regs, n_regs_count, 
                                 c_regs, c_regs_count, in_imgs, in_imgs_count);
             if (in_imgs)
                 myfree(in_imgs);
              myfree(n_regs);
             myfree(c_regs);
                                 n_regs, n_regs_count, 
                                 c_regs, c_regs_count, in_imgs, in_imgs_count);
             if (in_imgs)
                 myfree(in_imgs);
              myfree(n_regs);
             myfree(c_regs);
-             ST(0) = sv_newmortal();
-             if (RETVAL == 0) ST(0)=&PL_sv_undef;
-             else sv_setref_pv(ST(0), "Imager::ImgRaw", (void*)RETVAL);
+            if (result) {
+              SV *result_sv = sv_newmortal();
+              EXTEND(SP, 1);
+              sv_setref_pv(result_sv, "Imager::ImgRaw", (void*)result);
+              PUSHs(result_sv);
+            }
 
 
 void
 
 
 void
@@ -2956,6 +3016,40 @@ Imager::ImgRaw
 i_img_to_rgb(src)
         Imager::ImgRaw src
 
 i_img_to_rgb(src)
         Imager::ImgRaw src
 
+void
+i_img_make_palette(HV *quant_hv, ...)
+      PREINIT:
+        size_t count = items - 1;
+       i_quantize quant;
+       i_img **imgs = NULL;
+       ssize_t i;
+      PPCODE:
+        if (count <= 0)
+         croak("Please supply at least one image (%d)", (int)count);
+        imgs = mymalloc(sizeof(i_img *) * count);
+       for (i = 0; i < count; ++i) {
+         SV *img_sv = ST(i + 1);
+         if (SvROK(img_sv) && sv_derived_from(img_sv, "Imager::ImgRaw")) {
+           imgs[i] = INT2PTR(i_img *, SvIV((SV*)SvRV(img_sv)));
+         }
+         else {
+           myfree(imgs);
+           croak("Image %d is not an image object", (int)i+1);
+          }
+       }
+        memset(&quant, 0, sizeof(quant));
+       quant.version = 1;
+       quant.mc_size = 256;
+        ip_handle_quant_opts(aTHX_ &quant, quant_hv);
+       i_quant_makemap(&quant, imgs, count);
+       EXTEND(SP, quant.mc_count);
+       for (i = 0; i < quant.mc_count; ++i) {
+         SV *sv_c = make_i_color_sv(aTHX_ quant.mc_colors + i);
+         PUSHs(sv_c);
+       }
+       ip_cleanup_quant_opts(aTHX_ &quant);
+       
+
 void
 i_gpal(im, l, r, y)
         Imager::ImgRaw  im
 void
 i_gpal(im, l, r, y)
         Imager::ImgRaw  im
@@ -3114,11 +3208,7 @@ i_getcolors(im, index, ...)
         colors = mymalloc(sizeof(i_color) * count);
         if (i_getcolors(im, index, colors, count)) {
           for (i = 0; i < count; ++i) {
         colors = mymalloc(sizeof(i_color) * count);
         if (i_getcolors(im, index, colors, count)) {
           for (i = 0; i < count; ++i) {
-            i_color *pv;
-            SV *sv = sv_newmortal();
-            pv = mymalloc(sizeof(i_color));
-            *pv = colors[i];
-            sv_setref_pv(sv, "Imager::Color", (void *)pv);
+            SV *sv = make_i_color_sv(aTHX_ colors+i);
             PUSHs(sv);
           }
         }
             PUSHs(sv);
           }
         }
@@ -3162,27 +3252,19 @@ i_img_virtual(im)
         Imager::ImgRaw  im
 
 void
         Imager::ImgRaw  im
 
 void
-i_gsamp(im, l, r, y, ...)
+i_gsamp(im, l, r, y, channels)
         Imager::ImgRaw im
         i_img_dim l
         i_img_dim r
         i_img_dim y
         Imager::ImgRaw im
         i_img_dim l
         i_img_dim r
         i_img_dim y
+        i_channel_list channels
       PREINIT:
       PREINIT:
-        int *chans;
-        int chan_count;
         i_sample_t *data;
         i_img_dim count, i;
       PPCODE:
         i_sample_t *data;
         i_img_dim count, i;
       PPCODE:
-        if (items < 5)
-          croak("No channel numbers supplied to g_samp()");
         if (l < r) {
         if (l < r) {
-          chan_count = items - 4;
-          chans = mymalloc(sizeof(int) * chan_count);
-          for (i = 0; i < chan_count; ++i)
-            chans[i] = SvIV(ST(i+4));
-          data = mymalloc(sizeof(i_sample_t) * (r-l) * chan_count); /* XXX: memleak? */
-          count = i_gsamp(im, l, r, y, data, chans, chan_count);
-         myfree(chans);
+          data = mymalloc(sizeof(i_sample_t) * (r-l) * channels.count); /* XXX: memleak? */
+          count = i_gsamp(im, l, r, y, data, channels.channels, channels.count);
           if (GIMME_V == G_ARRAY) {
             EXTEND(SP, count);
             for (i = 0; i < count; ++i)
           if (GIMME_V == G_ARRAY) {
             EXTEND(SP, count);
             for (i = 0; i < count; ++i)
@@ -3202,7 +3284,7 @@ i_gsamp(im, l, r, y, ...)
         }
 
 undef_neg_int
         }
 
 undef_neg_int
-i_gsamp_bits(im, l, r, y, bits, target, offset, ...)
+i_gsamp_bits(im, l, r, y, bits, target, offset, channels)
         Imager::ImgRaw im
         i_img_dim l
         i_img_dim r
         Imager::ImgRaw im
         i_img_dim l
         i_img_dim r
@@ -3210,9 +3292,8 @@ i_gsamp_bits(im, l, r, y, bits, target, offset, ...)
        int bits
        AV *target
        STRLEN offset
        int bits
        AV *target
        STRLEN offset
+        i_channel_list channels
       PREINIT:
       PREINIT:
-        int *chans;
-        int chan_count;
         unsigned *data;
         i_img_dim count, i;
       CODE:
         unsigned *data;
         i_img_dim count, i;
       CODE:
@@ -3220,13 +3301,8 @@ i_gsamp_bits(im, l, r, y, bits, target, offset, ...)
         if (items < 8)
           croak("No channel numbers supplied to g_samp()");
         if (l < r) {
         if (items < 8)
           croak("No channel numbers supplied to g_samp()");
         if (l < r) {
-          chan_count = items - 7;
-          chans = mymalloc(sizeof(int) * chan_count);
-          for (i = 0; i < chan_count; ++i)
-            chans[i] = SvIV(ST(i+7));
-          data = mymalloc(sizeof(unsigned) * (r-l) * chan_count);
-          count = i_gsamp_bits(im, l, r, y, data, chans, chan_count, bits);
-         myfree(chans);
+          data = mymalloc(sizeof(unsigned) * (r-l) * channels.count);
+          count = i_gsamp_bits(im, l, r, y, data, channels.channels, channels.count, bits);
          for (i = 0; i < count; ++i) {
            av_store(target, i+offset, newSVuv(data[i]));
          }
          for (i = 0; i < count; ++i) {
            av_store(target, i+offset, newSVuv(data[i]));
          }
@@ -3240,70 +3316,116 @@ i_gsamp_bits(im, l, r, y, bits, target, offset, ...)
        RETVAL
 
 undef_neg_int
        RETVAL
 
 undef_neg_int
-i_psamp_bits(im, l, y, bits, channels_sv, data_av, data_offset = 0, pixel_count = -1)
+i_psamp_bits(im, l, y, bits, channels, data_av, data_offset = 0, pixel_count = -1)
         Imager::ImgRaw im
         i_img_dim l
         i_img_dim y
        int bits
         Imager::ImgRaw im
         i_img_dim l
         i_img_dim y
        int bits
-       SV *channels_sv
+       i_channel_list channels
        AV *data_av
        AV *data_av
-        int data_offset
-        int pixel_count
+        i_img_dim data_offset
+        i_img_dim pixel_count
       PREINIT:
       PREINIT:
-       int chan_count;
-       int *channels;
        STRLEN data_count;
        size_t data_used;
        unsigned *data;
        ptrdiff_t i;
       CODE:
        i_clear_error();
        STRLEN data_count;
        size_t data_used;
        unsigned *data;
        ptrdiff_t i;
       CODE:
        i_clear_error();
-       if (SvOK(channels_sv)) {
-         AV *channels_av;
-         if (!SvROK(channels_sv) || SvTYPE(SvRV(channels_sv)) != SVt_PVAV) {
-           croak("channels is not an array ref");
-         }
-         channels_av = (AV *)SvRV(channels_sv);
-         chan_count = av_len(channels_av) + 1;
-         if (chan_count < 1) {
-           croak("i_psamp_bits: no channels provided");
-         }
-         channels = mymalloc(sizeof(int) * chan_count);
-         for (i = 0; i < chan_count; ++i)
-           channels[i] = SvIV(*av_fetch(channels_av, i, 0));
-        }
-       else {
-         chan_count = im->channels;
-         channels = NULL;
-       }
 
        data_count = av_len(data_av) + 1;
        if (data_offset < 0) {
 
        data_count = av_len(data_av) + 1;
        if (data_offset < 0) {
-         croak("data_offset must by non-negative");
+         croak("data_offset must be non-negative");
        }
        if (data_offset > data_count) {
          croak("data_offset greater than number of samples supplied");
         }
        if (pixel_count == -1 || 
        }
        if (data_offset > data_count) {
          croak("data_offset greater than number of samples supplied");
         }
        if (pixel_count == -1 || 
-           data_offset + pixel_count * chan_count > data_count) {
-         pixel_count = (data_count - data_offset) / chan_count;
+           data_offset + pixel_count * channels.count > data_count) {
+         pixel_count = (data_count - data_offset) / channels.count;
        }
 
        }
 
-       data_used = pixel_count * chan_count;
+       data_used = pixel_count * channels.count;
        data = mymalloc(sizeof(unsigned) * data_count);
        for (i = 0; i < data_used; ++i)
          data[i] = SvUV(*av_fetch(data_av, data_offset + i, 0));
 
        data = mymalloc(sizeof(unsigned) * data_count);
        for (i = 0; i < data_used; ++i)
          data[i] = SvUV(*av_fetch(data_av, data_offset + i, 0));
 
-       RETVAL = i_psamp_bits(im, l, l + pixel_count, y, data, channels, 
-                             chan_count, bits);
+       RETVAL = i_psamp_bits(im, l, l + pixel_count, y, data, channels.channels
+                             channels.count, bits);
 
        if (data)
          myfree(data);
 
        if (data)
          myfree(data);
-       if (channels)
-         myfree(channels);
       OUTPUT:
        RETVAL
 
       OUTPUT:
        RETVAL
 
+undef_neg_int
+i_psamp(im, x, y, channels, data, offset = 0, width = -1)
+       Imager::ImgRaw im
+       i_img_dim x
+       i_img_dim y
+       i_channel_list channels
+        i_sample_list data
+       i_img_dim offset
+       i_img_dim width
+    PREINIT:
+       i_img_dim r;
+    CODE:
+       i_clear_error();
+       if (offset < 0) {
+         i_push_error(0, "offset must be non-negative");
+         XSRETURN_UNDEF;
+       }
+       if (offset > 0) {
+         if (offset > data.count) {
+           i_push_error(0, "offset greater than number of samples supplied");
+           XSRETURN_UNDEF;
+         }
+         data.samples += offset;
+         data.count -= offset;
+       }
+       if (width == -1 ||
+           width * channels.count > data.count) {
+         width = data.count / channels.count;
+        }
+       r = x + width;
+       RETVAL = i_psamp(im, x, r, y, data.samples, channels.channels, channels.count);
+    OUTPUT:
+       RETVAL
+
+undef_neg_int
+i_psampf(im, x, y, channels, data, offset = 0, width = -1)
+       Imager::ImgRaw im
+       i_img_dim x
+       i_img_dim y
+       i_channel_list channels
+        i_fsample_list data
+       i_img_dim offset
+       i_img_dim width
+    PREINIT:
+       i_img_dim r;
+    CODE:
+       i_clear_error();
+       if (offset < 0) {
+         i_push_error(0, "offset must be non-negative");
+         XSRETURN_UNDEF;
+       }
+       if (offset > 0) {
+         if (offset > data.count) {
+           i_push_error(0, "offset greater than number of samples supplied");
+           XSRETURN_UNDEF;
+         }
+         data.samples += offset;
+         data.count -= offset;
+       }
+       if (width == -1 ||
+           width * channels.count > data.count) {
+         width = data.count / channels.count;
+        }
+       r = x + width;
+       RETVAL = i_psampf(im, x, r, y, data.samples, channels.channels, channels.count);
+    OUTPUT:
+       RETVAL
+
 Imager::ImgRaw
 i_img_masked_new(targ, mask, x, y, w, h)
         Imager::ImgRaw targ
 Imager::ImgRaw
 i_img_masked_new(targ, mask, x, y, w, h)
         Imager::ImgRaw targ
@@ -3379,27 +3501,19 @@ i_ppixf(im, x, y, cl)
         Imager::Color::Float cl
 
 void
         Imager::Color::Float cl
 
 void
-i_gsampf(im, l, r, y, ...)
+i_gsampf(im, l, r, y, channels)
         Imager::ImgRaw im
         i_img_dim l
         i_img_dim r
         i_img_dim y
         Imager::ImgRaw im
         i_img_dim l
         i_img_dim r
         i_img_dim y
+       i_channel_list channels
       PREINIT:
       PREINIT:
-        int *chans;
-        int chan_count;
         i_fsample_t *data;
         i_img_dim count, i;
       PPCODE:
         i_fsample_t *data;
         i_img_dim count, i;
       PPCODE:
-        if (items < 5)
-          croak("No channel numbers supplied to g_sampf()");
         if (l < r) {
         if (l < r) {
-          chan_count = items - 4;
-          chans = mymalloc(sizeof(int) * chan_count);
-          for (i = 0; i < chan_count; ++i)
-            chans[i] = SvIV(ST(i+4));
-          data = mymalloc(sizeof(i_fsample_t) * (r-l) * chan_count);
-          count = i_gsampf(im, l, r, y, data, chans, chan_count);
-          myfree(chans);
+          data = mymalloc(sizeof(i_fsample_t) * (r-l) * channels.count);
+          count = i_gsampf(im, l, r, y, data, channels.channels, channels.count);
           if (GIMME_V == G_ARRAY) {
             EXTEND(SP, count);
             for (i = 0; i < count; ++i)
           if (GIMME_V == G_ARRAY) {
             EXTEND(SP, count);
             for (i = 0; i < count; ++i)
@@ -3500,11 +3614,7 @@ i_glin(im, l, r, y)
          if (GIMME_V == G_ARRAY) {
             EXTEND(SP, count);
             for (i = 0; i < count; ++i) {
          if (GIMME_V == G_ARRAY) {
             EXTEND(SP, count);
             for (i = 0; i < count; ++i) {
-              SV *sv;
-              i_color *col = mymalloc(sizeof(i_color));
-              *col = vals[i];
-              sv = sv_newmortal();
-              sv_setref_pv(sv, "Imager::Color", (void *)col);
+              SV *sv = make_i_color_sv(aTHX_ vals+i);
               PUSHs(sv);
             }
           }
               PUSHs(sv);
             }
           }
@@ -3551,6 +3661,12 @@ i_glinf(im, l, r, y)
           myfree(vals);
         }
 
           myfree(vals);
         }
 
+Imager::ImgRaw
+i_img_8_new(x, y, ch)
+        i_img_dim x
+        i_img_dim y
+        int ch
+
 Imager::ImgRaw
 i_img_16_new(x, y, ch)
         i_img_dim x
 Imager::ImgRaw
 i_img_16_new(x, y, ch)
         i_img_dim x