add new comparison method rgb_difference that resembles arithmetical difference per...
[imager.git] / image.c
diff --git a/image.c b/image.c
index 846126c..7da5eca 100644 (file)
--- a/image.c
+++ b/image.c
@@ -1,3 +1,5 @@
+#define IMAGER_NO_CONTEXT
+
 #include "imager.h"
 #include "imageri.h"
 
@@ -12,7 +14,7 @@ image.c - implements most of the basic functions of Imager and much of the rest
   i_color *c;
   c = i_color_new(red, green, blue, alpha);
   ICL_DESTROY(c);
-  i = i_img_new();
+  i = i_img_8_new();
   i_img_destroy(i);
   // and much more
 
@@ -30,6 +32,8 @@ Some of these functions are internal.
 =cut
 */
 
+im_context_t (*im_get_context)(void) = NULL;
+
 #define XAXIS 0
 #define YAXIS 1
 #define XYAXIS 2
@@ -37,20 +41,66 @@ Some of these functions are internal.
 #define minmax(a,b,i) ( ((a>=i)?a: ( (b<=i)?b:i   )) )
 
 /* Hack around an obscure linker bug on solaris - probably due to builtin gcc thingies */
-static void fake(void) { ceil(1); }
-
-static int i_ppix_d(i_img *im, int x, int y, const i_color *val);
-static int i_gpix_d(i_img *im, int x, int y, i_color *val);
-static int i_glin_d(i_img *im, int l, int r, int y, i_color *vals);
-static int i_plin_d(i_img *im, int l, int r, int y, const i_color *vals);
-static int i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val);
-static int i_gpixf_d(i_img *im, int x, int y, i_fcolor *val);
-static int i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals);
-static int i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals);
-static int i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, const int *chans, int chan_count);
-static int i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, const int *chans, int chan_count);
-/*static int i_psamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count);
-  static int i_psampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count);*/
+void i_linker_bug_fake(void) { ceil(1); }
+
+/*
+=item im_img_alloc(aIMCTX)
+X<im_img_alloc API>X<i_img_alloc API>
+=category Image Implementation
+=synopsis i_img *im = im_img_alloc(aIMCTX);
+=synopsis i_img *im = i_img_alloc();
+
+Allocates a new i_img structure.
+
+When implementing a new image type perform the following steps in your
+image object creation function:
+
+=over
+
+=item 1.
+
+allocate the image with i_img_alloc().
+
+=item 2.
+
+initialize any function pointers or other data as needed, you can
+overwrite the whole block if you need to.
+
+=item 3.
+
+initialize Imager's internal data by calling i_img_init() on the image
+object.
+
+=back
+
+=cut
+*/
+
+i_img *
+im_img_alloc(pIMCTX) {
+  return mymalloc(sizeof(i_img));
+}
+
+/*
+=item im_img_init(aIMCTX, image)
+X<im_img_init API>X<i_img_init API>
+=category Image Implementation
+=synopsis im_img_init(aIMCTX, im);
+=synopsis i_img_init(im);
+
+Imager internal initialization of images.
+
+See L</im_img_alloc(aIMCTX)> for more information.
+
+=cut
+*/
+
+void
+im_img_init(pIMCTX, i_img *img) {
+  img->im_data = NULL;
+  img->context = aIMCTX;
+  im_context_refinc(aIMCTX, "img_init");
+}
 
 /* 
 =item ICL_new_internal(r, g, b, a)
@@ -68,15 +118,16 @@ Return a new color object with values passed to it.
 i_color *
 ICL_new_internal(unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
   i_color *cl = NULL;
+  dIMCTX;
 
-  mm_log((1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a));
+  im_log((aIMCTX,1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a));
 
-  if ( (cl=mymalloc(sizeof(i_color))) == NULL) i_fatal(2,"malloc() error\n");
+  if ( (cl=mymalloc(sizeof(i_color))) == NULL) im_fatal(aIMCTX, 2,"malloc() error\n");
   cl->rgba.r = r;
   cl->rgba.g = g;
   cl->rgba.b = b;
   cl->rgba.a = a;
-  mm_log((1,"(%p) <- ICL_new_internal\n",cl));
+  im_log((aIMCTX,1,"(%p) <- ICL_new_internal\n",cl));
   return cl;
 }
 
@@ -97,15 +148,16 @@ ICL_new_internal(unsigned char r,unsigned char g,unsigned char b,unsigned char a
 
 i_color *
 ICL_set_internal(i_color *cl,unsigned char r,unsigned char g,unsigned char b,unsigned char a) {
-  mm_log((1,"ICL_set_internal(cl* %p,r %d,g %d,b %d,a %d)\n",cl,r,g,b,a));
+  dIMCTX;
+  im_log((aIMCTX,1,"ICL_set_internal(cl* %p,r %d,g %d,b %d,a %d)\n",cl,r,g,b,a));
   if (cl == NULL)
     if ( (cl=mymalloc(sizeof(i_color))) == NULL)
-      i_fatal(2,"malloc() error\n");
+      im_fatal(aIMCTX, 2,"malloc() error\n");
   cl->rgba.r=r;
   cl->rgba.g=g;
   cl->rgba.b=b;
   cl->rgba.a=a;
-  mm_log((1,"(%p) <- ICL_set_internal\n",cl));
+  im_log((aIMCTX,1,"(%p) <- ICL_set_internal\n",cl));
   return cl;
 }
 
@@ -143,8 +195,9 @@ Dump color information to log - strictly for debugging.
 
 void
 ICL_info(i_color const *cl) {
-  mm_log((1,"i_color_info(cl* %p)\n",cl));
-  mm_log((1,"i_color_info: (%d,%d,%d,%d)\n",cl->rgba.r,cl->rgba.g,cl->rgba.b,cl->rgba.a));
+  dIMCTX;
+  im_log((aIMCTX, 1,"i_color_info(cl* %p)\n",cl));
+  im_log((aIMCTX, 1,"i_color_info: (%d,%d,%d,%d)\n",cl->rgba.r,cl->rgba.g,cl->rgba.b,cl->rgba.a));
 }
 
 /* 
@@ -159,7 +212,8 @@ Destroy ancillary data for Color object.
 
 void
 ICL_DESTROY(i_color *cl) {
-  mm_log((1,"ICL_DESTROY(cl* %p)\n",cl));
+  dIMCTX;
+  im_log((aIMCTX, 1,"ICL_DESTROY(cl* %p)\n",cl));
   myfree(cl);
 }
 
@@ -170,15 +224,16 @@ ICL_DESTROY(i_color *cl) {
 */
 i_fcolor *i_fcolor_new(double r, double g, double b, double a) {
   i_fcolor *cl = NULL;
+  dIMCTX;
 
-  mm_log((1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a));
+  im_log((aIMCTX, 1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a));
 
-  if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) i_fatal(2,"malloc() error\n");
+  if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) im_fatal(aIMCTX, 2,"malloc() error\n");
   cl->rgba.r = r;
   cl->rgba.g = g;
   cl->rgba.b = b;
   cl->rgba.a = a;
-  mm_log((1,"(%p) <- i_fcolor_new\n",cl));
+  im_log((aIMCTX, 1,"(%p) <- i_fcolor_new\n",cl));
 
   return cl;
 }
@@ -192,205 +247,6 @@ void i_fcolor_destroy(i_fcolor *cl) {
   myfree(cl);
 }
 
-/*
-=item IIM_base_8bit_direct (static)
-
-A static i_img object used to initialize direct 8-bit per sample images.
-
-=cut
-*/
-static i_img IIM_base_8bit_direct =
-{
-  0, /* channels set */
-  0, 0, 0, /* xsize, ysize, bytes */
-  ~0U, /* ch_mask */
-  i_8_bits, /* bits */
-  i_direct_type, /* type */
-  0, /* virtual */
-  NULL, /* idata */
-  { 0, 0, NULL }, /* tags */
-  NULL, /* ext_data */
-
-  i_ppix_d, /* i_f_ppix */
-  i_ppixf_d, /* i_f_ppixf */
-  i_plin_d, /* i_f_plin */
-  i_plinf_d, /* i_f_plinf */
-  i_gpix_d, /* i_f_gpix */
-  i_gpixf_d, /* i_f_gpixf */
-  i_glin_d, /* i_f_glin */
-  i_glinf_d, /* i_f_glinf */
-  i_gsamp_d, /* i_f_gsamp */
-  i_gsampf_d, /* i_f_gsampf */
-
-  NULL, /* i_f_gpal */
-  NULL, /* i_f_ppal */
-  NULL, /* i_f_addcolors */
-  NULL, /* i_f_getcolors */
-  NULL, /* i_f_colorcount */
-  NULL, /* i_f_maxcolors */
-  NULL, /* i_f_findcolor */
-  NULL, /* i_f_setcolors */
-
-  NULL, /* i_f_destroy */
-};
-
-/*static void set_8bit_direct(i_img *im) {
-  im->i_f_ppix = i_ppix_d;
-  im->i_f_ppixf = i_ppixf_d;
-  im->i_f_plin = i_plin_d;
-  im->i_f_plinf = i_plinf_d;
-  im->i_f_gpix = i_gpix_d;
-  im->i_f_gpixf = i_gpixf_d;
-  im->i_f_glin = i_glin_d;
-  im->i_f_glinf = i_glinf_d;
-  im->i_f_gpal = NULL;
-  im->i_f_ppal = NULL;
-  im->i_f_addcolor = NULL;
-  im->i_f_getcolor = NULL;
-  im->i_f_colorcount = NULL;
-  im->i_f_findcolor = NULL;
-  }*/
-
-/*
-=item IIM_new(x, y, ch)
-
-=item i_img_8_new(x, y, ch)
-
-=category Image creation
-
-Creates a new image object I<x> pixels wide, and I<y> pixels high with
-I<ch> channels.
-
-=cut
-*/
-
-
-i_img *
-IIM_new(int x,int y,int ch) {
-  i_img *im;
-  mm_log((1,"IIM_new(x %d,y %d,ch %d)\n",x,y,ch));
-
-  im=i_img_empty_ch(NULL,x,y,ch);
-  
-  mm_log((1,"(%p) <- IIM_new\n",im));
-  return im;
-}
-
-
-void
-IIM_DESTROY(i_img *im) {
-  mm_log((1,"IIM_DESTROY(im* %p)\n",im));
-  i_img_destroy(im);
-  /*   myfree(cl); */
-}
-
-/* 
-=item i_img_new()
-
-Create new image reference - notice that this isn't an object yet and
-this should be fixed asap.
-
-=cut
-*/
-
-
-i_img *
-i_img_new() {
-  i_img *im;
-  
-  mm_log((1,"i_img_struct()\n"));
-  if ( (im=mymalloc(sizeof(i_img))) == NULL)
-    i_fatal(2,"malloc() error\n");
-  
-  *im = IIM_base_8bit_direct;
-  im->xsize=0;
-  im->ysize=0;
-  im->channels=3;
-  im->ch_mask=MAXINT;
-  im->bytes=0;
-  im->idata=NULL;
-  
-  mm_log((1,"(%p) <- i_img_struct\n",im));
-  return im;
-}
-
-/* 
-=item i_img_empty(im, x, y)
-
-Re-new image reference (assumes 3 channels)
-
-   im - Image pointer
-   x - xsize of destination image
-   y - ysize of destination image
-
-**FIXME** what happens if a live image is passed in here?
-
-Should this just call i_img_empty_ch()?
-
-=cut
-*/
-
-i_img *
-i_img_empty(i_img *im,int x,int y) {
-  mm_log((1,"i_img_empty(*im %p, x %d, y %d)\n",im, x, y));
-  return i_img_empty_ch(im, x, y, 3);
-}
-
-/* 
-=item i_img_empty_ch(im, x, y, ch)
-
-Re-new image reference 
-
-   im - Image pointer
-   x  - xsize of destination image
-   y  - ysize of destination image
-   ch - number of channels
-
-=cut
-*/
-
-i_img *
-i_img_empty_ch(i_img *im,int x,int y,int ch) {
-  int bytes;
-
-  mm_log((1,"i_img_empty_ch(*im %p, x %d, y %d, ch %d)\n", im, x, y, ch));
-
-  if (x < 1 || y < 1) {
-    i_push_error(0, "Image sizes must be positive");
-    return NULL;
-  }
-  if (ch < 1 || ch > MAXCHANNELS) {
-    i_push_errorf(0, "channels must be between 1 and %d", MAXCHANNELS);
-    return NULL;
-  }
-  /* check this multiplication doesn't overflow */
-  bytes = x*y*ch;
-  if (bytes / y / ch != x) {
-    i_push_errorf(0, "integer overflow calculating image allocation");
-    return NULL;
-  }
-
-  if (im == NULL)
-    if ( (im=mymalloc(sizeof(i_img))) == NULL)
-      i_fatal(2,"malloc() error\n");
-
-  memcpy(im, &IIM_base_8bit_direct, sizeof(i_img));
-  i_tags_new(&im->tags);
-  im->xsize    = x;
-  im->ysize    = y;
-  im->channels = ch;
-  im->ch_mask  = MAXINT;
-  im->bytes=bytes;
-  if ( (im->idata=mymalloc(im->bytes)) == NULL) 
-    i_fatal(2,"malloc() error\n"); 
-  memset(im->idata,0,(size_t)im->bytes);
-  
-  im->ext_data = NULL;
-  
-  mm_log((1,"(%p) <- i_img_empty_ch\n",im));
-  return im;
-}
-
 /* 
 =item i_img_exorcise(im)
 
@@ -403,7 +259,8 @@ Free image data.
 
 void
 i_img_exorcise(i_img *im) {
-  mm_log((1,"i_img_exorcise(im* 0x%x)\n",im));
+  dIMCTXim(im);
+  im_log((aIMCTX,1,"i_img_exorcise(im* %p)\n",im));
   i_tags_destroy(&im->tags);
   if (im->i_f_destroy)
     (im->i_f_destroy)(im);
@@ -413,30 +270,27 @@ i_img_exorcise(i_img *im) {
   im->ysize    = 0;
   im->channels = 0;
 
-  im->i_f_ppix=i_ppix_d;
-  im->i_f_gpix=i_gpix_d;
-  im->i_f_plin=i_plin_d;
-  im->i_f_glin=i_glin_d;
   im->ext_data=NULL;
 }
 
 /* 
-=item i_img_destroy(im)
+=item i_img_destroy(C<img>)
+=order 90
+=category Image creation/destruction
+=synopsis i_img_destroy(img)
 
-=category Image
-
-Destroy image and free data via exorcise.
-
-   im - Image pointer
+Destroy an image object
 
 =cut
 */
 
 void
 i_img_destroy(i_img *im) {
-  mm_log((1,"i_img_destroy(im %p)\n",im));
+  dIMCTXim(im);
+  im_log((aIMCTX, 1,"i_img_destroy(im %p)\n",im));
   i_img_exorcise(im);
-  if (im) { myfree(im); }
+  myfree(im);
+  im_context_refdec(aIMCTX, "img_destroy");
 }
 
 /* 
@@ -461,30 +315,30 @@ info is an array of 4 integers with the following values:
 
 
 void
-i_img_info(i_img *im,int *info) {
-  mm_log((1,"i_img_info(im 0x%x)\n",im));
-  if (im != NULL) {
-    mm_log((1,"i_img_info: xsize=%d ysize=%d channels=%d mask=%ud\n",im->xsize,im->ysize,im->channels,im->ch_mask));
-    mm_log((1,"i_img_info: idata=0x%d\n",im->idata));
-    info[0] = im->xsize;
-    info[1] = im->ysize;
-    info[2] = im->channels;
-    info[3] = im->ch_mask;
-  } else {
-    info[0] = 0;
-    info[1] = 0;
-    info[2] = 0;
-    info[3] = 0;
-  }
+i_img_info(i_img *im, i_img_dim *info) {
+  dIMCTXim(im);
+  im_log((aIMCTX,1,"i_img_info(im %p)\n",im));
+
+  im_log((aIMCTX,1,"i_img_info: xsize=%" i_DF " ysize=%" i_DF " channels=%d "
+         "mask=%ud\n",
+         i_DFc(im->xsize), i_DFc(im->ysize), im->channels,im->ch_mask));
+  im_log((aIMCTX,1,"i_img_info: idata=%p\n",im->idata));
+  info[0] = im->xsize;
+  info[1] = im->ysize;
+  info[2] = im->channels;
+  info[3] = im->ch_mask;
 }
 
 /*
-=item i_img_setmask(im, ch_mask)
-
-=synopsis // only channel 0 writeable 
+=item i_img_setmask(C<im>, C<ch_mask>)
+=category Image Information
+=synopsis // only channel 0 writable 
 =synopsis i_img_setmask(img, 0x01);
 
-Set the image channel mask for I<im> to I<ch_mask>.
+Set the image channel mask for C<im> to C<ch_mask>.
+
+The image channel mask gives some control over which channels can be
+written to in the image.
 
 =cut
 */
@@ -493,11 +347,11 @@ i_img_setmask(i_img *im,int ch_mask) { im->ch_mask=ch_mask; }
 
 
 /*
-=item i_img_getmask(im)
+=item i_img_getmask(C<im>)
+=category Image Information
+=synopsis int mask = i_img_getmask(img);
 
-=synopsis mask = i_img_getmask(img);
-
-Get the image channel mask for I<im>.
+Get the image channel mask for C<im>.
 
 =cut
 */
@@ -505,11 +359,11 @@ int
 i_img_getmask(i_img *im) { return im->ch_mask; }
 
 /*
-=item i_img_getchannels(im)
-
-=synopsis channels = i_img_getchannels(img);
+=item i_img_getchannels(C<im>)
+=category Image Information
+=synopsis int channels = i_img_getchannels(img);
 
-Get the number of channels in I<im>.
+Get the number of channels in C<im>.
 
 =cut
 */
@@ -517,9 +371,9 @@ int
 i_img_getchannels(i_img *im) { return im->channels; }
 
 /*
-=item i_img_get_width(im)
-
-=synopsis width = i_img_get_width(im);
+=item i_img_get_width(C<im>)
+=category Image Information
+=synopsis i_img_dim width = i_img_get_width(im);
 
 Returns the width in pixels of the image.
 
@@ -531,9 +385,9 @@ i_img_get_width(i_img *im) {
 }
 
 /*
-=item i_img_get_height(im)
-
-=synopsis height = i_img_get_height(im);
+=item i_img_get_height(C<im>)
+=category Image Information
+=synopsis i_img_dim height = i_img_get_height(im);
 
 Returns the height in pixels of the image.
 
@@ -545,24 +399,106 @@ i_img_get_height(i_img *im) {
 }
 
 /*
-=item i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, trans)
+=item i_img_color_model(im)
+=category Image Information
+=synopsis i_color_model_t cm = i_img_color_model(im);
+
+Returns the color model for the image.
+
+A future version of Imager will allow for images with extra channels
+beyond gray/rgb and alpha.
+
+=cut
+*/
+i_color_model_t
+i_img_color_model(i_img *im) {
+  return (i_color_model_t)im->channels;
+}
+
+/*
+=item i_img_alpha_channel(im, &channel)
+=category Image Information
+=synopsis int alpha_channel;
+=synopsis int has_alpha = i_img_alpha_channel(im, &alpha_channel);
+
+Work out the alpha channel for an image.
+
+If the image has an alpha channel, sets C<*channel> to the alpha
+channel index and returns non-zero.
+
+If the image has no alpha channel, returns zero and C<*channel> is not
+modified.
+
+C<channel> may be C<NULL>.
+
+=cut
+*/
+
+int
+i_img_alpha_channel(i_img *im, int *channel) {
+  i_color_model_t model = i_img_color_model(im);
+  switch (model) {
+  case icm_gray_alpha:
+  case icm_rgb_alpha:
+    if (channel) *channel = (int)model - 1;
+    return 1;
+
+  default:
+    return 0;
+  }
+}
+
+/*
+=item i_img_color_channels(im)
+=category Image Information
+=synopsis int color_channels = i_img_color_channels(im);
+
+Returns the number of color channels in the image.  For now this is
+always 1 (for grayscale) or 3 (for RGB) but may be 0 in some special
+cases in a future release of Imager.
+
+=cut
+*/
+
+int
+i_img_color_channels(i_img *im) {
+  i_color_model_t model = i_img_color_model(im);
+  switch (model) {
+  case icm_gray_alpha:
+  case icm_rgb_alpha:
+    return (int)model - 1;
+
+  case icm_gray:
+  case icm_rgb:
+    return (int)model;
+
+  default:
+    return 0;
+  }
+}
+
+/*
+=item i_copyto_trans(C<im>, C<src>, C<x1>, C<y1>, C<x2>, C<y2>, C<tx>, C<ty>, C<trans>)
 
 =category Image
 
-(x1,y1) (x2,y2) specifies the region to copy (in the source coordinates)
-(tx,ty) specifies the upper left corner for the target image.
-pass NULL in trans for non transparent i_colors.
+(C<x1>,C<y1>) (C<x2>,C<y2>) specifies the region to copy (in the
+source coordinates) (C<tx>,C<ty>) specifies the upper left corner for
+the target image.  pass NULL in C<trans> for non transparent i_colors.
 
 =cut
 */
 
 void
-i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,const i_color *trans) {
+i_copyto_trans(i_img *im,i_img *src,i_img_dim x1,i_img_dim y1,i_img_dim x2,i_img_dim y2,i_img_dim tx,i_img_dim ty,const i_color *trans) {
   i_color pv;
-  int x,y,t,ttx,tty,tt,ch;
+  i_img_dim x,y,t,ttx,tty,tt;
+  int ch;
+  dIMCTXim(im);
 
-  mm_log((1,"i_copyto_trans(im* %p,src 0x%x, x1 %d, y1 %d, x2 %d, y2 %d, tx %d, ty %d, trans* 0x%x)\n",
-         im, src, x1, y1, x2, y2, tx, ty, trans));
+  im_log((aIMCTX, 1,"i_copyto_trans(im* %p,src %p, p1(" i_DFp "), p2(" i_DFp "), "
+         "to(" i_DFp "), trans* %p)\n",
+         im, src, i_DFcp(x1, y1), i_DFcp(x2, y2), i_DFcp(tx, ty), trans));
   
   if (x2<x1) { t=x1; x1=x2; x2=t; }
   if (y2<y1) { t=y1; y1=y2; y2=t; }
@@ -587,80 +523,11 @@ i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,co
 }
 
 /*
-=item i_copyto(dest, src, x1, y1, x2, y2, tx, ty)
+=item i_copy(source)
 
 =category Image
 
-Copies image data from the area (x1,y1)-[x2,y2] in the source image to
-a rectangle the same size with it's top-left corner at (tx,ty) in the
-destination image.
-
-If x1 > x2 or y1 > y2 then the corresponding co-ordinates are swapped.
-
-=cut
-*/
-
-void
-i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty) {
-  int x, y, t, ttx, tty;
-  
-  if (x2<x1) { t=x1; x1=x2; x2=t; }
-  if (y2<y1) { t=y1; y1=y2; y2=t; }
-  if (tx < 0) {
-    /* adjust everything equally */
-    x1 += -tx;
-    x2 += -tx;
-    tx = 0;
-  }
-  if (ty < 0) {
-    y1 += -ty;
-    y2 += -ty;
-    ty = 0;
-  }
-  if (x1 >= src->xsize || y1 >= src->ysize)
-    return; /* nothing to do */
-  if (x2 > src->xsize)
-    x2 = src->xsize;
-  if (y2 > src->ysize)
-    y2 = src->ysize;
-  if (x1 == x2 || y1 == y2)
-    return; /* nothing to do */
-
-  mm_log((1,"i_copyto(im* %p, src %p, x1 %d, y1 %d, x2 %d, y2 %d, tx %d, ty %d)\n",
-         im, src, x1, y1, x2, y2, tx, ty));
-  
-  if (im->bits == i_8_bits) {
-    i_color *row = mymalloc(sizeof(i_color) * (x2-x1));
-    tty = ty;
-    for(y=y1; y<y2; y++) {
-      ttx = tx;
-      i_glin(src, x1, x2, y, row);
-      i_plin(im, tx, tx+x2-x1, tty, row);
-      tty++;
-    }
-    myfree(row);
-  }
-  else {
-    i_fcolor pv;
-    tty = ty;
-    for(y=y1; y<y2; y++) {
-      ttx = tx;
-      for(x=x1; x<x2; x++) {
-        i_gpixf(src, x,   y,   &pv);
-        i_ppixf(im,  ttx, tty, &pv);
-        ttx++;
-      }
-      tty++;
-    }
-  }
-}
-
-/*
-=item i_copy(src)
-
-=category Image
-
-Creates a new image that is a copy of src.
+Creates a new image that is a copy of the image C<source>.
 
 Tags are not copied, only the image data.
 
@@ -671,10 +538,11 @@ Returns: i_img *
 
 i_img *
 i_copy(i_img *src) {
-  int y, y1, x1;
+  i_img_dim y, y1, x1;
+  dIMCTXim(src);
   i_img *im = i_sametype(src, src->xsize, src->ysize);
 
-  mm_log((1,"i_copy(src %p)\n", src));
+  im_log((aIMCTX,1,"i_copy(src %p)\n", src));
 
   if (!im)
     return NULL;
@@ -704,20 +572,8 @@ i_copy(i_img *src) {
     }
   }
   else {
-    i_color temp;
-    int index;
-    int count;
     i_palidx *vals;
 
-    /* paletted image */
-    i_img_pal_new_low(im, x1, y1, src->channels, i_maxcolors(src));
-    /* copy across the palette */
-    count = i_colorcount(src);
-    for (index = 0; index < count; ++index) {
-      i_getcolors(src, index, &temp, 1);
-      i_addcolors(im, &temp, 1);
-    }
-
     vals = mymalloc(sizeof(i_palidx) * x1);
     for (y = 0; y < y1; ++y) {
       i_gpal(src, 0, x1, y, vals);
@@ -729,119 +585,12 @@ i_copy(i_img *src) {
   return im;
 }
 
-
 /*
-=item i_flipxy(im, axis)
-
-Flips the image inplace around the axis specified.
-Returns 0 if parameters are invalid.
 
-   im   - Image pointer
-   axis - 0 = x, 1 = y, 2 = both
+http://en.wikipedia.org/wiki/Lanczos_resampling
 
-=cut
 */
 
-undef_int
-i_flipxy(i_img *im, int direction) {
-  int x, x2, y, y2, xm, ym;
-  int xs = im->xsize;
-  int ys = im->ysize;
-  
-  mm_log((1, "i_flipxy(im %p, direction %d)\n", im, direction ));
-
-  if (!im) return 0;
-
-  switch (direction) {
-  case XAXIS: /* Horizontal flip */
-    xm = xs/2;
-    ym = ys;
-    for(y=0; y<ym; y++) {
-      x2 = xs-1;
-      for(x=0; x<xm; x++) {
-       i_color val1, val2;
-       i_gpix(im, x,  y,  &val1);
-       i_gpix(im, x2, y,  &val2);
-       i_ppix(im, x,  y,  &val2);
-       i_ppix(im, x2, y,  &val1);
-       x2--;
-      }
-    }
-    break;
-  case YAXIS: /* Vertical flip */
-    xm = xs;
-    ym = ys/2;
-    y2 = ys-1;
-    for(y=0; y<ym; y++) {
-      for(x=0; x<xm; x++) {
-       i_color val1, val2;
-       i_gpix(im, x,  y,  &val1);
-       i_gpix(im, x,  y2, &val2);
-       i_ppix(im, x,  y,  &val2);
-       i_ppix(im, x,  y2, &val1);
-      }
-      y2--;
-    }
-    break;
-  case XYAXIS: /* Horizontal and Vertical flip */
-    xm = xs/2;
-    ym = ys/2;
-    y2 = ys-1;
-    for(y=0; y<ym; y++) {
-      x2 = xs-1;
-      for(x=0; x<xm; x++) {
-       i_color val1, val2;
-       i_gpix(im, x,  y,  &val1);
-       i_gpix(im, x2, y2, &val2);
-       i_ppix(im, x,  y,  &val2);
-       i_ppix(im, x2, y2, &val1);
-
-       i_gpix(im, x2, y,  &val1);
-       i_gpix(im, x,  y2, &val2);
-       i_ppix(im, x2, y,  &val2);
-       i_ppix(im, x,  y2, &val1);
-       x2--;
-      }
-      y2--;
-    }
-    if (xm*2 != xs) { /* odd number of column */
-      mm_log((1, "i_flipxy: odd number of columns\n"));
-      x = xm;
-      y2 = ys-1;
-      for(y=0; y<ym; y++) {
-       i_color val1, val2;
-       i_gpix(im, x,  y,  &val1);
-       i_gpix(im, x,  y2, &val2);
-       i_ppix(im, x,  y,  &val2);
-       i_ppix(im, x,  y2, &val1);
-       y2--;
-      }
-    }
-    if (ym*2 != ys) { /* odd number of rows */
-      mm_log((1, "i_flipxy: odd number of rows\n"));
-      y = ym;
-      x2 = xs-1;
-      for(x=0; x<xm; x++) {
-       i_color val1, val2;
-       i_gpix(im, x,  y,  &val1);
-       i_gpix(im, x2, y,  &val2);
-       i_ppix(im, x,  y,  &val2);
-       i_ppix(im, x2, y,  &val1);
-       x2--;
-      }
-    }
-    break;
-  default:
-    mm_log((1, "i_flipxy: direction is invalid\n" ));
-    return 0;
-  }
-  return 1;
-}
-
-
-
-
-
 static
 float
 Lanczos(float x) {
@@ -866,22 +615,26 @@ wither the x-axis (I<axis> == 0) or the y-axis (I<axis> == 1).
 */
 
 i_img*
-i_scaleaxis(i_img *im, float Value, int Axis) {
-  int hsize, vsize, i, j, k, l, lMax, iEnd, jEnd;
-  int LanczosWidthFactor;
-  float *l0, *l1, OldLocation;
-  int T; 
-  float t;
+i_scaleaxis(i_img *im, double Value, int Axis) {
+  i_img_dim hsize, vsize, i, j, k, l, lMax, iEnd, jEnd;
+  i_img_dim LanczosWidthFactor;
+  float *l0, *l1;
+  double OldLocation;
+  i_img_dim T; 
+  double t;
   float F, PictureValue[MAXCHANNELS];
   short psave;
   i_color val,val1,val2;
   i_img *new_img;
+  int has_alpha = i_img_has_alpha(im);
+  int color_chans = i_img_color_channels(im);
+  dIMCTXim(im);
 
-  mm_log((1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis));
-
+  i_clear_error();
+  im_log((aIMCTX, 1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis));
 
   if (Axis == XAXIS) {
-    hsize = (int)(0.5 + im->xsize * Value);
+    hsize = (i_img_dim)(0.5 + im->xsize * Value);
     if (hsize < 1) {
       hsize = 1;
       Value = 1.0 / im->xsize;
@@ -892,7 +645,7 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
     iEnd = vsize;
   } else {
     hsize = im->xsize;
-    vsize = (int)(0.5 + im->ysize * Value);
+    vsize = (i_img_dim)(0.5 + im->ysize * Value);
 
     if (vsize < 1) {
       vsize = 1;
@@ -903,19 +656,23 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
     iEnd = hsize;
   }
   
-  new_img = i_img_empty_ch(NULL, hsize, vsize, im->channels);
+  new_img = i_img_8_new(hsize, vsize, im->channels);
+  if (!new_img) {
+    i_push_error(0, "cannot create output image");
+    return NULL;
+  }
   
   /* 1.4 is a magic number, setting it to 2 will cause rather blurred images */
-  LanczosWidthFactor = (Value >= 1) ? 1 : (int) (1.4/Value); 
+  LanczosWidthFactor = (Value >= 1) ? 1 : (i_img_dim) (1.4/Value); 
   lMax = LanczosWidthFactor << 1;
   
   l0 = mymalloc(lMax * sizeof(float));
   l1 = mymalloc(lMax * sizeof(float));
   
   for (j=0; j<jEnd; j++) {
-    OldLocation = ((float) j) / Value;
-    T = (int) (OldLocation);
-    F = OldLocation - (float) T;
+    OldLocation = ((double) j) / Value;
+    T = (i_img_dim) (OldLocation);
+    F = OldLocation - T;
     
     for (l = 0; l<lMax; l++) {
       l0[lMax-l-1] = Lanczos(((float) (lMax-l-1) + F) / (float) LanczosWidthFactor);
@@ -928,7 +685,7 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
       t+=l0[l];
       t+=l1[l];
     }
-    t /= (float)LanczosWidthFactor;
+    t /= (double)LanczosWidthFactor;
     
     for(l=0; l<lMax; l++) {
       l0[l] /= t;
@@ -940,22 +697,53 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
       for (i=0; i<iEnd; i++) {
        for (k=0; k<im->channels; k++) PictureValue[k] = 0.0;
        for (l=0; l<lMax; l++) {
-         int mx = T-lMax+l+1;
-         int Mx = T+l+1;
+         i_img_dim mx = T-lMax+l+1;
+         i_img_dim Mx = T+l+1;
          mx = (mx < 0) ? 0 : mx;
          Mx = (Mx >= im->xsize) ? im->xsize-1 : Mx;
          
          i_gpix(im, Mx, i, &val1);
          i_gpix(im, mx, i, &val2);
-         
-         for (k=0; k<im->channels; k++) {
-           PictureValue[k] += l1[l]        * val1.channel[k];
-           PictureValue[k] += l0[lMax-l-1] * val2.channel[k];
+
+         if (has_alpha) {
+           i_sample_t alpha1 = val1.channel[color_chans];
+           i_sample_t alpha2 = val2.channel[color_chans];
+           for (k=0; k < color_chans; k++) {
+             PictureValue[k] += l1[l]        * val1.channel[k] * alpha1 / 255;
+             PictureValue[k] += l0[lMax-l-1] * val2.channel[k] * alpha2 / 255;
+           }
+           PictureValue[color_chans] += l1[l] * val1.channel[color_chans];
+           PictureValue[color_chans] += l0[lMax-l-1] * val2.channel[color_chans];
+         }
+         else {
+           for (k=0; k<im->channels; k++) {
+             PictureValue[k] += l1[l]        * val1.channel[k];
+             PictureValue[k] += l0[lMax-l-1] * val2.channel[k];
+           }
          }
        }
-       for(k=0;k<im->channels;k++) {
-         psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor));
-         val.channel[k]=minmax(0,255,psave);
+
+       if (has_alpha) {
+         float fa = PictureValue[color_chans] / LanczosWidthFactor;
+         int alpha = minmax(0, 255, fa+0.5);
+         if (alpha) {
+           for (k = 0; k < color_chans; ++k) {
+             psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor * 255 / fa));
+             val.channel[k]=minmax(0,255,psave);
+           }
+           val.channel[color_chans] = alpha;
+         }
+         else {
+           /* zero alpha, so the pixel has no color */
+           for (k = 0; k < im->channels; ++k)
+             val.channel[k] = 0;
+         }
+       }
+       else {
+         for(k=0;k<im->channels;k++) {
+           psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor));
+           val.channel[k]=minmax(0,255,psave);
+         }
        }
        i_ppix(new_img, j, i, &val);
       }
@@ -965,21 +753,50 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
       for (i=0; i<iEnd; i++) {
        for (k=0; k<im->channels; k++) PictureValue[k] = 0.0;
        for (l=0; l < lMax; l++) {
-         int mx = T-lMax+l+1;
-         int Mx = T+l+1;
+         i_img_dim mx = T-lMax+l+1;
+         i_img_dim Mx = T+l+1;
          mx = (mx < 0) ? 0 : mx;
          Mx = (Mx >= im->ysize) ? im->ysize-1 : Mx;
 
          i_gpix(im, i, Mx, &val1);
          i_gpix(im, i, mx, &val2);
-         for (k=0; k<im->channels; k++) {
-           PictureValue[k] += l1[l]        * val1.channel[k];
-           PictureValue[k] += l0[lMax-l-1] * val2.channel[k]; 
+         if (has_alpha) {
+           i_sample_t alpha1 = val1.channel[color_chans];
+           i_sample_t alpha2 = val2.channel[color_chans];
+           for (k=0; k < color_chans; k++) {
+             PictureValue[k] += l1[l]        * val1.channel[k] * alpha1 / 255;
+             PictureValue[k] += l0[lMax-l-1] * val2.channel[k] * alpha2 / 255;
+           }
+           PictureValue[color_chans] += l1[l] * val1.channel[color_chans];
+           PictureValue[color_chans] += l0[lMax-l-1] * val2.channel[color_chans];
+         }
+         else {
+           for (k=0; k<im->channels; k++) {
+             PictureValue[k] += l1[l]        * val1.channel[k];
+             PictureValue[k] += l0[lMax-l-1] * val2.channel[k];
+           }
+         }
+       }
+       if (has_alpha) {
+         float fa = PictureValue[color_chans] / LanczosWidthFactor;
+         int alpha = minmax(0, 255, fa+0.5);
+         if (alpha) {
+           for (k = 0; k < color_chans; ++k) {
+             psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor * 255 / fa));
+             val.channel[k]=minmax(0,255,psave);
+           }
+           val.channel[color_chans] = alpha;
+         }
+         else {
+           for (k = 0; k < im->channels; ++k)
+             val.channel[k] = 0;
          }
        }
-       for (k=0; k<im->channels; k++) {
-         psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor));
-         val.channel[k] = minmax(0, 255, psave);
+       else {
+         for(k=0;k<im->channels;k++) {
+           psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor));
+           val.channel[k]=minmax(0,255,psave);
+         }
        }
        i_ppix(new_img, i, j, &val);
       }
@@ -989,7 +806,7 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
   myfree(l0);
   myfree(l1);
 
-  mm_log((1,"(%p) <- i_scaleaxis\n", new_img));
+  im_log((aIMCTX, 1,"(%p) <- i_scaleaxis\n", new_img));
 
   return new_img;
 }
@@ -1007,41 +824,44 @@ nothing is gained by doing it in two steps
 
 
 i_img*
-i_scale_nn(i_img *im, float scx, float scy) {
+i_scale_nn(i_img *im, double scx, double scy) {
 
-  int nxsize,nysize,nx,ny;
+  i_img_dim nxsize,nysize,nx,ny;
   i_img *new_img;
   i_color val;
+  dIMCTXim(im);
 
-  mm_log((1,"i_scale_nn(im 0x%x,scx %.2f,scy %.2f)\n",im,scx,scy));
+  im_log((aIMCTX, 1,"i_scale_nn(im %p,scx %.2f,scy %.2f)\n",im,scx,scy));
 
-  nxsize = (int) ((float) im->xsize * scx);
+  nxsize = (i_img_dim) ((double) im->xsize * scx);
   if (nxsize < 1) {
     nxsize = 1;
-    scx = 1 / im->xsize;
+    scx = 1.0 / im->xsize;
   }
-  nysize = (int) ((float) im->ysize * scy);
+  nysize = (i_img_dim) ((double) im->ysize * scy);
   if (nysize < 1) {
     nysize = 1;
-    scy = 1 / im->ysize;
+    scy = 1.0 / im->ysize;
   }
+  im_assert(scx != 0 && scy != 0);
     
   new_img=i_img_empty_ch(NULL,nxsize,nysize,im->channels);
   
   for(ny=0;ny<nysize;ny++) for(nx=0;nx<nxsize;nx++) {
-    i_gpix(im,((float)nx)/scx,((float)ny)/scy,&val);
+    i_gpix(im,((double)nx)/scx,((double)ny)/scy,&val);
     i_ppix(new_img,nx,ny,&val);
   }
 
-  mm_log((1,"(0x%x) <- i_scale_nn\n",new_img));
+  im_log((aIMCTX, 1,"(%p) <- i_scale_nn\n",new_img));
 
   return new_img;
 }
 
 /*
-=item i_sametype(i_img *im, int xsize, int ysize)
+=item i_sametype(C<im>, C<xsize>, C<ysize>)
 
-=category Image creation
+=category Image creation/destruction
+=synopsis i_img *img = i_sametype(src, width, height);
 
 Returns an image of the same type (sample size, channels, paletted/direct).
 
@@ -1050,7 +870,10 @@ For paletted images the palette is copied from the source.
 =cut
 */
 
-i_img *i_sametype(i_img *src, int xsize, int ysize) {
+i_img *
+i_sametype(i_img *src, i_img_dim xsize, i_img_dim ysize) {
+  dIMCTXim(src);
+
   if (src->type == i_direct_type) {
     if (src->bits == 8) {
       return i_img_empty_ch(NULL, xsize, ysize, src->channels);
@@ -1081,9 +904,10 @@ i_img *i_sametype(i_img *src, int xsize, int ysize) {
 }
 
 /*
-=item i_sametype_chans(i_img *im, int xsize, int ysize, int channels)
+=item i_sametype_chans(C<im>, C<xsize>, C<ysize>, C<channels>)
 
-=category Image creation
+=category Image creation/destruction
+=synopsis i_img *img = i_sametype_chans(src, width, height, channels);
 
 Returns an image of the same type (sample size).
 
@@ -1092,7 +916,10 @@ For paletted images the equivalent direct type is returned.
 =cut
 */
 
-i_img *i_sametype_chans(i_img *src, int xsize, int ysize, int channels) {
+i_img *
+i_sametype_chans(i_img *src, i_img_dim xsize, i_img_dim ysize, int channels) {
+  dIMCTXim(src);
+
   if (src->bits == 8) {
     return i_img_empty_ch(NULL, xsize, ysize, channels);
   }
@@ -1130,11 +957,12 @@ The operators for this function are defined in L<stackmach.c>.
 i_img*
 i_transform(i_img *im, int *opx,int opxl,int *opy,int opyl,double parm[],int parmlen) {
   double rx,ry;
-  int nxsize,nysize,nx,ny;
+  i_img_dim nxsize,nysize,nx,ny;
   i_img *new_img;
   i_color val;
+  dIMCTXim(im);
   
-  mm_log((1,"i_transform(im 0x%x, opx 0x%x, opxl %d, opy 0x%x, opyl %d, parm 0x%x, parmlen %d)\n",im,opx,opxl,opy,opyl,parm,parmlen));
+  im_log((aIMCTX, 1,"i_transform(im %p, opx %p, opxl %d, opy %p, opyl %d, parm %p, parmlen %d)\n",im,opx,opxl,opy,opyl,parm,parmlen));
 
   nxsize = im->xsize;
   nysize = im->ysize ;
@@ -1156,7 +984,7 @@ i_transform(i_img *im, int *opx,int opxl,int *opy,int opyl,double parm[],int par
     i_ppix(new_img,nx,ny,&val);
   }
 
-  mm_log((1,"(0x%x) <- i_transform\n",new_img));
+  im_log((aIMCTX, 1,"(%p) <- i_transform\n",new_img));
   return new_img;
 }
 
@@ -1172,19 +1000,23 @@ can return zero.
 
 =cut
 */
+
 float
 i_img_diff(i_img *im1,i_img *im2) {
-  int x,y,ch,xb,yb,chb;
+  i_img_dim x, y, xb, yb;
+  int ch, chb;
   float tdiff;
   i_color val1,val2;
+  dIMCTXim(im1);
 
-  mm_log((1,"i_img_diff(im1 0x%x,im2 0x%x)\n",im1,im2));
+  im_log((aIMCTX, 1,"i_img_diff(im1 %p,im2 %p)\n",im1,im2));
 
   xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
   yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
   chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
 
-  mm_log((1,"i_img_diff: xb=%d xy=%d chb=%d\n",xb,yb,chb));
+  im_log((aIMCTX, 1,"i_img_diff: b=(" i_DFp ") chb=%d\n",
+         i_DFcp(xb,yb), chb));
 
   tdiff=0;
   for(y=0;y<yb;y++) for(x=0;x<xb;x++) {
@@ -1193,38 +1025,124 @@ i_img_diff(i_img *im1,i_img *im2) {
 
     for(ch=0;ch<chb;ch++) tdiff+=(val1.channel[ch]-val2.channel[ch])*(val1.channel[ch]-val2.channel[ch]);
   }
-  mm_log((1,"i_img_diff <- (%.2f)\n",tdiff));
+  im_log((aIMCTX, 1,"i_img_diff <- (%.2f)\n",tdiff));
   return tdiff;
 }
 
-/* just a tiny demo of haar wavelets */
+/*
+=item i_img_diffd(im1, im2)
 
-i_img*
-i_haar(i_img *im) {
-  int mx,my;
-  int fx,fy;
-  int x,y;
-  int ch,c;
-  i_img *new_img,*new_img2;
-  i_color val1,val2,dval1,dval2;
-  
-  mx=im->xsize;
-  my=im->ysize;
-  fx=(mx+1)/2;
-  fy=(my+1)/2;
+Calculates the sum of the squares of the differences between
+correspoding channels in two images.
 
+If the images are not the same size then only the common area is 
+compared, hence even if images are different sizes this function 
+can return zero.
 
-  /* horizontal pass */
-  
-  new_img=i_img_empty_ch(NULL,fx*2,fy*2,im->channels);
-  new_img2=i_img_empty_ch(NULL,fx*2,fy*2,im->channels);
+This is like i_img_diff() but looks at floating point samples instead.
 
-  c=0; 
-  for(y=0;y<my;y++) for(x=0;x<fx;x++) {
-    i_gpix(im,x*2,y,&val1);
-    i_gpix(im,x*2+1,y,&val2);
-    for(ch=0;ch<im->channels;ch++) {
-      dval1.channel[ch]=(val1.channel[ch]+val2.channel[ch])/2;
+=cut
+*/
+
+double
+i_img_diffd(i_img *im1,i_img *im2) {
+  i_img_dim x, y, xb, yb;
+  int ch, chb;
+  double tdiff;
+  i_fcolor val1,val2;
+  dIMCTXim(im1);
+
+  im_log((aIMCTX, 1,"i_img_diffd(im1 %p,im2 %p)\n",im1,im2));
+
+  xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
+  yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
+  chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
+
+  im_log((aIMCTX, 1,"i_img_diffd: b(" i_DFp ") chb=%d\n",
+         i_DFcp(xb, yb), chb));
+
+  tdiff=0;
+  for(y=0;y<yb;y++) for(x=0;x<xb;x++) {
+    i_gpixf(im1,x,y,&val1);
+    i_gpixf(im2,x,y,&val2);
+
+    for(ch=0;ch<chb;ch++) {
+      double sdiff = val1.channel[ch]-val2.channel[ch];
+      tdiff += sdiff * sdiff;
+    }
+  }
+  im_log((aIMCTX, 1,"i_img_diffd <- (%.2f)\n",tdiff));
+
+  return tdiff;
+}
+
+int
+i_img_samef(i_img *im1,i_img *im2, double epsilon, char const *what) {
+  i_img_dim x,y,xb,yb;
+  int ch, chb;
+  i_fcolor val1,val2;
+  dIMCTXim(im1);
+
+  if (what == NULL)
+    what = "(null)";
+
+  im_log((aIMCTX,1,"i_img_samef(im1 %p,im2 %p, epsilon %g, what '%s')\n", im1, im2, epsilon, what));
+
+  xb=(im1->xsize<im2->xsize)?im1->xsize:im2->xsize;
+  yb=(im1->ysize<im2->ysize)?im1->ysize:im2->ysize;
+  chb=(im1->channels<im2->channels)?im1->channels:im2->channels;
+
+  im_log((aIMCTX, 1,"i_img_samef: b(" i_DFp ") chb=%d\n",
+         i_DFcp(xb, yb), chb));
+
+  for(y = 0; y < yb; y++) {
+    for(x = 0; x < xb; x++) {
+      i_gpixf(im1, x, y, &val1);
+      i_gpixf(im2, x, y, &val2);
+      
+      for(ch = 0; ch < chb; ch++) {
+       double sdiff = val1.channel[ch] - val2.channel[ch];
+       if (fabs(sdiff) > epsilon) {
+         im_log((aIMCTX, 1,"i_img_samef <- different %g @(" i_DFp ")\n",
+                 sdiff, i_DFcp(x, y)));
+         return 0;
+       }
+      }
+    }
+  }
+  im_log((aIMCTX, 1,"i_img_samef <- same\n"));
+
+  return 1;
+}
+
+/* just a tiny demo of haar wavelets */
+
+i_img*
+i_haar(i_img *im) {
+  i_img_dim mx,my;
+  i_img_dim fx,fy;
+  i_img_dim x,y;
+  int ch;
+  i_img *new_img,*new_img2;
+  i_color val1,val2,dval1,dval2;
+  dIMCTXim(im);
+  
+  mx=im->xsize;
+  my=im->ysize;
+  fx=(mx+1)/2;
+  fy=(my+1)/2;
+
+
+  /* horizontal pass */
+  
+  new_img=i_img_empty_ch(NULL,fx*2,fy*2,im->channels);
+  new_img2=i_img_empty_ch(NULL,fx*2,fy*2,im->channels);
+
+  for(y=0;y<my;y++) for(x=0;x<fx;x++) {
+    i_gpix(im,x*2,y,&val1);
+    i_gpix(im,x*2+1,y,&val2);
+    for(ch=0;ch<im->channels;ch++) {
+      dval1.channel[ch]=(val1.channel[ch]+val2.channel[ch])/2;
       dval2.channel[ch]=(255+val1.channel[ch]-val2.channel[ch])/2;
     }
     i_ppix(new_img,x,y,&dval1);
@@ -1259,13 +1177,13 @@ to indicate that it was more than max colors
 int
 i_count_colors(i_img *im,int maxc) {
   struct octt *ct;
-  int x,y;
+  i_img_dim x,y;
   int colorcnt;
   int channels[3];
   int *samp_chans;
   i_sample_t * samp;
-  int xsize = im->xsize; 
-  int ysize = im->ysize;
+  i_img_dim xsize = im->xsize; 
+  i_img_dim ysize = im->ysize;
   int samp_cnt = 3 * xsize;
 
   if (im->channels >= 3) {
@@ -1286,7 +1204,8 @@ i_count_colors(i_img *im,int maxc) {
       for(x = 0; x < samp_cnt; ) {
           colorcnt += octt_add(ct, samp[x], samp[x+1], samp[x+2]);
           x += 3;
-          if (colorcnt > maxc) { 
+          if (colorcnt > maxc) {
+             myfree(samp);
               octt_delete(ct); 
               return -1; 
           }
@@ -1348,15 +1267,15 @@ hpsort(unsigned int n, unsigned *ra) {
 int
 i_get_anonymous_color_histo(i_img *im, unsigned int **col_usage, int maxc) {
   struct octt *ct;
-  int x,y;
+  i_img_dim x,y;
   int colorcnt;
   unsigned int *col_usage_it;
   i_sample_t * samp;
   int channels[3];
   int *samp_chans;
   
-  int xsize = im->xsize; 
-  int ysize = im->ysize;
+  i_img_dim xsize = im->xsize; 
+  i_img_dim ysize = im->ysize;
   int samp_cnt = 3 * xsize;
   ct = octt_new();
   
@@ -1377,7 +1296,8 @@ i_get_anonymous_color_histo(i_img *im, unsigned int **col_usage, int maxc) {
       colorcnt += octt_add(ct, samp[x], samp[x+1], samp[x+2]);
       x += 3;
       if (colorcnt > maxc) { 
-       octt_delete(ct); 
+       octt_delete(ct);
+       myfree(samp);
        return -1; 
       }
     }
@@ -1394,378 +1314,6 @@ i_get_anonymous_color_histo(i_img *im, unsigned int **col_usage, int maxc) {
 /*
 =back
 
-=head2 8-bit per sample image internal functions
-
-These are the functions installed in an 8-bit per sample image.
-
-=over
-
-=item i_ppix_d(im, x, y, col)
-
-Internal function.
-
-This is the function kept in the i_f_ppix member of an i_img object.
-It does a normal store of a pixel into the image with range checking.
-
-Returns 0 if the pixel could be set, -1 otherwise.
-
-=cut
-*/
-static
-int
-i_ppix_d(i_img *im, int x, int y, const i_color *val) {
-  int ch;
-  
-  if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
-    for(ch=0;ch<im->channels;ch++)
-      if (im->ch_mask&(1<<ch)) 
-       im->idata[(x+y*im->xsize)*im->channels+ch]=val->channel[ch];
-    return 0;
-  }
-  return -1; /* error was clipped */
-}
-
-/*
-=item i_gpix_d(im, x, y, &col)
-
-Internal function.
-
-This is the function kept in the i_f_gpix member of an i_img object.
-It does normal retrieval of a pixel from the image with range checking.
-
-Returns 0 if the pixel could be set, -1 otherwise.
-
-=cut
-*/
-static
-int 
-i_gpix_d(i_img *im, int x, int y, i_color *val) {
-  int ch;
-  if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
-    for(ch=0;ch<im->channels;ch++) 
-      val->channel[ch]=im->idata[(x+y*im->xsize)*im->channels+ch];
-    return 0;
-  }
-  for(ch=0;ch<im->channels;ch++) val->channel[ch] = 0;
-  return -1; /* error was cliped */
-}
-
-/*
-=item i_glin_d(im, l, r, y, vals)
-
-Reads a line of data from the image, storing the pixels at vals.
-
-The line runs from (l,y) inclusive to (r,y) non-inclusive
-
-vals should point at space for (r-l) pixels.
-
-l should never be less than zero (to avoid confusion about where to
-put the pixels in vals).
-
-Returns the number of pixels copied (eg. if r, l or y is out of range)
-
-=cut
-*/
-static
-int
-i_glin_d(i_img *im, int l, int r, int y, i_color *vals) {
-  int ch, count, i;
-  unsigned char *data;
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    count = r - l;
-    for (i = 0; i < count; ++i) {
-      for (ch = 0; ch < im->channels; ++ch)
-       vals[i].channel[ch] = *data++;
-    }
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=item i_plin_d(im, l, r, y, vals)
-
-Writes a line of data into the image, using the pixels at vals.
-
-The line runs from (l,y) inclusive to (r,y) non-inclusive
-
-vals should point at (r-l) pixels.
-
-l should never be less than zero (to avoid confusion about where to
-get the pixels in vals).
-
-Returns the number of pixels copied (eg. if r, l or y is out of range)
-
-=cut
-*/
-static
-int
-i_plin_d(i_img *im, int l, int r, int y, const i_color *vals) {
-  int ch, count, i;
-  unsigned char *data;
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    count = r - l;
-    for (i = 0; i < count; ++i) {
-      for (ch = 0; ch < im->channels; ++ch) {
-       if (im->ch_mask & (1 << ch)) 
-         *data = vals[i].channel[ch];
-       ++data;
-      }
-    }
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=item i_ppixf_d(im, x, y, val)
-
-=cut
-*/
-static
-int
-i_ppixf_d(i_img *im, int x, int y, const i_fcolor *val) {
-  int ch;
-  
-  if ( x>-1 && x<im->xsize && y>-1 && y<im->ysize ) {
-    for(ch=0;ch<im->channels;ch++)
-      if (im->ch_mask&(1<<ch)) {
-       im->idata[(x+y*im->xsize)*im->channels+ch] = 
-          SampleFTo8(val->channel[ch]);
-      }
-    return 0;
-  }
-  return -1; /* error was clipped */
-}
-
-/*
-=item i_gpixf_d(im, x, y, val)
-
-=cut
-*/
-static
-int
-i_gpixf_d(i_img *im, int x, int y, i_fcolor *val) {
-  int ch;
-  if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) {
-    for(ch=0;ch<im->channels;ch++) {
-      val->channel[ch] = 
-        Sample8ToF(im->idata[(x+y*im->xsize)*im->channels+ch]);
-    }
-    return 0;
-  }
-  return -1; /* error was cliped */
-}
-
-/*
-=item i_glinf_d(im, l, r, y, vals)
-
-Reads a line of data from the image, storing the pixels at vals.
-
-The line runs from (l,y) inclusive to (r,y) non-inclusive
-
-vals should point at space for (r-l) pixels.
-
-l should never be less than zero (to avoid confusion about where to
-put the pixels in vals).
-
-Returns the number of pixels copied (eg. if r, l or y is out of range)
-
-=cut
-*/
-static
-int
-i_glinf_d(i_img *im, int l, int r, int y, i_fcolor *vals) {
-  int ch, count, i;
-  unsigned char *data;
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    count = r - l;
-    for (i = 0; i < count; ++i) {
-      for (ch = 0; ch < im->channels; ++ch)
-       vals[i].channel[ch] = Sample8ToF(*data++);
-    }
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=item i_plinf_d(im, l, r, y, vals)
-
-Writes a line of data into the image, using the pixels at vals.
-
-The line runs from (l,y) inclusive to (r,y) non-inclusive
-
-vals should point at (r-l) pixels.
-
-l should never be less than zero (to avoid confusion about where to
-get the pixels in vals).
-
-Returns the number of pixels copied (eg. if r, l or y is out of range)
-
-=cut
-*/
-static
-int
-i_plinf_d(i_img *im, int l, int r, int y, const i_fcolor *vals) {
-  int ch, count, i;
-  unsigned char *data;
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    count = r - l;
-    for (i = 0; i < count; ++i) {
-      for (ch = 0; ch < im->channels; ++ch) {
-       if (im->ch_mask & (1 << ch)) 
-         *data = SampleFTo8(vals[i].channel[ch]);
-       ++data;
-      }
-    }
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=item i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count)
-
-Reads sample values from im for the horizontal line (l, y) to (r-1,y)
-for the channels specified by chans, an array of int with chan_count
-elements.
-
-Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
-
-=cut
-*/
-static
-int
-i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, 
-              const int *chans, int chan_count) {
-  int ch, count, i, w;
-  unsigned char *data;
-
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    w = r - l;
-    count = 0;
-
-    if (chans) {
-      /* make sure we have good channel numbers */
-      for (ch = 0; ch < chan_count; ++ch) {
-        if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
-          return 0;
-        }
-      }
-      for (i = 0; i < w; ++i) {
-        for (ch = 0; ch < chan_count; ++ch) {
-          *samps++ = data[chans[ch]];
-          ++count;
-        }
-        data += im->channels;
-      }
-    }
-    else {
-      for (i = 0; i < w; ++i) {
-        for (ch = 0; ch < chan_count; ++ch) {
-          *samps++ = data[ch];
-          ++count;
-        }
-        data += im->channels;
-      }
-    }
-
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=item i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count)
-
-Reads sample values from im for the horizontal line (l, y) to (r-1,y)
-for the channels specified by chan_mask, where bit 0 is the first
-channel.
-
-Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
-
-=cut
-*/
-static
-int
-i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, 
-           const int *chans, int chan_count) {
-  int ch, count, i, w;
-  unsigned char *data;
-  for (ch = 0; ch < chan_count; ++ch) {
-    if (chans[ch] < 0 || chans[ch] >= im->channels) {
-      i_push_errorf(0, "No channel %d in this image", chans[ch]);
-    }
-  }
-  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
-    if (r > im->xsize)
-      r = im->xsize;
-    data = im->idata + (l+y*im->xsize) * im->channels;
-    w = r - l;
-    count = 0;
-
-    if (chans) {
-      /* make sure we have good channel numbers */
-      for (ch = 0; ch < chan_count; ++ch) {
-        if (chans[ch] < 0 || chans[ch] >= im->channels) {
-          i_push_errorf(0, "No channel %d in this image", chans[ch]);
-          return 0;
-        }
-      }
-      for (i = 0; i < w; ++i) {
-        for (ch = 0; ch < chan_count; ++ch) {
-          *samps++ = Sample8ToF(data[chans[ch]]);
-          ++count;
-        }
-        data += im->channels;
-      }
-    }
-    else {
-      for (i = 0; i < w; ++i) {
-        for (ch = 0; ch < chan_count; ++ch) {
-          *samps++ = Sample8ToF(data[ch]);
-          ++count;
-        }
-        data += im->channels;
-      }
-    }
-    return count;
-  }
-  else {
-    return 0;
-  }
-}
-
-/*
-=back
-
 =head2 Image method wrappers
 
 These functions provide i_fsample_t functions in terms of their
@@ -1773,12 +1321,12 @@ i_sample_t versions.
 
 =over
 
-=item i_ppixf_fp(i_img *im, int x, int y, i_fcolor *pix)
+=item i_ppixf_fp(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix)
 
 =cut
 */
 
-int i_ppixf_fp(i_img *im, int x, int y, const i_fcolor *pix) {
+int i_ppixf_fp(i_img *im, i_img_dim x, i_img_dim y, const i_fcolor *pix) {
   i_color temp;
   int ch;
 
@@ -1789,15 +1337,15 @@ int i_ppixf_fp(i_img *im, int x, int y, const i_fcolor *pix) {
 }
 
 /*
-=item i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix)
+=item i_gpixf_fp(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix)
 
 =cut
 */
-int i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix) {
+int i_gpixf_fp(i_img *im, i_img_dim x, i_img_dim y, i_fcolor *pix) {
   i_color temp;
   int ch;
 
-  if (i_gpix(im, x, y, &temp)) {
+  if (i_gpix(im, x, y, &temp) == 0) {
     for (ch = 0; ch < im->channels; ++ch)
       pix->channel[ch] = Sample8ToF(temp.channel[ch]);
     return 0;
@@ -1807,19 +1355,21 @@ int i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix) {
 }
 
 /*
-=item i_plinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix)
+=item i_plinf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *pix)
 
 =cut
 */
-int i_plinf_fp(i_img *im, int l, int r, int y, const i_fcolor *pix) {
+i_img_dim
+i_plinf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, const i_fcolor *pix) {
   i_color *work;
 
   if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
     if (r > im->xsize)
       r = im->xsize;
     if (r > l) {
-      int ret;
-      int i, ch;
+      i_img_dim ret;
+      i_img_dim i;
+      int ch;
       work = mymalloc(sizeof(i_color) * (r-l));
       for (i = 0; i < r-l; ++i) {
         for (ch = 0; ch < im->channels; ++ch) 
@@ -1840,19 +1390,21 @@ int i_plinf_fp(i_img *im, int l, int r, int y, const i_fcolor *pix) {
 }
 
 /*
-=item i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix)
+=item i_glinf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *pix)
 
 =cut
 */
-int i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix) {
+i_img_dim
+i_glinf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fcolor *pix) {
   i_color *work;
 
   if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
     if (r > im->xsize)
       r = im->xsize;
     if (r > l) {
-      int ret;
-      int i, ch;
+      i_img_dim ret;
+      i_img_dim i;
+      int ch;
       work = mymalloc(sizeof(i_color) * (r-l));
       ret = i_plin(im, l, r, y, work);
       for (i = 0; i < r-l; ++i) {
@@ -1873,11 +1425,13 @@ int i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix) {
 }
 
 /*
-=item i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp, int *chans, int chan_count)
+=item i_gsampf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samp, int *chans, int chan_count)
 
 =cut
 */
-int i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp, 
+
+i_img_dim
+i_gsampf_fp(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, i_fsample_t *samp, 
                 int const *chans, int chan_count) {
   i_sample_t *work;
 
@@ -1885,8 +1439,8 @@ int i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp,
     if (r > im->xsize)
       r = im->xsize;
     if (r > l) {
-      int ret;
-      int i;
+      i_img_dim ret;
+      i_img_dim i;
       work = mymalloc(sizeof(i_sample_t) * (r-l));
       ret = i_gsamp(im, l, r, y, work, chans, chan_count);
       for (i = 0; i < ret; ++i) {
@@ -1972,200 +1526,82 @@ int i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry) {
 /*
 =back
 
-=head2 Stream reading and writing wrapper functions
+=head2 Fallback handler
 
 =over
 
-=item i_gen_reader(i_gen_read_data *info, char *buf, int length)
-
-Performs general read buffering for file readers that permit reading
-to be done through a callback.
-
-The final callback gets two parameters, a I<need> value, and a I<want>
-value, where I<need> is the amount of data that the file library needs
-to read, and I<want> is the amount of space available in the buffer
-maintained by these functions.
-
-This means if you need to read from a stream that you don't know the
-length of, you can return I<need> bytes, taking the performance hit of
-possibly expensive callbacks (eg. back to perl code), or if you are
-reading from a stream where it doesn't matter if some data is lost, or
-if the total length of the stream is known, you can return I<want>
-bytes.
-
-=cut 
-*/
-
-int
-i_gen_reader(i_gen_read_data *gci, char *buf, int length) {
-  int total;
-
-  if (length < gci->length - gci->cpos) {
-    /* simplest case */
-    memcpy(buf, gci->buffer+gci->cpos, length);
-    gci->cpos += length;
-    return length;
-  }
-  
-  total = 0;
-  memcpy(buf, gci->buffer+gci->cpos, gci->length-gci->cpos);
-  total  += gci->length - gci->cpos;
-  length -= gci->length - gci->cpos;
-  buf    += gci->length - gci->cpos;
-  if (length < (int)sizeof(gci->buffer)) {
-    int did_read;
-    int copy_size;
-    while (length
-          && (did_read = (gci->cb)(gci->userdata, gci->buffer, length, 
-                                   sizeof(gci->buffer))) > 0) {
-      gci->cpos = 0;
-      gci->length = did_read;
-
-      copy_size = i_min(length, gci->length);
-      memcpy(buf, gci->buffer, copy_size);
-      gci->cpos += copy_size;
-      buf += copy_size;
-      total += copy_size;
-      length -= copy_size;
-    }
-  }
-  else {
-    /* just read the rest - too big for our buffer*/
-    int did_read;
-    while ((did_read = (gci->cb)(gci->userdata, buf, length, length)) > 0) {
-      length -= did_read;
-      total += did_read;
-      buf += did_read;
-    }
-  }
-  return total;
-}
-
-/*
-=item i_gen_read_data_new(i_read_callback_t cb, char *userdata)
-
-For use by callback file readers to initialize the reader buffer.
-
-Allocates, initializes and returns the reader buffer.
-
-See also L<image.c/free_gen_read_data> and L<image.c/i_gen_reader>.
+=item i_gsamp_bits_fb
 
 =cut
 */
-i_gen_read_data *
-i_gen_read_data_new(i_read_callback_t cb, char *userdata) {
-  i_gen_read_data *self = mymalloc(sizeof(i_gen_read_data));
-  self->cb = cb;
-  self->userdata = userdata;
-  self->length = 0;
-  self->cpos = 0;
-
-  return self;
-}
-
-/*
-=item i_free_gen_read_data(i_gen_read_data *)
-
-Cleans up.
 
-=cut
-*/
-void i_free_gen_read_data(i_gen_read_data *self) {
-  myfree(self);
-}
+i_img_dim
+i_gsamp_bits_fb(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *samps, 
+               const int *chans, int chan_count, int bits) {
+  dIMCTXim(im);
 
-/*
-=item i_gen_writer(i_gen_write_data *info, char const *data, int size)
+  if (bits < 1 || bits > 32) {
+    i_push_error(0, "Invalid bits, must be 1..32");
+    return -1;
+  }
 
-Performs write buffering for a callback based file writer.
+  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+    double scale;
+    int ch;
+    i_img_dim count, i, w;
+    
+    if (bits == 32)
+      scale = 4294967295.0;
+    else
+      scale = (double)(1 << bits) - 1;
 
-Failures are considered fatal, if a write fails then data will be
-dropped.
+    if (r > im->xsize)
+      r = im->xsize;
+    w = r - l;
+    count = 0;
 
-=cut
-*/
-int 
-i_gen_writer(
-i_gen_write_data *self, 
-char const *data, 
-int size)
-{
-  if (self->filledto && self->filledto+size > self->maxlength) {
-    if (self->cb(self->userdata, self->buffer, self->filledto)) {
-      self->filledto = 0;
+    if (chans) {
+      /* make sure we have good channel numbers */
+      for (ch = 0; ch < chan_count; ++ch) {
+        if (chans[ch] < 0 || chans[ch] >= im->channels) {
+          im_push_errorf(aIMCTX, 0, "No channel %d in this image", chans[ch]);
+          return -1;
+        }
+      }
+      for (i = 0; i < w; ++i) {
+       i_fcolor c;
+       i_gpixf(im, l+i, y, &c);
+        for (ch = 0; ch < chan_count; ++ch) {
+          *samps++ = (unsigned)(c.channel[ch] * scale + 0.5);
+          ++count;
+        }
+      }
     }
     else {
-      self->filledto = 0;
-      return 0;
+      if (chan_count <= 0 || chan_count > im->channels) {
+       i_push_error(0, "Invalid channel count");
+       return -1;
+      }
+      for (i = 0; i < w; ++i) {
+       i_fcolor c;
+       i_gpixf(im, l+i, y, &c);
+        for (ch = 0; ch < chan_count; ++ch) {
+          *samps++ = (unsigned)(c.channel[ch] * scale + 0.5);
+          ++count;
+        }
+      }
     }
+
+    return count;
   }
-  if (self->filledto+size <= self->maxlength) {
-    /* just save it */
-    memcpy(self->buffer+self->filledto, data, size);
-    self->filledto += size;
-    return 1;
+  else {
+    i_push_error(0, "Image position outside of image");
+    return -1;
   }
-  /* doesn't fit - hand it off */
-  return self->cb(self->userdata, data, size);
 }
 
-/*
-=item i_gen_write_data_new(i_write_callback_t cb, char *userdata, int max_length)
-
-Allocates and initializes the data structure used by i_gen_writer.
-
-This should be released with L<image.c/i_free_gen_write_data>
-
-=cut
-*/
-i_gen_write_data *i_gen_write_data_new(i_write_callback_t cb, 
-                                      char *userdata, int max_length)
-{
-  i_gen_write_data *self = mymalloc(sizeof(i_gen_write_data));
-  self->cb = cb;
-  self->userdata = userdata;
-  self->maxlength = i_min(max_length, sizeof(self->buffer));
-  if (self->maxlength < 0)
-    self->maxlength = sizeof(self->buffer);
-  self->filledto = 0;
-
-  return self;
-}
-
-/*
-=item i_free_gen_write_data(i_gen_write_data *info, int flush)
-
-Cleans up the write buffer.
-
-Will flush any left-over data if I<flush> is non-zero.
-
-Returns non-zero if flush is zero or if info->cb() returns non-zero.
-
-Return zero only if flush is non-zero and info->cb() returns zero.
-ie. if it fails.
-
-=cut
-*/
-
-int i_free_gen_write_data(i_gen_write_data *info, int flush)
-{
-  int result = !flush || 
-    info->filledto == 0 ||
-    info->cb(info->userdata, info->buffer, info->filledto);
-  myfree(info);
-
-  return result;
-}
-
-struct magic_entry {
-  unsigned char *magic;
-  size_t magic_size;
-  char *name;
-  unsigned char *mask;  
-};
-
 static int
-test_magic(unsigned char *buffer, size_t length, struct magic_entry const *magic) {
+test_magic(unsigned char *buffer, size_t length, struct file_magic_entry const *magic) {
   if (length < magic->magic_size)
     return 0;
   if (magic->mask) {
@@ -2203,8 +1639,8 @@ Check the beginning of the supplied file for a 'magic number'
   { (unsigned char *)(magic ""), sizeof(magic)-1, type, (unsigned char *)(mask) }
 
 const char *
-i_test_format_probe(io_glue *data, int length) {
-  static const struct magic_entry formats[] = {
+im_test_format_probe(im_context_t ctx, io_glue *data, int length) {
+  static const struct file_magic_entry formats[] = {
     FORMAT_ENTRY("\xFF\xD8", "jpeg"),
     FORMAT_ENTRY("GIF87a", "gif"),
     FORMAT_ENTRY("GIF89a", "gif"),
@@ -2259,8 +1695,19 @@ i_test_format_probe(io_glue *data, int length) {
 
     /* bzip2 compressed */
     FORMAT_ENTRY("BZh", "bzip2"),
+
+    /* WEBP
+       http://code.google.com/speed/webp/docs/riff_container.html */
+    FORMAT_ENTRY2("RIFF    WEBP", "webp", "xxxx    xxxx"),
+
+    /* JPEG 2000 
+       This might match a little loosely */
+    FORMAT_ENTRY("\x00\x00\x00\x0CjP  \x0D\x0A\x87\x0A", "jp2"),
+
+    /* FLIF - Free Lossless Image Format - https://flif.info/spec.html */
+    FORMAT_ENTRY("FLIF", "flif")
   };
-  static const struct magic_entry more_formats[] = {
+  static const struct file_magic_entry more_formats[] = {
     /* these were originally both listed as ico, but cur files can
        include hotspot information */
     FORMAT_ENTRY("\x00\x00\x01\x00", "ico"), /* Windows icon */
@@ -2273,13 +1720,30 @@ i_test_format_probe(io_glue *data, int length) {
   unsigned char head[18];
   ssize_t rc;
 
-  io_glue_commit_types(data);
-  rc = data->readcb(data, head, 18);
+  rc = i_io_peekn(data, head, 18);
   if (rc == -1) return NULL;
-  data->seekcb(data, -rc, SEEK_CUR);
+#if 0
+  {
+    int i;
+    fprintf(stderr, "%d bytes -", (int)rc);
+    for (i = 0; i < rc; ++i)
+      fprintf(stderr, " %02x", head[i]);
+    fprintf(stderr, "\n");
+  }
+#endif
+
+  {
+    im_file_magic *p = ctx->file_magic;
+    while (p) {
+      if (test_magic(head, rc, &p->m)) {
+       return p->m.name;
+      }
+      p = p->next;
+    }
+  }
 
   for(i=0; i<sizeof(formats)/sizeof(formats[0]); i++) { 
-    struct magic_entry const *entry = formats + i;
+    struct file_magic_entry const *entry = formats + i;
 
     if (test_magic(head, rc, entry)) 
       return entry->name;
@@ -2290,7 +1754,7 @@ i_test_format_probe(io_glue *data, int length) {
     return "tga";
 
   for(i=0; i<sizeof(more_formats)/sizeof(more_formats[0]); i++) { 
-    struct magic_entry const *entry = more_formats + i;
+    struct file_magic_entry const *entry = more_formats + i;
 
     if (test_magic(head, rc, entry)) 
       return entry->name;
@@ -2302,10 +1766,13 @@ i_test_format_probe(io_glue *data, int length) {
 /*
 =item i_img_is_monochrome(img, &zero_is_white)
 
+=category Image Information
+
 Tests an image to check it meets our monochrome tests.
 
 The idea is that a file writer can use this to test where it should
-write the image in whatever bi-level format it uses, eg. pbm for pnm.
+write the image in whatever bi-level format it uses, eg. C<pbm> for
+C<pnm>.
 
 For performance of encoders we require monochrome images:
 
@@ -2317,12 +1784,12 @@ be paletted
 
 =item *
 
-have a palette of two colors, containing only (0,0,0) and
-(255,255,255) in either order.
+have a palette of two colors, containing only C<(0,0,0)> and
+C<(255,255,255)> in either order.
 
 =back
 
-zero_is_white is set to non-zero iff the first palette entry is white.
+C<zero_is_white> is set to non-zero if the first palette entry is white.
 
 =cut
 */
@@ -2332,7 +1799,8 @@ i_img_is_monochrome(i_img *im, int *zero_is_white) {
   if (im->type == i_palette_type
       && i_colorcount(im) == 2) {
     i_color colors[2];
-    i_getcolors(im, 0, colors, 2);
+    if (!i_getcolors(im, 0, colors, 2))
+      return 0;
     if (im->channels == 3) {
       if (colors[0].rgb.r == 255 && 
           colors[0].rgb.g == 255 &&
@@ -2340,7 +1808,7 @@ i_img_is_monochrome(i_img *im, int *zero_is_white) {
           colors[1].rgb.r == 0 &&
           colors[1].rgb.g == 0 &&
           colors[1].rgb.b == 0) {
-        *zero_is_white = 0;
+        *zero_is_white = 1;
         return 1;
       }
       else if (colors[0].rgb.r == 0 && 
@@ -2349,19 +1817,19 @@ i_img_is_monochrome(i_img *im, int *zero_is_white) {
                colors[1].rgb.r == 255 &&
                colors[1].rgb.g == 255 &&
                colors[1].rgb.b == 255) {
-        *zero_is_white = 1;
+        *zero_is_white = 0;
         return 1;
       }
     }
     else if (im->channels == 1) {
       if (colors[0].channel[0] == 255 &&
-          colors[1].channel[1] == 0) {
-        *zero_is_white = 0;
+          colors[1].channel[0] == 0) {
+        *zero_is_white = 1;
         return 1;
       }
       else if (colors[0].channel[0] == 0 &&
-               colors[0].channel[0] == 255) {
-        *zero_is_white = 1;
+               colors[1].channel[0] == 255) {
+        *zero_is_white = 0;
         return 1;         
       }
     }
@@ -2371,6 +1839,62 @@ i_img_is_monochrome(i_img *im, int *zero_is_white) {
   return 0;
 }
 
+/*
+=item i_get_file_background(im, &bg)
+
+=category Files
+
+Retrieve the file write background color tag from the image.
+
+If not present, C<bg> is set to black.
+
+Returns 1 if the C<i_background> tag was found and valid.
+
+=cut
+*/
+
+int
+i_get_file_background(i_img *im, i_color *bg) {
+  int result = i_tags_get_color(&im->tags, "i_background", 0, bg);
+  if (!result) {
+    /* black default */
+    bg->channel[0] = bg->channel[1] = bg->channel[2] = 0;
+  }
+  /* always full alpha */
+  bg->channel[3] = 255;
+
+  return result;
+}
+
+/*
+=item i_get_file_backgroundf(im, &bg)
+
+=category Files
+
+Retrieve the file write background color tag from the image as a
+floating point color.
+
+Implemented in terms of i_get_file_background().
+
+If not present, C<bg> is set to black.
+
+Returns 1 if the C<i_background> tag was found and valid.
+
+=cut
+*/
+
+int
+i_get_file_backgroundf(i_img *im, i_fcolor *fbg) {
+  i_color bg;
+  int result = i_get_file_background(im, &bg);
+  fbg->rgba.r = Sample8ToF(bg.rgba.r);
+  fbg->rgba.g = Sample8ToF(bg.rgba.g);
+  fbg->rgba.b = Sample8ToF(bg.rgba.b);
+  fbg->rgba.a = 1.0;
+
+  return result;
+}
+
 /*
 =back
 
@@ -2378,7 +1902,7 @@ i_img_is_monochrome(i_img *im, int *zero_is_white) {
 
 Arnar M. Hrafnkelsson <addi@umich.edu>
 
-Tony Cook <tony@develop-help.com>
+Tony Cook <tonyc@cpan.org>
 
 =head1 SEE ALSO