]> git.imager.perl.org - imager.git/blobdiff - Imager.xs
Various changes:
[imager.git] / Imager.xs
index 2e5baba4a5a5e25ece3c1002f6471825e146870b..3997a1e5c4e044f58fa6dd729eee5a6725a3b174 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -4,28 +4,26 @@ 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;
-
-#ifdef HAVE_LIBTT
-typedef TT_Fonthandle* Imager__Font__TT;
+#if i_int_hlines_testing()
+#include "imageri.h"
 #endif
 
-#ifdef HAVE_FT2
-typedef FT2_Fonthandle* Imager__Font__FT2;
-#endif
+#include "imperl.h"
 
 /* These functions are all shared - then comes platform dependant code */
 static int getstr(void *hv_t,char *key,char **store) {
@@ -110,7 +108,7 @@ void my_SvREFCNT_dec(void *p) {
 
 
 static void
-log_entry(char *string, int level) {
+i_log_entry(char *string, int level) {
   mm_log((level, string));
 }
 
@@ -274,7 +272,7 @@ static ssize_t call_writer(struct cbdata *cbd, void const *buf, size_t size) {
   FREETMPS;
   LEAVE;
 
-  return success ? size : 0;
+  return success ? size : -1;
 }
 
 static ssize_t call_reader(struct cbdata *cbd, void *buf, size_t size, 
@@ -327,9 +325,14 @@ static ssize_t call_reader(struct cbdata *cbd, void *buf, size_t size,
 static ssize_t write_flush(struct cbdata *cbd) {
   ssize_t result;
 
-  result = call_writer(cbd, cbd->buffer, cbd->used);
-  cbd->used = 0;
-  return 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) {
@@ -379,12 +382,11 @@ static off_t io_seeker(void *p, off_t offset, int whence) {
 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);*/
+  /* 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 */
-        *(char *)0 = 0;
       if (io_seeker(p, cbd->where - cbd->used, SEEK_CUR) < 0) {
         return -1;
       }
@@ -394,8 +396,9 @@ static ssize_t io_writer(void *p, void const *data, size_t size) {
   }
   cbd->writing = 1;
   if (cbd->used && cbd->used + size > cbd->maxlength) {
-    if (write_flush(cbd) <= 0) {
-      return 0;
+    int write_res = write_flush(cbd);
+    if (write_res <= 0) {
+      return write_res;
     }
     cbd->used = 0;
   }
@@ -408,11 +411,13 @@ static ssize_t io_writer(void *p, void const *data, size_t size) {
   return call_writer(cbd, data, size);
 }
 
-static ssize_t io_reader(void *p, void *data, size_t 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;
@@ -432,7 +437,7 @@ static ssize_t io_reader(void *p, void *data, size_t size) {
   size  -= cbd->used - cbd->where;
   out   += cbd->used - cbd->where;
   if (size < sizeof(cbd->buffer)) {
-    int did_read;
+    int did_read = 0;
     int copy_size;
     while (size
           && (did_read = call_reader(cbd, cbd->buffer, size, 
@@ -447,6 +452,8 @@ static ssize_t io_reader(void *p, void *data, size_t size) {
       total += copy_size;
       size  -= copy_size;
     }
+    if (did_read < 0)
+      return -1;
   }
   else {
     /* just read the rest - too big for our buffer*/
@@ -456,16 +463,19 @@ static ssize_t io_reader(void *p, void *data, size_t size) {
       total += did_read;
       out   += did_read;
     }
+    if (did_read < 0)
+      return -1;
   }
 
   return total;
 }
 
-static void io_closer(void *p) {
+static int io_closer(void *p) {
   struct cbdata *cbd = p;
 
   if (cbd->writing && cbd->used > 0) {
-    write_flush(cbd);
+    if (write_flush(cbd) < 0)
+      return -1;
     cbd->writing = 0;
   }
 
@@ -484,6 +494,8 @@ static void io_closer(void *p) {
     FREETMPS;
     LEAVE;
   }
+
+  return 0;
 }
 
 static void io_destroyer(void *p) {
@@ -523,6 +535,8 @@ static struct value_name make_color_names[] =
   { "webmap", mc_web_map, },
   { "addi", mc_addi, },
   { "mediancut", mc_median_cut, },
+  { "mono", mc_mono, },
+  { "monochrome", mc_mono, },
 };
 
 static struct value_name translate_names[] =
@@ -558,30 +572,6 @@ static struct value_name orddith_names[] =
   { "custom", od_custom, },
 };
 
-static int
-hv_fetch_bool(HV *hv, char *name, int def) {
-  SV **sv;
-
-  sv = hv_fetch(hv, name, strlen(name), 0);
-  if (sv && *sv) {
-    return SvTRUE(*sv);
-  }
-  else
-    return def;
-}
-
-static int
-hv_fetch_int(HV *hv, char *name, int def) {
-  SV **sv;
-
-  sv = hv_fetch(hv, name, strlen(name), 0);
-  if (sv && *sv) {
-    return SvIV(*sv);
-  }
-  else
-    return def;
-}
-
 /* look through the hash for quantization options */
 static void handle_quant_opts(i_quantize *quant, HV *hv)
 {
@@ -720,90 +710,6 @@ static void cleanup_quant_opts(i_quantize *quant) {
     myfree(quant->ed_map);
 }
 
-#if 0
-/* look through the hash for options to add to opts */
-static void handle_gif_opts(i_gif_opts *opts, HV *hv)
-{
-  SV **sv;
-  int i;
-  /**((char *)0) = '\0';*/
-  opts->each_palette = hv_fetch_bool(hv, "gif_each_palette", 0);
-  opts->interlace = hv_fetch_bool(hv, "interlace", 0);
-
-  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 = INT2PTR(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 */
-  opts->loop_count = hv_fetch_int(hv, "gif_loop_count", 0);
-
-  opts->eliminate_unused = hv_fetch_bool(hv, "gif_eliminate_unused", 1);
-}
-
-static void cleanup_gif_opts(i_gif_opts *opts) {
-  if (opts->delays)
-    myfree(opts->delays);
-  if (opts->user_input_flags)
-    myfree(opts->user_input_flags);
-  if (opts->disposal)
-    myfree(opts->disposal);
-  if (opts->positions) 
-    myfree(opts->positions);
-}
-
-#endif
-
 /* copies the color map from the hv into the colors member of the HV */
 static void copy_colors_back(HV *hv, i_quantize *quant) {
   SV **sv;
@@ -845,7 +751,6 @@ load_fount_segs(AV *asegs, int *count) {
   */
   int i, j;
   AV *aseg;
-  SV *sv;
   i_fountain_seg *segs;
   double work[3];
   int worki[2];
@@ -911,6 +816,29 @@ load_fount_segs(AV *asegs, int *count) {
   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
@@ -918,17 +846,81 @@ load_fount_segs(AV *asegs, int *count) {
 #define ICLF_new_internal(r, g, b, a) i_fcolor_new((r), (g), (b), (a))
 #define ICLF_DESTROY(cl) i_fcolor_destroy(cl)
 
-/* for the fill objects
-   Since a fill object may later have dependent images, (or fills!)
-   we need perl wrappers - oh well
-*/
-#define IFILL_DESTROY(fill) i_fill_destroy(fill);
-typedef i_fill_t* Imager__FillHandle;
 
 /* 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
@@ -1051,7 +1043,6 @@ i_rgb_to_hsv(c)
         i_rgb_to_hsvf(RETVAL);
       OUTPUT:
         RETVAL
-        
 
 MODULE = Imager                PACKAGE = Imager::ImgRaw        PREFIX = IIM_
 
@@ -1134,18 +1125,121 @@ io_slurp(ig)
               myfree(data);
 
 
-MODULE = Imager                PACKAGE = Imager::IO    PREFIX = io_glue_
+undef_int
+i_set_image_file_limits(width, height, bytes)
+       int width
+       int height
+       int bytes
 
 void
-io_glue_DESTROY(ig)
-        Imager::IO     ig
+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_
 
-MODULE = Imager                PACKAGE = Imager
+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
 
-PROTOTYPES: ENABLE
+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()
@@ -1193,12 +1287,12 @@ i_sametype_chans(im, x, y, channels)
                int channels
 
 void
-m_init_log(name,level)
+i_init_log(name,level)
              char*    name
               int     level
 
 void
-log_entry(string,level)
+i_log_entry(string,level)
              char*    string
               int     level
 
@@ -1307,6 +1401,16 @@ 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
@@ -1317,6 +1421,15 @@ i_arc_cfill(im,x,y,rad,d1,d2,fill)
              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
@@ -1447,6 +1560,22 @@ i_flood_cfill(im,seedx,seedy,fill)
               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)
@@ -1472,9 +1601,8 @@ 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
 
 
@@ -1854,7 +1982,6 @@ i_t1_glyph_name(handle, text_sv, utf8 = 0)
         char const *text;
         STRLEN work_len;
         int len;
-        int outsize;
         char name[255];
       PPCODE:
 #ifdef SvUTF8
@@ -1864,7 +1991,7 @@ i_t1_glyph_name(handle, text_sv, utf8 = 0)
         text = SvPV(text_sv, work_len);
         len = work_len;
         while (len) {
-          unsigned char ch;
+          unsigned long ch;
           if (utf8) {
             ch = i_utf8_advance(&text, &len);
             if (ch == ~0UL) {
@@ -1877,7 +2004,7 @@ i_t1_glyph_name(handle, text_sv, utf8 = 0)
             --len;
           }
           EXTEND(SP, 1);
-          if (outsize = i_t1_glyph_name(handle, ch, name, sizeof(name))) {
+          if (i_t1_glyph_name(handle, ch, name, sizeof(name))) {
             PUSHs(sv_2mortal(newSVpv(name, 0)));
           }
           else {
@@ -1908,7 +2035,7 @@ MODULE = Imager         PACKAGE = Imager
 
 
 undef_int
-i_tt_text(handle,im,xb,yb,cl,points,str_sv,len_ignored,smooth,utf8)
+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
@@ -1916,9 +2043,9 @@ i_tt_text(handle,im,xb,yb,cl,points,str_sv,len_ignored,smooth,utf8)
      Imager::Color     cl
              float     points
              SV *     str_sv
-              int     len_ignored
               int     smooth
                int     utf8
+               int     align
              PREINIT:
                char *str;
                STRLEN len;
@@ -1929,13 +2056,13 @@ i_tt_text(handle,im,xb,yb,cl,points,str_sv,len_ignored,smooth,utf8)
 #endif
                str = SvPV(str_sv, len);
                RETVAL = i_tt_text(handle, im, xb, yb, cl, points, str, 
-                                  len, smooth, utf8);
+                                  len, smooth, utf8, align);
              OUTPUT:
                RETVAL                
 
 
 undef_int
-i_tt_cp(handle,im,xb,yb,channel,points,str_sv,len_ignored,smooth,utf8)
+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
@@ -1943,9 +2070,9 @@ i_tt_cp(handle,im,xb,yb,channel,points,str_sv,len_ignored,smooth,utf8)
               int     channel
              float     points
              SV *     str_sv
-              int     len_ignored
               int     smooth
                int     utf8
+               int     align
              PREINIT:
                char *str;
                STRLEN len;
@@ -1956,17 +2083,16 @@ i_tt_cp(handle,im,xb,yb,channel,points,str_sv,len_ignored,smooth,utf8)
 #endif
                str = SvPV(str_sv, len);
                RETVAL = i_tt_cp(handle, im, xb, yb, channel, points, str, len,
-                                smooth, utf8);
+                                smooth, utf8, align);
              OUTPUT:
                 RETVAL
 
 
-undef_int
+void
 i_tt_bbox(handle,point,str_sv,len_ignored, utf8)
   Imager::Font::TT     handle
             float     point
               SV*    str_sv
-              int     len_ignored
                int     utf8
             PREINIT:
               int     cords[BOUNDING_BOX_COUNT],rc;
@@ -2053,7 +2179,7 @@ i_tt_glyph_name(handle, text_sv, utf8 = 0)
         text = SvPV(text_sv, work_len);
         len = work_len;
         while (len) {
-          unsigned char ch;
+          unsigned long ch;
           if (utf8) {
             ch = i_utf8_advance(&text, &len);
             if (ch == ~0UL) {
@@ -2066,7 +2192,7 @@ i_tt_glyph_name(handle, text_sv, utf8 = 0)
             --len;
           }
           EXTEND(SP, 1);
-          if (outsize = i_tt_glyph_name(handle, ch, name, sizeof(name))) {
+          if ((outsize = i_tt_glyph_name(handle, ch, name, sizeof(name))) != 0) {
             PUSHs(sv_2mortal(newSVpv(name, 0)));
           }
           else {
@@ -2110,11 +2236,13 @@ i_readjpeg_wiol(ig)
                     myfree(iptc_itext);
              }
 
+int
+i_exif_enabled()
 
 #endif
 
 
-char *
+const char *
 i_test_format_probe(ig, length)
         Imager::IO     ig
               int     length
@@ -2124,9 +2252,10 @@ i_test_format_probe(ig, length)
 #ifdef HAVE_LIBTIFF
 
 Imager::ImgRaw
-i_readtiff_wiol(ig, length)
+i_readtiff_wiol(ig, allow_partial, page=0)
         Imager::IO     ig
-              int     length
+              int     allow_partial
+               int     page
 
 void
 i_readtiff_multi_wiol(ig, length)
@@ -2295,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;
             }
@@ -2566,6 +2695,11 @@ i_readgif_wiol(ig)
             PUSHs(newRV_noinc((SV*)ct));
         }
 
+Imager::ImgRaw
+i_readgif_single_wiol(ig, page=0)
+       Imager::IO      ig
+        int            page
+
 void
 i_readgif_scalar(...)
           PROTOTYPE: $
@@ -2619,8 +2753,6 @@ void
 i_readgif_callback(...)
           PROTOTYPE: &
             PREINIT:
-               char*    data;
-               int     length;
                int*    colour_table;
                int     colours, q, w;
              i_img*    rimg;
@@ -2751,9 +2883,9 @@ i_readgif_multi_wiol(ig)
 
 
 Imager::ImgRaw
-i_readpnm_wiol(ig, length)
+i_readpnm_wiol(ig, allow_partial)
         Imager::IO     ig
-              int     length
+              int     allow_partial
 
 
 undef_int
@@ -2782,8 +2914,9 @@ i_writebmp_wiol(im,ig)
         Imager::IO     ig
 
 Imager::ImgRaw
-i_readbmp_wiol(ig)
+i_readbmp_wiol(ig, allow_partial=0)
         Imager::IO     ig
+        int            allow_partial
 
 
 undef_int
@@ -2843,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
@@ -3135,7 +3274,7 @@ i_diff_image(im, im2, mindist=0)
     Imager::ImgRaw     im2
                int     mindist
 
-void
+undef_int
 i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_param, segs)
     Imager::ImgRaw     im
             double     xa
@@ -3157,9 +3296,11 @@ i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, ssample_para
         
        asegs = (AV *)SvRV(ST(10));
         segs = load_fount_segs(asegs, &count);
-        i_fountain(im, xa, ya, xb, yb, type, repeat, combine, super_sample, 
-                   ssample_param, count, segs);
+        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)
@@ -3213,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:
@@ -3257,26 +3406,13 @@ i_nearest_color(im, ...)
          }
          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
@@ -3332,11 +3468,6 @@ DSO_call(handle,func_index,hv)
 
 
 
-# this is mostly for testing...
-# this function results in 'RETVAL' : unreferenced local variable
-# in VC++, and might be subtley wrong
-# the most obvious change may result in a double free so I'm leaving it
-# for now
 SV *
 i_get_pixel(im, x, y)
        Imager::ImgRaw im
@@ -3347,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
@@ -3441,6 +3574,7 @@ i_ppal(im, l, y, ...)
           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);
         }
@@ -3450,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
@@ -3469,20 +3625,22 @@ i_addcolors(im, ...)
           }
           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
 
 undef_int 
 i_setcolors(im, index, ...)
@@ -3540,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)
@@ -3576,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)
@@ -3666,23 +3806,35 @@ i_plin(im, l, y, ...)
       PREINIT:
         i_color *work;
         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] = *INT2PTR(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;
@@ -3718,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)
@@ -3727,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) {
@@ -3743,23 +3897,36 @@ i_plinf(im, l, y, ...)
       PREINIT:
         i_fcolor *work;
         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] = *INT2PTR(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;
@@ -3777,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
@@ -3798,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);
         }
@@ -3822,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);
         }
@@ -3897,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)
@@ -3914,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)
@@ -3971,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
@@ -3982,54 +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[BOUNDING_BOX_COUNT];
         int rc, i;
+       char const *text;
+         STRLEN text_len;
       PPCODE:
-        if (rc = i_wf_bbox(face, size, text, strlen(text), cords)) {
+        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
 
@@ -4177,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
@@ -4185,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
 
@@ -4275,7 +4517,6 @@ i_ft2_glyph_name(handle, text_sv, utf8 = 0, reliable_only = 1)
         char const *text;
         STRLEN work_len;
         int len;
-        int outsize;
         char name[255];
       PPCODE:
 #ifdef SvUTF8
@@ -4298,7 +4539,7 @@ i_ft2_glyph_name(handle, text_sv, utf8 = 0, reliable_only = 1)
             --len;
           }
           EXTEND(SP, 1);
-          if (outsize = i_ft2_glyph_name(handle, ch, name, sizeof(name), 
+          if (i_ft2_glyph_name(handle, ch, name, sizeof(name), 
                                          reliable_only)) {
             PUSHs(sv_2mortal(newSVpv(name, 0)));
           }
@@ -4314,6 +4555,58 @@ 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_
@@ -4412,3 +4705,42 @@ i_new_fill_image(src, matrix, 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;