]> git.imager.perl.org - imager.git/blobdiff - Imager.xs
change C<allow_partial> to C<allow_incomplete> to match the tag name
[imager.git] / Imager.xs
index 12a49dc1b5001cdfe7a40540270198813f53b066..32abacc93fd947a49e4354b3da9733616c159017 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -4,29 +4,114 @@ extern "C" {
 #include "EXTERN.h"
 #include "perl.h"
 #include "XSUB.h"
+#define NEED_newRV_noinc
+#define NEED_sv_2pv_nolen
 #include "ppport.h"
 #ifdef __cplusplus
-
+}
 #endif
 
-#include "image.h"
+#define i_int_hlines_testing() 1
+
+#include "imager.h"
 #include "feat.h"
 #include "dynaload.h"
 #include "regmach.h"
+#include "imextdef.h"
 
-typedef io_glue* Imager__IO;
-typedef i_color* Imager__Color;
-typedef i_fcolor* Imager__Color__Float;
-typedef i_img*   Imager__ImgRaw;
+#if i_int_hlines_testing()
+#include "imageri.h"
+#endif
 
+#include "imperl.h"
 
-#ifdef HAVE_LIBTT
-typedef TT_Fonthandle* Imager__TTHandle;
-#endif
+/* These functions are all shared - then comes platform dependant code */
+static int getstr(void *hv_t,char *key,char **store) {
+  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));
+
+  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
+
+  svpp=hv_fetch(hv, key, strlen(key), 0);
+  *store=SvPV(*svpp, PL_na );
+
+  return 1;
+}
+
+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));
+
+  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
+
+  svpp=hv_fetch(hv, key, strlen(key), 0);
+  *store=(int)SvIV(*svpp);
+  return 1;
+}
+
+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));
+
+  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
+  svpp=hv_fetch(hv, key, strlen(key), 0);
+  *store=(float)SvNV(*svpp);
+  return 1;
+}
+
+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));
+
+  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
+
+  svpp=hv_fetch(hv, key, strlen(key), 0);
+  *store = INT2PTR(void*, SvIV(*svpp));
+
+  return 1;
+}
+
+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));
+
+  if ( !hv_exists(hv,key,strlen(key)) ) return 0;
+
+  svpp=hv_fetch(hv, key, strlen(key), 0);
+
+  if (sv_derived_from(*svpp,type)) {
+    IV tmp = SvIV((SV*)SvRV(*svpp));
+    *store = INT2PTR(void*, tmp);
+  } else {
+    mm_log((1,"getobj: key exists in hash but is not of correct type"));
+    return 0;
+  }
+
+  return 1;
+}
+
+UTIL_table_t i_UTIL_table={getstr,getint,getdouble,getvoid,getobj};
+
+void my_SvREFCNT_dec(void *p) {
+  SvREFCNT_dec((SV*)p);
+}
+
+
+static void
+i_log_entry(char *string, int level) {
+  mm_log((level, string));
+}
 
-#ifdef HAVE_FT2
-typedef FT2_Fonthandle* Imager__Font__FT2;
-#endif
 
 typedef struct i_reader_data_tag
 {
@@ -118,6 +203,311 @@ static int write_callback(char *userdata, char const *data, int size) {
   return success;
 }
 
+#define CBDATA_BUFSIZE 8192
+
+struct cbdata {
+  /* the SVs we use to call back to Perl */
+  SV *writecb;
+  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) {
+  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) {
+  int count;
+  int result;
+  SV *data;
+  dSP;
+
+  if (!SvOK(cbd->readcb))
+    return -1;
+
+  ENTER;
+  SAVETMPS;
+  EXTEND(SP, 2);
+  PUSHMARK(SP);
+  PUSHs(sv_2mortal(newSViv(size)));
+  PUSHs(sv_2mortal(newSViv(maxread)));
+  PUTBACK;
+
+  count = perl_call_sv(cbd->readcb, 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 > maxread)
+      croak("Too much data returned in reader callback");
+    
+    memcpy(buf, ptr, len);
+    result = len;
+  }
+  else {
+    result = -1;
+  }
+
+  PUTBACK;
+  FREETMPS;
+  LEAVE;
+
+  return result;
+}
+
+static ssize_t write_flush(struct cbdata *cbd) {
+  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) {
+  struct cbdata *cbd = p;
+  int count;
+  off_t result;
+  dSP;
+
+  if (!SvOK(cbd->seekcb))
+    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);
+  PUSHMARK(SP);
+  PUSHs(sv_2mortal(newSViv(offset)));
+  PUSHs(sv_2mortal(newSViv(whence)));
+  PUTBACK;
+
+  count = perl_call_sv(cbd->seekcb, G_SCALAR);
+
+  SPAGAIN;
+
+  if (count != 1)
+    croak("Result of perl_call_sv(..., G_SCALAR) != 1");
+
+  result = POPi;
+
+  PUTBACK;
+  FREETMPS;
+  LEAVE;
+
+  return result;
+}
+
+static ssize_t io_writer(void *p, void const *data, size_t size) {
+  struct cbdata *cbd = p;
+
+  /* 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;
+  }
+  /* it doesn't fit - just pass it up */
+  return call_writer(cbd, data, size);
+}
+
+static ssize_t 
+io_reader(void *p, void *data, size_t size) {
+  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;
+}
+
+static int io_closer(void *p) {
+  struct cbdata *cbd = p;
+
+  if (cbd->writing && cbd->used > 0) {
+    if (write_flush(cbd) < 0)
+      return -1;
+    cbd->writing = 0;
+  }
+
+  if (SvOK(cbd->closecb)) {
+    dSP;
+
+    ENTER;
+    SAVETMPS;
+    PUSHMARK(SP);
+    PUTBACK;
+
+    perl_call_sv(cbd->closecb, G_VOID);
+
+    SPAGAIN;
+    PUTBACK;
+    FREETMPS;
+    LEAVE;
+  }
+
+  return 0;
+}
+
+static void io_destroyer(void *p) {
+  struct cbdata *cbd = p;
+
+  SvREFCNT_dec(cbd->writecb);
+  SvREFCNT_dec(cbd->readcb);
+  SvREFCNT_dec(cbd->seekcb);
+  SvREFCNT_dec(cbd->closecb);
+  myfree(cbd);
+}
+
 struct value_name {
   char *name;
   int value;
@@ -144,6 +534,9 @@ static struct value_name make_color_names[] =
   { "none", mc_none, },
   { "webmap", mc_web_map, },
   { "addi", mc_addi, },
+  { "mediancut", mc_median_cut, },
+  { "mono", mc_mono, },
+  { "monochrome", mc_mono, },
 };
 
 static struct value_name translate_names[] =
@@ -188,6 +581,8 @@ static void handle_quant_opts(i_quantize *quant, HV *hv)
   STRLEN len;
   char *str;
 
+  quant->mc_colors = mymalloc(quant->mc_size * sizeof(i_color));
+
   sv = hv_fetch(hv, "transp", 6, 0);
   if (sv && *sv && (str = SvPV(*sv, len))) {
     quant->transp = 
@@ -247,7 +642,7 @@ static void handle_quant_opts(i_quantize *quant, HV *hv)
     for (i = 0; i < quant->mc_count; ++i) {
       SV **sv1 = av_fetch(av, i, 0);
       if (sv1 && *sv1 && SvROK(*sv1) && sv_derived_from(*sv1, "Imager::Color")) {
-       i_color *col = (i_color *)SvIV((SV*)SvRV(*sv1));
+       i_color *col = INT2PTR(i_color *, SvIV((SV*)SvRV(*sv1)));
        quant->mc_colors[i] = *col;
       }
     }
@@ -309,78 +704,10 @@ static void handle_quant_opts(i_quantize *quant, HV *hv)
     quant->perturb = SvIV(*sv);
 }
 
-/* look through the hash for options to add to opts */
-static void handle_gif_opts(i_gif_opts *opts, HV *hv)
-{
-  /*** FIXME: POSSIBLY BROKEN: do I need to unref the SV from hv_fetch? ***/
-  SV **sv;
-  int i;
-  /**((char *)0) = '\0';*/
-  sv = hv_fetch(hv, "gif_each_palette", 16, 0);
-  if (sv && *sv)
-    opts->each_palette = SvIV(*sv);
-  sv = hv_fetch(hv, "interlace", 9, 0);
-  if (sv && *sv)
-    opts->interlace = SvIV(*sv);
-  sv = hv_fetch(hv, "gif_delays", 10, 0);
-  if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
-    AV *av = (AV*)SvRV(*sv);
-    opts->delay_count = av_len(av)+1;
-    opts->delays = mymalloc(sizeof(int) * opts->delay_count);
-    for (i = 0; i < opts->delay_count; ++i) {
-      SV *sv1 = *av_fetch(av, i, 0);
-      opts->delays[i] = SvIV(sv1);
-    }
-  }
-  sv = hv_fetch(hv, "gif_user_input", 14, 0);
-  if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
-    AV *av = (AV*)SvRV(*sv);
-    opts->user_input_count = av_len(av)+1;
-    opts->user_input_flags = mymalloc(opts->user_input_count);
-    for (i = 0; i < opts->user_input_count; ++i) {
-      SV *sv1 = *av_fetch(av, i, 0);
-      opts->user_input_flags[i] = SvIV(sv1) != 0;
-    }
-  }
-  sv = hv_fetch(hv, "gif_disposal", 12, 0);
-  if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
-    AV *av = (AV*)SvRV(*sv);
-    opts->disposal_count = av_len(av)+1;
-    opts->disposal = mymalloc(opts->disposal_count);
-    for (i = 0; i < opts->disposal_count; ++i) {
-      SV *sv1 = *av_fetch(av, i, 0);
-      opts->disposal[i] = SvIV(sv1);
-    }
-  }
-  sv = hv_fetch(hv, "gif_tran_color", 14, 0);
-  if (sv && *sv && SvROK(*sv) && sv_derived_from(*sv, "Imager::Color")) {
-    i_color *col = (i_color *)SvIV((SV *)SvRV(*sv));
-    opts->tran_color = *col;
-  }
-  sv = hv_fetch(hv, "gif_positions", 13, 0);
-  if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
-    AV *av = (AV *)SvRV(*sv);
-    opts->position_count = av_len(av) + 1;
-    opts->positions = mymalloc(sizeof(i_gif_pos) * opts->position_count);
-    for (i = 0; i < opts->position_count; ++i) {
-      SV **sv2 = av_fetch(av, i, 0);
-      opts->positions[i].x = opts->positions[i].y = 0;
-      if (sv && *sv && SvROK(*sv) && SvTYPE(SvRV(*sv)) == SVt_PVAV) {
-       AV *av2 = (AV*)SvRV(*sv2);
-       SV **sv3;
-       sv3 = av_fetch(av2, 0, 0);
-       if (sv3 && *sv3)
-         opts->positions[i].x = SvIV(*sv3);
-       sv3 = av_fetch(av2, 1, 0);
-       if (sv3 && *sv3)
-         opts->positions[i].y = SvIV(*sv3);
-      }
-    }
-  }
-  /* Netscape2.0 loop count extension */
-  sv = hv_fetch(hv, "gif_loop_count", 14, 0);
-  if (sv && *sv)
-    opts->loop_count = SvIV(*sv);
+static void cleanup_quant_opts(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 */
@@ -413,6 +740,105 @@ static void copy_colors_back(HV *hv, i_quantize *quant) {
   }
 }
 
+/* loads the segments of a fountain fill into an array */
+static i_fountain_seg *
+load_fount_segs(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;
+  AV *aseg;
+  i_fountain_seg *segs;
+  double work[3];
+  int worki[2];
+
+  *count = av_len(asegs)+1;
+  if (*count < 1) 
+    croak("i_fountain must have at least one segment");
+  segs = mymalloc(sizeof(i_fountain_seg) * *count);
+  for(i = 0; i < *count; i++) {
+    SV **sv1 = av_fetch(asegs, i, 0);
+    if (!sv1 || !*sv1 || !SvROK(*sv1) 
+        || SvTYPE(SvRV(*sv1)) != SVt_PVAV) {
+      myfree(segs);
+      croak("i_fountain: segs must be an arrayref of arrayrefs");
+    }
+    aseg = (AV *)SvRV(*sv1);
+    if (av_len(aseg) != 7-1) {
+      myfree(segs);
+      croak("i_fountain: a segment must have 7 members");
+    }
+    for (j = 0; j < 3; ++j) {
+      SV **sv2 = av_fetch(aseg, j, 0);
+      if (!sv2 || !*sv2) {
+        myfree(segs);
+        croak("i_fountain: XS error");
+      }
+      work[j] = SvNV(*sv2);
+    }
+    segs[i].start  = work[0];
+    segs[i].middle = work[1];
+    segs[i].end    = work[2];
+    for (j = 0; j < 2; ++j) {
+      SV **sv3 = av_fetch(aseg, 3+j, 0);
+      if (!sv3 || !*sv3 || !SvROK(*sv3) ||
+          (!sv_derived_from(*sv3, "Imager::Color")
+           && !sv_derived_from(*sv3, "Imager::Color::Float"))) {
+        myfree(segs);
+        croak("i_fountain: segs must contain colors in elements 3 and 4");
+      }
+      if (sv_derived_from(*sv3, "Imager::Color::Float")) {
+        segs[i].c[j] = *INT2PTR(i_fcolor *, SvIV((SV *)SvRV(*sv3)));
+      }
+      else {
+        i_color c = *INT2PTR(i_color *, SvIV((SV *)SvRV(*sv3)));
+        int ch;
+        for (ch = 0; ch < MAXCHANNELS; ++ch) {
+          segs[i].c[j].channel[ch] = c.channel[ch] / 255.0;
+        }
+      }
+    }
+    for (j = 0; j < 2; ++j) {
+      SV **sv2 = av_fetch(aseg, j+5, 0);
+      if (!sv2 || !*sv2) {
+        myfree(segs);
+        croak("i_fountain: XS error");
+      }
+      worki[j] = SvIV(*sv2);
+    }
+    segs[i].type = worki[0];
+    segs[i].color = worki[1];
+  }
+
+  return segs;
+}
+
+/* validates the indexes supplied to i_ppal
+
+i_ppal() doesn't do that for speed, but I'm not comfortable doing that
+for calls from perl.
+
+*/
+static void
+validate_i_ppal(i_img *im, i_palidx const *indexes, int count) {
+  int color_count = i_colorcount(im);
+  int i;
+
+  if (color_count == -1)
+    croak("i_plin() called on direct color image");
+  
+  for (i = 0; i < count; ++i) {
+    if (indexes[i] >= color_count) {
+      croak("i_plin() called with out of range color index %d (max %d)",
+        indexes[i], color_count-1);
+    }
+  }
+}
+
+
 /* 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
@@ -420,6 +846,81 @@ static void copy_colors_back(HV *hv, i_quantize *quant) {
 #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
+
+#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 *result = mymalloc(sizeof(i_int_hlines));
+  i_int_init_hlines(result, start_y, count_y, start_x, count_x);
+
+  return result;
+}
+
+static i_int_hlines *
+i_int_hlines_new_img(i_img *im) {
+  i_int_hlines *result = mymalloc(sizeof(i_int_hlines));
+  i_int_init_hlines_img(result, im);
+
+  return result;
+}
+
+static void
+i_int_hlines_DESTROY(i_int_hlines *hlines) {
+  i_int_hlines_destroy(hlines);
+  myfree(hlines);
+}
+
+static int seg_compare(const void *vleft, const void *vright) {
+  const i_int_hline_seg *left = vleft;
+  const i_int_hline_seg *right = vright;
+
+  return left->minx - right->minx;
+}
+
+static SV *
+i_int_hlines_dump(i_int_hlines *hlines) {
+  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;
+  
+  for (y = hlines->start_y; y < hlines->limit_y; ++y) {
+    i_int_hline_entry *entry = hlines->entries[y-hlines->start_y];
+    if (entry) {
+      int i;
+      /* sort the segments, if any */
+      if (entry->count)
+        qsort(entry->segs, entry->count, sizeof(i_int_hline_seg), seg_compare);
+
+      sv_catpvf(dump, " %d (%d):", y, entry->count);
+      for (i = 0; i < entry->count; ++i) {
+        sv_catpvf(dump, " [%d, %d)", entry->segs[i].minx, 
+                  entry->segs[i].x_limit);
+      }
+      sv_catpv(dump, "\n");
+    }
+  }
+
+  return dump;
+}
+
+#endif
+
+#ifdef IMEXIF_ENABLE
+#define i_exif_enabled() 1
+#else
+#define i_exif_enabled() 0
+#endif
+
+/* trying to use more C style names, map them here */
+#define i_io_DESTROY(ig) io_glue_destroy(ig)
+
 MODULE = Imager                PACKAGE = Imager::Color PREFIX = ICL_
 
 Imager::Color
@@ -461,7 +962,26 @@ ICL_rgba(cl)
                PUSHs(sv_2mortal(newSVnv(cl->rgba.b)));
                PUSHs(sv_2mortal(newSVnv(cl->rgba.a)));
 
-
+Imager::Color
+i_hsv_to_rgb(c)
+        Imager::Color c
+      CODE:
+        RETVAL = mymalloc(sizeof(i_color));
+        *RETVAL = *c;
+        i_hsv_to_rgb(RETVAL);
+      OUTPUT:
+        RETVAL
+        
+Imager::Color
+i_rgb_to_hsv(c)
+        Imager::Color c
+      CODE:
+        RETVAL = mymalloc(sizeof(i_color));
+        *RETVAL = *c;
+        i_rgb_to_hsv(RETVAL);
+      OUTPUT:
+        RETVAL
+        
 
 
 MODULE = Imager        PACKAGE = Imager::Color::Float  PREFIX=ICLF_
@@ -504,6 +1024,26 @@ ICLF_set_internal(cl,r,g,b,a)
         EXTEND(SP, 1);
         PUSHs(ST(0));
 
+Imager::Color::Float
+i_hsv_to_rgb(c)
+        Imager::Color::Float c
+      CODE:
+        RETVAL = mymalloc(sizeof(i_fcolor));
+        *RETVAL = *c;
+        i_hsv_to_rgbf(RETVAL);
+      OUTPUT:
+        RETVAL
+        
+Imager::Color::Float
+i_rgb_to_hsv(c)
+        Imager::Color::Float c
+      CODE:
+        RETVAL = mymalloc(sizeof(i_fcolor));
+        *RETVAL = *c;
+        i_rgb_to_hsvf(RETVAL);
+      OUTPUT:
+        RETVAL
+
 MODULE = Imager                PACKAGE = Imager::ImgRaw        PREFIX = IIM_
 
 Imager::ImgRaw
@@ -531,22 +1071,175 @@ Imager::IO
 io_new_bufchain()
 
 
+Imager::IO
+io_new_buffer(data)
+         char   *data
+       PREINIT:
+         size_t length;
+       CODE:
+         SvPV(ST(0), length);
+          SvREFCNT_inc(ST(0));
+         RETVAL = io_new_buffer(data, length, my_SvREFCNT_dec, ST(0));
+        OUTPUT:
+          RETVAL
+
+Imager::IO
+io_new_cb(writecb, readcb, seekcb, closecb, maxwrite = CBDATA_BUFSIZE)
+        SV *writecb;
+        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);
+      OUTPUT:
+        RETVAL
+
 void
 io_slurp(ig)
         Imager::IO     ig
             PREINIT:
              unsigned char*    data;
-            size_t    tlength;
-                SV*    r;
+             size_t    tlength;
             PPCODE:
              data    = NULL;
               tlength = io_slurp(ig, &data);
-              r = sv_newmortal();
               EXTEND(SP,1);
-              PUSHs(sv_2mortal(newSVpv(data,tlength)));
+              PUSHs(sv_2mortal(newSVpv((char *)data,tlength)));
               myfree(data);
 
 
+undef_int
+i_set_image_file_limits(width, height, bytes)
+       int width
+       int height
+       int bytes
+
+void
+i_get_image_file_limits()
+      PREINIT:
+        int width, height, 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)));
+        }
+
+MODULE = Imager                PACKAGE = Imager::IO    PREFIX = i_io_
+
+int
+i_io_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);
+      OUTPUT:
+       RETVAL
+
+void
+i_io_read(ig, buffer_sv, size)
+       Imager::IO ig
+       SV *buffer_sv
+       int size
+      PREINIT:
+        void *buffer;
+       int 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
+       int size
+      PREINIT:
+       SV *buffer_sv;
+        void *buffer;
+       int 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);
+        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);
+        }
+
+int
+i_io_seek(ig, position, whence)
+       Imager::IO ig
+       long position
+       int whence
+
+int
+i_io_close(ig)
+       Imager::IO ig
+
+void
+i_io_DESTROY(ig)
+        Imager::IO     ig
+
+MODULE = Imager                PACKAGE = Imager
+
+PROTOTYPES: ENABLE
 
 void
 i_list_formats()
@@ -580,11 +1273,30 @@ i_img_empty_ch(im,x,y,ch)
               int     y
               int     ch
 
+Imager::ImgRaw
+i_sametype(im, x, y)
+    Imager::ImgRaw im
+               int x
+               int y
+
+Imager::ImgRaw
+i_sametype_chans(im, x, y, channels)
+    Imager::ImgRaw im
+               int x
+               int y
+               int channels
+
 void
-init_log(name,level)
+i_init_log(name,level)
              char*    name
               int     level
 
+void
+i_log_entry(string,level)
+             char*    string
+              int     level
+
+
 void
 i_img_exorcise(im)
     Imager::ImgRaw     im
@@ -627,27 +1339,30 @@ i_img_getdata(im)
     Imager::ImgRaw     im
              PPCODE:
               EXTEND(SP, 1);
-               PUSHs(im->idata ? sv_2mortal(newSVpv(im->idata, im->bytes)) 
+               PUSHs(im->idata ? 
+                    sv_2mortal(newSVpv((char *)im->idata, im->bytes)) 
                     : &PL_sv_undef);
 
 
 void
-i_draw(im,x1,y1,x2,y2,val)
+i_line(im,x1,y1,x2,y2,val,endp)
     Imager::ImgRaw     im
               int     x1
               int     y1
               int     x2
               int     y2
      Imager::Color     val
+              int     endp
 
 void
-i_line_aa(im,x1,y1,x2,y2,val)
+i_line_aa(im,x1,y1,x2,y2,val,endp)
     Imager::ImgRaw     im
               int     x1
               int     y1
               int     x2
               int     y2
      Imager::Color     val
+              int     endp
 
 void
 i_box(im,x1,y1,x2,y2,val)
@@ -667,6 +1382,15 @@ i_box_filled(im,x1,y1,x2,y2,val)
               int     y2
           Imager::Color    val
 
+void
+i_box_cfill(im,x1,y1,x2,y2,fill)
+    Imager::ImgRaw     im
+              int     x1
+              int     y1
+              int     x2
+              int     y2
+          Imager::FillHandle    fill
+
 void
 i_arc(im,x,y,rad,d1,d2,val)
     Imager::ImgRaw     im
@@ -677,6 +1401,35 @@ i_arc(im,x,y,rad,d1,d2,val)
              float     d2
           Imager::Color    val
 
+void
+i_arc_aa(im,x,y,rad,d1,d2,val)
+    Imager::ImgRaw     im
+           double     x
+           double     y
+            double     rad
+            double     d1
+            double     d2
+          Imager::Color    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
+          Imager::FillHandle    fill
+
+void
+i_arc_aa_cfill(im,x,y,rad,d1,d2,fill)
+    Imager::ImgRaw     im
+           double     x
+           double     y
+            double     rad
+            double     d1
+            double     d2
+          Imager::FillHandle   fill
 
 
 void
@@ -758,15 +1511,71 @@ i_poly_aa(im,xc,yc,val)
              myfree(x);
              myfree(y);
 
+void
+i_poly_aa_cfill(im,xc,yc,fill)
+    Imager::ImgRaw     im
+     Imager::FillHandle     fill
+            PREINIT:
+            double   *x,*y;
+            int       len;
+            AV       *av1;
+            AV       *av2;
+            SV       *sv1;
+            SV       *sv2;
+            int i;
+            PPCODE:
+            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);
+            }
+             i_poly_aa_cfill(im,len,x,y,fill);
+             myfree(x);
+             myfree(y);
+
 
 
-void
+undef_int
 i_flood_fill(im,seedx,seedy,dcol)
     Imager::ImgRaw     im
               int     seedx
               int     seedy
      Imager::Color     dcol
 
+undef_int
+i_flood_cfill(im,seedx,seedy,fill)
+    Imager::ImgRaw     im
+              int     seedx
+              int     seedy
+     Imager::FillHandle     fill
+
+undef_int
+i_flood_fill_border(im,seedx,seedy,dcol, border)
+    Imager::ImgRaw     im
+              int     seedx
+              int     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
+     Imager::FillHandle     fill
+     Imager::Color     border
+
 
 void
 i_copyto(im,src,x1,y1,x2,y2,tx,ty)
@@ -792,18 +1601,22 @@ i_copyto_trans(im,src,x1,y1,x2,y2,tx,ty,trans)
               int     ty
      Imager::Color     trans
 
-void
-i_copy(im,src)
-    Imager::ImgRaw     im
+Imager::ImgRaw
+i_copy(src)
     Imager::ImgRaw     src
 
 
 undef_int
-i_rubthru(im,src,tx,ty)
+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
+
 
 undef_int
 i_flipxy(im, direction)
@@ -816,12 +1629,34 @@ i_rotate90(im, degrees)
                int      degrees
 
 Imager::ImgRaw
-i_rotate_exact(im, amount)
+i_rotate_exact(im, amount, ...)
     Imager::ImgRaw      im
             double      amount
+      PREINIT:
+       i_color *backp = NULL;
+       i_fcolor *fbackp = NULL;
+       int i;
+       SV * sv1;
+      CODE:
+       /* extract the bg colors if any */
+       /* yes, this is kind of strange */
+       for (i = 2; 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_rotate_exact_bg(im, amount, backp, fbackp);
+      OUTPUT:
+       RETVAL
 
 Imager::ImgRaw
-i_matrix_transform(im, xsize, ysize, matrix)
+i_matrix_transform(im, xsize, ysize, matrix, ...)
     Imager::ImgRaw      im
                int      xsize
                int      ysize
@@ -831,6 +1666,8 @@ i_matrix_transform(im, xsize, ysize, matrix)
         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");
@@ -844,7 +1681,20 @@ i_matrix_transform(im, xsize, ysize, matrix)
         }
         for (; i < 9; ++i)
           matrix[i] = 0;
-        RETVAL = i_matrix_transform(im, xsize, ysize, matrix);        
+       /* 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
 
@@ -853,6 +1703,12 @@ i_gaussian(im,stdev)
     Imager::ImgRaw     im
             float     stdev
 
+void
+i_unsharp_mask(im,stdev,scale)
+    Imager::ImgRaw     im
+            float     stdev
+             double    scale
+
 void
 i_conv(im,pcoef)
     Imager::ImgRaw     im
@@ -885,7 +1741,6 @@ i_convert(im, src, coeff)
          int inchan;
          AV *avmain;
           SV **temp;
-         SV *svsub;
           AV *avsub;
          int len;
          int i, j;
@@ -974,7 +1829,8 @@ i_img_diff(im1,im2)
 
 
 undef_int        
-i_init_fonts()
+i_init_fonts(t1log=0)
+    int t1log
 
 #ifdef HAVE_LIBT1
 
@@ -983,7 +1839,7 @@ i_t1_set_aa(st)
               int     st
 
 int
-i_t1_new(pfb,afm=NULL)
+i_t1_new(pfb,afm)
                      char*    pfb
                      char*    afm
 
@@ -993,116 +1849,360 @@ i_t1_destroy(font_id)
 
 
 undef_int
-i_t1_cp(im,xb,yb,channel,fontnum,points,str,len,align)
+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
-             char*    str
-              int     len
+               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,len)
+i_t1_bbox(fontnum,point,str_sv,len_ignored,utf8=0,flags="")
                int     fontnum
             float     point
-             char*    str
-              int     len
+               SV*    str_sv
+               int     utf8
+              char*    flags
             PREINIT:
-              int     cords[6];
+               char *str;
+               STRLEN len;
+              int     cords[BOUNDING_BOX_COUNT];
+               int i;
+               int rc;
             PPCODE:
-              i_t1_bbox(fontnum,point,str,len,cords);
-               EXTEND(SP, 4);
-               PUSHs(sv_2mortal(newSViv(cords[0])));
-               PUSHs(sv_2mortal(newSViv(cords[1])));
-               PUSHs(sv_2mortal(newSViv(cords[2])));
-               PUSHs(sv_2mortal(newSViv(cords[3])));
-               PUSHs(sv_2mortal(newSViv(cords[4])));
-               PUSHs(sv_2mortal(newSViv(cords[5])));
+#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,len,align)
+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
-             char*    str
-              int     len
+               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))));
+        }
+
+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);
+          } 
+        }
 
 #endif 
 
 #ifdef HAVE_LIBTT
 
 
-Imager::TTHandle
+Imager::Font::TT
 i_tt_new(fontname)
              char*     fontname
 
+
+MODULE = Imager         PACKAGE = Imager::Font::TT      PREFIX=TT_
+
+#define TT_DESTROY(handle) i_tt_destroy(handle)
+
 void
-i_tt_destroy(handle)
-     Imager::TTHandle    handle
+TT_DESTROY(handle)
+     Imager::Font::TT   handle
 
 
+MODULE = Imager         PACKAGE = Imager
+
 
 undef_int
-i_tt_text(handle,im,xb,yb,cl,points,str,len,smooth)
-  Imager::TTHandle     handle
+i_tt_text(handle,im,xb,yb,cl,points,str_sv,len_ignored,smooth,utf8,align=1)
+  Imager::Font::TT     handle
     Imager::ImgRaw     im
               int     xb
               int     yb
      Imager::Color     cl
              float     points
-             char*    str
-              int     len
+             SV *     str_sv
               int     smooth
+               int     utf8
+               int     align
+             PREINIT:
+               char *str;
+               STRLEN len;
+             CODE:
+#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:
+               RETVAL                
 
 
 undef_int
-i_tt_cp(handle,im,xb,yb,channel,points,str,len,smooth)
-  Imager::TTHandle     handle
+i_tt_cp(handle,im,xb,yb,channel,points,str_sv,len_ignored,smooth,utf8,align=1)
+  Imager::Font::TT     handle
     Imager::ImgRaw     im
               int     xb
               int     yb
               int     channel
              float     points
-             char*    str
-              int     len
+             SV *     str_sv
               int     smooth
+               int     utf8
+               int     align
+             PREINIT:
+               char *str;
+               STRLEN len;
+             CODE:
+#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
 
 
-
-undef_int
-i_tt_bbox(handle,point,str,len)
-  Imager::TTHandle     handle
+void
+i_tt_bbox(handle,point,str_sv,len_ignored, utf8)
+  Imager::Font::TT     handle
             float     point
-             char*    str
-              int     len
+              SV*    str_sv
+               int     utf8
             PREINIT:
-              int     cords[6],rc;
+              int     cords[BOUNDING_BOX_COUNT],rc;
+               char *  str;
+               STRLEN len;
+               int i;
             PPCODE:
-              if ((rc=i_tt_bbox(handle,point,str,len,cords))) {
-                 EXTEND(SP, 4);
-                 PUSHs(sv_2mortal(newSViv(cords[0])));
-                 PUSHs(sv_2mortal(newSViv(cords[1])));
-                 PUSHs(sv_2mortal(newSViv(cords[2])));
-                 PUSHs(sv_2mortal(newSViv(cords[3])));
-                 PUSHs(sv_2mortal(newSViv(cords[4])));
-                 PUSHs(sv_2mortal(newSViv(cords[5])));
+#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)
@@ -1136,18 +2236,46 @@ i_readjpeg_wiol(ig)
                     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, length)
+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
@@ -1155,17 +2283,97 @@ 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
 
-
-#endif /* HAVE_LIBTIFF */
-
+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
 
 
+#endif /* HAVE_LIBTIFF */
 
 
 #ifdef HAVE_LIBPNG
@@ -1216,7 +2424,7 @@ i_writegif(im,fd,colors,pixdev,fixed)
               sv1=(*(av_fetch(av,i,0)));
                if (sv_derived_from(sv1, "Imager::Color")) {
                  Itmp = SvIV((SV*)SvRV(sv1));
-                 tmp = (i_color*) Itmp;
+                 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;
             }
@@ -1242,7 +2450,6 @@ i_writegif_gen(fd, ...)
       PROTOTYPE: $$@
       PREINIT:
        i_quantize quant;
-       i_gif_opts opts;
        i_img **imgs = NULL;
        int img_count;
        int i;
@@ -1255,10 +2462,7 @@ i_writegif_gen(fd, ...)
        hv = (HV *)SvRV(ST(1));
        memset(&quant, 0, sizeof(quant));
        quant.mc_size = 256;
-       quant.mc_colors = mymalloc(quant.mc_size * sizeof(i_color));
-       memset(&opts, 0, sizeof(opts));
        handle_quant_opts(&quant, hv);
-       handle_gif_opts(&opts, hv);
        img_count = items - 2;
        RETVAL = 1;
        if (img_count < 1) {
@@ -1272,7 +2476,7 @@ i_writegif_gen(fd, ...)
            SV *sv = ST(2+i);
            imgs[i] = NULL;
            if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
-             imgs[i] = (i_img *)SvIV((SV*)SvRV(sv));
+             imgs[i] = INT2PTR(i_img *, SvIV((SV*)SvRV(sv)));
            }
            else {
              i_clear_error();
@@ -1282,23 +2486,24 @@ i_writegif_gen(fd, ...)
             }
          }
           if (RETVAL) {
-           RETVAL = i_writegif_gen(&quant, fd, imgs, img_count, &opts);
+           RETVAL = i_writegif_gen(&quant, fd, imgs, img_count);
           }
          myfree(imgs);
           if (RETVAL) {
            copy_colors_back(hv, &quant);
           }
        }
-             ST(0) = sv_newmortal();
-             if (RETVAL == 0) ST(0)=&PL_sv_undef;
-             else sv_setiv(ST(0), (IV)RETVAL);
+        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_gif_opts opts;
        i_img **imgs = NULL;
        int img_count;
        int i;
@@ -1312,10 +2517,7 @@ i_writegif_callback(cb, maxbuffer,...)
        hv = (HV *)SvRV(ST(2));
        memset(&quant, 0, sizeof(quant));
        quant.mc_size = 256;
-       quant.mc_colors = mymalloc(quant.mc_size * sizeof(i_color));
-       memset(&opts, 0, sizeof(opts));
        handle_quant_opts(&quant, hv);
-       handle_gif_opts(&opts, hv);
        img_count = items - 3;
        RETVAL = 1;
        if (img_count < 1) {
@@ -1327,7 +2529,7 @@ i_writegif_callback(cb, maxbuffer,...)
            SV *sv = ST(3+i);
            imgs[i] = NULL;
            if (SvROK(sv) && sv_derived_from(sv, "Imager::ImgRaw")) {
-             imgs[i] = (i_img *)SvIV((SV*)SvRV(sv));
+             imgs[i] = INT2PTR(i_img *, SvIV((SV*)SvRV(sv)));
            }
            else {
              RETVAL = 0;
@@ -1336,16 +2538,66 @@ i_writegif_callback(cb, maxbuffer,...)
          }
           if (RETVAL) {
            wd.sv = ST(0);
-           RETVAL = i_writegif_callback(&quant, write_callback, (char *)&wd, maxbuffer, imgs, img_count, &opts);
+           RETVAL = i_writegif_callback(&quant, write_callback, (char *)&wd, maxbuffer, imgs, img_count);
           }
          myfree(imgs);
           if (RETVAL) {
            copy_colors_back(hv, &quant);
           }
        }
-             ST(0) = sv_newmortal();
-             if (RETVAL == 0) ST(0)=&PL_sv_undef;
-             else sv_setiv(ST(0), (IV)RETVAL);
+       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;
+       handle_quant_opts(&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(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)
@@ -1395,16 +2647,65 @@ i_readgif(fd)
             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;
-       unsigned int     length;
+             STRLEN     length;
                int*    colour_table;
                int     colours, q, w;
              i_img*    rimg;
@@ -1452,8 +2753,6 @@ void
 i_readgif_callback(...)
           PROTOTYPE: &
             PREINIT:
-               char*    data;
-               int     length;
                int*    colour_table;
                int     colours, q, w;
              i_img*    rimg;
@@ -1524,7 +2823,7 @@ i_readgif_multi_scalar(data)
         i_img **imgs;
         int count;
         char *data;
-        unsigned int length;
+        STRLEN length;
         int i;
       PPCODE:
         data = (char *)SvPV(ST(0), length);
@@ -1559,14 +2858,34 @@ i_readgif_multi_callback(cb)
           myfree(imgs);
         }
 
+void
+i_readgif_multi_wiol(ig)
+        Imager::IO ig
+      PREINIT:
+        i_img **imgs;
+        int count;
+        int i;
+      PPCODE:
+        imgs = i_readgif_multi_wiol(ig, &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);
+        }
+
+
 #endif
 
 
 
 Imager::ImgRaw
-i_readpnm_wiol(ig, length)
+i_readpnm_wiol(ig, allow_incomplete)
         Imager::IO     ig
-              int     length
+              int     allow_incomplete
 
 
 undef_int
@@ -1594,6 +2913,57 @@ i_writebmp_wiol(im,ig)
     Imager::ImgRaw     im
         Imager::IO     ig
 
+Imager::ImgRaw
+i_readbmp_wiol(ig, allow_incomplete=0)
+        Imager::IO     ig
+        int            allow_incomplete
+
+
+undef_int
+i_writetga_wiol(im,ig, wierdpack, compress, idstring)
+    Imager::ImgRaw     im
+        Imager::IO     ig
+               int     wierdpack
+               int     compress
+              char*    idstring
+            PREINIT:
+                int idlen;
+              CODE:
+                idlen  = SvCUR(ST(4));
+                RETVAL = i_writetga_wiol(im, ig, wierdpack, compress, idstring, idlen);
+                OUTPUT:
+                RETVAL
+
+
+Imager::ImgRaw
+i_readtga_wiol(ig, length)
+        Imager::IO     ig
+               int     length
+
+
+undef_int
+i_writergb_wiol(im,ig, wierdpack, compress, idstring)
+    Imager::ImgRaw     im
+        Imager::IO     ig
+               int     wierdpack
+               int     compress
+              char*    idstring
+            PREINIT:
+                int idlen;
+              CODE:
+                idlen  = SvCUR(ST(4));
+                RETVAL = i_writergb_wiol(im, ig, wierdpack, compress, idstring, idlen);
+                OUTPUT:
+                RETVAL
+
+
+Imager::ImgRaw
+i_readrgb_wiol(ig, length)
+        Imager::IO     ig
+               int     length
+
+
+
 Imager::ImgRaw
 i_scaleaxis(im,Value,Axis)
     Imager::ImgRaw     im
@@ -1606,6 +2976,12 @@ i_scale_nn(im,scx,scy)
              float     scx
              float     scy
 
+Imager::ImgRaw
+i_scale_mixing(im, width, height)
+    Imager::ImgRaw     im
+              int     width
+              int     height
+
 Imager::ImgRaw
 i_haar(im)
     Imager::ImgRaw     im
@@ -1666,13 +3042,19 @@ i_transform(im,opx,opy,parm)
              else sv_setref_pv(ST(0), "Imager::ImgRaw", (void*)RETVAL);
 
 Imager::ImgRaw
-i_transform2(width,height,ops,n_regs,c_regs,in_imgs)
+i_transform2(sv_width,sv_height,channels,sv_ops,av_n_regs,av_c_regs,av_in_imgs)
+       SV *sv_width
+       SV *sv_height
+       SV *sv_ops
+       AV *av_n_regs
+       AV *av_c_regs
+       AV *av_in_imgs
+       int channels
             PREINIT:
              int width;
              int height;
-            double* parm;
             struct rm_op *ops;
-            unsigned int ops_len;
+            STRLEN ops_len;
             int ops_count;
              double *n_regs;
              int n_regs_count;
@@ -1680,42 +3062,27 @@ i_transform2(width,height,ops,n_regs,c_regs,in_imgs)
             int c_regs_count;
              int in_imgs_count;
              i_img **in_imgs;
-            AV* av;
-            SV* sv1;
+             SV *sv1;
              IV tmp;
             int i;
              CODE:
-            if (!SvROK(ST(3))) croak("Imager: Parameter 4 must be a reference to an array\n");
-            if (!SvROK(ST(4))) croak("Imager: Parameter 5 must be a reference to an array\n");
-            if (!SvROK(ST(5))) croak("Imager: Parameter 6 must be a reference to an array of images\n");
-            if (SvTYPE(SvRV(ST(3))) != SVt_PVAV) croak("Imager: Parameter 4 must be a reference to an array\n");
-            if (SvTYPE(SvRV(ST(4))) != SVt_PVAV) croak("Imager: Parameter 5 must be a reference to an array\n");
-
-       /*if (SvTYPE(SvRV(ST(5))) != SVt_PVAV) croak("Imager: Parameter 6 must be a reference to an array\n");*/
-
-             if (SvTYPE(SvRV(ST(5))) == SVt_PVAV) {
-              av = (AV*)SvRV(ST(5));
-               in_imgs_count = av_len(av)+1;
-              for (i = 0; i < in_imgs_count; ++i) {
-                sv1 = *av_fetch(av, i, 0);
-                if (!sv_derived_from(sv1, "Imager::ImgRaw")) {
-                  croak("Parameter 5 must contain only images");
-                }
+
+             in_imgs_count = av_len(av_in_imgs)+1;
+            for (i = 0; i < in_imgs_count; ++i) {
+              sv1 = *av_fetch(av_in_imgs, i, 0);
+              if (!sv_derived_from(sv1, "Imager::ImgRaw")) {
+                croak("sv_in_img must contain only images");
               }
             }
-            else {
-              in_imgs_count = 0;
-             }
-             if (SvTYPE(SvRV(ST(5))) == SVt_PVAV) {
-               av = (AV*)SvRV(ST(5));
+             if (in_imgs_count > 0) {
                in_imgs = mymalloc(in_imgs_count*sizeof(i_img*));
                for (i = 0; i < in_imgs_count; ++i) {              
-                sv1 = *av_fetch(av,i,0);
+                sv1 = *av_fetch(av_in_imgs,i,0);
                 if (!sv_derived_from(sv1, "Imager::ImgRaw")) {
                   croak("Parameter 5 must contain only images");
                 }
                  tmp = SvIV((SV*)SvRV(sv1));
-                in_imgs[i] = (i_img*)tmp;
+                in_imgs[i] = INT2PTR(i_img*, tmp);
               }
             }
              else {
@@ -1723,38 +3090,37 @@ i_transform2(width,height,ops,n_regs,c_regs,in_imgs)
               in_imgs = NULL;
              }
              /* default the output size from the first input if possible */
-             if (SvOK(ST(0)))
-              width = SvIV(ST(0));
+             if (SvOK(sv_width))
+              width = SvIV(sv_width);
              else if (in_imgs_count)
               width = in_imgs[0]->xsize;
              else
               croak("No output image width supplied");
 
-             if (SvOK(ST(1)))
-              height = SvIV(ST(1));
+             if (SvOK(sv_height))
+              height = SvIV(sv_height);
              else if (in_imgs_count)
               height = in_imgs[0]->ysize;
              else
               croak("No output image height supplied");
 
-            ops = (struct rm_op *)SvPV(ST(2), ops_len);
+            ops = (struct rm_op *)SvPV(sv_ops, ops_len);
              if (ops_len % sizeof(struct rm_op))
                 croak("Imager: Parameter 3 must be a bitmap of regops\n");
             ops_count = ops_len / sizeof(struct rm_op);
-            av = (AV*)SvRV(ST(3));
-            n_regs_count = av_len(av)+1;
+
+            n_regs_count = av_len(av_n_regs)+1;
              n_regs = mymalloc(n_regs_count * sizeof(double));
             for (i = 0; i < n_regs_count; ++i) {
-              sv1 = *av_fetch(av,i,0);
+              sv1 = *av_fetch(av_n_regs,i,0);
               if (SvOK(sv1))
                 n_regs[i] = SvNV(sv1);
             }
-             av = (AV*)SvRV(ST(4));
-             c_regs_count = av_len(av)+1;
+             c_regs_count = av_len(av_c_regs)+1;
              c_regs = mymalloc(c_regs_count * sizeof(i_color));
              /* I don't bother initializing the colou?r registers */
 
-            RETVAL=i_transform2(width, height, 3, ops, ops_count, 
+            RETVAL=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)
@@ -1790,6 +3156,26 @@ i_bumpmap(im,bump,channel,light_x,light_y,strength)
                int     light_y
                int     strength
 
+
+void
+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
+             float     cd
+             float     cs
+             float     n
+     Imager::Color     Ia
+     Imager::Color     Il
+     Imager::Color     Is
+
+
+
 void
 i_postlevels(im,levels)
     Imager::ImgRaw     im
@@ -1875,13 +3261,73 @@ i_gradgen(im, ...)
            free(axx); free(ayy); free(ac);
             croak("i_gradgen: Element of fourth argument is not derived from Imager::Color");
          }
-         ival[i] = *(i_color *)SvIV((SV *)SvRV(sv));
+         ival[i] = *INT2PTR(i_color *, SvIV((SV *)SvRV(sv)));
        }
         i_gradgen(im, num, xo, yo, ival, dmeasure);
+        myfree(xo);
+        myfree(yo);
+        myfree(ival);
 
+Imager::ImgRaw
+i_diff_image(im, im2, mindist=0)
+    Imager::ImgRaw     im
+    Imager::ImgRaw     im2
+               int     mindist
 
+undef_int
+i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, segs)
+    Imager::ImgRaw     im
+            double     xa
+            double     ya
+            double     xb
+            double     yb
+               int     type
+               int     repeat
+               int     combine
+               int     super_sample
+            double     ssample_param
+      PREINIT:
+        AV *asegs;
+        int count;
+        i_fountain_seg *segs;
+      CODE:
+       if (!SvROK(ST(10)) || ! SvTYPE(SvRV(ST(10))))
+           croak("i_fountain: argument 11 must be an array ref");
+        
+       asegs = (AV *)SvRV(ST(10));
+        segs = load_fount_segs(asegs, &count);
+        RETVAL = i_fountain(im, xa, ya, xb, yb, type, repeat, combine, 
+                            super_sample, ssample_param, count, segs);
+        myfree(segs);
+      OUTPUT:
+        RETVAL
 
-
+Imager::FillHandle
+i_new_fill_fount(xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, segs)
+            double     xa
+            double     ya
+            double     xb
+            double     yb
+               int     type
+               int     repeat
+               int     combine
+               int     super_sample
+            double     ssample_param
+      PREINIT:
+        AV *asegs;
+        int count;
+        i_fountain_seg *segs;
+      CODE:
+       if (!SvROK(ST(9)) || ! SvTYPE(SvRV(ST(9))))
+           croak("i_fountain: argument 11 must be an array ref");
+        
+       asegs = (AV *)SvRV(ST(9));
+        segs = load_fount_segs(asegs, &count);
+        RETVAL = i_new_fill_fount(xa, ya, xb, yb, type, repeat, combine, 
+                                  super_sample, ssample_param, count, segs);
+        myfree(segs);        
+      OUTPUT:
+        RETVAL
 
 void
 i_errors()
@@ -1889,7 +3335,6 @@ i_errors()
         i_errmsg *errors;
        int i;
        AV *av;
-       SV *ref;
        SV *sv;
       PPCODE:
        errors = i_errors();
@@ -1909,6 +3354,14 @@ i_errors()
        }
 
 void
+i_clear_error()
+
+void
+i_push_error(code, msg)
+       int code
+       const char *msg
+
+undef_int
 i_nearest_color(im, ...)
     Imager::ImgRaw     im
       PREINIT:
@@ -1951,28 +3404,15 @@ i_nearest_color(im, ...)
            free(axx); free(ayy); free(ac);
             croak("i_nearest_color: Element of fourth argument is not derived from Imager::Color");
          }
-         ival[i] = *(i_color *)SvIV((SV *)SvRV(sv));
+         ival[i] = *INT2PTR(i_color *, SvIV((SV *)SvRV(sv)));
        }
-        i_nearest_color(im, num, xo, yo, ival, dmeasure);
-
-
-
+        RETVAL = i_nearest_color(im, num, xo, yo, ival, dmeasure);
+      OUTPUT:
+        RETVAL
 
 void
 malloc_state()
 
-void
-hashinfo(hv)
-            PREINIT:
-              HV* hv;
-              int stuff;
-            PPCODE:
-              if (!SvROK(ST(0))) croak("Imager: Parameter 0 must be a reference to a hash\n");        
-              hv=(HV*)SvRV(ST(0));
-              if (SvTYPE(hv)!=SVt_PVHV) croak("Imager: Parameter 0 must be a reference to a hash\n");
-              if (getint(hv,"stuff",&stuff)) printf("ok: %d\n",stuff); else printf("key doesn't exist\n");
-              if (getint(hv,"stuff2",&stuff)) printf("ok: %d\n",stuff); else printf("key doesn't exist\n");
-              
 void
 DSO_open(filename)
              char*       filename
@@ -1984,11 +3424,11 @@ DSO_open(filename)
                if (rc!=NULL) {
                  if (evstr!=NULL) {
                    EXTEND(SP,2); 
-                   PUSHs(sv_2mortal(newSViv((IV)rc)));
+                   PUSHs(sv_2mortal(newSViv(PTR2IV(rc))));
                    PUSHs(sv_2mortal(newSVpvn(evstr, strlen(evstr))));
                  } else {
                    EXTEND(SP,1);
-                   PUSHs(sv_2mortal(newSViv((IV)rc)));
+                   PUSHs(sv_2mortal(newSViv(PTR2IV(rc))));
                  }
                }
 
@@ -2028,7 +3468,6 @@ DSO_call(handle,func_index,hv)
 
 
 
-# this is mostly for testing...
 SV *
 i_get_pixel(im, x, y)
        Imager::ImgRaw im
@@ -2039,13 +3478,15 @@ i_get_pixel(im, x, y)
       CODE:
        color = (i_color *)mymalloc(sizeof(i_color));
        if (i_gpix(im, x, y, color) == 0) {
-          ST(0) = sv_newmortal();
-          sv_setref_pv(ST(0), "Imager::Color", (void *)color);
+          RETVAL = NEWSV(0, 0);
+          sv_setref_pv(RETVAL, "Imager::Color", (void *)color);
         }
         else {
           myfree(color);
-          ST(0) = &PL_sv_undef;
+          RETVAL = &PL_sv_undef;
         }
+      OUTPUT:
+        RETVAL
         
 
 int
@@ -2074,13 +3515,12 @@ i_img_to_pal(src, quant)
         hv = (HV *)SvRV(ST(1));
         memset(&quant, 0, sizeof(quant));
         quant.mc_size = 256;
-        quant.mc_colors = mymalloc(quant.mc_size * sizeof(i_color));
        handle_quant_opts(&quant, hv);
         RETVAL = i_img_to_pal(src, &quant);
         if (RETVAL) {
           copy_colors_back(hv, &quant);
         }
-        myfree(quant.mc_colors);
+       cleanup_quant_opts(&quant);
       OUTPUT:
         RETVAL
 
@@ -2109,7 +3549,7 @@ i_gpal(im, l, r, y)
           }
           else {
             EXTEND(SP, 1);
-            PUSHs(sv_2mortal(newSVpv(work, count * sizeof(i_palidx))));
+            PUSHs(sv_2mortal(newSVpv((char *)work, count * sizeof(i_palidx))));
           }
           myfree(work);
         }
@@ -2127,13 +3567,14 @@ i_ppal(im, l, y, ...)
         int     y
       PREINIT:
         i_palidx *work;
-        int count, i;
+        int i;
       CODE:
         if (items > 3) {
           work = mymalloc(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);
         }
@@ -2143,6 +3584,28 @@ i_ppal(im, l, y, ...)
       OUTPUT:
         RETVAL
 
+int
+i_ppal_p(im, l, y, data)
+        Imager::ImgRaw  im
+        int     l
+        int     y
+        SV *data
+      PREINIT:
+        i_palidx const *work;
+        STRLEN len;
+      CODE:
+        work = (i_palidx const *)SvPV(data, len);
+        len /= sizeof(i_palidx);
+        if (len > 0) {
+          validate_i_ppal(im, work, len);
+          RETVAL = i_ppal(im, l, l+len, y, work);
+        }
+        else {
+          RETVAL = 0;
+        }
+      OUTPUT:
+        RETVAL
+
 SV *
 i_addcolors(im, ...)
         Imager::ImgRaw  im
@@ -2158,26 +3621,28 @@ i_addcolors(im, ...)
           if (sv_isobject(ST(i+1)) 
               && sv_derived_from(ST(i+1), "Imager::Color")) {
             IV tmp = SvIV((SV *)SvRV(ST(i+1)));
-            colors[i] = *(i_color *)tmp;
+            colors[i] = *INT2PTR(i_color *, tmp);
           }
           else {
             myfree(colors);
-            croak("i_plin: pixels must be Imager::Color objects");
+            croak("i_addcolor: pixels must be Imager::Color objects");
           }
         }
         index = i_addcolors(im, colors, items-1);
         myfree(colors);
         if (index == 0) {
-          ST(0) = sv_2mortal(newSVpv("0 but true", 0));
+          RETVAL = newSVpv("0 but true", 0);
         }
         else if (index == -1) {
-          ST(0) = &PL_sv_undef;
+          RETVAL = &PL_sv_undef;
         }
         else {
-          ST(0) = sv_2mortal(newSViv(index));
+          RETVAL = newSViv(index);
         }
+      OUTPUT:
+        RETVAL
 
-int 
+undef_int 
 i_setcolors(im, index, ...)
         Imager::ImgRaw  im
         int index
@@ -2192,7 +3657,7 @@ i_setcolors(im, index, ...)
           if (sv_isobject(ST(i+2)) 
               && sv_derived_from(ST(i+2), "Imager::Color")) {
             IV tmp = SvIV((SV *)SvRV(ST(i+2)));
-            colors[i] = *(i_color *)tmp;
+            colors[i] = *INT2PTR(i_color *, tmp);
           }
           else {
             myfree(colors);
@@ -2201,6 +3666,8 @@ i_setcolors(im, index, ...)
         }
         RETVAL = i_setcolors(im, index, colors, items-2);
         myfree(colors);
+      OUTPUT:
+       RETVAL
 
 void
 i_getcolors(im, index, ...)
@@ -2231,33 +3698,13 @@ i_getcolors(im, index, ...)
         myfree(colors);
 
 
-SV *
+undef_neg_int
 i_colorcount(im)
         Imager::ImgRaw im
-      PREINIT:
-        int count;
-      CODE:
-        count = i_colorcount(im);
-        if (count >= 0) {
-          ST(0) = sv_2mortal(newSViv(count));
-        }
-        else {
-          ST(0) = &PL_sv_undef;
-        }
 
-SV *
+undef_neg_int
 i_maxcolors(im)
         Imager::ImgRaw im
-      PREINIT:
-        int count;
-      CODE:
-        count = i_maxcolors(im);
-        if (count >= 0) {
-          ST(0) = sv_2mortal(newSViv(count));
-        }
-        else {
-          ST(0) = &PL_sv_undef;
-        }
 
 SV *
 i_findcolor(im, color)
@@ -2267,11 +3714,13 @@ i_findcolor(im, color)
         i_palidx index;
       CODE:
         if (i_findcolor(im, color, &index)) {
-          ST(0) = sv_2mortal(newSViv(index));
+          RETVAL = newSViv(index);
         }
         else {
-          ST(0) = &PL_sv_undef;
+          RETVAL = &PL_sv_undef;
         }
+      OUTPUT:
+        RETVAL
 
 int
 i_img_bits(im)
@@ -2304,8 +3753,9 @@ i_gsamp(im, l, r, y, ...)
           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);
+          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);
           if (GIMME_V == G_ARRAY) {
             EXTEND(SP, count);
             for (i = 0; i < count; ++i)
@@ -2313,8 +3763,9 @@ i_gsamp(im, l, r, y, ...)
           }
           else {
             EXTEND(SP, 1);
-            PUSHs(sv_2mortal(newSVpv(data, count * sizeof(i_sample_t))));
+            PUSHs(sv_2mortal(newSVpv((char *)data, count * sizeof(i_sample_t))));
           }
+         myfree(data);
         }
         else {
           if (GIMME_V != G_ARRAY) {
@@ -2323,6 +3774,7 @@ i_gsamp(im, l, r, y, ...)
           }
         }
 
+
 Imager::ImgRaw
 i_img_masked_new(targ, mask, x, y, w, h)
         Imager::ImgRaw targ
@@ -2338,7 +3790,7 @@ i_img_masked_new(targ, mask, x, y, w, h)
               || !sv_derived_from(ST(1), "Imager::ImgRaw")) {
             croak("i_img_masked_new: parameter 2 must undef or an image");
           }
-          mask = (i_img *)SvIV((SV *)SvRV(ST(1)));
+          mask = INT2PTR(i_img *, SvIV((SV *)SvRV(ST(1))));
         }
         else
           mask = NULL;
@@ -2353,24 +3805,36 @@ i_plin(im, l, y, ...)
         int     y
       PREINIT:
         i_color *work;
-        int count, i;
+        int i;
+        STRLEN len;
+        int count;
       CODE:
         if (items > 3) {
-          work = mymalloc(sizeof(i_color) * (items-3));
-          for (i=0; i < items-3; ++i) {
-            if (sv_isobject(ST(i+3)) 
-                && sv_derived_from(ST(i+3), "Imager::Color")) {
-              IV tmp = SvIV((SV *)SvRV(ST(i+3)));
-              work[i] = *(i_color *)tmp;
+          if (items == 4 && SvOK(ST(3)) && !SvROK(ST(3))) {
+           /* supplied as a byte string */
+            work = (i_color *)SvPV(ST(3), len);
+            count = len / sizeof(i_color);
+           if (count * sizeof(i_color) != len) {
+              croak("i_plin: length of scalar argument must be multiple of sizeof i_color");
             }
-            else {
-              myfree(work);
-              croak("i_plin: pixels must be Imager::Color objects");
+            RETVAL = i_plin(im, l, l+count, y, work);
+          }
+         else {
+            work = mymalloc(sizeof(i_color) * (items-3));
+            for (i=0; i < items-3; ++i) {
+              if (sv_isobject(ST(i+3)) 
+                  && sv_derived_from(ST(i+3), "Imager::Color")) {
+                IV tmp = SvIV((SV *)SvRV(ST(i+3)));
+                work[i] = *INT2PTR(i_color *, tmp);
+              }
+              else {
+                myfree(work);
+                croak("i_plin: pixels must be Imager::Color objects");
+              }
             }
+            RETVAL = i_plin(im, l, l+items-3, y, work);
+            myfree(work);
           }
-          /**(char *)0 = 1;*/
-          RETVAL = i_plin(im, l, l+items-3, y, work);
-          myfree(work);
         }
         else {
           RETVAL = 0;
@@ -2406,6 +3870,7 @@ i_gsampf(im, l, r, y, ...)
             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);
           if (GIMME_V == G_ARRAY) {
             EXTEND(SP, count);
             for (i = 0; i < count; ++i)
@@ -2415,6 +3880,7 @@ i_gsampf(im, l, r, y, ...)
             EXTEND(SP, 1);
             PUSHs(sv_2mortal(newSVpv((void *)data, count * sizeof(i_fsample_t))));
           }
+          myfree(data);
         }
         else {
           if (GIMME_V != G_ARRAY) {
@@ -2430,24 +3896,37 @@ i_plinf(im, l, y, ...)
         int     y
       PREINIT:
         i_fcolor *work;
-        int count, i;
+        int i;
+        STRLEN len;
+        int count;
       CODE:
         if (items > 3) {
-          work = mymalloc(sizeof(i_fcolor) * (items-3));
-          for (i=0; i < items-3; ++i) {
-            if (sv_isobject(ST(i+3)) 
-                && sv_derived_from(ST(i+3), "Imager::Color::Float")) {
-              IV tmp = SvIV((SV *)SvRV(ST(i+3)));
-              work[i] = *(i_fcolor *)tmp;
+          if (items == 4 && SvOK(ST(3)) && !SvROK(ST(3))) {
+           /* supplied as a byte string */
+            work = (i_fcolor *)SvPV(ST(3), len);
+            count = len / sizeof(i_fcolor);
+           if (count * sizeof(i_fcolor) != len) {
+              croak("i_plin: length of scalar argument must be multiple of sizeof i_fcolor");
             }
-            else {
-              myfree(work);
-              croak("i_plin: pixels must be Imager::Color::Float objects");
+            RETVAL = i_plinf(im, l, l+count, y, work);
+          }
+         else {
+            work = mymalloc(sizeof(i_fcolor) * (items-3));
+            for (i=0; i < items-3; ++i) {
+              if (sv_isobject(ST(i+3)) 
+                  && sv_derived_from(ST(i+3), "Imager::Color::Float")) {
+                IV tmp = SvIV((SV *)SvRV(ST(i+3)));
+                work[i] = *INT2PTR(i_fcolor *, tmp);
+              }
+              else {
+                myfree(work);
+                croak("i_plinf: pixels must be Imager::Color::Float objects");
+              }
             }
+            /**(char *)0 = 1;*/
+            RETVAL = i_plinf(im, l, l+items-3, y, work);
+            myfree(work);
           }
-          /**(char *)0 = 1;*/
-          RETVAL = i_plinf(im, l, l+items-3, y, work);
-          myfree(work);
         }
         else {
           RETVAL = 0;
@@ -2465,14 +3944,16 @@ i_gpixf(im, x, y)
       CODE:
        color = (i_fcolor *)mymalloc(sizeof(i_fcolor));
        if (i_gpixf(im, x, y, color) == 0) {
-          ST(0) = sv_newmortal();
-          sv_setref_pv(ST(0), "Imager::Color::Float", (void *)color);
+          RETVAL = NEWSV(0,0);
+          sv_setref_pv(RETVAL, "Imager::Color::Float", (void *)color);
         }
         else {
           myfree(color);
-          ST(0) = &PL_sv_undef;
+          RETVAL = &PL_sv_undef;
         }
-        
+      OUTPUT:
+        RETVAL
+
 void
 i_glin(im, l, r, y)
         Imager::ImgRaw im
@@ -2486,13 +3967,20 @@ i_glin(im, l, r, y)
         if (l < r) {
           vals = mymalloc((r-l) * sizeof(i_color));
           count = i_glin(im, l, r, y, vals);
-          EXTEND(SP, count);
-          for (i = 0; i < count; ++i) {
-            SV *sv;
-            i_color *col = mymalloc(sizeof(i_color));
-            sv = sv_newmortal();
-            sv_setref_pv(sv, "Imager::Color", (void *)col);
-            PUSHs(sv);
+         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);
+              PUSHs(sv);
+            }
+          }
+          else if (count) {
+           EXTEND(SP, 1);
+           PUSHs(sv_2mortal(newSVpv((void *)vals, count * sizeof(i_color))));
           }
           myfree(vals);
         }
@@ -2510,14 +3998,20 @@ i_glinf(im, l, r, y)
         if (l < r) {
           vals = mymalloc((r-l) * sizeof(i_fcolor));
           count = i_glinf(im, l, r, y, vals);
-          EXTEND(SP, count);
-          for (i = 0; i < count; ++i) {
-            SV *sv;
-            i_fcolor *col = mymalloc(sizeof(i_fcolor));
-            *col = vals[i];
-            sv = sv_newmortal();
-            sv_setref_pv(sv, "Imager::Color::Float", (void *)col);
-            PUSHs(sv);
+          if (GIMME_V == G_ARRAY) {
+            EXTEND(SP, count);
+            for (i = 0; i < count; ++i) {
+              SV *sv;
+              i_fcolor *col = mymalloc(sizeof(i_fcolor));
+              *col = vals[i];
+              sv = sv_newmortal();
+              sv_setref_pv(sv, "Imager::Color::Float", (void *)col);
+              PUSHs(sv);
+            }
+          }
+          else if (count) {
+            EXTEND(SP, 1);
+            PUSHs(sv_2mortal(newSVpv((void *)vals, count * sizeof(i_fcolor))));
           }
           myfree(vals);
         }
@@ -2528,6 +4022,12 @@ i_img_16_new(x, y, ch)
         int y
         int ch
 
+Imager::ImgRaw
+i_img_double_new(x, y, ch)
+        int x
+        int y
+        int ch
+
 undef_int
 i_tags_addn(im, name, code, idata)
         Imager::ImgRaw im
@@ -2579,12 +4079,14 @@ i_tags_find(im, name, start)
       CODE:
         if (i_tags_find(&im->tags, name, start, &entry)) {
           if (entry == 0)
-            ST(0) = sv_2mortal(newSVpv("0 but true", 0));
+            RETVAL = newSVpv("0 but true", 0);
           else
-            ST(0) = sv_2mortal(newSViv(entry));
+            RETVAL = newSViv(entry);
         } else {
-          ST(0) = &PL_sv_undef;
+          RETVAL = &PL_sv_undef;
         }
+      OUTPUT:
+        RETVAL
 
 SV *
 i_tags_findn(im, code, start)
@@ -2596,12 +4098,15 @@ i_tags_findn(im, code, start)
       CODE:
         if (i_tags_findn(&im->tags, code, start, &entry)) {
           if (entry == 0)
-            ST(0) = sv_2mortal(newSVpv("0 but true", 0));
+            RETVAL = newSVpv("0 but true", 0);
           else
-            ST(0) = sv_2mortal(newSViv(entry));
+            RETVAL = newSViv(entry);
         }
-        else
-          ST(0) = &PL_sv_undef;
+        else {
+          RETVAL = &PL_sv_undef;
+        }
+      OUTPUT:
+        RETVAL
 
 int
 i_tags_delete(im, entry)
@@ -2653,6 +4158,28 @@ i_tags_get(im, index)
           }
         }
 
+void
+i_tags_get_string(im, what_sv)
+        Imager::ImgRaw  im
+        SV *what_sv
+      PREINIT:
+        char const *name = NULL;
+        int code;
+        char buffer[200];
+      PPCODE:
+        if (SvIOK(what_sv)) {
+          code = SvIV(what_sv);
+          name = NULL;
+        }
+        else {
+          name = SvPV_nolen(what_sv);
+          code = 0;
+        }
+        if (i_tags_get_string(&im->tags, name, code, buffer, sizeof(buffer))) {
+          EXTEND(SP, 1);
+          PUSHs(sv_2mortal(newSVpv(buffer, 0)));
+        }
+
 int
 i_tags_count(im)
         Imager::ImgRaw  im
@@ -2664,57 +4191,83 @@ i_tags_count(im)
 #ifdef HAVE_WIN32
 
 void
-i_wf_bbox(face, size, text)
+i_wf_bbox(face, size, text_sv, utf8=0)
        char *face
        int size
-       char *text
+       SV *text_sv
+       int utf8
       PREINIT:
-       int cords[6];
+       int cords[BOUNDING_BOX_COUNT];
+        int rc, i;
+       char const *text;
+         STRLEN text_len;
       PPCODE:
-        if (i_wf_bbox(face, size, text, strlen(text), cords)) {
-          EXTEND(SP, 6);  
-          PUSHs(sv_2mortal(newSViv(cords[0])));
-          PUSHs(sv_2mortal(newSViv(cords[1])));
-          PUSHs(sv_2mortal(newSViv(cords[2])));
-          PUSHs(sv_2mortal(newSViv(cords[3])));
-          PUSHs(sv_2mortal(newSViv(cords[4])));
-          PUSHs(sv_2mortal(newSViv(cords[5])));
+        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, align, aa)
+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
-       char *text
+       SV *text_sv
        int align
        int aa
+       int utf8
+      PREINIT:
+       char const *text;
+       STRLEN text_len;
       CODE:
-       RETVAL = i_wf_text(face, im, tx, ty, cl, size, text, strlen(text), 
-                          align, aa);
+        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, align, aa)
+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
-       char *text
+       SV *text_sv
        int align
        int aa
+       int utf8
+      PREINIT:
+       char const *text;
+       STRLEN text_len;
       CODE:
-       RETVAL = i_wf_cp(face, im, tx, ty, channel, size, text, strlen(text), 
-                        align, aa);
+        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
 
 #endif
 
@@ -2785,18 +4338,28 @@ i_ft2_settransform(font, matrix)
         RETVAL
 
 void
-i_ft2_bbox(font, cheight, cwidth, text)
+i_ft2_bbox(font, cheight, cwidth, text_sv, utf8)
         Imager::Font::FT2 font
         double cheight
         double cwidth
-        char *text
+        SV *text_sv
+       int utf8
       PREINIT:
-        int bbox[6];
+        int bbox[BOUNDING_BOX_COUNT];
         int i;
+        char *text;
+        STRLEN text_len;
+        int rc;
       PPCODE:
-        if (i_ft2_bbox(font, cheight, cwidth, text, strlen(text), bbox)) {
-          EXTEND(SP, 6);
-          for (i = 0; i < 6; ++i)
+        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])));
         }
 
@@ -2852,7 +4415,7 @@ i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, align, aa, vlayout, utf8
         RETVAL
 
 undef_int
-i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text, align, aa, vlayout, utf8)
+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
@@ -2860,18 +4423,22 @@ i_ft2_cp(font, im, tx, ty, channel, cheight, cwidth, text, align, aa, vlayout, u
         int channel
         double cheight
         double cwidth
-        char *text
+        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,
-                          strlen(text), align, aa, vlayout, 1);
+                          len, align, aa, vlayout, 1);
       OUTPUT:
         RETVAL
 
@@ -2892,6 +4459,288 @@ ft2_transform_box(font, x0, x1, x2, x3)
           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_
+
+void
+IFILL_DESTROY(fill)
+        Imager::FillHandle fill
+
+MODULE = Imager         PACKAGE = Imager
+
+Imager::FillHandle
+i_new_fill_solid(cl, combine)
+        Imager::Color cl
+        int combine
+
+Imager::FillHandle
+i_new_fill_solidf(cl, combine)
+        Imager::Color::Float cl
+        int combine
+
+Imager::FillHandle
+i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy)
+        Imager::Color fg
+        Imager::Color bg
+        int combine
+        int hatch
+        int dx
+        int dy
+      PREINIT:
+        unsigned char *cust_hatch;
+        STRLEN len;
+      CODE:
+        if (SvOK(ST(4))) {
+          cust_hatch = (unsigned char *)SvPV(ST(4), len);
+        }
+        else
+          cust_hatch = NULL;
+        RETVAL = i_new_fill_hatch(fg, bg, combine, hatch, cust_hatch, dx, dy);
+      OUTPUT:
+        RETVAL
+
+Imager::FillHandle
+i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy)
+        Imager::Color::Float fg
+        Imager::Color::Float bg
+        int combine
+        int hatch
+        int dx
+        int dy
+      PREINIT:
+        unsigned char *cust_hatch;
+        STRLEN len;
+      CODE:
+        if (SvOK(ST(4))) {
+          cust_hatch = (unsigned char *)SvPV(ST(4), len);
+        }
+        else
+          cust_hatch = NULL;
+        RETVAL = i_new_fill_hatchf(fg, bg, combine, hatch, cust_hatch, dx, dy);
+      OUTPUT:
+        RETVAL
+
+Imager::FillHandle
+i_new_fill_image(src, matrix, xoff, yoff, combine)
+        Imager::ImgRaw src
+        int xoff
+        int yoff
+        int combine
+      PREINIT:
+        double matrix[9];
+        double *matrixp;
+        AV *av;
+        IV len;
+        SV *sv1;
+        int i;
+      CODE:
+        if (!SvOK(ST(1))) {
+          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));
+         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;
+          matrixp = matrix;
+        }
+        RETVAL = i_new_fill_image(src, matrixp, xoff, yoff, combine);
+      OUTPUT:
+        RETVAL
+
+MODULE = Imager  PACKAGE = Imager::Internal::Hlines  PREFIX=i_int_hlines_
+
+# this class is only exposed for testing
+
+int
+i_int_hlines_testing()
+
+#if i_int_hlines_testing()
+
+Imager::Internal::Hlines
+i_int_hlines_new(start_y, count_y, start_x, count_x)
+       int start_y
+       int count_y
+       int start_x
+       int count_x
+
+Imager::Internal::Hlines
+i_int_hlines_new_img(im)
+       Imager::ImgRaw im
+
+void
+i_int_hlines_add(hlines, y, minx, width)
+       Imager::Internal::Hlines hlines
+       int y
+       int minx
+       int width
+
+void
+i_int_hlines_DESTROY(hlines)
+       Imager::Internal::Hlines hlines
+
+SV *
+i_int_hlines_dump(hlines)
+       Imager::Internal::Hlines hlines
+
 #endif
 
+BOOT:
+        PERL_SET_GLOBAL_CALLBACKS;