]> git.imager.perl.org - imager.git/blobdiff - Imager.xs
access to poly_poly from perl as polypolygon()
[imager.git] / Imager.xs
index 837d02dfcb3ccecf34faaa6d87455af755882fbf..5fe6b4dceab387b5075172a740f7a39c1620fdcd 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -7,6 +7,7 @@ extern "C" {
 #include "XSUB.h"
 #define NEED_newRV_noinc
 #define NEED_sv_2pv_nolen
+#define NEED_sv_2pvbyte
 #include "ppport.h"
 #ifdef __cplusplus
 }
@@ -19,6 +20,9 @@ extern "C" {
 #include "dynaload.h"
 #include "regmach.h"
 #include "imextdef.h"
+#include "imextpltypes.h"
+#include "imperlio.h"
+#include <float.h>
 
 #if i_int_hlines_testing()
 #include "imageri.h"
@@ -26,13 +30,142 @@ extern "C" {
 
 #include "imperl.h"
 
+#define ARRAY_COUNT(array) (sizeof(array)/sizeof(*array))
+
+/*
+
+Context object management
+
+*/
+
+typedef im_context_t Imager__Context;
+
+#define im_context_DESTROY(ctx) im_context_refdec((ctx), "DESTROY")
+
+#ifdef PERL_IMPLICIT_CONTEXT
+
+#define MY_CXT_KEY "Imager::_context" XS_VERSION
+
+typedef struct {
+  im_context_t ctx;
+} my_cxt_t;
+
+START_MY_CXT
+
+im_context_t fallback_context;
+
+static void
+start_context(pTHX) {
+  dMY_CXT;
+  MY_CXT.ctx = im_context_new();
+  sv_setref_pv(get_sv("Imager::_context", GV_ADD), "Imager::Context", MY_CXT.ctx);
+
+  /* Ideally we'd free this reference, but the error message memory
+     was never released on exit, so the associated memory here is reasonable
+     to keep.
+     With logging enabled we always need at least one context, since
+     objects may be released fairly late and attempt to get the log file.
+  */
+  im_context_refinc(MY_CXT.ctx, "start_context");
+  fallback_context = MY_CXT.ctx;
+}
+
+static im_context_t
+perl_get_context(void) {
+  dTHX;
+  dMY_CXT;
+  
+  return MY_CXT.ctx ? MY_CXT.ctx : fallback_context;
+}
+
+#else
+
+static im_context_t perl_context;
+
+static void
+start_context(pTHX) {
+  perl_context = im_context_new();
+  im_context_refinc(perl_context, "start_context");
+}
+
+static im_context_t
+perl_get_context(void) {
+  return perl_context;
+}
+
+#endif
+
+/* 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;
+
+typedef struct {
+  size_t count;
+  const i_polygon_t *polygons;
+} i_polygon_list;
+
+/*
+
+Allocate memory that will be discarded when mortals are discarded.
+
+*/
+
+static void *
+malloc_temp(pTHX_ size_t size) {
+  SV *sv = sv_2mortal(newSV(size));
+
+  return SvPVX(sv);
+}
+
+static void *
+calloc_temp(pTHX_ size_t size) {
+  void *result = malloc_temp(aTHX_ size);
+  memset(result, 0, size);
+
+  return result;
+}
+
+/* for use with the T_AVARRAY typemap */
+#define doublePtr(size) ((double *)calloc_temp(aTHX_ sizeof(double) * (size)))
+#define SvDouble(sv, pname) (SvNV(sv))
+
+#define intPtr(size) ((int *)calloc_temp(aTHX_ sizeof(int) * (size)))
+#define SvInt(sv, pname) (SvIV(sv))
+
+#define i_img_dimPtr(size) ((i_img_dim *)calloc_temp(aTHX_ sizeof(i_img_dim) * (size)))
+#define SvI_img_dim(sv, pname) (SvIV(sv))
+
+#define i_colorPtr(size) ((i_color *)calloc_temp(aTHX_ sizeof(i_color *) * (size)))
+
+#define SvI_color(sv, pname) S_sv_to_i_color(aTHX_ sv, pname)
+
+static i_color
+S_sv_to_i_color(pTHX_ SV *sv, const char *pname) {
+  if (!sv_derived_from(sv, "Imager::Color")) {
+    croak("%s: not a color object", pname);
+  }
+  return *INT2PTR(i_color *, SvIV((SV *)SvRV(sv)));
+}
+
 /* These functions are all shared - then comes platform dependant code */
 static int getstr(void *hv_t,char *key,char **store) {
   dTHX;
   SV** svpp;
   HV* hv=(HV*)hv_t;
 
-  mm_log((1,"getstr(hv_t 0x%X, key %s, store 0x%X)\n",hv_t,key,store));
+  mm_log((1,"getstr(hv_t %p, key %s, store %p)\n",hv_t,key,store));
 
   if ( !hv_exists(hv,key,strlen(key)) ) return 0;
 
@@ -47,7 +180,7 @@ static int getint(void *hv_t,char *key,int *store) {
   SV** svpp;
   HV* hv=(HV*)hv_t;  
 
-  mm_log((1,"getint(hv_t 0x%X, key %s, store 0x%X)\n",hv_t,key,store));
+  mm_log((1,"getint(hv_t %p, key %s, store %p)\n",hv_t,key,store));
 
   if ( !hv_exists(hv,key,strlen(key)) ) return 0;
 
@@ -61,11 +194,11 @@ static int getdouble(void *hv_t,char* key,double *store) {
   SV** svpp;
   HV* hv=(HV*)hv_t;
 
-  mm_log((1,"getdouble(hv_t 0x%X, key %s, store 0x%X)\n",hv_t,key,store));
+  mm_log((1,"getdouble(hv_t %p, key %s, store %p)\n",hv_t,key,store));
 
   if ( !hv_exists(hv,key,strlen(key)) ) return 0;
   svpp=hv_fetch(hv, key, strlen(key), 0);
-  *store=(float)SvNV(*svpp);
+  *store=(double)SvNV(*svpp);
   return 1;
 }
 
@@ -74,7 +207,7 @@ static int getvoid(void *hv_t,char* key,void **store) {
   SV** svpp;
   HV* hv=(HV*)hv_t;
 
-  mm_log((1,"getvoid(hv_t 0x%X, key %s, store 0x%X)\n",hv_t,key,store));
+  mm_log((1,"getvoid(hv_t %p, key %s, store %p)\n",hv_t,key,store));
 
   if ( !hv_exists(hv,key,strlen(key)) ) return 0;
 
@@ -89,7 +222,7 @@ static int getobj(void *hv_t,char *key,char *type,void **store) {
   SV** svpp;
   HV* hv=(HV*)hv_t;
 
-  mm_log((1,"getobj(hv_t 0x%X, key %s,type %s, store 0x%X)\n",hv_t,key,type,store));
+  mm_log((1,"getobj(hv_t %p, key %s,type %s, store %p)\n",hv_t,key,type,store));
 
   if ( !hv_exists(hv,key,strlen(key)) ) return 0;
 
@@ -108,108 +241,26 @@ static int getobj(void *hv_t,char *key,char *type,void **store) {
 
 UTIL_table_t i_UTIL_table={getstr,getint,getdouble,getvoid,getobj};
 
-void my_SvREFCNT_dec(void *p) {
-  dTHX;
-  SvREFCNT_dec((SV*)p);
+static void
+free_buffer(void *p) {
+  myfree(p);
 }
 
 
 static void
 i_log_entry(char *string, int level) {
-  mm_log((level, string));
-}
-
-
-typedef struct i_reader_data_tag
-{
-  /* presumably a CODE ref or name of a sub */
-  SV *sv;
-} i_reader_data;
-
-/* used by functions that want callbacks */
-static int read_callback(char *userdata, char *buffer, int need, int want) {
-  dTHX;
-  i_reader_data *rd = (i_reader_data *)userdata;
-  int count;
-  int result;
-  SV *data;
-  dSP; dTARG = sv_newmortal();
-  /* thanks to Simon Cozens for help with the dTARG above */
-
-  ENTER;
-  SAVETMPS;
-  EXTEND(SP, 2);
-  PUSHMARK(SP);
-  PUSHi(want);
-  PUSHi(need);
-  PUTBACK;
-
-  count = perl_call_sv(rd->sv, G_SCALAR);
-
-  SPAGAIN;
-
-  if (count != 1)
-    croak("Result of perl_call_sv(..., G_SCALAR) != 1");
-
-  data = POPs;
-
-  if (SvOK(data)) {
-    STRLEN len;
-    char *ptr = SvPV(data, len);
-    if (len > want)
-      croak("Too much data returned in reader callback");
-    
-    memcpy(buffer, ptr, len);
-    result = len;
-  }
-  else {
-    result = -1;
-  }
-
-  PUTBACK;
-  FREETMPS;
-  LEAVE;
-
-  return result;
+  mm_log((level, "%s", string));
 }
 
-typedef struct
-{
-  SV *sv; /* a coderef or sub name */
-} i_writer_data;
-
-/* used by functions that want callbacks */
-static int write_callback(char *userdata, char const *data, int size) {
-  dTHX;
-  i_writer_data *wd = (i_writer_data *)userdata;
-  int count;
-  int success;
+static SV *
+make_i_color_sv(pTHX_ const i_color *c) {
   SV *sv;
-  dSP; 
-
-  ENTER;
-  SAVETMPS;
-  EXTEND(SP, 1);
-  PUSHMARK(SP);
-  XPUSHs(sv_2mortal(newSVpv((char *)data, size)));
-  PUTBACK;
-
-  count = perl_call_sv(wd->sv, G_SCALAR);
-
-  SPAGAIN;
-
-  if (count != 1)
-    croak("Result of perl_call_sv(..., G_SCALAR) != 1");
+  i_color *col = mymalloc(sizeof(i_color));
+  *col = *c;
+  sv = sv_newmortal();
+  sv_setref_pv(sv, "Imager::Color", (void *)col);
 
-  sv = POPs;
-  success = SvTRUE(sv);
-
-
-  PUTBACK;
-  FREETMPS;
-  LEAVE;
-
-  return success;
+  return sv;
 }
 
 #define CBDATA_BUFSIZE 8192
@@ -220,81 +271,22 @@ struct cbdata {
   SV *readcb;
   SV *seekcb;
   SV *closecb;
-
-  /* we need to remember whether the buffer contains write data or 
-     read data
-   */
-  int reading;
-  int writing;
-
-  /* how far we've read into the buffer (not used for writing) */
-  int where;
-
-  /* the amount of space used/data available in the buffer */
-  int used;
-
-  /* the maximum amount to fill the buffer before flushing
-     If any write is larger than this then the buffer is flushed and 
-     the full write is performed.  The write is _not_ split into 
-     maxwrite sized calls
-   */
-  int maxlength;
-
-  char buffer[CBDATA_BUFSIZE];
 };
 
-/* 
-
-call_writer(cbd, buf, size)
-
-Low-level function to call the perl writer callback.
-
-*/
-
-static ssize_t call_writer(struct cbdata *cbd, void const *buf, size_t size) {
-  dTHX;
-  int count;
-  int success;
-  SV *sv;
-  dSP;
-
-  if (!SvOK(cbd->writecb))
-    return -1;
-
-  ENTER;
-  SAVETMPS;
-  EXTEND(SP, 1);
-  PUSHMARK(SP);
-  PUSHs(sv_2mortal(newSVpv((char *)buf, size)));
-  PUTBACK;
-
-  count = perl_call_sv(cbd->writecb, G_SCALAR);
-
-  SPAGAIN;
-  if (count != 1)
-    croak("Result of perl_call_sv(..., G_SCALAR) != 1");
-
-  sv = POPs;
-  success = SvTRUE(sv);
-
-
-  PUTBACK;
-  FREETMPS;
-  LEAVE;
-
-  return success ? size : -1;
-}
-
-static ssize_t call_reader(struct cbdata *cbd, void *buf, size_t size, 
-                           size_t maxread) {
+static ssize_t
+call_reader(struct cbdata *cbd, void *buf, size_t size, 
+            size_t maxread) {
   dTHX;
   int count;
   int result;
   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;
+  }
 
   ENTER;
   SAVETMPS;
@@ -315,9 +307,10 @@ static ssize_t call_reader(struct cbdata *cbd, void *buf, size_t size,
 
   if (SvOK(data)) {
     STRLEN len;
-    char *ptr = SvPV(data, len);
+    char *ptr = SvPVbyte(data, len);
     if (len > maxread)
-      croak("Too much data returned in reader callback");
+      croak("Too much data returned in reader callback (wanted %d, got %d, expected %d)",
+      (int)size, (int)len, (int)maxread);
     
     memcpy(buf, ptr, len);
     result = len;
@@ -333,41 +326,20 @@ static ssize_t call_reader(struct cbdata *cbd, void *buf, size_t size,
   return result;
 }
 
-static ssize_t write_flush(struct cbdata *cbd) {
-  dTHX;
-  ssize_t result;
-
-  if (cbd->used) {
-    result = call_writer(cbd, cbd->buffer, cbd->used);
-    cbd->used = 0;
-    return result;
-  }
-  else {
-    return 1; /* success of some sort */
-  }
-}
-
-static off_t io_seeker(void *p, off_t offset, int whence) {
+static off_t
+io_seeker(void *p, off_t offset, int whence) {
   dTHX;
   struct cbdata *cbd = p;
   int count;
   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;
-
-  if (cbd->writing) {
-    if (cbd->used && write_flush(cbd) <= 0)
-      return -1;
-    cbd->writing = 0;
-  }
-  if (whence == SEEK_CUR && cbd->reading && cbd->where != cbd->used) {
-    offset -= cbd->where - cbd->used;
   }
-  cbd->reading = 0;
-  cbd->where = cbd->used = 0;
-  
+
   ENTER;
   SAVETMPS;
   EXTEND(SP, 2);
@@ -392,126 +364,83 @@ static off_t io_seeker(void *p, off_t offset, int whence) {
   return result;
 }
 
-static ssize_t io_writer(void *p, void const *data, size_t size) {
+static ssize_t
+io_writer(void *p, void const *data, size_t size) {
   dTHX;
   struct cbdata *cbd = p;
+  I32 count;
+  SV *sv;
+  dSP;
+  bool success;
 
-  /* printf("io_writer(%p, %p, %u)\n", p, data, size); */
-  if (!cbd->writing) {
-    if (cbd->reading && cbd->where < cbd->used) {
-      /* we read past the place where the caller expected us to be
-         so adjust our position a bit */
-      if (io_seeker(p, cbd->where - cbd->used, SEEK_CUR) < 0) {
-        return -1;
-      }
-      cbd->reading = 0;
-    }
-    cbd->where = cbd->used = 0;
-  }
-  cbd->writing = 1;
-  if (cbd->used && cbd->used + size > cbd->maxlength) {
-    int write_res = write_flush(cbd);
-    if (write_res <= 0) {
-      return write_res;
-    }
-    cbd->used = 0;
-  }
-  if (cbd->used+size <= cbd->maxlength) {
-    memcpy(cbd->buffer + cbd->used, data, size);
-    cbd->used += size;
-    return size;
+  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;
   }
-  /* it doesn't fit - just pass it up */
-  return call_writer(cbd, data, size);
+
+  ENTER;
+  SAVETMPS;
+  EXTEND(SP, 1);
+  PUSHMARK(SP);
+  PUSHs(sv_2mortal(newSVpv((char *)data, size)));
+  PUTBACK;
+
+  count = perl_call_sv(cbd->writecb, G_SCALAR);
+
+  SPAGAIN;
+  if (count != 1)
+    croak("Result of perl_call_sv(..., G_SCALAR) != 1");
+
+  sv = POPs;
+  success = SvTRUE(sv);
+
+
+  PUTBACK;
+  FREETMPS;
+  LEAVE;
+
+  return success ? size : -1;
 }
 
 static ssize_t 
 io_reader(void *p, void *data, size_t size) {
-  dTHX;
   struct cbdata *cbd = p;
-  ssize_t total;
-  char *out = data; /* so we can do pointer arithmetic */
-
-  /* printf("io_reader(%p, %p, %d)\n", p, data, size); */
-  if (cbd->writing) {
-    if (write_flush(cbd) <= 0)
-      return 0;
-    cbd->writing = 0;
-  }
-
-  cbd->reading = 1;
-  if (size <= cbd->used - cbd->where) {
-    /* simplest case */
-    memcpy(data, cbd->buffer+cbd->where, size);
-    cbd->where += size;
-    return size;
-  }
-  total = 0;
-  memcpy(out, cbd->buffer + cbd->where, cbd->used - cbd->where);
-  total += cbd->used - cbd->where;
-  size  -= cbd->used - cbd->where;
-  out   += cbd->used - cbd->where;
-  if (size < sizeof(cbd->buffer)) {
-    int did_read = 0;
-    int copy_size;
-    while (size
-          && (did_read = call_reader(cbd, cbd->buffer, size, 
-                                   sizeof(cbd->buffer))) > 0) {
-      cbd->where = 0;
-      cbd->used  = did_read;
-
-      copy_size = i_min(size, cbd->used);
-      memcpy(out, cbd->buffer, copy_size);
-      cbd->where += copy_size;
-      out   += copy_size;
-      total += copy_size;
-      size  -= copy_size;
-    }
-    if (did_read < 0)
-      return -1;
-  }
-  else {
-    /* just read the rest - too big for our buffer*/
-    int did_read;
-    while ((did_read = call_reader(cbd, out, size, size)) > 0) {
-      size  -= did_read;
-      total += did_read;
-      out   += did_read;
-    }
-    if (did_read < 0)
-      return -1;
-  }
 
-  return total;
+  return call_reader(cbd, data, size, size);
 }
 
 static int io_closer(void *p) {
   dTHX;
   struct cbdata *cbd = p;
-
-  if (cbd->writing && cbd->used > 0) {
-    if (write_flush(cbd) < 0)
-      return -1;
-    cbd->writing = 0;
-  }
+  int success = 1;
 
   if (SvOK(cbd->closecb)) {
     dSP;
+    I32 count;
 
     ENTER;
     SAVETMPS;
     PUSHMARK(SP);
     PUTBACK;
 
-    perl_call_sv(cbd->closecb, G_VOID);
+    count = perl_call_sv(cbd->closecb, G_SCALAR);
 
     SPAGAIN;
+    
+    if (count) {
+      SV *sv = POPs;
+      success = SvTRUE(sv);
+    }
+    else
+      success = 0;
+
     PUTBACK;
     FREETMPS;
     LEAVE;
   }
 
-  return 0;
+  return success ? 0 : -1;
 }
 
 static void io_destroyer(void *p) {
@@ -525,11 +454,99 @@ static void io_destroyer(void *p) {
   myfree(cbd);
 }
 
+static bool
+im_SvREFSCALAR(SV *sv) {
+  svtype type = SvTYPE(sv);
+
+  switch (type) {
+  case SVt_PV:
+  case SVt_PVIV:
+  case SVt_PVNV:
+  case SVt_PVMG:
+  case SVt_IV:
+  case SVt_NV:
+  case SVt_PVLV:
+#if PERL_VERSION > 10
+  case SVt_REGEXP:
+#endif
+    return 1;
+
+  default:
+    return 0;
+  }
+}
+
+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_buffer(pTHX_ SV *data_sv) {
+  const char *data;
+  char *data_copy;
+  STRLEN length;
+  SV *sv;
+
+  SvGETMAGIC(data_sv);
+  if (SvROK(data_sv)) {
+    if (im_SvREFSCALAR(SvRV(data_sv))) {
+      sv = SvRV(data_sv);
+    }
+    else {
+      i_push_errorf(0, "data is not a scalar or a reference to scalar");
+      return NULL;
+    }
+  }
+  else {
+    sv = data_sv;
+  }
+
+  /* previously this would keep the SV around, but this is unsafe in
+     many ways, so always copy the bytes */
+  data = SvPVbyte(sv, length);
+  data_copy = mymalloc(length);
+  memcpy(data_copy, data, length);
+  return io_new_buffer(data_copy, length, free_buffer, data_copy);
+}
+
+static i_io_glue_t *
+do_io_new_cb(pTHX_ SV *writecb, SV *readcb, SV *seekcb, SV *closecb) {
+  struct cbdata *cbd;
+
+  cbd = mymalloc(sizeof(struct cbdata));
+  cbd->writecb = newSVsv(writecb);
+  cbd->readcb = newSVsv(readcb);
+  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);
+}
+
 struct value_name {
   char *name;
   int value;
 };
-static int lookup_name(struct value_name *names, int count, char *name, int def_value)
+static int lookup_name(const struct value_name *names, int count, char *name, int def_value)
 {
   int i;
   for (i = 0; i < count; ++i)
@@ -554,13 +571,14 @@ static struct value_name make_color_names[] =
   { "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[] =
 {
-#ifdef HAVE_LIBGIF
   { "giflib", pt_giflib, },
-#endif
   { "closest", pt_closest, },
   { "perturb", pt_perturb, },
   { "errdiff", pt_errdiff, },
@@ -590,7 +608,8 @@ static struct value_name orddith_names[] =
 };
 
 /* look through the hash for quantization options */
-static void handle_quant_opts(pTHX_ i_quantize *quant, HV *hv)
+static void
+ip_handle_quant_opts(pTHX_ i_quantize *quant, HV *hv)
 {
   /*** POSSIBLY BROKEN: do I need to unref the SV from hv_fetch ***/
   SV **sv;
@@ -641,11 +660,11 @@ static void handle_quant_opts(pTHX_ i_quantize *quant, HV *hv)
       }
     }
   }
-  quant->make_colors = mc_addi;
+  quant->make_colors = mc_median_cut;
   sv = hv_fetch(hv, "make_colors", 11, 0);
   if (sv && *sv && (str = SvPV(*sv, len))) {
     quant->make_colors = 
-      lookup_name(make_color_names, sizeof(make_color_names)/sizeof(*make_color_names), str, mc_addi);
+      lookup_name(make_color_names, sizeof(make_color_names)/sizeof(*make_color_names), str, mc_median_cut);
   }
   sv = hv_fetch(hv, "colors", 6, 0);
   if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
@@ -721,14 +740,16 @@ static void handle_quant_opts(pTHX_ i_quantize *quant, HV *hv)
     quant->perturb = SvIV(*sv);
 }
 
-static void cleanup_quant_opts(i_quantize *quant) {
+static void
+ip_cleanup_quant_opts(pTHX_ i_quantize *quant) {
   myfree(quant->mc_colors);
   if (quant->ed_map)
     myfree(quant->ed_map);
 }
 
 /* copies the color map from the hv into the colors member of the HV */
-static void copy_colors_back(pTHX_ HV *hv, i_quantize *quant) {
+static void
+ip_copy_colors_back(pTHX_ HV *hv, i_quantize *quant) {
   SV **sv;
   AV *av;
   int i;
@@ -753,13 +774,102 @@ static void copy_colors_back(pTHX_ HV *hv, i_quantize *quant) {
   }
 }
 
-/* loads the segments of a fountain fill into an array */
-static i_fountain_seg *
-load_fount_segs(pTHX_ AV *asegs, int *count) {
-  /* Each element of segs must contain:
-     [ start, middle, end, c0, c1, segtype, colortrans ]
-     start, middle, end are doubles from 0 to 1
-     c0, c1 are Imager::Color::Float or Imager::Color objects
+static struct value_name
+poly_fill_mode_names[] =
+{
+  { "evenodd", i_pfm_evenodd },
+  { "nonzero", i_pfm_nonzero }
+};
+
+static i_poly_fill_mode_t
+S_get_poly_fill_mode(pTHX_ SV *sv) {   
+  if (looks_like_number(sv)) {
+    IV work = SvIV(sv);
+    if (work < (IV)i_pfm_evenodd || work > (IV)i_pfm_nonzero)
+      work = (IV)i_pfm_evenodd;
+    return (i_poly_fill_mode_t)work;
+  }
+  else {
+    return (i_poly_fill_mode_t)lookup_name
+    (poly_fill_mode_names, ARRAY_COUNT(poly_fill_mode_names),
+     SvPV_nolen(sv), i_pfm_evenodd);
+  }
+}
+
+static void
+S_get_polygon_list(pTHX_ i_polygon_list *polys, SV *sv) {
+  AV *av;
+  int i;
+  i_polygon_t *s;
+
+  SvGETMAGIC(sv);
+  if (!SvOK(sv) || !SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV) 
+    croak("polys must be an arrayref");
+
+  av = (AV*)SvRV(sv);
+  polys->count = av_len(av) + 1;
+  if (polys->count < 1)
+    croak("polypolygon: no polygons provided");
+  s = malloc_temp(aTHX_ sizeof(i_polygon_t) * polys->count);
+  for (i = 0; i < polys->count; ++i) {
+    SV **poly_sv = av_fetch(av, i, 0);
+    AV *poly_av;
+    SV **x_sv, **y_sv;
+    AV *x_av, *y_av;
+    double *x_data, *y_data;
+    ssize_t j;
+    ssize_t point_count;
+
+    if (!poly_sv)
+      croak("poly_polygon: nothing found for polygon %d", i);
+    /* needs to be another av */
+    SvGETMAGIC(*poly_sv);
+    if (!SvOK(*poly_sv) || !SvROK(*poly_sv) || SvTYPE(SvRV(*poly_sv)) != SVt_PVAV)
+      croak("poly_polygon: polygon %d isn't an arrayref", i);
+    poly_av = (AV*)SvRV(*poly_sv);
+    /* with two elements */
+    if (av_len(poly_av) != 1)
+      croak("poly_polygon: polygon %d should contain two arrays", i);
+    x_sv = av_fetch(poly_av, 0, 0);
+    y_sv = av_fetch(poly_av, 1, 0);
+    if (!x_sv)
+      croak("poly_polygon: polygon %d has no x elements", i);
+    if (!y_sv)
+      croak("poly_polygon: polygon %d has no y elements", i);
+    SvGETMAGIC(*x_sv);
+    SvGETMAGIC(*y_sv);
+    if (!SvOK(*x_sv) || !SvROK(*x_sv) || SvTYPE(SvRV(*x_sv)) != SVt_PVAV)
+      croak("poly_polygon: polygon %d x elements isn't an array", i);
+    if (!SvOK(*y_sv) || !SvROK(*y_sv) || SvTYPE(SvRV(*y_sv)) != SVt_PVAV)
+      croak("poly_polygon: polygon %d y elements isn't an array", i);
+    x_av = (AV*)SvRV(*x_sv);
+    y_av = (AV*)SvRV(*y_sv);
+    if (av_len(x_av) != av_len(y_av))
+      croak("poly_polygon: polygon %d x and y arrays different lengths", i+1);
+    point_count = av_len(x_av)+1;
+    x_data = malloc_temp(aTHX_ sizeof(double) * point_count * 2);
+    y_data = x_data + point_count;
+
+    for (j = 0; j < point_count; ++j) {
+      SV **x_item_sv = av_fetch(x_av, j, 0);
+      SV **y_item_sv = av_fetch(y_av, j, 0);
+      x_data[j] = x_item_sv ? SvNV(*x_item_sv) : 0;
+      y_data[j] = y_item_sv ? SvNV(*y_item_sv) : 0;
+    }
+    s[i].x = x_data;
+    s[i].y = y_data;
+    s[i].count = point_count;
+  }
+  polys->polygons = s;
+}
+
+/* loads the segments of a fountain fill into an array */
+static i_fountain_seg *
+load_fount_segs(pTHX_ AV *asegs, int *count) {
+  /* Each element of segs must contain:
+     [ start, middle, end, c0, c1, segtype, colortrans ]
+     start, middle, end are doubles from 0 to 1
+     c0, c1 are Imager::Color::Float or Imager::Color objects
      segtype, colortrans are ints
   */
   int i, j;
@@ -851,7 +961,6 @@ validate_i_ppal(i_img *im, i_palidx const *indexes, int count) {
   }
 }
 
-
 /* I don't think ICLF_* names belong at the C interface
    this makes the XS code think we have them, to let us avoid 
    putting function bodies in the XS code
@@ -859,17 +968,18 @@ validate_i_ppal(i_img *im, i_palidx const *indexes, int count) {
 #define ICLF_new_internal(r, g, b, a) i_fcolor_new((r), (g), (b), (a))
 #define ICLF_DESTROY(cl) i_fcolor_destroy(cl)
 
-
-/* the m_init_log() function was called init_log(), renamed to reduce
-    potential naming conflicts */
-#define init_log m_init_log
+#ifdef IMAGER_LOG
+#define i_log_enabled() 1
+#else
+#define i_log_enabled() 0
+#endif
 
 #if i_int_hlines_testing()
 
 typedef i_int_hlines *Imager__Internal__Hlines;
 
 static i_int_hlines *
-i_int_hlines_new(int start_y, int count_y, int start_x, int count_x) {
+i_int_hlines_new(i_img_dim start_y, i_img_dim count_y, i_img_dim start_x, i_img_dim count_x) {
   i_int_hlines *result = mymalloc(sizeof(i_int_hlines));
   i_int_init_hlines(result, start_y, count_y, start_x, count_x);
 
@@ -902,9 +1012,9 @@ static int seg_compare(const void *vleft, const void *vright) {
 static SV *
 i_int_hlines_dump(i_int_hlines *hlines) {
   dTHX;
-  SV *dump = newSVpvf("start_y: %d limit_y: %d start_x: %d limit_x: %d\n",
-       hlines->start_y, hlines->limit_y, hlines->start_x, hlines->limit_x);
-  int y;
+  SV *dump = newSVpvf("start_y: %" i_DF " limit_y: %" i_DF " start_x: %" i_DF " limit_x: %" i_DF"\n",
+       i_DFc(hlines->start_y), i_DFc(hlines->limit_y), i_DFc(hlines->start_x), i_DFc(hlines->limit_x));
+  i_img_dim y;
   
   for (y = hlines->start_y; y < hlines->limit_y; ++y) {
     i_int_hline_entry *entry = hlines->entries[y-hlines->start_y];
@@ -914,10 +1024,10 @@ i_int_hlines_dump(i_int_hlines *hlines) {
       if (entry->count)
         qsort(entry->segs, entry->count, sizeof(i_int_hline_seg), seg_compare);
 
-      sv_catpvf(dump, " %d (%d):", y, entry->count);
+      sv_catpvf(dump, " %" i_DF " (%" i_DF "):", i_DFc(y), i_DFc(entry->count));
       for (i = 0; i < entry->count; ++i) {
-        sv_catpvf(dump, " [%d, %d)", entry->segs[i].minx
-                  entry->segs[i].x_limit);
+        sv_catpvf(dump, " [%" i_DF ", %" i_DF ")", i_DFc(entry->segs[i].minx)
+                  i_DFc(entry->segs[i].x_limit));
       }
       sv_catpv(dump, "\n");
     }
@@ -928,6 +1038,40 @@ i_int_hlines_dump(i_int_hlines *hlines) {
 
 #endif
 
+static off_t
+i_sv_off_t(pTHX_ SV *sv) {
+#if LSEEKSIZE > IVSIZE
+  return (off_t)SvNV(sv);
+#else
+  return (off_t)SvIV(sv);
+#endif
+}
+
+static SV *
+i_new_sv_off_t(pTHX_ off_t off) {
+#if LSEEKSIZE > IVSIZE
+  return newSVnv(off);
+#else
+  return newSViv(off);
+#endif
+}
+
+static im_pl_ext_funcs im_perl_funcs =
+{
+  IMAGER_PL_API_VERSION,
+  IMAGER_PL_API_LEVEL,
+  ip_handle_quant_opts,
+  ip_cleanup_quant_opts,
+  ip_copy_colors_back
+};
+
+#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
+typedef int SysRet;
+
 #ifdef IMEXIF_ENABLE
 #define i_exif_enabled() 1
 #else
@@ -937,6 +1081,14 @@ i_int_hlines_dump(i_int_hlines *hlines) {
 /* trying to use more C style names, map them here */
 #define i_io_DESTROY(ig) io_glue_destroy(ig)
 
+#define i_img_get_width(im) ((im)->xsize)
+#define i_img_get_height(im) ((im)->ysize)
+
+#define i_img_epsilonf() (DBL_EPSILON * 4)
+
+/* avoid some xsubpp strangeness */
+#define NEWLINE '\n'
+
 MODULE = Imager                PACKAGE = Imager::Color PREFIX = ICL_
 
 Imager::Color
@@ -973,10 +1125,10 @@ ICL_rgba(cl)
              Imager::Color     cl
            PPCODE:
                EXTEND(SP, 4);
-               PUSHs(sv_2mortal(newSVnv(cl->rgba.r)));
-               PUSHs(sv_2mortal(newSVnv(cl->rgba.g)));
-               PUSHs(sv_2mortal(newSVnv(cl->rgba.b)));
-               PUSHs(sv_2mortal(newSVnv(cl->rgba.a)));
+               PUSHs(sv_2mortal(newSViv(cl->rgba.r)));
+               PUSHs(sv_2mortal(newSViv(cl->rgba.g)));
+               PUSHs(sv_2mortal(newSViv(cl->rgba.b)));
+               PUSHs(sv_2mortal(newSViv(cl->rgba.a)));
 
 Imager::Color
 i_hsv_to_rgb(c)
@@ -1064,8 +1216,8 @@ MODULE = Imager           PACKAGE = Imager::ImgRaw        PREFIX = IIM_
 
 Imager::ImgRaw
 IIM_new(x,y,ch)
-               int     x
-              int     y
+               i_img_dim     x
+              i_img_dim     y
               int     ch
 
 void
@@ -1088,14 +1240,13 @@ io_new_bufchain()
 
 
 Imager::IO
-io_new_buffer(data)
-         char   *data
-       PREINIT:
-         size_t length;
+io_new_buffer(data_sv)
+         SV   *data_sv
        CODE:
-         SvPV(ST(0), length);
-          SvREFCNT_inc(ST(0));
-         RETVAL = io_new_buffer(data, length, my_SvREFCNT_dec, ST(0));
+         i_clear_error();
+         RETVAL = do_io_new_buffer(aTHX_ data_sv);
+         if (!RETVAL)
+           XSRETURN(0);
         OUTPUT:
           RETVAL
 
@@ -1105,104 +1256,151 @@ io_new_cb(writecb, readcb, seekcb, closecb, maxwrite = CBDATA_BUFSIZE)
         SV *readcb;
         SV *seekcb;
         SV *closecb;
-        int maxwrite;
-      PREINIT:
-        struct cbdata *cbd;
       CODE:
-        cbd = mymalloc(sizeof(struct cbdata));
-        SvREFCNT_inc(writecb);
-        cbd->writecb = writecb;
-        SvREFCNT_inc(readcb);
-        cbd->readcb = readcb;
-        SvREFCNT_inc(seekcb);
-        cbd->seekcb = seekcb;
-        SvREFCNT_inc(closecb);
-        cbd->closecb = closecb;
-        cbd->reading = cbd->writing = cbd->where = cbd->used = 0;
-        if (maxwrite > CBDATA_BUFSIZE)
-          maxwrite = CBDATA_BUFSIZE;
-        cbd->maxlength = maxwrite;
-        RETVAL = io_new_cb(cbd, io_reader, io_writer, io_seeker, io_closer, 
-                           io_destroyer);
+        RETVAL = do_io_new_cb(aTHX_ writecb, readcb, seekcb, closecb);
       OUTPUT:
         RETVAL
 
-void
+SV *
 io_slurp(ig)
         Imager::IO     ig
             PREINIT:
              unsigned char*    data;
              size_t    tlength;
-            PPCODE:
+            CODE:
              data    = NULL;
               tlength = io_slurp(ig, &data);
-              EXTEND(SP,1);
-              PUSHs(sv_2mortal(newSVpv((char *)data,tlength)));
+              RETVAL = newSVpv((char *)data,tlength);
               myfree(data);
+            OUTPUT:
+             RETVAL
 
 
 undef_int
 i_set_image_file_limits(width, height, bytes)
-       int width
-       int height
-       int bytes
+       i_img_dim width
+       i_img_dim height
+       size_t bytes
 
 void
 i_get_image_file_limits()
       PREINIT:
-        int width, height, bytes;
+        i_img_dim width, height;
+       size_t bytes;
       PPCODE:
         if (i_get_image_file_limits(&width, &height, &bytes)) {
          EXTEND(SP, 3);
           PUSHs(sv_2mortal(newSViv(width)));
           PUSHs(sv_2mortal(newSViv(height)));
-          PUSHs(sv_2mortal(newSViv(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
+io_new_fd(class, fd)
+       int fd
+    CODE:
+       RETVAL = io_new_fd(fd);
+    OUTPUT:
+       RETVAL
+
+Imager::IO
+io_new_buffer(class, data_sv)
+       SV *data_sv
+    CODE:
+        i_clear_error();
+        RETVAL = do_io_new_buffer(aTHX_ data_sv);
+       if (!RETVAL)
+         XSRETURN(0);
+    OUTPUT:
+        RETVAL
+
+Imager::IO
+io_new_cb(class, writecb, readcb, seekcb, closecb)
+        SV *writecb;
+        SV *readcb;
+        SV *seekcb;
+        SV *closecb;
+    CODE:
+        RETVAL = do_io_new_cb(aTHX_ writecb, readcb, seekcb, closecb);
+    OUTPUT:
+        RETVAL
+
+Imager::IO
+io_new_bufchain(class)
+    CODE:
+       RETVAL = io_new_bufchain();
+    OUTPUT:
+        RETVAL
+
+Imager::IO
+io__new_perlio(class, io)
+       PerlIO *io
+  CODE:
+        RETVAL = im_io_new_perlio(aTHX_ io);
+  OUTPUT:
+       RETVAL
+
+SV *
+io_slurp(class, ig)
+        Imager::IO     ig
+    PREINIT:
+       unsigned char*    data;
+       size_t    tlength;
+    CODE:
+       data    = NULL;
+       tlength = io_slurp(ig, &data);
+       RETVAL = newSVpv((char *)data,tlength);
+       myfree(data);
+    OUTPUT:
+       RETVAL
+
 MODULE = Imager                PACKAGE = Imager::IO    PREFIX = i_io_
 
-int
-i_io_write(ig, data_sv)
+IV
+i_io_raw_write(ig, data_sv)
        Imager::IO ig
        SV *data_sv
       PREINIT:
         void *data;
        STRLEN size;
       CODE:
-#ifdef SvUTF8
-        if (SvUTF8(data_sv)) {
-         data_sv = sv_2mortal(newSVsv(data_sv));
-          /* yes, we want this to croak() if the SV can't be downgraded */
-         sv_utf8_downgrade(data_sv, FALSE);
-       }
-#endif        
-       data = SvPV(data_sv, size);
-        RETVAL = i_io_write(ig, data, size);
+       data = SvPVbyte(data_sv, size);
+        RETVAL = i_io_raw_write(ig, data, size);
       OUTPUT:
        RETVAL
 
 void
-i_io_read(ig, buffer_sv, size)
+i_io_raw_read(ig, buffer_sv, size)
        Imager::IO ig
        SV *buffer_sv
-       int size
+       IV size
       PREINIT:
         void *buffer;
-       int result;
+       ssize_t result;
       PPCODE:
         if (size <= 0)
-         croak("size negative in call to i_io_read()");
+         croak("size negative in call to i_io_raw_read()");
         /* prevent an undefined value warning if they supplied an 
-          undef buffer.
+          undef buffer.
            Orginally conditional on !SvOK(), but this will prevent the
-          downgrade from croaking */
-       sv_setpvn(buffer_sv, "", 0);
+          downgrade from croaking */
+       sv_setpvn(buffer_sv, "", 0);
 #ifdef SvUTF8
-       if (SvUTF8(buffer_sv))
+       if (SvUTF8(buffer_sv))
           sv_utf8_downgrade(buffer_sv, FALSE);
 #endif
        buffer = SvGROW(buffer_sv, size+1);
-        result = i_io_read(ig, buffer, size);
+        result = i_io_raw_read(ig, buffer, size);
         if (result >= 0) {
          SvCUR_set(buffer_sv, result);
          *SvEND(buffer_sv) = '\0';
@@ -1214,19 +1412,19 @@ i_io_read(ig, buffer_sv, size)
        SvSETMAGIC(ST(1));
 
 void
-i_io_read2(ig, size)
+i_io_raw_read2(ig, size)
        Imager::IO ig
-       int size
+       IV size
       PREINIT:
        SV *buffer_sv;
         void *buffer;
-       int result;
+       ssize_t result;
       PPCODE:
         if (size <= 0)
          croak("size negative in call to i_io_read2()");
        buffer_sv = newSV(size);
        buffer = SvGROW(buffer_sv, size+1);
-        result = i_io_read(ig, buffer, size);
+        result = i_io_raw_read(ig, buffer, size);
         if (result >= 0) {
          SvCUR_set(buffer_sv, result);
          *SvEND(buffer_sv) = '\0';
@@ -1239,14 +1437,14 @@ i_io_read2(ig, size)
          SvREFCNT_dec(buffer_sv);
         }
 
-int
-i_io_seek(ig, position, whence)
+off_t
+i_io_raw_seek(ig, position, whence)
        Imager::IO ig
-       long position
+       off_t position
        int whence
 
 int
-i_io_close(ig)
+i_io_raw_close(ig)
        Imager::IO ig
 
 void
@@ -1256,10 +1454,178 @@ i_io_DESTROY(ig)
 int
 i_io_CLONE_SKIP(...)
     CODE:
+        (void)items; /* avoid unused warning for XS variable */
        RETVAL = 1;
     OUTPUT:
        RETVAL
 
+int
+i_io_getc(ig)
+       Imager::IO ig
+
+int
+i_io_putc(ig, c)
+       Imager::IO ig
+        int c
+
+int
+i_io_close(ig)
+       Imager::IO ig
+
+int
+i_io_flush(ig)
+       Imager::IO ig
+
+int
+i_io_peekc(ig)
+       Imager::IO ig
+
+int
+i_io_seek(ig, off, whence)
+       Imager::IO ig
+       off_t off
+        int whence
+
+void
+i_io_peekn(ig, size)
+       Imager::IO ig
+       STRLEN size
+      PREINIT:
+       SV *buffer_sv;
+        void *buffer;
+       ssize_t result;
+      PPCODE:
+       buffer_sv = newSV(size+1);
+       buffer = SvGROW(buffer_sv, size+1);
+        result = i_io_peekn(ig, buffer, size);
+        if (result >= 0) {
+         SvCUR_set(buffer_sv, result);
+         *SvEND(buffer_sv) = '\0';
+         SvPOK_only(buffer_sv);
+         EXTEND(SP, 1);
+         PUSHs(sv_2mortal(buffer_sv));
+       }
+       else {
+          /* discard it */
+         SvREFCNT_dec(buffer_sv);
+        }
+
+void
+i_io_read(ig, buffer_sv, size)
+       Imager::IO ig
+       SV *buffer_sv
+       IV size
+      PREINIT:
+        void *buffer;
+       ssize_t result;
+      PPCODE:
+        if (size <= 0)
+         croak("size negative in call to i_io_read()");
+        /* prevent an undefined value warning if they supplied an 
+          undef buffer.
+           Orginally conditional on !SvOK(), but this will prevent the
+          downgrade from croaking */
+       sv_setpvn(buffer_sv, "", 0);
+#ifdef SvUTF8
+       if (SvUTF8(buffer_sv))
+          sv_utf8_downgrade(buffer_sv, FALSE);
+#endif
+       buffer = SvGROW(buffer_sv, size+1);
+        result = i_io_read(ig, buffer, size);
+        if (result >= 0) {
+         SvCUR_set(buffer_sv, result);
+         *SvEND(buffer_sv) = '\0';
+         SvPOK_only(buffer_sv);
+         EXTEND(SP, 1);
+         PUSHs(sv_2mortal(newSViv(result)));
+       }
+       ST(1) = buffer_sv;
+       SvSETMAGIC(ST(1));
+
+void
+i_io_read2(ig, size)
+       Imager::IO ig
+       STRLEN size
+      PREINIT:
+       SV *buffer_sv;
+        void *buffer;
+       ssize_t result;
+      PPCODE:
+        if (size == 0)
+         croak("size zero in call to read2()");
+       buffer_sv = newSV(size);
+       buffer = SvGROW(buffer_sv, size+1);
+        result = i_io_read(ig, buffer, size);
+        if (result > 0) {
+         SvCUR_set(buffer_sv, result);
+         *SvEND(buffer_sv) = '\0';
+         SvPOK_only(buffer_sv);
+         EXTEND(SP, 1);
+         PUSHs(sv_2mortal(buffer_sv));
+       }
+       else {
+          /* discard it */
+         SvREFCNT_dec(buffer_sv);
+        }
+
+void
+i_io_gets(ig, size = 8192, eol = NEWLINE)
+       Imager::IO ig
+       STRLEN size
+       int eol
+      PREINIT:
+       SV *buffer_sv;
+        void *buffer;
+       ssize_t result;
+      PPCODE:
+        if (size < 2)
+         croak("size too small in call to gets()");
+       buffer_sv = sv_2mortal(newSV(size+1));
+       buffer = SvPVX(buffer_sv);
+        result = i_io_gets(ig, buffer, size+1, eol);
+        if (result > 0) {
+         SvCUR_set(buffer_sv, result);
+         *SvEND(buffer_sv) = '\0';
+         SvPOK_only(buffer_sv);
+         EXTEND(SP, 1);
+         PUSHs(buffer_sv);
+       }
+
+IV
+i_io_write(ig, data_sv)
+       Imager::IO ig
+       SV *data_sv
+      PREINIT:
+        void *data;
+       STRLEN size;
+      CODE:
+       data = SvPVbyte(data_sv, size);
+        RETVAL = i_io_write(ig, data, size);
+      OUTPUT:
+       RETVAL
+
+void
+i_io_dump(ig, flags = I_IO_DUMP_DEFAULT)
+       Imager::IO ig
+       int flags
+
+bool
+i_io_set_buffered(ig, flag = 1)
+       Imager::IO ig
+       int flag
+
+bool
+i_io_is_buffered(ig)
+       Imager::IO ig
+
+bool
+i_io_eof(ig)
+       Imager::IO ig
+
+bool
+i_io_error(ig)
+       Imager::IO ig
+
 MODULE = Imager                PACKAGE = Imager
 
 PROTOTYPES: ENABLE
@@ -1276,67 +1642,43 @@ i_list_formats()
                      PUSHs(sv_2mortal(newSVpv(item,0)));
               }
 
-undef_int
-i_has_format(frmt)
-              char*    frmt
-
-Imager::ImgRaw
-i_img_new()
-
-Imager::ImgRaw
-i_img_empty(im,x,y)
-    Imager::ImgRaw     im
-               int     x
-              int     y
-
-Imager::ImgRaw
-i_img_empty_ch(im,x,y,ch)
-    Imager::ImgRaw     im
-               int     x
-              int     y
-              int     ch
-
 Imager::ImgRaw
 i_sametype(im, x, y)
     Imager::ImgRaw im
-               int x
-               int y
+               i_img_dim x
+               i_img_dim y
 
 Imager::ImgRaw
 i_sametype_chans(im, x, y, channels)
     Imager::ImgRaw im
-               int x
-               int y
+               i_img_dim x
+               i_img_dim y
                int channels
 
-void
+int
 i_init_log(name_sv,level)
              SV*    name_sv
               int     level
        PREINIT:
          const char *name = SvOK(name_sv) ? SvPV_nolen(name_sv) : NULL;
        CODE:
-         i_init_log(name, level);
+         RETVAL = i_init_log(name, level);
+       OUTPUT:
+         RETVAL
 
 void
 i_log_entry(string,level)
              char*    string
               int     level
 
-
-void
-i_img_exorcise(im)
-    Imager::ImgRaw     im
-
-void
-i_img_destroy(im)
-    Imager::ImgRaw     im
+int
+i_log_enabled()
 
 void
 i_img_info(im)
     Imager::ImgRaw     im
             PREINIT:
-              int     info[4];
+              i_img_dim     info[4];
             PPCODE:
               i_img_info(im,info);
                EXTEND(SP, 4);
@@ -1370,6 +1712,15 @@ i_img_getdata(im)
                     sv_2mortal(newSVpv((char *)im->idata, im->bytes)) 
                     : &PL_sv_undef);
 
+IV
+i_img_get_width(im)
+    Imager::ImgRaw     im
+
+IV
+i_img_get_height(im)
+    Imager::ImgRaw     im
+
+
 void
 i_img_is_monochrome(im)
        Imager::ImgRaw im
@@ -1393,58 +1744,67 @@ i_img_is_monochrome(im)
 void
 i_line(im,x1,y1,x2,y2,val,endp)
     Imager::ImgRaw     im
-              int     x1
-              int     y1
-              int     x2
-              int     y2
+              i_img_dim     x1
+              i_img_dim     y1
+              i_img_dim     x2
+              i_img_dim     y2
      Imager::Color     val
               int     endp
 
 void
 i_line_aa(im,x1,y1,x2,y2,val,endp)
     Imager::ImgRaw     im
-              int     x1
-              int     y1
-              int     x2
-              int     y2
+              i_img_dim     x1
+              i_img_dim     y1
+              i_img_dim     x2
+              i_img_dim     y2
      Imager::Color     val
               int     endp
 
 void
 i_box(im,x1,y1,x2,y2,val)
     Imager::ImgRaw     im
-              int     x1
-              int     y1
-              int     x2
-              int     y2
+              i_img_dim     x1
+              i_img_dim     y1
+              i_img_dim     x2
+              i_img_dim     y2
      Imager::Color     val
 
 void
 i_box_filled(im,x1,y1,x2,y2,val)
     Imager::ImgRaw     im
-              int     x1
-              int     y1
-              int     x2
-              int     y2
+              i_img_dim     x1
+              i_img_dim     y1
+              i_img_dim     x2
+              i_img_dim     y2
           Imager::Color    val
 
+int
+i_box_filledf(im,x1,y1,x2,y2,val)
+    Imager::ImgRaw     im
+              i_img_dim     x1
+              i_img_dim     y1
+              i_img_dim     x2
+              i_img_dim     y2
+          Imager::Color::Float    val
+
 void
 i_box_cfill(im,x1,y1,x2,y2,fill)
     Imager::ImgRaw     im
-              int     x1
-              int     y1
-              int     x2
-              int     y2
+              i_img_dim     x1
+              i_img_dim     y1
+              i_img_dim     x2
+              i_img_dim     y2
           Imager::FillHandle    fill
 
 void
 i_arc(im,x,y,rad,d1,d2,val)
     Imager::ImgRaw     im
-              int     x
-              int     y
-             float     rad
-             float     d1
-             float     d2
+              i_img_dim     x
+              i_img_dim     y
+             double     rad
+             double     d1
+             double     d2
           Imager::Color    val
 
 void
@@ -1460,11 +1820,11 @@ i_arc_aa(im,x,y,rad,d1,d2,val)
 void
 i_arc_cfill(im,x,y,rad,d1,d2,fill)
     Imager::ImgRaw     im
-              int     x
-              int     y
-             float     rad
-             float     d1
-             float     d2
+              i_img_dim     x
+              i_img_dim     y
+             double     rad
+             double     d1
+             double     d2
           Imager::FillHandle    fill
 
 void
@@ -1481,12 +1841,20 @@ i_arc_aa_cfill(im,x,y,rad,d1,d2,fill)
 void
 i_circle_aa(im,x,y,rad,val)
     Imager::ImgRaw     im
-            float     x
-            float     y
-             float     rad
+            double     x
+            double     y
+             double     rad
           Imager::Color    val
 
-int
+void
+i_circle_aa_fill(im,x,y,rad,fill)
+    Imager::ImgRaw     im
+            double     x
+            double     y
+             double     rad
+          Imager::FillHandle    fill
+
+int
 i_circle_out(im,x,y,rad,val)
     Imager::ImgRaw     im
             i_img_dim     x
@@ -1508,8 +1876,8 @@ i_arc_out(im,x,y,rad,d1,d2,val)
             i_img_dim     x
             i_img_dim     y
              i_img_dim     rad
-            float d1
-            float d2
+            double d1
+            double d2
           Imager::Color    val
 
 int
@@ -1518,146 +1886,108 @@ i_arc_out_aa(im,x,y,rad,d1,d2,val)
             i_img_dim     x
             i_img_dim     y
              i_img_dim     rad
-            float d1
-            float d2
+            double d1
+            double d2
           Imager::Color    val
 
 
 void
-i_bezier_multi(im,xc,yc,val)
+i_bezier_multi(im,x,y,val)
     Imager::ImgRaw     im
-             Imager::Color  val
-            PREINIT:
-            double   *x,*y;
-            int       len;
-            AV       *av1;
-            AV       *av2;
-            SV       *sv1;
-            SV       *sv2;
-            int i;
-            PPCODE:
-            ICL_info(val);
-            if (!SvROK(ST(1))) croak("Imager: Parameter 1 to i_bezier_multi must be a reference to an array\n");
-            if (SvTYPE(SvRV(ST(1))) != SVt_PVAV) croak("Imager: Parameter 1 to i_bezier_multi must be a reference to an array\n");
-            if (!SvROK(ST(2))) croak("Imager: Parameter 2 to i_bezier_multi must be a reference to an array\n");
-            if (SvTYPE(SvRV(ST(2))) != SVt_PVAV) croak("Imager: Parameter 2 to i_bezier_multi must be a reference to an array\n");
-            av1=(AV*)SvRV(ST(1));
-            av2=(AV*)SvRV(ST(2));
-            if (av_len(av1) != av_len(av2)) croak("Imager: x and y arrays to i_bezier_multi must be equal length\n");
-            len=av_len(av1)+1;
-            x=mymalloc( len*sizeof(double) );
-            y=mymalloc( len*sizeof(double) );
-            for(i=0;i<len;i++) {
-              sv1=(*(av_fetch(av1,i,0)));
-              sv2=(*(av_fetch(av2,i,0)));
-              x[i]=(double)SvNV(sv1);
-              y[i]=(double)SvNV(sv2);
-            }
-             i_bezier_multi(im,len,x,y,val);
-             myfree(x);
-             myfree(y);
-
+    double *x
+    double *y
+    Imager::Color  val
+  PREINIT:
+    STRLEN size_x;
+    STRLEN size_y;
+  PPCODE:
+    if (size_x != size_y)
+      croak("Imager: x and y arrays to i_bezier_multi must be equal length\n");
+    i_bezier_multi(im,size_x,x,y,val);
 
 int
-i_poly_aa(im,xc,yc,val)
+i_poly_aa_m(im,x,y,mode,val)
     Imager::ImgRaw     im
-             Imager::Color  val
-            PREINIT:
-            double   *x,*y;
-            int       len;
-            AV       *av1;
-            AV       *av2;
-            SV       *sv1;
-            SV       *sv2;
-            int i;
-            CODE:
-            ICL_info(val);
-            if (!SvROK(ST(1))) croak("Imager: Parameter 1 to i_poly_aa must be a reference to an array\n");
-            if (SvTYPE(SvRV(ST(1))) != SVt_PVAV) croak("Imager: Parameter 1 to i_poly_aa must be a reference to an array\n");
-            if (!SvROK(ST(2))) croak("Imager: Parameter 1 to i_poly_aa must be a reference to an array\n");
-            if (SvTYPE(SvRV(ST(2))) != SVt_PVAV) croak("Imager: Parameter 1 to i_poly_aa must be a reference to an array\n");
-            av1=(AV*)SvRV(ST(1));
-            av2=(AV*)SvRV(ST(2));
-            if (av_len(av1) != av_len(av2)) croak("Imager: x and y arrays to i_poly_aa must be equal length\n");
-            len=av_len(av1)+1;
-            x=mymalloc( len*sizeof(double) );
-            y=mymalloc( len*sizeof(double) );
-            for(i=0;i<len;i++) {
-              sv1=(*(av_fetch(av1,i,0)));
-              sv2=(*(av_fetch(av2,i,0)));
-              x[i]=(double)SvNV(sv1);
-              y[i]=(double)SvNV(sv2);
-            }
-             RETVAL = i_poly_aa(im,len,x,y,val);
-             myfree(x);
-             myfree(y);
-            OUTPUT:
-              RETVAL
+    double *x
+    double *y
+    i_poly_fill_mode_t mode
+    Imager::Color  val
+  PREINIT:
+    STRLEN   size_x;
+    STRLEN   size_y;
+  CODE:
+    if (size_x != size_y)
+      croak("Imager: x and y arrays to i_poly_aa must be equal length\n");
+    RETVAL = i_poly_aa_m(im, size_x, x, y, mode, val);
+  OUTPUT:
+    RETVAL
 
 int
-i_poly_aa_cfill(im,xc,yc,fill)
+i_poly_aa_cfill_m(im, x, y, mode, fill)
     Imager::ImgRaw     im
-     Imager::FillHandle     fill
-            PREINIT:
-            double   *x,*y;
-            int       len;
-            AV       *av1;
-            AV       *av2;
-            SV       *sv1;
-            SV       *sv2;
-            int i;
-            CODE:
-            if (!SvROK(ST(1))) croak("Imager: Parameter 1 to i_poly_aa_cfill must be a reference to an array\n");
-            if (SvTYPE(SvRV(ST(1))) != SVt_PVAV) croak("Imager: Parameter 1 to i_poly_aa_cfill must be a reference to an array\n");
-            if (!SvROK(ST(2))) croak("Imager: Parameter 1 to i_poly_aa_cfill must be a reference to an array\n");
-            if (SvTYPE(SvRV(ST(2))) != SVt_PVAV) croak("Imager: Parameter 1 to i_poly_aa_cfill must be a reference to an array\n");
-            av1=(AV*)SvRV(ST(1));
-            av2=(AV*)SvRV(ST(2));
-            if (av_len(av1) != av_len(av2)) croak("Imager: x and y arrays to i_poly_aa_cfill must be equal length\n");
-            len=av_len(av1)+1;
-            x=mymalloc( len*sizeof(double) );
-            y=mymalloc( len*sizeof(double) );
-            for(i=0;i<len;i++) {
-              sv1=(*(av_fetch(av1,i,0)));
-              sv2=(*(av_fetch(av2,i,0)));
-              x[i]=(double)SvNV(sv1);
-              y[i]=(double)SvNV(sv2);
-            }
-             RETVAL = i_poly_aa_cfill(im,len,x,y,fill);
-             myfree(x);
-             myfree(y);
-            OUTPUT:
-              RETVAL
+    double *x
+    double *y
+    i_poly_fill_mode_t mode
+    Imager::FillHandle     fill
+  PREINIT:
+    STRLEN size_x;
+    STRLEN size_y;
+  CODE:
+    if (size_x != size_y)
+      croak("Imager: x and y arrays to i_poly_aa_cfill must be equal length\n");
+    RETVAL = i_poly_aa_cfill_m(im, size_x, x, y, mode, fill);
+  OUTPUT:
+    RETVAL
 
+int
+i_poly_poly_aa(im, polys, mode, color)
+    Imager::ImgRaw     im
+         i_polygon_list polys
+        i_poly_fill_mode_t mode
+        Imager::Color color
+    CODE:
+        RETVAL = i_poly_poly_aa(im, polys.count, polys.polygons, mode, color);
+    OUTPUT:
+        RETVAL
 
+int
+i_poly_poly_aa_cfill(im, polys, mode, fill)
+    Imager::ImgRaw     im
+         i_polygon_list polys
+        i_poly_fill_mode_t mode
+        Imager::FillHandle fill
+    CODE:
+        RETVAL = i_poly_poly_aa_cfill(im, polys.count, polys.polygons, mode, fill);
+    OUTPUT:
+        RETVAL
 
 undef_int
 i_flood_fill(im,seedx,seedy,dcol)
     Imager::ImgRaw     im
-              int     seedx
-              int     seedy
+              i_img_dim     seedx
+              i_img_dim     seedy
      Imager::Color     dcol
 
 undef_int
 i_flood_cfill(im,seedx,seedy,fill)
     Imager::ImgRaw     im
-              int     seedx
-              int     seedy
+              i_img_dim     seedx
+              i_img_dim     seedy
      Imager::FillHandle     fill
 
 undef_int
 i_flood_fill_border(im,seedx,seedy,dcol, border)
     Imager::ImgRaw     im
-              int     seedx
-              int     seedy
+              i_img_dim     seedx
+              i_img_dim     seedy
      Imager::Color     dcol
      Imager::Color     border
 
 undef_int
 i_flood_cfill_border(im,seedx,seedy,fill, border)
     Imager::ImgRaw     im
-              int     seedx
-              int     seedy
+              i_img_dim     seedx
+              i_img_dim     seedy
      Imager::FillHandle     fill
      Imager::Color     border
 
@@ -1666,24 +1996,24 @@ void
 i_copyto(im,src,x1,y1,x2,y2,tx,ty)
     Imager::ImgRaw     im
     Imager::ImgRaw     src
-              int     x1
-              int     y1
-              int     x2
-              int     y2
-              int     tx
-              int     ty
+              i_img_dim     x1
+              i_img_dim     y1
+              i_img_dim     x2
+              i_img_dim     y2
+              i_img_dim     tx
+              i_img_dim     ty
 
 
 void
 i_copyto_trans(im,src,x1,y1,x2,y2,tx,ty,trans)
     Imager::ImgRaw     im
     Imager::ImgRaw     src
-              int     x1
-              int     y1
-              int     x2
-              int     y2
-              int     tx
-              int     ty
+              i_img_dim     x1
+              i_img_dim     y1
+              i_img_dim     x2
+              i_img_dim     y2
+              i_img_dim     tx
+              i_img_dim     ty
      Imager::Color     trans
 
 Imager::ImgRaw
@@ -1695,23 +2025,23 @@ undef_int
 i_rubthru(im,src,tx,ty,src_minx,src_miny,src_maxx,src_maxy)
     Imager::ImgRaw     im
     Imager::ImgRaw     src
-              int     tx
-              int     ty
-              int     src_minx
-              int     src_miny
-              int     src_maxx
-              int     src_maxy
+              i_img_dim     tx
+              i_img_dim     ty
+              i_img_dim     src_minx
+              i_img_dim     src_miny
+              i_img_dim     src_maxx
+              i_img_dim     src_maxy
 
 undef_int
 i_compose(out, src, out_left, out_top, src_left, src_top, width, height, combine = ic_normal, opacity = 0.0)
     Imager::ImgRaw out
     Imager::ImgRaw src
-       int out_left
-       int out_top
-       int src_left
-       int src_top
-       int width
-       int height
+       i_img_dim out_left
+       i_img_dim out_top
+       i_img_dim src_left
+       i_img_dim src_top
+       i_img_dim width
+       i_img_dim height
        int combine
        double opacity
 
@@ -1720,17 +2050,58 @@ i_compose_mask(out, src, mask, out_left, out_top, src_left, src_top, mask_left,
     Imager::ImgRaw out
     Imager::ImgRaw src
     Imager::ImgRaw mask
-       int out_left
-       int out_top
-       int src_left
-       int src_top
-       int mask_left
-       int mask_top
-       int width
-       int height
+       i_img_dim out_left
+       i_img_dim out_top
+       i_img_dim src_left
+       i_img_dim src_top
+       i_img_dim mask_left
+       i_img_dim mask_top
+       i_img_dim width
+       i_img_dim height
        int combine
        double opacity
 
+Imager::ImgRaw
+i_combine(src_av, channels_av = NULL)
+       AV *src_av
+       AV *channels_av
+  PREINIT:
+       i_img **imgs = NULL;
+       STRLEN in_count;
+       int *channels = NULL;
+       int i;
+       SV **psv;
+       IV tmp;
+  CODE:
+       in_count = av_len(src_av) + 1;
+       if (in_count > 0) {
+         imgs = mymalloc(sizeof(i_img*) * in_count);
+         channels = mymalloc(sizeof(int) * in_count);
+         for (i = 0; i < in_count; ++i) {
+           psv = av_fetch(src_av, i, 0);
+           if (!psv || !*psv || !sv_derived_from(*psv, "Imager::ImgRaw")) {
+             myfree(imgs);
+             myfree(channels);
+             croak("imgs must contain only images");
+           }
+           tmp = SvIV((SV*)SvRV(*psv));
+           imgs[i] = INT2PTR(i_img*, tmp);
+           if (channels_av &&
+               (psv = av_fetch(channels_av, i, 0)) != NULL &&
+               *psv) {
+             channels[i] = SvIV(*psv);
+           }
+           else {
+             channels[i] = 0;
+           }
+         }
+       }
+       RETVAL = i_combine(imgs, channels, in_count);
+       myfree(imgs);
+       myfree(channels);
+  OUTPUT:
+       RETVAL
+
 undef_int
 i_flipxy(im, direction)
     Imager::ImgRaw     im
@@ -1769,47 +2140,44 @@ i_rotate_exact(im, amount, ...)
        RETVAL
 
 Imager::ImgRaw
-i_matrix_transform(im, xsize, ysize, matrix, ...)
+i_matrix_transform(im, xsize, ysize, matrix_av, ...)
     Imager::ImgRaw      im
-               int      xsize
-               int      ysize
-      PREINIT:
-        double matrix[9];
-        AV *av;
-        IV len;
-        SV *sv1;
-        int i;
-       i_color *backp = NULL;
-       i_fcolor *fbackp = NULL;
-      CODE:
-        if (!SvROK(ST(3)) || SvTYPE(SvRV(ST(3))) != SVt_PVAV)
-          croak("i_matrix_transform: parameter 4 must be an array ref\n");
-       av=(AV*)SvRV(ST(3));
-       len=av_len(av)+1;
-        if (len > 9)
-          len = 9;
-        for (i = 0; i < len; ++i) {
-         sv1=(*(av_fetch(av,i,0)));
-         matrix[i] = SvNV(sv1);
-        }
-        for (; i < 9; ++i)
-          matrix[i] = 0;
-       /* extract the bg colors if any */
-       /* yes, this is kind of strange */
-       for (i = 4; i < items; ++i) {
-          sv1 = ST(i);
-          if (sv_derived_from(sv1, "Imager::Color")) {
-           IV tmp = SvIV((SV*)SvRV(sv1));
-           backp = INT2PTR(i_color *, tmp);
-         }
-         else if (sv_derived_from(sv1, "Imager::Color::Float")) {
-           IV tmp = SvIV((SV*)SvRV(sv1));
-           fbackp = INT2PTR(i_fcolor *, tmp);
-         }
-       }
-        RETVAL = i_matrix_transform_bg(im, xsize, ysize, matrix, backp, fbackp);
-      OUTPUT:
-        RETVAL
+    i_img_dim      xsize
+    i_img_dim      ysize
+    AV *matrix_av
+  PREINIT:
+    double matrix[9];
+    STRLEN len;
+    SV *sv1;
+    int i;
+    i_color *backp = NULL;
+    i_fcolor *fbackp = NULL;
+  CODE:
+    len=av_len(matrix_av)+1;
+    if (len > 9)
+      len = 9;
+    for (i = 0; i < len; ++i) {
+      sv1=(*(av_fetch(matrix_av,i,0)));
+      matrix[i] = SvNV(sv1);
+    }
+    for (; i < 9; ++i)
+      matrix[i] = 0;
+    /* extract the bg colors if any */
+    /* yes, this is kind of strange */
+    for (i = 4; i < items; ++i) {
+      sv1 = ST(i);
+      if (sv_derived_from(sv1, "Imager::Color")) {
+        IV tmp = SvIV((SV*)SvRV(sv1));
+       backp = INT2PTR(i_color *, tmp);
+      }
+      else if (sv_derived_from(sv1, "Imager::Color::Float")) {
+        IV tmp = SvIV((SV*)SvRV(sv1));
+        fbackp = INT2PTR(i_fcolor *, tmp);
+      }
+    }
+    RETVAL = i_matrix_transform_bg(im, xsize, ysize, matrix, backp, fbackp);
+  OUTPUT:
+    RETVAL
 
 undef_int
 i_gaussian(im,stdev)
@@ -1819,7 +2187,7 @@ i_gaussian(im,stdev)
 void
 i_unsharp_mask(im,stdev,scale)
     Imager::ImgRaw     im
-            float     stdev
+            double    stdev
              double    scale
 
 int
@@ -1848,7 +2216,7 @@ i_convert(src, avmain)
     Imager::ImgRaw     src
     AV *avmain
        PREINIT:
-         float *coeff;
+         double *coeff;
          int outchan;
          int inchan;
           SV **temp;
@@ -1867,8 +2235,12 @@ i_convert(src, avmain)
              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(float) * outchan * inchan);
+          coeff = mymalloc(sizeof(double) * outchan * inchan);
          for (j = 0; j < outchan; ++j) {
            avsub = (AV*)SvRV(*av_fetch(avmain, j, 0));
            len = av_len(avsub)+1;
@@ -1888,46 +2260,44 @@ i_convert(src, avmain)
          RETVAL
 
 
-void
-i_map(im, pmaps)
+undef_int
+i_map(im, pmaps_av)
     Imager::ImgRaw     im
-       PREINIT:
-         unsigned int mask = 0;
-         AV *avmain;
-         AV *avsub;
-          SV **temp;
-         int len;
-         int i, j;
-         unsigned char (*maps)[256];
-        CODE:
-         if (!SvROK(ST(1)) || SvTYPE(SvRV(ST(1))) != SVt_PVAV)
-           croak("i_map: parameter 2 must be an arrayref\n");
-          avmain = (AV*)SvRV(ST(1));
-         len = av_len(avmain)+1;
-         if (im->channels < len) len = im->channels;
-
-         maps = mymalloc( len * sizeof(unsigned char [256]) );
-
-         for (j=0; j<len ; j++) {
-           temp = av_fetch(avmain, j, 0);
-           if (temp && SvROK(*temp) && (SvTYPE(SvRV(*temp)) == SVt_PVAV) ) {
-             avsub = (AV*)SvRV(*temp);
-             if(av_len(avsub) != 255) continue;
-             mask |= 1<<j;
-              for (i=0; i<256 ; i++) {
-               int val;
-               temp = av_fetch(avsub, i, 0);
-               val = temp ? SvIV(*temp) : 0;
-               if (val<0) val = 0;
-               if (val>255) val = 255;
-               maps[j][i] = val;
-             }
-            }
-          }
-          i_map(im, maps, mask);
-         myfree(maps);
-
-
+    AV *pmaps_av
+  PREINIT:
+    unsigned int mask = 0;
+    AV *avsub;
+    SV **temp;
+    int len;
+    int i, j;
+    unsigned char (*maps)[256];
+  CODE:
+    len = av_len(pmaps_av)+1;
+    if (im->channels < len)
+      len = im->channels;
+    maps = mymalloc( len * sizeof(unsigned char [256]) );
+    for (j=0; j<len ; j++) {
+      temp = av_fetch(pmaps_av, j, 0);
+      if (temp && SvROK(*temp) && (SvTYPE(SvRV(*temp)) == SVt_PVAV) ) {
+        avsub = (AV*)SvRV(*temp);
+        if(av_len(avsub) != 255)
+          continue;
+        mask |= 1<<j;
+        for (i=0; i<256 ; i++) {
+          int val;
+          temp = av_fetch(avsub, i, 0);
+          val = temp ? SvIV(*temp) : 0;
+          if (val<0) val = 0;
+          if (val>255) val = 255;
+          maps[j][i] = val;
+        }
+      }
+    }
+    i_map(im, maps, mask);
+    myfree(maps);
+    RETVAL = 1;
+  OUTPUT:
+    RETVAL
 
 float
 i_img_diff(im1,im2)
@@ -1939,191 +2309,26 @@ i_img_diffd(im1,im2)
     Imager::ImgRaw     im1
     Imager::ImgRaw     im2
 
-undef_int        
-i_init_fonts(t1log=0)
-    int t1log
-
-#ifdef HAVE_LIBT1
-
-void
-i_t1_set_aa(st)
-              int     st
-
-int
-i_t1_new(pfb,afm)
-                     char*    pfb
-                     char*    afm
-
 int
-i_t1_destroy(font_id)
-                      int     font_id
-
-
-undef_int
-i_t1_cp(im,xb,yb,channel,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="")
-    Imager::ImgRaw     im
-              int     xb
-              int     yb
-              int     channel
-              int     fontnum
-             float     points
-               SV*    str_sv
-              int     align
-               int     utf8
-              char*    flags
-             PREINIT:
-               char *str;
-               STRLEN len;
-             CODE:
-#ifdef SvUTF8
-               if (SvUTF8(str_sv))
-                 utf8 = 1;
-#endif
-               str = SvPV(str_sv, len);
-               RETVAL = i_t1_cp(im, xb,yb,channel,fontnum,points,str,len,align,
-                                  utf8,flags);
-           OUTPUT:
-             RETVAL
-
-
-void
-i_t1_bbox(fontnum,point,str_sv,len_ignored,utf8=0,flags="")
-               int     fontnum
-            float     point
-               SV*    str_sv
-               int     utf8
-              char*    flags
-            PREINIT:
-               char *str;
-               STRLEN len;
-              int     cords[BOUNDING_BOX_COUNT];
-               int i;
-               int rc;
-            PPCODE:
-#ifdef SvUTF8
-               if (SvUTF8(str_sv))
-                 utf8 = 1;
-#endif
-               str = SvPV(str_sv, len);
-               rc = i_t1_bbox(fontnum,point,str,len,cords,utf8,flags);
-               if (rc > 0) {
-                 EXTEND(SP, rc);
-                 for (i = 0; i < rc; ++i)
-                   PUSHs(sv_2mortal(newSViv(cords[i])));
-               }
-
-
-
-undef_int
-i_t1_text(im,xb,yb,cl,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="")
-    Imager::ImgRaw     im
-              int     xb
-              int     yb
-     Imager::Color    cl
-              int     fontnum
-             float     points
-               SV*    str_sv
-              int     align
-               int     utf8
-              char*    flags
-             PREINIT:
-               char *str;
-               STRLEN len;
-             CODE:
-#ifdef SvUTF8
-               if (SvUTF8(str_sv))
-                 utf8 = 1;
-#endif
-               str = SvPV(str_sv, len);
-               RETVAL = i_t1_text(im, xb,yb,cl,fontnum,points,str,len,align,
-                                  utf8,flags);
-           OUTPUT:
-             RETVAL
-
-void
-i_t1_has_chars(handle, text_sv, utf8 = 0)
-        int handle
-        SV  *text_sv
-        int utf8
-      PREINIT:
-        char const *text;
-        STRLEN len;
-        char *work;
-        int count;
-        int i;
-      PPCODE:
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-        text = SvPV(text_sv, len);
-        work = mymalloc(len);
-        count = i_t1_has_chars(handle, text, len, utf8, work);
-        if (GIMME_V == G_ARRAY) {
-          EXTEND(SP, count);
-          for (i = 0; i < count; ++i) {
-            PUSHs(sv_2mortal(newSViv(work[i])));
-          }
-        }
-        else {
-          EXTEND(SP, 1);
-          PUSHs(sv_2mortal(newSVpv(work, count)));
-        }
-        myfree(work);
-
-void
-i_t1_face_name(handle)
-        int handle
-      PREINIT:
-        char name[255];
-        int len;
-      PPCODE:
-        len = i_t1_face_name(handle, name, sizeof(name));
-        if (len) {
-          EXTEND(SP, 1);
-          PUSHs(sv_2mortal(newSVpv(name, strlen(name))));
-        }
+i_img_samef(im1, im2, epsilon = i_img_epsilonf(), what=NULL)
+    Imager::ImgRaw    im1
+    Imager::ImgRaw    im2
+    double epsilon
+    const char *what
 
-void
-i_t1_glyph_name(handle, text_sv, utf8 = 0)
-        int handle
-        SV *text_sv
-        int utf8
-      PREINIT:
-        char const *text;
-        STRLEN work_len;
-        int len;
-        char name[255];
-      PPCODE:
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-        text = SvPV(text_sv, work_len);
-        len = work_len;
-        while (len) {
-          unsigned long ch;
-          if (utf8) {
-            ch = i_utf8_advance(&text, &len);
-            if (ch == ~0UL) {
-              i_push_error(0, "invalid UTF8 character");
-              break;
-            }
-          }
-          else {
-            ch = *text++;
-            --len;
-          }
-          EXTEND(SP, 1);
-          if (i_t1_glyph_name(handle, ch, name, sizeof(name))) {
-            PUSHs(sv_2mortal(newSVpv(name, 0)));
-          }
-          else {
-            PUSHs(&PL_sv_undef);
-          } 
-        }
+double
+i_img_epsilonf()
 
-#endif 
+bool
+_is_color_object(sv)
+       SV* sv
+    CODE:
+        SvGETMAGIC(sv);
+        RETVAL = SvOK(sv) && SvROK(sv) &&
+          (sv_derived_from(sv, "Imager::Color")
+          || sv_derived_from(sv, "Imager::Color::Float"));
+    OUTPUT:
+        RETVAL
 
 #ifdef HAVE_LIBTT
 
@@ -2144,6 +2349,7 @@ TT_DESTROY(handle)
 int
 TT_CLONE_SKIP(...)
     CODE:
+        (void)items; /* avoid unused warning */
         RETVAL = 1;
     OUTPUT:
         RETVAL
@@ -2153,13 +2359,13 @@ MODULE = Imager         PACKAGE = Imager
 
 
 undef_int
-i_tt_text(handle,im,xb,yb,cl,points,str_sv,len_ignored,smooth,utf8,align=1)
+i_tt_text(handle,im,xb,yb,cl,points,str_sv,smooth,utf8,align=1)
   Imager::Font::TT     handle
     Imager::ImgRaw     im
-              int     xb
-              int     yb
+              i_img_dim     xb
+              i_img_dim     yb
      Imager::Color     cl
-             float     points
+             double     points
              SV *     str_sv
               int     smooth
                int     utf8
@@ -2168,11 +2374,11 @@ i_tt_text(handle,im,xb,yb,cl,points,str_sv,len_ignored,smooth,utf8,align=1)
                char *str;
                STRLEN len;
              CODE:
+               str = SvPV(str_sv, len);
 #ifdef SvUTF8
                if (SvUTF8(str_sv))
                  utf8 = 1;
 #endif
-               str = SvPV(str_sv, len);
                RETVAL = i_tt_text(handle, im, xb, yb, cl, points, str, 
                                   len, smooth, utf8, align);
              OUTPUT:
@@ -2180,13 +2386,13 @@ i_tt_text(handle,im,xb,yb,cl,points,str_sv,len_ignored,smooth,utf8,align=1)
 
 
 undef_int
-i_tt_cp(handle,im,xb,yb,channel,points,str_sv,len_ignored,smooth,utf8,align=1)
+i_tt_cp(handle,im,xb,yb,channel,points,str_sv,smooth,utf8,align=1)
   Imager::Font::TT     handle
     Imager::ImgRaw     im
-              int     xb
-              int     yb
+              i_img_dim     xb
+              i_img_dim     yb
               int     channel
-             float     points
+             double     points
              SV *     str_sv
               int     smooth
                int     utf8
@@ -2195,808 +2401,158 @@ i_tt_cp(handle,im,xb,yb,channel,points,str_sv,len_ignored,smooth,utf8,align=1)
                char *str;
                STRLEN len;
              CODE:
+               str = SvPV(str_sv, len);
 #ifdef SvUTF8
                if (SvUTF8(str_sv))
                  utf8 = 1;
 #endif
-               str = SvPV(str_sv, len);
                RETVAL = i_tt_cp(handle, im, xb, yb, channel, points, str, len,
                                 smooth, utf8, align);
              OUTPUT:
-                RETVAL
-
-
-void
-i_tt_bbox(handle,point,str_sv,len_ignored, utf8)
-  Imager::Font::TT     handle
-            float     point
-              SV*    str_sv
-               int     utf8
-            PREINIT:
-              int     cords[BOUNDING_BOX_COUNT],rc;
-               char *  str;
-               STRLEN len;
-               int i;
-            PPCODE:
-#ifdef SvUTF8
-               if (SvUTF8(ST(2)))
-                 utf8 = 1;
-#endif
-               str = SvPV(str_sv, len);
-              if ((rc=i_tt_bbox(handle,point,str,len,cords, utf8))) {
-                 EXTEND(SP, rc);
-                 for (i = 0; i < rc; ++i) {
-                   PUSHs(sv_2mortal(newSViv(cords[i])));
-                 }
-               }
-
-void
-i_tt_has_chars(handle, text_sv, utf8)
-        Imager::Font::TT handle
-        SV  *text_sv
-        int utf8
-      PREINIT:
-        char const *text;
-        STRLEN len;
-        char *work;
-        int count;
-        int i;
-      PPCODE:
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-        text = SvPV(text_sv, len);
-        work = mymalloc(len);
-        count = i_tt_has_chars(handle, text, len, utf8, work);
-        if (GIMME_V == G_ARRAY) {
-          EXTEND(SP, count);
-          for (i = 0; i < count; ++i) {
-            PUSHs(sv_2mortal(newSViv(work[i])));
-          }
-        }
-        else {
-          EXTEND(SP, 1);
-          PUSHs(sv_2mortal(newSVpv(work, count)));
-        }
-        myfree(work);
-
-void
-i_tt_dump_names(handle)
-        Imager::Font::TT handle
-
-void
-i_tt_face_name(handle)
-        Imager::Font::TT handle
-      PREINIT:
-        char name[255];
-        int len;
-      PPCODE:
-        len = i_tt_face_name(handle, name, sizeof(name));
-        if (len) {
-          EXTEND(SP, 1);
-          PUSHs(sv_2mortal(newSVpv(name, strlen(name))));
-        }
-
-void
-i_tt_glyph_name(handle, text_sv, utf8 = 0)
-        Imager::Font::TT handle
-        SV *text_sv
-        int utf8
-      PREINIT:
-        char const *text;
-        STRLEN work_len;
-        int len;
-        int outsize;
-        char name[255];
-      PPCODE:
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-        text = SvPV(text_sv, work_len);
-        len = work_len;
-        while (len) {
-          unsigned long ch;
-          if (utf8) {
-            ch = i_utf8_advance(&text, &len);
-            if (ch == ~0UL) {
-              i_push_error(0, "invalid UTF8 character");
-              break;
-            }
-          }
-          else {
-            ch = *text++;
-            --len;
-          }
-          EXTEND(SP, 1);
-          if ((outsize = i_tt_glyph_name(handle, ch, name, sizeof(name))) != 0) {
-            PUSHs(sv_2mortal(newSVpv(name, 0)));
-          }
-          else {
-            PUSHs(&PL_sv_undef);
-          } 
-        }
-
-#endif 
-
-
-#ifdef HAVE_LIBJPEG
-undef_int
-i_writejpeg_wiol(im, ig, qfactor)
-    Imager::ImgRaw     im
-        Imager::IO     ig
-              int     qfactor
-
-
-void
-i_readjpeg_wiol(ig)
-        Imager::IO     ig
-            PREINIT:
-             char*    iptc_itext;
-              int     tlength;
-            i_img*    rimg;
-                SV*    r;
-            PPCODE:
-             iptc_itext = NULL;
-             rimg = i_readjpeg_wiol(ig,-1,&iptc_itext,&tlength);
-             if (iptc_itext == NULL) {
-                   r = sv_newmortal();
-                   EXTEND(SP,1);
-                   sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
-                   PUSHs(r);
-             } else {
-                   r = sv_newmortal();
-                   EXTEND(SP,2);
-                   sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
-                   PUSHs(r);
-                   PUSHs(sv_2mortal(newSVpv(iptc_itext,tlength)));
-                    myfree(iptc_itext);
-             }
-
-int
-i_exif_enabled()
-
-#endif
-
-
-const char *
-i_test_format_probe(ig, length)
-        Imager::IO     ig
-              int     length
-
-
-
-#ifdef HAVE_LIBTIFF
-
-Imager::ImgRaw
-i_readtiff_wiol(ig, allow_incomplete, page=0)
-        Imager::IO     ig
-              int     allow_incomplete
-               int     page
-
-void
-i_readtiff_multi_wiol(ig, length)
-        Imager::IO     ig
-              int     length
-      PREINIT:
-        i_img **imgs;
-        int count;
-        int i;
-      PPCODE:
-        imgs = i_readtiff_multi_wiol(ig, length, &count);
-        if (imgs) {
-          EXTEND(SP, count);
-          for (i = 0; i < count; ++i) {
-            SV *sv = sv_newmortal();
-            sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
-            PUSHs(sv);
-          }
-          myfree(imgs);
-        }
-
-
-undef_int
-i_writetiff_wiol(im, ig)
-    Imager::ImgRaw     im
-        Imager::IO     ig
-
-undef_int
-i_writetiff_multi_wiol(ig, ...)
-        Imager::IO     ig
-      PREINIT:
-        int i;
-        int img_count;
-        i_img **imgs;
-      CODE:
-        if (items < 2)
-          croak("Usage: i_writetiff_multi_wiol(ig, images...)");
-        img_count = items - 1;
-        RETVAL = 1;
-       if (img_count < 1) {
-         RETVAL = 0;
-         i_clear_error();
-         i_push_error(0, "You need to specify images to save");
-       }
-       else {
-          imgs = mymalloc(sizeof(i_img *) * img_count);
-          for (i = 0; i < img_count; ++i) {
-           SV *sv = ST(1+i);
-           imgs[i] = NULL;
-           if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
-             imgs[i] = INT2PTR(i_img *, SvIV((SV*)SvRV(sv)));
-           }
-           else {
-             i_clear_error();
-             i_push_error(0, "Only images can be saved");
-              myfree(imgs);
-             RETVAL = 0;
-             break;
-            }
-         }
-          if (RETVAL) {
-           RETVAL = i_writetiff_multi_wiol(ig, imgs, img_count);
-          }
-         myfree(imgs);
-       }
-      OUTPUT:
-        RETVAL
-
-undef_int
-i_writetiff_wiol_faxable(im, ig, fine)
-    Imager::ImgRaw     im
-        Imager::IO     ig
-              int     fine
-
-undef_int
-i_writetiff_multi_wiol_faxable(ig, fine, ...)
-        Imager::IO     ig
-        int fine
-      PREINIT:
-        int i;
-        int img_count;
-        i_img **imgs;
-      CODE:
-        if (items < 3)
-          croak("Usage: i_writetiff_multi_wiol_faxable(ig, fine, images...)");
-        img_count = items - 2;
-        RETVAL = 1;
-       if (img_count < 1) {
-         RETVAL = 0;
-         i_clear_error();
-         i_push_error(0, "You need to specify images to save");
-       }
-       else {
-          imgs = mymalloc(sizeof(i_img *) * img_count);
-          for (i = 0; i < img_count; ++i) {
-           SV *sv = ST(2+i);
-           imgs[i] = NULL;
-           if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
-             imgs[i] = INT2PTR(i_img *, SvIV((SV*)SvRV(sv)));
-           }
-           else {
-             i_clear_error();
-             i_push_error(0, "Only images can be saved");
-              myfree(imgs);
-             RETVAL = 0;
-             break;
-            }
-         }
-          if (RETVAL) {
-           RETVAL = i_writetiff_multi_wiol_faxable(ig, imgs, img_count, fine);
-          }
-         myfree(imgs);
-       }
-      OUTPUT:
-        RETVAL
-
-const char *
-i_tiff_libversion()
-
-bool
-i_tiff_has_compression(name)
-       const char *name
-
-#endif /* HAVE_LIBTIFF */
-
-
-#ifdef HAVE_LIBPNG
-
-Imager::ImgRaw
-i_readpng_wiol(ig, length)
-        Imager::IO     ig
-              int     length
-
-
-undef_int
-i_writepng_wiol(im, ig)
-    Imager::ImgRaw     im
-        Imager::IO     ig
-
-
-#endif
-
-
-#ifdef HAVE_LIBGIF
-
-void
-i_giflib_version()
-       PPCODE:
-         PUSHs(sv_2mortal(newSVnv(IM_GIFMAJOR+IM_GIFMINOR*0.1)));
-
-undef_int
-i_writegif(im,fd,colors,pixdev,fixed)
-    Imager::ImgRaw     im
-              int     fd
-              int     colors
-               int     pixdev
-            PREINIT:
-             int     fixedlen;
-            Imager__Color  fixed;
-            Imager__Color  tmp;
-            AV* av;
-            SV* sv1;
-             IV  Itmp;
-            int i;
-            CODE:
-            if (!SvROK(ST(4))) croak("Imager: Parameter 4 must be a reference to an array\n");
-            if (SvTYPE(SvRV(ST(4))) != SVt_PVAV) croak("Imager: Parameter 4 must be a reference to an array\n");
-            av=(AV*)SvRV(ST(4));
-            fixedlen=av_len(av)+1;
-            fixed=mymalloc( fixedlen*sizeof(i_color) );
-            for(i=0;i<fixedlen;i++) {
-              sv1=(*(av_fetch(av,i,0)));
-               if (sv_derived_from(sv1, "Imager::Color")) {
-                 Itmp = SvIV((SV*)SvRV(sv1));
-                 tmp = INT2PTR(i_color*, Itmp);
-               } else croak("Imager: one of the elements of array ref is not of Imager::Color type\n");
-               fixed[i]=*tmp;
-            }
-            RETVAL=i_writegif(im,fd,colors,pixdev,fixedlen,fixed);
-             myfree(fixed);
-             ST(0) = sv_newmortal();
-             if (RETVAL == 0) ST(0)=&PL_sv_undef;
-             else sv_setiv(ST(0), (IV)RETVAL);
-
-
-
-
-undef_int
-i_writegifmc(im,fd,colors)
-    Imager::ImgRaw    im
-              int     fd
-              int     colors
-
-
-undef_int
-i_writegif_gen(fd, ...)
-              int     fd
-      PROTOTYPE: $$@
-      PREINIT:
-       i_quantize quant;
-       i_img **imgs = NULL;
-       int img_count;
-       int i;
-       HV *hv;
-      CODE:
-       if (items < 3)
-           croak("Usage: i_writegif_gen(fd,hashref, images...)");
-       if (!SvROK(ST(1)) || ! SvTYPE(SvRV(ST(1))))
-           croak("i_writegif_gen: Second argument must be a hash ref");
-       hv = (HV *)SvRV(ST(1));
-       memset(&quant, 0, sizeof(quant));
-       quant.mc_size = 256;
-       quant.transp = tr_threshold;
-       quant.tr_threshold = 127;
-       handle_quant_opts(aTHX_ &quant, hv);
-       img_count = items - 2;
-       RETVAL = 1;
-       if (img_count < 1) {
-         RETVAL = 0;
-         i_clear_error();
-         i_push_error(0, "You need to specify images to save");
-       }
-       else {
-          imgs = mymalloc(sizeof(i_img *) * img_count);
-          for (i = 0; i < img_count; ++i) {
-           SV *sv = ST(2+i);
-           imgs[i] = NULL;
-           if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
-             imgs[i] = INT2PTR(i_img *, SvIV((SV*)SvRV(sv)));
-           }
-           else {
-             i_clear_error();
-             i_push_error(0, "Only images can be saved");
-             RETVAL = 0;
-             break;
-            }
-         }
-          if (RETVAL) {
-           RETVAL = i_writegif_gen(&quant, fd, imgs, img_count);
-          }
-         myfree(imgs);
-          if (RETVAL) {
-           copy_colors_back(aTHX_ hv, &quant);
-          }
-       }
-        ST(0) = sv_newmortal();
-        if (RETVAL == 0) ST(0)=&PL_sv_undef;
-        else sv_setiv(ST(0), (IV)RETVAL);
-       cleanup_quant_opts(&quant);
-
-
-undef_int
-i_writegif_callback(cb, maxbuffer,...)
-       int maxbuffer;
-      PREINIT:
-       i_quantize quant;
-       i_img **imgs = NULL;
-       int img_count;
-       int i;
-       HV *hv;
-        i_writer_data wd;
-      CODE:
-       if (items < 4)
-           croak("Usage: i_writegif_callback(\\&callback,maxbuffer,hashref, images...)");
-       if (!SvROK(ST(2)) || ! SvTYPE(SvRV(ST(2))))
-           croak("i_writegif_callback: Second argument must be a hash ref");
-       hv = (HV *)SvRV(ST(2));
-       memset(&quant, 0, sizeof(quant));
-       quant.mc_size = 256;
-       quant.transp = tr_threshold;
-       quant.tr_threshold = 127;
-       handle_quant_opts(aTHX_ &quant, hv);
-       img_count = items - 3;
-       RETVAL = 1;
-       if (img_count < 1) {
-         RETVAL = 0;
-       }
-       else {
-          imgs = mymalloc(sizeof(i_img *) * img_count);
-          for (i = 0; i < img_count; ++i) {
-           SV *sv = ST(3+i);
-           imgs[i] = NULL;
-           if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
-             imgs[i] = INT2PTR(i_img *, SvIV((SV*)SvRV(sv)));
-           }
-           else {
-             RETVAL = 0;
-             break;
-            }
-         }
-          if (RETVAL) {
-           wd.sv = ST(0);
-           RETVAL = i_writegif_callback(&quant, write_callback, (char *)&wd, maxbuffer, imgs, img_count);
-          }
-         myfree(imgs);
-          if (RETVAL) {
-           copy_colors_back(aTHX_ hv, &quant);
-          }
-       }
-       ST(0) = sv_newmortal();
-       if (RETVAL == 0) ST(0)=&PL_sv_undef;
-       else sv_setiv(ST(0), (IV)RETVAL);
-       cleanup_quant_opts(&quant);
-
-undef_int
-i_writegif_wiol(ig, opts,...)
-       Imager::IO ig
-      PREINIT:
-       i_quantize quant;
-       i_img **imgs = NULL;
-       int img_count;
-       int i;
-       HV *hv;
-      CODE:
-       if (items < 3)
-           croak("Usage: i_writegif_wiol(IO,hashref, images...)");
-       if (!SvROK(ST(1)) || ! SvTYPE(SvRV(ST(1))))
-           croak("i_writegif_callback: Second argument must be a hash ref");
-       hv = (HV *)SvRV(ST(1));
-       memset(&quant, 0, sizeof(quant));
-       quant.mc_size = 256;
-       quant.transp = tr_threshold;
-       quant.tr_threshold = 127;
-       handle_quant_opts(aTHX_ &quant, hv);
-       img_count = items - 2;
-       RETVAL = 1;
-       if (img_count < 1) {
-         RETVAL = 0;
-       }
-       else {
-          imgs = mymalloc(sizeof(i_img *) * img_count);
-          for (i = 0; i < img_count; ++i) {
-           SV *sv = ST(2+i);
-           imgs[i] = NULL;
-           if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
-             imgs[i] = INT2PTR(i_img *, SvIV((SV*)SvRV(sv)));
-           }
-           else {
-             RETVAL = 0;
-             break;
-            }
-         }
-          if (RETVAL) {
-           RETVAL = i_writegif_wiol(ig, &quant, imgs, img_count);
-          }
-         myfree(imgs);
-          if (RETVAL) {
-           copy_colors_back(aTHX_ hv, &quant);
-          }
-       }
-       ST(0) = sv_newmortal();
-       if (RETVAL == 0) ST(0)=&PL_sv_undef;
-       else sv_setiv(ST(0), (IV)RETVAL);
-       cleanup_quant_opts(&quant);
-
-void
-i_readgif(fd)
-              int     fd
-             PREINIT:
-               int*    colour_table;
-               int     colours, q, w;
-             i_img*    rimg;
-                 SV*    temp[3];
-                 AV*    ct; 
-                 SV*    r;
-              PPCODE:
-              colour_table = NULL;
-               colours = 0;
-
-       if(GIMME_V == G_ARRAY) {
-            rimg = i_readgif(fd,&colour_table,&colours);
-        } else {
-            /* don't waste time with colours if they aren't wanted */
-            rimg = i_readgif(fd,NULL,NULL);
-        }
-       
-       if (colour_table == NULL) {
-            EXTEND(SP,1);
-            r=sv_newmortal();
-            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
-            PUSHs(r);
-       } else {
-            /* the following creates an [[r,g,b], [r, g, b], [r, g, b]...] */
-            /* I don't know if I have the reference counts right or not :( */
-            /* Neither do I :-) */
-            /* No Idea here either */
-
-            ct=newAV();
-            av_extend(ct, colours);
-            for(q=0; q<colours; q++) {
-                for(w=0; w<3; w++)
-                    temp[w]=sv_2mortal(newSViv(colour_table[q*3 + w]));
-                av_store(ct, q, (SV*)newRV_noinc((SV*)av_make(3, temp)));
-            }
-            myfree(colour_table);
-
-            EXTEND(SP,2);
-            r = sv_newmortal();
-            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
-            PUSHs(r);
-            PUSHs(newRV_noinc((SV*)ct));
-        }
-
-void
-i_readgif_wiol(ig)
-     Imager::IO         ig
-             PREINIT:
-               int*    colour_table;
-               int     colours, q, w;
-             i_img*    rimg;
-                 SV*    temp[3];
-                 AV*    ct; 
-                 SV*    r;
-              PPCODE:
-              colour_table = NULL;
-               colours = 0;
-
-       if(GIMME_V == G_ARRAY) {
-            rimg = i_readgif_wiol(ig,&colour_table,&colours);
-        } else {
-            /* don't waste time with colours if they aren't wanted */
-            rimg = i_readgif_wiol(ig,NULL,NULL);
-        }
-       
-       if (colour_table == NULL) {
-            EXTEND(SP,1);
-            r=sv_newmortal();
-            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
-            PUSHs(r);
-       } else {
-            /* the following creates an [[r,g,b], [r, g, b], [r, g, b]...] */
-            /* I don't know if I have the reference counts right or not :( */
-            /* Neither do I :-) */
-            /* No Idea here either */
-
-            ct=newAV();
-            av_extend(ct, colours);
-            for(q=0; q<colours; q++) {
-                for(w=0; w<3; w++)
-                    temp[w]=sv_2mortal(newSViv(colour_table[q*3 + w]));
-                av_store(ct, q, (SV*)newRV_noinc((SV*)av_make(3, temp)));
-            }
-            myfree(colour_table);
-
-            EXTEND(SP,2);
-            r = sv_newmortal();
-            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
-            PUSHs(r);
-            PUSHs(newRV_noinc((SV*)ct));
-        }
-
-Imager::ImgRaw
-i_readgif_single_wiol(ig, page=0)
-       Imager::IO      ig
-        int            page
-
-void
-i_readgif_scalar(...)
-          PROTOTYPE: $
-            PREINIT:
-               char*    data;
-             STRLEN     length;
-               int*    colour_table;
-               int     colours, q, w;
-             i_img*    rimg;
-                 SV*    temp[3];
-                 AV*    ct; 
-                 SV*    r;
-              PPCODE:
-        data = (char *)SvPV(ST(0), length);
-        colour_table=NULL;
-        colours=0;
-
-       if(GIMME_V == G_ARRAY) {  
-            rimg=i_readgif_scalar(data,length,&colour_table,&colours);
-        } else {
-            /* don't waste time with colours if they aren't wanted */
-            rimg=i_readgif_scalar(data,length,NULL,NULL);
-        }
-
-       if (colour_table == NULL) {
-            EXTEND(SP,1);
-            r=sv_newmortal();
-            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
-            PUSHs(r);
-       } else {
-            /* the following creates an [[r,g,b], [r, g, b], [r, g, b]...] */
-            /* I don't know if I have the reference counts right or not :( */
-            /* Neither do I :-) */
-            ct=newAV();
-            av_extend(ct, colours);
-            for(q=0; q<colours; q++) {
-                for(w=0; w<3; w++)
-                    temp[w]=sv_2mortal(newSViv(colour_table[q*3 + w]));
-                av_store(ct, q, (SV*)newRV_noinc((SV*)av_make(3, temp)));
-            }
-            myfree(colour_table);
-            
-            EXTEND(SP,2);
-            r=sv_newmortal();
-            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
-            PUSHs(r);
-            PUSHs(newRV_noinc((SV*)ct));
-        }
-
-void
-i_readgif_callback(...)
-          PROTOTYPE: &
-            PREINIT:
-               int*    colour_table;
-               int     colours, q, w;
-             i_img*    rimg;
-                 SV*    temp[3];
-                 AV*    ct; 
-                 SV*    r;
-       i_reader_data    rd;
-              PPCODE:
-       rd.sv = ST(0);
-        colour_table=NULL;
-        colours=0;
-
-       if(GIMME_V == G_ARRAY) {  
-            rimg=i_readgif_callback(read_callback, (char *)&rd,&colour_table,&colours);
-        } else {
-            /* don't waste time with colours if they aren't wanted */
-            rimg=i_readgif_callback(read_callback, (char *)&rd,NULL,NULL);
-        }
+                RETVAL
 
-       if (colour_table == NULL) {
-            EXTEND(SP,1);
-            r=sv_newmortal();
-            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
-            PUSHs(r);
-       } else {
-            /* the following creates an [[r,g,b], [r, g, b], [r, g, b]...] */
-            /* I don't know if I have the reference counts right or not :( */
-            /* Neither do I :-) */
-            /* Neither do I - maybe I'll move this somewhere */
-            ct=newAV();
-            av_extend(ct, colours);
-            for(q=0; q<colours; q++) {
-                for(w=0; w<3; w++)
-                    temp[w]=sv_2mortal(newSViv(colour_table[q*3 + w]));
-                av_store(ct, q, (SV*)newRV_noinc((SV*)av_make(3, temp)));
-            }
-            myfree(colour_table);
-            
-            EXTEND(SP,2);
-            r=sv_newmortal();
-            sv_setref_pv(r, "Imager::ImgRaw", (void*)rimg);
-            PUSHs(r);
-            PUSHs(newRV_noinc((SV*)ct));
-        }
 
 void
-i_readgif_multi(fd)
-        int     fd
+i_tt_bbox(handle,point,str_sv,utf8)
+  Imager::Font::TT     handle
+            double     point
+              SV*    str_sv
+               int     utf8
+            PREINIT:
+              i_img_dim cords[BOUNDING_BOX_COUNT];
+              int rc;
+               char *  str;
+               STRLEN len;
+               int i;
+            PPCODE:
+               str = SvPV(str_sv, len);
+#ifdef SvUTF8
+               if (SvUTF8(ST(2)))
+                 utf8 = 1;
+#endif
+              if ((rc=i_tt_bbox(handle,point,str,len,cords, utf8))) {
+                 EXTEND(SP, rc);
+                 for (i = 0; i < rc; ++i) {
+                   PUSHs(sv_2mortal(newSViv(cords[i])));
+                 }
+               }
+
+void
+i_tt_has_chars(handle, text_sv, utf8)
+        Imager::Font::TT handle
+        SV  *text_sv
+        int utf8
       PREINIT:
-        i_img **imgs;
-        int count;
-        int i;
+        char const *text;
+        STRLEN len;
+        char *work;
+        size_t count;
+        size_t i;
       PPCODE:
-        imgs = i_readgif_multi(fd, &count);
-        if (imgs) {
+        i_clear_error();
+        text = SvPV(text_sv, len);
+#ifdef SvUTF8
+        if (SvUTF8(text_sv))
+          utf8 = 1;
+#endif
+        work = mymalloc(len);
+        count = i_tt_has_chars(handle, text, len, utf8, work);
+        if (GIMME_V == G_ARRAY) {
           EXTEND(SP, count);
           for (i = 0; i < count; ++i) {
-            SV *sv = sv_newmortal();
-            sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
-            PUSHs(sv);
+            PUSHs(boolSV(work[i]));
           }
-          myfree(imgs);
         }
+        else {
+          EXTEND(SP, 1);
+          PUSHs(sv_2mortal(newSVpv(work, count)));
+        }
+        myfree(work);
+
+void
+i_tt_dump_names(handle)
+        Imager::Font::TT handle
 
 void
-i_readgif_multi_scalar(data)
+i_tt_face_name(handle)
+        Imager::Font::TT handle
       PREINIT:
-        i_img **imgs;
-        int count;
-        char *data;
-        STRLEN length;
-        int i;
+        char name[255];
+        size_t len;
       PPCODE:
-        data = (char *)SvPV(ST(0), length);
-        imgs = i_readgif_multi_scalar(data, length, &count);
-        if (imgs) {
-          EXTEND(SP, count);
-          for (i = 0; i < count; ++i) {
-            SV *sv = sv_newmortal();
-            sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
-            PUSHs(sv);
-          }
-          myfree(imgs);
+        len = i_tt_face_name(handle, name, sizeof(name));
+        if (len) {
+          EXTEND(SP, 1);
+          PUSHs(sv_2mortal(newSVpv(name, len-1)));
         }
 
 void
-i_readgif_multi_callback(cb)
+i_tt_glyph_name(handle, text_sv, utf8 = 0)
+        Imager::Font::TT handle
+        SV *text_sv
+        int utf8
       PREINIT:
-        i_reader_data rd;
-        i_img **imgs;
-        int count;
-        int i;
+        char const *text;
+        STRLEN work_len;
+        size_t len;
+        size_t outsize;
+        char name[255];
+       SSize_t count = 0;
       PPCODE:
-        rd.sv = ST(0);
-        imgs = i_readgif_multi_callback(read_callback, (char *)&rd, &count);
-        if (imgs) {
-          EXTEND(SP, count);
-          for (i = 0; i < count; ++i) {
-            SV *sv = sv_newmortal();
-            sv_setref_pv(sv, "Imager::ImgRaw", (void *)imgs[i]);
-            PUSHs(sv);
+        i_clear_error();
+        text = SvPV(text_sv, work_len);
+#ifdef SvUTF8
+        if (SvUTF8(text_sv))
+          utf8 = 1;
+#endif
+        len = work_len;
+        while (len) {
+          unsigned long ch;
+          if (utf8) {
+            ch = i_utf8_advance(&text, &len);
+            if (ch == ~0UL) {
+              i_push_error(0, "invalid UTF8 character");
+              XSRETURN_EMPTY;
+            }
           }
-          myfree(imgs);
+          else {
+            ch = *text++;
+            --len;
+          }
+          EXTEND(SP, count+1);
+          if ((outsize = i_tt_glyph_name(handle, ch, name, sizeof(name))) != 0) {
+           ST(count) = sv_2mortal(newSVpv(name, 0));
+          }
+          else {
+           ST(count) = &PL_sv_undef;
+          }
+          ++count;
         }
+       XSRETURN(count);
+
+#endif 
+
+const char *
+i_test_format_probe(ig, length)
+        Imager::IO     ig
+              int     length
+
+Imager::ImgRaw
+i_readpnm_wiol(ig, allow_incomplete)
+        Imager::IO     ig
+              int     allow_incomplete
+
 
 void
-i_readgif_multi_wiol(ig)
+i_readpnm_multi_wiol(ig, allow_incomplete)
         Imager::IO ig
+              int     allow_incomplete
       PREINIT:
         i_img **imgs;
-        int count;
+        int count=0;
         int i;
       PPCODE:
-        imgs = i_readgif_multi_wiol(ig, &count);
+        imgs = i_readpnm_multi_wiol(ig, &count, allow_incomplete);
         if (imgs) {
           EXTEND(SP, count);
           for (i = 0; i < count; ++i) {
@@ -3007,28 +2563,20 @@ i_readgif_multi_wiol(ig)
           myfree(imgs);
         }
 
-
-#endif
-
-
-
-Imager::ImgRaw
-i_readpnm_wiol(ig, allow_incomplete)
-        Imager::IO     ig
-              int     allow_incomplete
-
-
 undef_int
 i_writeppm_wiol(im, ig)
     Imager::ImgRaw     im
         Imager::IO     ig
 
 
+
+
+
 Imager::ImgRaw
 i_readraw_wiol(ig,x,y,datachannels,storechannels,intrl)
         Imager::IO     ig
-              int     x
-              int     y
+              i_img_dim     x
+              i_img_dim     y
               int     datachannels
               int     storechannels
               int     intrl
@@ -3076,20 +2624,20 @@ i_readtga_wiol(ig, length)
 Imager::ImgRaw
 i_scaleaxis(im,Value,Axis)
     Imager::ImgRaw     im
-             float     Value
+             double     Value
               int     Axis
 
 Imager::ImgRaw
 i_scale_nn(im,scx,scy)
     Imager::ImgRaw     im
-             float     scx
-             float     scy
+             double    scx
+             double    scy
 
 Imager::ImgRaw
 i_scale_mixing(im, width, height)
     Imager::ImgRaw     im
-              int     width
-              int     height
+              i_img_dim     width
+              i_img_dim     height
 
 Imager::ImgRaw
 i_haar(im)
@@ -3118,56 +2666,25 @@ i_get_anonymous_color_histo(im, maxc = 0x40000000)
         XSRETURN(col_cnt);
 
 
-Imager::ImgRaw
-i_transform(im,opx,opy,parm)
+void
+i_transform(im, opx, opy, parm)
     Imager::ImgRaw     im
+    int *opx
+    int *opy
+    double *parm
              PREINIT:
-             double* parm;
-             int*    opx;
-             int*    opy;
-             int     opxl;
-             int     opyl;
-             int     parmlen;
-             AV* av;
-             SV* sv1;
-             int i;
-             CODE:
-             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 (SvTYPE(SvRV(ST(1))) != SVt_PVAV) croak("Imager: Parameter 1 must be a reference to an array\n");
-             if (SvTYPE(SvRV(ST(2))) != SVt_PVAV) croak("Imager: Parameter 2 must be a reference to an array\n");
-             if (SvTYPE(SvRV(ST(3))) != SVt_PVAV) croak("Imager: Parameter 3 must be a reference to an array\n");
-             av=(AV*)SvRV(ST(1));
-             opxl=av_len(av)+1;
-             opx=mymalloc( opxl*sizeof(int) );
-             for(i=0;i<opxl;i++) {
-               sv1=(*(av_fetch(av,i,0)));
-               opx[i]=(int)SvIV(sv1);
-             }
-             av=(AV*)SvRV(ST(2));
-             opyl=av_len(av)+1;
-             opy=mymalloc( opyl*sizeof(int) );
-             for(i=0;i<opyl;i++) {
-               sv1=(*(av_fetch(av,i,0)));
-               opy[i]=(int)SvIV(sv1);
-             }
-             av=(AV*)SvRV(ST(3));
-             parmlen=av_len(av)+1;
-             parm=mymalloc( parmlen*sizeof(double) );
-             for(i=0;i<parmlen;i++) { /* FIXME: Bug? */
-               sv1=(*(av_fetch(av,i,0)));
-               parm[i]=(double)SvNV(sv1);
-             }
-             RETVAL=i_transform(im,opx,opxl,opy,opyl,parm,parmlen);
-             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);
+            STRLEN size_opx, size_opy, size_parm;
+            i_img *result;
+             PPCODE:
+             result=i_transform(im,opx,size_opx,opy,size_opy,parm,size_parm);
+            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
@@ -3177,8 +2694,8 @@ i_transform2(sv_width,sv_height,channels,sv_ops,av_n_regs,av_c_regs,av_in_imgs)
        AV *av_in_imgs
        int channels
             PREINIT:
-             int width;
-             int height;
+             i_img_dim width;
+             i_img_dim height;
             struct rm_op *ops;
             STRLEN ops_len;
             int ops_count;
@@ -3191,7 +2708,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;
-             CODE:
+            i_img *result;
+             PPCODE:
 
              in_imgs_count = av_len(av_in_imgs)+1;
             for (i = 0; i < in_imgs_count; ++i) {
@@ -3246,16 +2764,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 */
 
-            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);
-             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
@@ -3267,6 +2788,10 @@ void
 i_hardinvert(im)
     Imager::ImgRaw     im
 
+void
+i_hardinvertall(im)
+    Imager::ImgRaw     im
+
 void
 i_noise(im,amount,type)
     Imager::ImgRaw     im
@@ -3278,9 +2803,9 @@ i_bumpmap(im,bump,channel,light_x,light_y,strength)
     Imager::ImgRaw     im
     Imager::ImgRaw     bump
                int     channel
-               int     light_x
-               int     light_y
-               int     strength
+         i_img_dim     light_x
+         i_img_dim     light_y
+         i_img_dim     strength
 
 
 void
@@ -3288,11 +2813,11 @@ i_bumpmap_complex(im,bump,channel,tx,ty,Lx,Ly,Lz,cd,cs,n,Ia,Il,Is)
     Imager::ImgRaw     im
     Imager::ImgRaw     bump
                int     channel
-               int     tx
-               int     ty
-             float     Lx
-             float     Ly
-             float     Lz
+               i_img_dim     tx
+               i_img_dim     ty
+             double     Lx
+             double     Ly
+             double     Lz
              float     cd
              float     cs
              float     n
@@ -3310,14 +2835,14 @@ i_postlevels(im,levels)
 void
 i_mosaic(im,size)
     Imager::ImgRaw     im
-               int     size
+         i_img_dim     size
 
 void
 i_watermark(im,wmark,tx,ty,pixdiff)
     Imager::ImgRaw     im
     Imager::ImgRaw     wmark
-               int     tx
-               int     ty
+               i_img_dim     tx
+               i_img_dim     ty
                int     pixdiff
 
 
@@ -3328,6 +2853,12 @@ i_autolevels(im,lsat,usat,skew)
              float     usat
              float     skew
 
+void
+i_autolevels_mono(im,lsat,usat)
+    Imager::ImgRaw     im
+             float     lsat
+             float     usat
+
 void
 i_radnoise(im,xo,yo,rscale,ascale)
     Imager::ImgRaw     im
@@ -3345,54 +2876,22 @@ i_turbnoise(im, xo, yo, scale)
 
 
 void
-i_gradgen(im, ...)
+i_gradgen(im, xo, yo, ac, dmeasure)
     Imager::ImgRaw     im
+    i_img_dim *xo
+    i_img_dim *yo
+    i_color *ac
+    int dmeasure
       PREINIT:
-       int num;
-       int *xo;
-       int *yo;
-        i_color *ival;
-       int dmeasure;
-       int i;
-       SV *sv;
-       AV *axx;
-       AV *ayy;
-       AV *ac;
+       STRLEN size_xo;
+       STRLEN size_yo;
+        STRLEN size_ac;
       CODE:
-       if (items != 5)
-           croak("Usage: i_gradgen(im, xo, yo, ival, dmeasure)");
-       if (!SvROK(ST(1)) || ! SvTYPE(SvRV(ST(1))))
-           croak("i_gradgen: Second argument must be an array ref");
-       if (!SvROK(ST(2)) || ! SvTYPE(SvRV(ST(2))))
-           croak("i_gradgen: Third argument must be an array ref");
-       if (!SvROK(ST(3)) || ! SvTYPE(SvRV(ST(3))))
-           croak("i_gradgen: Fourth argument must be an array ref");
-       axx = (AV *)SvRV(ST(1));
-       ayy = (AV *)SvRV(ST(2));
-       ac  = (AV *)SvRV(ST(3));
-       dmeasure = (int)SvIV(ST(4));
-       
-        num = av_len(axx) < av_len(ayy) ? av_len(axx) : av_len(ayy);
-       num = num <= av_len(ac) ? num : av_len(ac);
-       num++; 
-       if (num < 2) croak("Usage: i_gradgen array refs must have more than 1 entry each");
-       xo = mymalloc( sizeof(int) * num );
-       yo = mymalloc( sizeof(int) * num );
-       ival = mymalloc( sizeof(i_color) * num );
-       for(i = 0; i<num; i++) {
-         xo[i]   = (int)SvIV(* av_fetch(axx, i, 0));
-         yo[i]   = (int)SvIV(* av_fetch(ayy, i, 0));
-          sv = *av_fetch(ac, i, 0);
-         if ( !sv_derived_from(sv, "Imager::Color") ) {
-           free(axx); free(ayy); free(ac);
-            croak("i_gradgen: Element of fourth argument is not derived from Imager::Color");
-         }
-         ival[i] = *INT2PTR(i_color *, SvIV((SV *)SvRV(sv)));
-       }
-        i_gradgen(im, num, xo, yo, ival, dmeasure);
-        myfree(xo);
-        myfree(yo);
-        myfree(ival);
+        if (size_xo != size_yo || size_xo != size_ac)
+         croak("i_gradgen: x, y and color arrays must be the same size");
+       if (size_xo < 2)
+          croak("Usage: i_gradgen array refs must have more than 1 entry each");
+        i_gradgen(im, size_xo, xo, yo, ac, dmeasure);
 
 Imager::ImgRaw
 i_diff_image(im, im2, mindist=0)
@@ -3497,8 +2996,8 @@ i_nearest_color(im, ...)
     Imager::ImgRaw     im
       PREINIT:
        int num;
-       int *xo;
-       int *yo;
+       i_img_dim *xo;
+       i_img_dim *yo;
         i_color *ival;
        int dmeasure;
        int i;
@@ -3524,12 +3023,12 @@ i_nearest_color(im, ...)
        num = num <= av_len(ac) ? num : av_len(ac);
        num++; 
        if (num < 2) croak("Usage: i_nearest_color array refs must have more than 1 entry each");
-       xo = mymalloc( sizeof(int) * num );
-       yo = mymalloc( sizeof(int) * num );
+       xo = mymalloc( sizeof(i_img_dim) * num );
+       yo = mymalloc( sizeof(i_img_dim) * num );
        ival = mymalloc( sizeof(i_color) * num );
        for(i = 0; i<num; i++) {
-         xo[i]   = (int)SvIV(* av_fetch(axx, i, 0));
-         yo[i]   = (int)SvIV(* av_fetch(ayy, i, 0));
+         xo[i]   = (i_img_dim)SvIV(* av_fetch(axx, i, 0));
+         yo[i]   = (i_img_dim)SvIV(* av_fetch(ayy, i, 0));
           sv = *av_fetch(ac, i, 0);
          if ( !sv_derived_from(sv, "Imager::Color") ) {
            free(axx); free(ayy); free(ac);
@@ -3590,30 +3089,20 @@ void
 DSO_call(handle,func_index,hv)
               void*  handle
               int    func_index
-            PREINIT:
-              HV* hv;
+              HV *hv
             PPCODE:
-              if (!SvROK(ST(2))) croak("Imager: Parameter 2 must be a reference to a hash\n");        
-              hv=(HV*)SvRV(ST(2));
-              if (SvTYPE(hv)!=SVt_PVHV) croak("Imager: Parameter 2 must be a reference to a hash\n");
               DSO_call( (DSO_handle *)handle,func_index,hv);
 
-SV *
+Imager::Color
 i_get_pixel(im, x, y)
        Imager::ImgRaw im
-       int x
-       int y;
-      PREINIT:
-        i_color *color;
+       i_img_dim x
+       i_img_dim y;
       CODE:
-       color = (i_color *)mymalloc(sizeof(i_color));
-       if (i_gpix(im, x, y, color) == 0) {
-          RETVAL = NEWSV(0, 0);
-          sv_setref_pv(RETVAL, "Imager::Color", (void *)color);
-        }
-        else {
-          myfree(color);
-          RETVAL = &PL_sv_undef;
+       RETVAL = (i_color *)mymalloc(sizeof(i_color));
+       if (i_gpix(im, x, y, RETVAL) != 0) {
+          myfree(RETVAL);
+         XSRETURN_UNDEF;
         }
       OUTPUT:
         RETVAL
@@ -3622,14 +3111,14 @@ i_get_pixel(im, x, y)
 int
 i_ppix(im, x, y, cl)
         Imager::ImgRaw im
-        int x
-        int y
+        i_img_dim x
+        i_img_dim y
         Imager::Color cl
 
 Imager::ImgRaw
 i_img_pal_new(x, y, channels, maxpal)
-       int     x
-        int    y
+       i_img_dim x
+        i_img_dim y
         int     channels
        int     maxpal
 
@@ -3644,13 +3133,14 @@ i_img_to_pal(src, quant)
           croak("i_img_to_pal: second argument must be a hash ref");
         hv = (HV *)SvRV(ST(1));
         memset(&quant, 0, sizeof(quant));
+       quant.version = 1;
         quant.mc_size = 256;
-       handle_quant_opts(aTHX_ &quant, hv);
+       ip_handle_quant_opts(aTHX_ &quant, hv);
         RETVAL = i_img_to_pal(src, &quant);
         if (RETVAL) {
-          copy_colors_back(aTHX_ hv, &quant);
+          ip_copy_colors_back(aTHX_ hv, &quant);
         }
-       cleanup_quant_opts(&quant);
+       ip_cleanup_quant_opts(aTHX_ &quant);
       OUTPUT:
         RETVAL
 
@@ -3658,12 +3148,46 @@ Imager::ImgRaw
 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
-        int     l
-        int     r
-        int     y
+        i_img_dim     l
+        i_img_dim     r
+        i_img_dim     y
       PREINIT:
         i_palidx *work;
         int count, i;
@@ -3693,20 +3217,19 @@ i_gpal(im, l, r, y)
 int
 i_ppal(im, l, y, ...)
         Imager::ImgRaw  im
-        int     l
-        int     y
+        i_img_dim     l
+        i_img_dim     y
       PREINIT:
         i_palidx *work;
-        int i;
+        i_img_dim i;
       CODE:
         if (items > 3) {
-          work = mymalloc(sizeof(i_palidx) * (items-3));
+          work = malloc_temp(aTHX_ sizeof(i_palidx) * (items-3));
           for (i=0; i < items-3; ++i) {
             work[i] = SvIV(ST(i+3));
           }
           validate_i_ppal(im, work, items - 3);
           RETVAL = i_ppal(im, l, l+items-3, y, work);
-          myfree(work);
         }
         else {
           RETVAL = 0;
@@ -3717,8 +3240,8 @@ i_ppal(im, l, y, ...)
 int
 i_ppal_p(im, l, y, data)
         Imager::ImgRaw  im
-        int     l
-        int     y
+        i_img_dim     l
+        i_img_dim     y
         SV *data
       PREINIT:
         i_palidx const *work;
@@ -3736,11 +3259,10 @@ i_ppal_p(im, l, y, data)
       OUTPUT:
         RETVAL
 
-SV *
+SysRet
 i_addcolors(im, ...)
         Imager::ImgRaw  im
       PREINIT:
-        int index;
         i_color *colors;
         int i;
       CODE:
@@ -3758,17 +3280,7 @@ i_addcolors(im, ...)
             croak("i_addcolor: pixels must be Imager::Color objects");
           }
         }
-        index = i_addcolors(im, colors, items-1);
-        myfree(colors);
-        if (index == 0) {
-          RETVAL = newSVpv("0 but true", 0);
-        }
-        else if (index == -1) {
-          RETVAL = &PL_sv_undef;
-        }
-        else {
-          RETVAL = newSViv(index);
-        }
+        RETVAL = i_addcolors(im, colors, items-1);
       OUTPUT:
         RETVAL
 
@@ -3800,33 +3312,24 @@ i_setcolors(im, index, ...)
        RETVAL
 
 void
-i_getcolors(im, index, ...)
+i_getcolors(im, index, count=1)
         Imager::ImgRaw im
         int index
+       int count
       PREINIT:
         i_color *colors;
-        int count = 1;
         int i;
       PPCODE:
-        if (items > 3)
-          croak("i_getcolors: too many arguments");
-        if (items == 3)
-          count = SvIV(ST(2));
         if (count < 1)
           croak("i_getcolors: count must be positive");
-        colors = mymalloc(sizeof(i_color) * count);
+        colors = malloc_temp(aTHX_ sizeof(i_color) * count);
         if (i_getcolors(im, index, colors, count)) {
+         EXTEND(SP, 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);
           }
         }
-        myfree(colors);
-
 
 undef_neg_int
 i_colorcount(im)
@@ -3836,18 +3339,13 @@ undef_neg_int
 i_maxcolors(im)
         Imager::ImgRaw im
 
-SV *
+i_palidx
 i_findcolor(im, color)
         Imager::ImgRaw im
         Imager::Color color
-      PREINIT:
-        i_palidx index;
       CODE:
-        if (i_findcolor(im, color, &index)) {
-          RETVAL = newSViv(index);
-        }
-        else {
-          RETVAL = &PL_sv_undef;
+        if (!i_findcolor(im, color, &RETVAL)) {
+         XSRETURN_UNDEF;
         }
       OUTPUT:
         RETVAL
@@ -3865,27 +3363,19 @@ i_img_virtual(im)
         Imager::ImgRaw  im
 
 void
-i_gsamp(im, l, r, y, ...)
+i_gsamp(im, l, r, y, channels)
         Imager::ImgRaw im
-        int l
-        int r
-        int y
+        i_img_dim l
+        i_img_dim r
+        i_img_dim y
+        i_channel_list channels
       PREINIT:
-        int *chans;
-        int chan_count;
         i_sample_t *data;
-        int count, i;
+        i_img_dim count, i;
       PPCODE:
-        if (items < 5)
-          croak("No channel numbers supplied to g_samp()");
         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);
+          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)
@@ -3899,37 +3389,30 @@ i_gsamp(im, l, r, y, ...)
         }
         else {
           if (GIMME_V != G_ARRAY) {
-            EXTEND(SP, 1);
-            PUSHs(&PL_sv_undef);
+           XSRETURN_UNDEF;
           }
         }
 
 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
-        int l
-        int r
-        int y
+        i_img_dim l
+        i_img_dim r
+        i_img_dim y
        int bits
        AV *target
-       int offset
+       STRLEN offset
+        i_channel_list channels
       PREINIT:
-        int *chans;
-        int chan_count;
         unsigned *data;
-        int count, i;
+        i_img_dim count, i;
       CODE:
        i_clear_error();
         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]));
          }
@@ -3943,77 +3426,123 @@ i_gsamp_bits(im, l, r, y, bits, target, offset, ...)
        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
-        int l
-        int y
+        i_img_dim l
+        i_img_dim y
        int bits
-       SV *channels_sv
+       i_channel_list channels
        AV *data_av
-        int data_offset
-        int pixel_count
+        i_img_dim data_offset
+        i_img_dim pixel_count
       PREINIT:
-       int chan_count;
-       int *channels;
-       int data_count;
-       int data_used;
+       STRLEN data_count;
+       size_t data_used;
        unsigned *data;
-       int i;
+       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) {
-         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 || 
-           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));
 
-       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 (channels)
-         myfree(channels);
       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
-        int x
-        int y
-        int w
-        int h
+        i_img_dim x
+        i_img_dim y
+        i_img_dim w
+        i_img_dim h
       PREINIT:
         i_img *mask;
       CODE:
@@ -4033,13 +3562,13 @@ i_img_masked_new(targ, mask, x, y, w, h)
 int
 i_plin(im, l, y, ...)
         Imager::ImgRaw  im
-        int     l
-        int     y
+        i_img_dim     l
+        i_img_dim     y
       PREINIT:
         i_color *work;
-        int i;
+        STRLEN i;
         STRLEN len;
-        int count;
+        size_t count;
       CODE:
         if (items > 3) {
           if (items == 4 && SvOK(ST(3)) && !SvROK(ST(3))) {
@@ -4077,32 +3606,24 @@ i_plin(im, l, y, ...)
 int
 i_ppixf(im, x, y, cl)
         Imager::ImgRaw im
-        int x
-        int y
+        i_img_dim x
+        i_img_dim y
         Imager::Color::Float cl
 
 void
-i_gsampf(im, l, r, y, ...)
+i_gsampf(im, l, r, y, channels)
         Imager::ImgRaw im
-        int l
-        int r
-        int y
+        i_img_dim l
+        i_img_dim r
+        i_img_dim y
+       i_channel_list channels
       PREINIT:
-        int *chans;
-        int chan_count;
         i_fsample_t *data;
-        int count, i;
+        i_img_dim count, i;
       PPCODE:
-        if (items < 5)
-          croak("No channel numbers supplied to g_sampf()");
         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)
@@ -4116,21 +3637,20 @@ i_gsampf(im, l, r, y, ...)
         }
         else {
           if (GIMME_V != G_ARRAY) {
-            EXTEND(SP, 1);
-            PUSHs(&PL_sv_undef);
+           XSRETURN_UNDEF;
           }
         }
 
 int
 i_plinf(im, l, y, ...)
         Imager::ImgRaw  im
-        int     l
-        int     y
+        i_img_dim     l
+        i_img_dim     y
       PREINIT:
         i_fcolor *work;
-        int i;
+        i_img_dim i;
         STRLEN len;
-        int count;
+        size_t count;
       CODE:
         if (items > 3) {
           if (items == 4 && SvOK(ST(3)) && !SvROK(ST(3))) {
@@ -4166,22 +3686,16 @@ i_plinf(im, l, y, ...)
       OUTPUT:
         RETVAL
 
-SV *
+Imager::Color::Float
 i_gpixf(im, x, y)
        Imager::ImgRaw im
-       int x
-       int y;
-      PREINIT:
-        i_fcolor *color;
+       i_img_dim x
+       i_img_dim y;
       CODE:
-       color = (i_fcolor *)mymalloc(sizeof(i_fcolor));
-       if (i_gpixf(im, x, y, color) == 0) {
-          RETVAL = NEWSV(0,0);
-          sv_setref_pv(RETVAL, "Imager::Color::Float", (void *)color);
-        }
-        else {
-          myfree(color);
-          RETVAL = &PL_sv_undef;
+       RETVAL = (i_fcolor *)mymalloc(sizeof(i_fcolor));
+       if (i_gpixf(im, x, y, RETVAL) != 0) {
+          myfree(RETVAL);
+          XSRETURN_UNDEF;
         }
       OUTPUT:
         RETVAL
@@ -4189,12 +3703,12 @@ i_gpixf(im, x, y)
 void
 i_glin(im, l, r, y)
         Imager::ImgRaw im
-        int l
-        int r
-        int y
+        i_img_dim l
+        i_img_dim r
+        i_img_dim y
       PREINIT:
         i_color *vals;
-        int count, i;
+        i_img_dim count, i;
       PPCODE:
         if (l < r) {
           vals = mymalloc((r-l) * sizeof(i_color));
@@ -4203,11 +3717,7 @@ i_glin(im, l, r, y)
          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);
             }
           }
@@ -4221,12 +3731,12 @@ i_glin(im, l, r, y)
 void
 i_glinf(im, l, r, y)
         Imager::ImgRaw im
-        int l
-        int r
-        int y
+        i_img_dim l
+        i_img_dim r
+        i_img_dim y
       PREINIT:
         i_fcolor *vals;
-        int count, i;
+        i_img_dim count, i;
         i_fcolor zero;
       PPCODE:
        for (i = 0; i < MAXCHANNELS; ++i)
@@ -4254,10 +3764,16 @@ i_glinf(im, l, r, y)
           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)
-        int x
-        int y
+        i_img_dim x
+        i_img_dim y
         int ch
 
 Imager::ImgRaw
@@ -4266,21 +3782,27 @@ i_img_to_rgb16(im)
 
 Imager::ImgRaw
 i_img_double_new(x, y, ch)
-        int x
-        int y
+        i_img_dim x
+        i_img_dim y
         int ch
 
+Imager::ImgRaw
+i_img_to_drgb(im)
+       Imager::ImgRaw im
+
 undef_int
-i_tags_addn(im, name, code, idata)
+i_tags_addn(im, name_sv, code, idata)
         Imager::ImgRaw im
+       SV *name_sv
         int     code
         int     idata
       PREINIT:
         char *name;
         STRLEN len;
       CODE:
-        if (SvOK(ST(1)))
-          name = SvPV(ST(1), len);
+        SvGETMAGIC(name_sv);
+        if (SvOK(name_sv))
+          name = SvPV_nomg(name_sv, len);
         else
           name = NULL;
         RETVAL = i_tags_addn(&im->tags, name, code, idata);
@@ -4288,21 +3810,25 @@ i_tags_addn(im, name, code, idata)
         RETVAL
 
 undef_int
-i_tags_add(im, name, code, data, idata)
+i_tags_add(im, name_sv, code, data_sv, idata)
         Imager::ImgRaw  im
+       SV *name_sv
         int code
+       SV *data_sv
         int idata
       PREINIT:
         char *name;
         char *data;
         STRLEN len;
       CODE:
-        if (SvOK(ST(1)))
-          name = SvPV(ST(1), len);
+        SvGETMAGIC(name_sv);
+        if (SvOK(name_sv))
+          name = SvPV_nomg(name_sv, len);
         else
           name = NULL;
-        if (SvOK(ST(3)))
-          data = SvPV(ST(3), len);
+       SvGETMAGIC(data_sv);
+        if (SvOK(data_sv))
+          data = SvPV(data_sv, len);
         else {
           data = NULL;
           len = 0;
@@ -4311,7 +3837,7 @@ i_tags_add(im, name, code, data, idata)
       OUTPUT:
         RETVAL
 
-SV *
+SysRet
 i_tags_find(im, name, start)
         Imager::ImgRaw  im
         char *name
@@ -4320,17 +3846,14 @@ i_tags_find(im, name, start)
         int entry;
       CODE:
         if (i_tags_find(&im->tags, name, start, &entry)) {
-          if (entry == 0)
-            RETVAL = newSVpv("0 but true", 0);
-          else
-            RETVAL = newSViv(entry);
+         RETVAL = entry;
         } else {
-          RETVAL = &PL_sv_undef;
+          XSRETURN_UNDEF;
         }
       OUTPUT:
         RETVAL
 
-SV *
+SysRet
 i_tags_findn(im, code, start)
         Imager::ImgRaw  im
         int             code
@@ -4339,13 +3862,10 @@ i_tags_findn(im, code, start)
         int entry;
       CODE:
         if (i_tags_findn(&im->tags, code, start, &entry)) {
-          if (entry == 0)
-            RETVAL = newSVpv("0 but true", 0);
-          else
-            RETVAL = newSViv(entry);
+          RETVAL = entry;
         }
         else {
-          RETVAL = &PL_sv_undef;
+          XSRETURN_UNDEF;
         }
       OUTPUT:
         RETVAL
@@ -4430,437 +3950,7 @@ i_tags_count(im)
       OUTPUT:
         RETVAL
 
-#ifdef HAVE_WIN32
-
-void
-i_wf_bbox(face, size, text_sv, utf8=0)
-       char *face
-       int size
-       SV *text_sv
-       int utf8
-      PREINIT:
-       int cords[BOUNDING_BOX_COUNT];
-        int rc, i;
-       char const *text;
-         STRLEN text_len;
-      PPCODE:
-        text = SvPV(text_sv, text_len);
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-        if (rc = i_wf_bbox(face, size, text, text_len, cords, utf8)) {
-          EXTEND(SP, rc);  
-          for (i = 0; i < rc; ++i) 
-            PUSHs(sv_2mortal(newSViv(cords[i])));
-        }
-
-undef_int
-i_wf_text(face, im, tx, ty, cl, size, text_sv, align, aa, utf8 = 0)
-       char *face
-       Imager::ImgRaw im
-       int tx
-       int ty
-       Imager::Color cl
-       int size
-       SV *text_sv
-       int align
-       int aa
-       int utf8
-      PREINIT:
-       char const *text;
-       STRLEN text_len;
-      CODE:
-        text = SvPV(text_sv, text_len);
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-       RETVAL = i_wf_text(face, im, tx, ty, cl, size, text, text_len, 
-                          align, aa, utf8);
-      OUTPUT:
-       RETVAL
-
-undef_int
-i_wf_cp(face, im, tx, ty, channel, size, text_sv, align, aa, utf8 = 0)
-       char *face
-       Imager::ImgRaw im
-       int tx
-       int ty
-       int channel
-       int size
-       SV *text_sv
-       int align
-       int aa
-       int utf8
-      PREINIT:
-       char const *text;
-       STRLEN text_len;
-      CODE:
-        text = SvPV(text_sv, text_len);
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-       RETVAL = i_wf_cp(face, im, tx, ty, channel, size, text, text_len, 
-                        align, aa, utf8);
-      OUTPUT:
-       RETVAL
-
-undef_int
-i_wf_addfont(font)
-        char *font
-
-undef_int
-i_wf_delfont(font)
-        char *font
-
-#endif
-
-#ifdef HAVE_FT2
-
-MODULE = Imager         PACKAGE = Imager::Font::FT2     PREFIX=FT2_
-
-#define FT2_DESTROY(font) i_ft2_destroy(font)
-
-void
-FT2_DESTROY(font)
-        Imager::Font::FT2 font
-
-int
-FT2_CLONE_SKIP(...)
-    CODE:
-        RETVAL = 1;
-    OUTPUT:
-        RETVAL
-
-MODULE = Imager         PACKAGE = Imager::Font::FreeType2 
-
-Imager::Font::FT2
-i_ft2_new(name, index)
-        char *name
-        int index
-
-undef_int
-i_ft2_setdpi(font, xdpi, ydpi)
-        Imager::Font::FT2 font
-        int xdpi
-        int ydpi
-
-void
-i_ft2_getdpi(font)
-        Imager::Font::FT2 font
-      PREINIT:
-        int xdpi, ydpi;
-      CODE:
-        if (i_ft2_getdpi(font, &xdpi, &ydpi)) {
-          EXTEND(SP, 2);
-          PUSHs(sv_2mortal(newSViv(xdpi)));
-          PUSHs(sv_2mortal(newSViv(ydpi)));
-        }
-
-undef_int
-i_ft2_sethinting(font, hinting)
-        Imager::Font::FT2 font
-        int hinting
-
-undef_int
-i_ft2_settransform(font, matrix)
-        Imager::Font::FT2 font
-      PREINIT:
-        double matrix[6];
-        int len;
-        AV *av;
-        SV *sv1;
-        int i;
-      CODE:
-        if (!SvROK(ST(1)) || SvTYPE(SvRV(ST(1))) != SVt_PVAV)
-          croak("i_ft2_settransform: parameter 2 must be an array ref\n");
-       av=(AV*)SvRV(ST(1));
-       len=av_len(av)+1;
-        if (len > 6)
-          len = 6;
-        for (i = 0; i < len; ++i) {
-         sv1=(*(av_fetch(av,i,0)));
-         matrix[i] = SvNV(sv1);
-        }
-        for (; i < 6; ++i)
-          matrix[i] = 0;
-        RETVAL = i_ft2_settransform(font, matrix);
-      OUTPUT:
-        RETVAL
-
-void
-i_ft2_bbox(font, cheight, cwidth, text_sv, utf8)
-        Imager::Font::FT2 font
-        double cheight
-        double cwidth
-        SV *text_sv
-       int utf8
-      PREINIT:
-        int bbox[BOUNDING_BOX_COUNT];
-        int i;
-        char *text;
-        STRLEN text_len;
-        int rc;
-      PPCODE:
-        text = SvPV(text_sv, text_len);
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-        rc = i_ft2_bbox(font, cheight, cwidth, text, text_len, bbox, utf8);
-        if (rc) {
-          EXTEND(SP, rc);
-          for (i = 0; i < rc; ++i)
-            PUSHs(sv_2mortal(newSViv(bbox[i])));
-        }
-
-void
-i_ft2_bbox_r(font, cheight, cwidth, text, vlayout, utf8)
-        Imager::Font::FT2 font
-        double cheight
-        double cwidth
-        char *text
-        int vlayout
-        int utf8
-      PREINIT:
-        int bbox[8];
-        int i;
-      PPCODE:
-#ifdef SvUTF8
-        if (SvUTF8(ST(3)))
-          utf8 = 1;
-#endif
-        if (i_ft2_bbox_r(font, cheight, cwidth, text, strlen(text), vlayout,
-                         utf8, bbox)) {
-          EXTEND(SP, 8);
-          for (i = 0; i < 8; ++i)
-            PUSHs(sv_2mortal(newSViv(bbox[i])));
-        }
-
-undef_int
-i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, align, aa, vlayout, utf8)
-        Imager::Font::FT2 font
-        Imager::ImgRaw im
-        int tx
-        int ty
-        Imager::Color cl
-        double cheight
-        double cwidth
-        int align
-        int aa
-        int vlayout
-        int utf8
-      PREINIT:
-        char *text;
-        STRLEN len;
-      CODE:
-#ifdef SvUTF8
-        if (SvUTF8(ST(7))) {
-          utf8 = 1;
-        }
-#endif
-        text = SvPV(ST(7), len);
-        RETVAL = i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text,
-                            len, align, aa, vlayout, utf8);
-      OUTPUT:
-        RETVAL
-
-undef_int
-i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text_sv, align, aa, vlayout, utf8)
-        Imager::Font::FT2 font
-        Imager::ImgRaw im
-        int tx
-        int ty
-        int channel
-        double cheight
-        double cwidth
-        SV *text_sv
-        int align
-        int aa
-        int vlayout
-        int utf8
-      PREINIT:
-       char const *text;
-       STRLEN len;
-      CODE:
-#ifdef SvUTF8
-        if (SvUTF8(ST(7)))
-          utf8 = 1;
-#endif
-       text = SvPV(text_sv, len);
-        RETVAL = i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text,
-                          len, align, aa, vlayout, 1);
-      OUTPUT:
-        RETVAL
-
-void
-ft2_transform_box(font, x0, x1, x2, x3)
-        Imager::Font::FT2 font
-        int x0
-        int x1
-        int x2
-        int x3
-      PREINIT:
-        int box[4];
-      PPCODE:
-        box[0] = x0; box[1] = x1; box[2] = x2; box[3] = x3;
-        ft2_transform_box(font, box);
-          EXTEND(SP, 4);
-          PUSHs(sv_2mortal(newSViv(box[0])));
-          PUSHs(sv_2mortal(newSViv(box[1])));
-          PUSHs(sv_2mortal(newSViv(box[2])));
-          PUSHs(sv_2mortal(newSViv(box[3])));
-
-void
-i_ft2_has_chars(handle, text_sv, utf8)
-        Imager::Font::FT2 handle
-        SV  *text_sv
-        int utf8
-      PREINIT:
-        char *text;
-        STRLEN len;
-        char *work;
-        int count;
-        int i;
-      PPCODE:
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-        text = SvPV(text_sv, len);
-        work = mymalloc(len);
-        count = i_ft2_has_chars(handle, text, len, utf8, work);
-        if (GIMME_V == G_ARRAY) {
-          EXTEND(SP, count);
-          for (i = 0; i < count; ++i) {
-            PUSHs(sv_2mortal(newSViv(work[i])));
-          }
-        }
-        else {
-          EXTEND(SP, 1);
-          PUSHs(sv_2mortal(newSVpv(work, count)));
-        }
-        myfree(work);
-
-void
-i_ft2_face_name(handle)
-        Imager::Font::FT2 handle
-      PREINIT:
-        char name[255];
-        int len;
-      PPCODE:
-        len = i_ft2_face_name(handle, name, sizeof(name));
-        if (len) {
-          EXTEND(SP, 1);
-          PUSHs(sv_2mortal(newSVpv(name, 0)));
-        }
-
-undef_int
-i_ft2_can_face_name()
-
-void
-i_ft2_glyph_name(handle, text_sv, utf8 = 0, reliable_only = 1)
-        Imager::Font::FT2 handle
-        SV *text_sv
-        int utf8
-        int reliable_only
-      PREINIT:
-        char const *text;
-        STRLEN work_len;
-        int len;
-        char name[255];
-      PPCODE:
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-        text = SvPV(text_sv, work_len);
-        len = work_len;
-        while (len) {
-          unsigned long ch;
-          if (utf8) {
-            ch = i_utf8_advance(&text, &len);
-            if (ch == ~0UL) {
-              i_push_error(0, "invalid UTF8 character");
-              break;
-            }
-          }
-          else {
-            ch = *text++;
-            --len;
-          }
-          EXTEND(SP, 1);
-          if (i_ft2_glyph_name(handle, ch, name, sizeof(name), 
-                                         reliable_only)) {
-            PUSHs(sv_2mortal(newSVpv(name, 0)));
-          }
-          else {
-            PUSHs(&PL_sv_undef);
-          } 
-        }
-
-int
-i_ft2_can_do_glyph_names()
-
-int
-i_ft2_face_has_glyph_names(handle)
-        Imager::Font::FT2 handle
-
-int
-i_ft2_is_multiple_master(handle)
-        Imager::Font::FT2 handle
-
-void
-i_ft2_get_multiple_masters(handle)
-        Imager::Font::FT2 handle
-      PREINIT:
-        i_font_mm mm;
-        int i;
-      PPCODE:
-        if (i_ft2_get_multiple_masters(handle, &mm)) {
-          EXTEND(SP, 2+mm.num_axis);
-          PUSHs(sv_2mortal(newSViv(mm.num_axis)));
-          PUSHs(sv_2mortal(newSViv(mm.num_designs)));
-          for (i = 0; i < mm.num_axis; ++i) {
-            AV *av = newAV();
-            SV *sv;
-            av_extend(av, 3);
-            sv = newSVpv(mm.axis[i].name, strlen(mm.axis[i].name));
-            SvREFCNT_inc(sv);
-            av_store(av, 0, sv);
-            sv = newSViv(mm.axis[i].minimum);
-            SvREFCNT_inc(sv);
-            av_store(av, 1, sv);
-            sv = newSViv(mm.axis[i].maximum);
-            SvREFCNT_inc(sv);
-            av_store(av, 2, sv);
-            PUSHs(newRV_noinc((SV *)av));
-          }
-        }
-
-undef_int
-i_ft2_set_mm_coords(handle, ...)
-        Imager::Font::FT2 handle
-      PROTOTYPE: DISABLE
-      PREINIT:
-        long *coords;
-        int ix_coords, i;
-      CODE:
-        /* T_ARRAY handling by xsubpp seems to be busted in 5.6.1, so
-           transfer the array manually */
-        ix_coords = items-1;
-        coords = mymalloc(sizeof(long) * ix_coords);
-       for (i = 0; i < ix_coords; ++i) {
-          coords[i] = (long)SvIV(ST(1+i));
-        }
-        RETVAL = i_ft2_set_mm_coords(handle, ix_coords, coords);
-        myfree(coords);
-      OUTPUT:
-        RETVAL
 
-#endif
 
 MODULE = Imager         PACKAGE = Imager::FillHandle PREFIX=IFILL_
 
@@ -4871,6 +3961,7 @@ IFILL_DESTROY(fill)
 int
 IFILL_CLONE_SKIP(...)
     CODE:
+        (void)items; /* avoid unused warning for XS variable */
         RETVAL = 1;
     OUTPUT:
         RETVAL
@@ -4888,19 +3979,21 @@ i_new_fill_solidf(cl, combine)
         int combine
 
 Imager::FillHandle
-i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy)
+i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch_sv, dx, dy)
         Imager::Color fg
         Imager::Color bg
         int combine
         int hatch
-        int dx
-        int dy
+       SV *cust_hatch_sv
+        i_img_dim dx
+        i_img_dim dy
       PREINIT:
         unsigned char *cust_hatch;
         STRLEN len;
       CODE:
-        if (SvOK(ST(4))) {
-          cust_hatch = (unsigned char *)SvPV(ST(4), len);
+        SvGETMAGIC(cust_hatch_sv);
+        if (SvOK(cust_hatch_sv)) {
+          cust_hatch = (unsigned char *)SvPV_nomg(cust_hatch_sv, len);
         }
         else
           cust_hatch = NULL;
@@ -4909,19 +4002,21 @@ i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy)
         RETVAL
 
 Imager::FillHandle
-i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy)
+i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch_sv, dx, dy)
         Imager::Color::Float fg
         Imager::Color::Float bg
         int combine
         int hatch
-        int dx
-        int dy
+        SV *cust_hatch_sv
+        i_img_dim dx
+        i_img_dim dy
       PREINIT:
         unsigned char *cust_hatch;
         STRLEN len;
       CODE:
-        if (SvOK(ST(4))) {
-          cust_hatch = (unsigned char *)SvPV(ST(4), len);
+        SvGETMAGIC(cust_hatch_sv);
+        if (SvOK(cust_hatch_sv)) {
+          cust_hatch = (unsigned char *)SvPV(cust_hatch_sv, len);
         }
         else
           cust_hatch = NULL;
@@ -4930,10 +4025,11 @@ i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy)
         RETVAL
 
 Imager::FillHandle
-i_new_fill_image(src, matrix, xoff, yoff, combine)
+i_new_fill_image(src, matrix_sv, xoff, yoff, combine)
         Imager::ImgRaw src
-        int xoff
-        int yoff
+       SV *matrix_sv
+        i_img_dim xoff
+        i_img_dim yoff
         int combine
       PREINIT:
         double matrix[9];
@@ -4943,13 +4039,14 @@ i_new_fill_image(src, matrix, xoff, yoff, combine)
         SV *sv1;
         int i;
       CODE:
-        if (!SvOK(ST(1))) {
+        SvGETMAGIC(matrix_sv);
+        if (!SvOK(matrix_sv)) {
           matrixp = NULL;
         }
         else {
-          if (!SvROK(ST(1)) || SvTYPE(SvRV(ST(1))) != SVt_PVAV)
-            croak("i_new_fill_image: parameter must be an arrayref");
-         av=(AV*)SvRV(ST(1));
+          if (!SvROK(matrix_sv) || SvTYPE(SvRV(matrix_sv)) != SVt_PVAV)
+            croak("i_new_fill_image: matrix parameter must be an arrayref or undef");
+         av=(AV*)SvRV(matrix_sv);
          len=av_len(av)+1;
           if (len > 9)
             len = 9;
@@ -4976,9 +4073,9 @@ i_int_hlines_testing()
 
 Imager::Internal::Hlines
 i_int_hlines_new(start_y, count_y, start_x, count_x)
-       int start_y
+       i_img_dim start_y
        int count_y
-       int start_x
+       i_img_dim start_x
        int count_x
 
 Imager::Internal::Hlines
@@ -4988,9 +4085,9 @@ i_int_hlines_new_img(im)
 void
 i_int_hlines_add(hlines, y, minx, width)
        Imager::Internal::Hlines hlines
-       int y
-       int minx
-       int width
+       i_img_dim y
+       i_img_dim minx
+       i_img_dim width
 
 void
 i_int_hlines_DESTROY(hlines)
@@ -5002,9 +4099,40 @@ i_int_hlines_dump(hlines)
 
 int
 i_int_hlines_CLONE_SKIP(cls)
-       SV *cls
+
+#endif
+
+MODULE = Imager  PACKAGE = Imager::Context PREFIX=im_context_
+
+void
+im_context_DESTROY(ctx)
+   Imager::Context ctx
+
+#ifdef PERL_IMPLICIT_CONTEXT
+
+void
+im_context_CLONE(...)
+    CODE:
+      MY_CXT_CLONE;
+      (void)items;
+      /* the following sv_setref_pv() will free this inc */
+      im_context_refinc(MY_CXT.ctx, "CLONE");
+      MY_CXT.ctx = im_context_clone(MY_CXT.ctx, "CLONE");
+      sv_setref_pv(get_sv("Imager::_context", GV_ADD), "Imager::Context", MY_CXT.ctx);
 
 #endif
 
 BOOT:
         PERL_SET_GLOBAL_CALLBACKS;
+       PERL_PL_SET_GLOBAL_CALLBACKS;
+#ifdef PERL_IMPLICIT_CONTEXT
+       {
+          MY_CXT_INIT;
+         (void)MY_CXT;
+       }
+#endif
+       start_context(aTHX);
+       im_get_context = perl_get_context;
+#ifdef HAVE_LIBTT
+        i_tt_start();
+#endif