]> git.imager.perl.org - imager.git/blobdiff - image.c
- images with an translucent alpha channel were not scaled correctly
[imager.git] / image.c
diff --git a/image.c b/image.c
index 4f21c161cee7a54f3d677e14ef15ccf274795822..bf25b7d253b70e872e4858b337d2c7feab0acd8b 100644 (file)
--- a/image.c
+++ b/image.c
@@ -49,8 +49,58 @@ 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);*/
+
+/*
+=item i_img_alloc()
+=category Image Implementation
+
+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 *
+i_img_alloc(void) {
+  return mymalloc(sizeof(i_img));
+}
+
+/*
+=item i_img_init(C<img>)
+=category Image Implementation
+
+Imager internal initialization of images.
+
+Currently this does very little, in the future it may be used to
+support threads, or color profiles.
+
+=cut
+*/
+
+void
+i_img_init(i_img *img) {
+  img->im_data = NULL;
+}
 
 /* 
 =item ICL_new_internal(r, g, b, a)
@@ -232,6 +282,9 @@ static i_img IIM_base_8bit_direct =
   NULL, /* i_f_setcolors */
 
   NULL, /* i_f_destroy */
+
+  i_gsamp_bits_fb,
+  NULL, /* i_f_psamp_bits */
 };
 
 /*static void set_8bit_direct(i_img *im) {
@@ -256,7 +309,9 @@ static i_img IIM_base_8bit_direct =
 
 =item i_img_8_new(x, y, ch)
 
-=category Image creation
+=category Image creation/destruction
+
+=synopsis i_img *img = i_img_8_new(width, height, channels);
 
 Creates a new image object I<x> pixels wide, and I<y> pixels high with
 I<ch> channels.
@@ -299,8 +354,8 @@ 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 = i_img_alloc();
   
   *im = IIM_base_8bit_direct;
   im->xsize=0;
@@ -309,6 +364,8 @@ i_img_new() {
   im->ch_mask=MAXINT;
   im->bytes=0;
   im->idata=NULL;
+
+  i_img_init(im);
   
   mm_log((1,"(%p) <- i_img_struct\n",im));
   return im;
@@ -371,8 +428,7 @@ i_img_empty_ch(i_img *im,int x,int y,int ch) {
   }
 
   if (im == NULL)
-    if ( (im=mymalloc(sizeof(i_img))) == NULL)
-      i_fatal(2,"malloc() error\n");
+    im = i_img_alloc();
 
   memcpy(im, &IIM_base_8bit_direct, sizeof(i_img));
   i_tags_new(&im->tags);
@@ -386,6 +442,8 @@ i_img_empty_ch(i_img *im,int x,int y,int ch) {
   memset(im->idata,0,(size_t)im->bytes);
   
   im->ext_data = NULL;
+
+  i_img_init(im);
   
   mm_log((1,"(%p) <- i_img_empty_ch\n",im));
   return im;
@@ -421,13 +479,12 @@ i_img_exorcise(i_img *im) {
 }
 
 /* 
-=item i_img_destroy(im)
-
-=category Image
-
-Destroy image and free data via exorcise.
+=item i_img_destroy(C<img>)
+=order 90
+=category Image creation/destruction
+=synopsis i_img_destroy(img)
 
-   im - Image pointer
+Destroy an image object
 
 =cut
 */
@@ -479,12 +536,15 @@ i_img_info(i_img *im,int *info) {
 }
 
 /*
-=item i_img_setmask(im, ch_mask)
-
+=item i_img_setmask(C<im>, C<ch_mask>)
+=category Image Information
 =synopsis // only channel 0 writeable 
 =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 +553,11 @@ i_img_setmask(i_img *im,int ch_mask) { im->ch_mask=ch_mask; }
 
 
 /*
-=item i_img_getmask(im)
-
-=synopsis mask = i_img_getmask(img);
+=item i_img_getmask(C<im>)
+=category Image Information
+=synopsis int mask = i_img_getmask(img);
 
-Get the image channel mask for I<im>.
+Get the image channel mask for C<im>.
 
 =cut
 */
@@ -505,11 +565,11 @@ int
 i_img_getmask(i_img *im) { return im->ch_mask; }
 
 /*
-=item i_img_getchannels(im)
+=item i_img_getchannels(C<im>)
+=category Image Information
+=synopsis int channels = i_img_getchannels(img);
 
-=synopsis channels = i_img_getchannels(img);
-
-Get the number of channels in I<im>.
+Get the number of channels in C<im>.
 
 =cut
 */
@@ -517,9 +577,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 +591,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,13 +605,13 @@ i_img_get_height(i_img *im) {
 }
 
 /*
-=item i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, trans)
+=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
 */
@@ -587,80 +647,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)
-
-=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)
+=item i_copy(source)
 
 =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.
 
@@ -704,20 +695,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);
@@ -730,114 +709,6 @@ i_copy(i_img *src) {
 }
 
 
-/*
-=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
-
-=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;
-}
-
 
 
 
@@ -876,10 +747,12 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
   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);
 
+  i_clear_error();
   mm_log((1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis));
 
-
   if (Axis == XAXIS) {
     hsize = (int)(0.5 + im->xsize * Value);
     if (hsize < 1) {
@@ -904,6 +777,10 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
   }
   
   new_img = i_img_empty_ch(NULL, 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); 
@@ -947,15 +824,46 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
          
          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);
       }
@@ -972,14 +880,43 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
 
          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);
       }
@@ -1018,13 +955,14 @@ i_scale_nn(i_img *im, float scx, float scy) {
   nxsize = (int) ((float) im->xsize * scx);
   if (nxsize < 1) {
     nxsize = 1;
-    scx = 1 / im->xsize;
+    scx = 1.0 / im->xsize;
   }
   nysize = (int) ((float) 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);
   
@@ -1039,9 +977,10 @@ i_scale_nn(i_img *im, float scx, float scy) {
 }
 
 /*
-=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).
 
@@ -1081,9 +1020,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).
 
@@ -1172,6 +1112,7 @@ can return zero.
 
 =cut
 */
+
 float
 i_img_diff(i_img *im1,i_img *im2) {
   int x,y,ch,xb,yb,chb;
@@ -1197,6 +1138,50 @@ i_img_diff(i_img *im1,i_img *im2) {
   return tdiff;
 }
 
+/*
+=item i_img_diffd(im1, im2)
+
+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.
+
+This is like i_img_diff() but looks at floating point samples instead.
+
+=cut
+*/
+
+double
+i_img_diffd(i_img *im1,i_img *im2) {
+  int x,y,ch,xb,yb,chb;
+  double tdiff;
+  i_fcolor val1,val2;
+
+  mm_log((1,"i_img_diffd(im1 0x%x,im2 0x%x)\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));
+
+  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;
+    }
+  }
+  mm_log((1,"i_img_diffd <- (%.2f)\n",tdiff));
+
+  return tdiff;
+}
+
 /* just a tiny demo of haar wavelets */
 
 i_img*
@@ -1254,26 +1239,139 @@ to indicate that it was more than max colors
 
 =cut
 */
+/* This function has been changed and is now faster. It's using
+ * i_gsamp instead of i_gpix */
 int
 i_count_colors(i_img *im,int maxc) {
   struct octt *ct;
   int x,y;
-  int xsize,ysize;
-  i_color val;
   int colorcnt;
+  int channels[3];
+  int *samp_chans;
+  i_sample_t * samp;
+  int xsize = im->xsize; 
+  int ysize = im->ysize;
+  int samp_cnt = 3 * xsize;
+
+  if (im->channels >= 3) {
+    samp_chans = NULL;
+  }
+  else {
+    channels[0] = channels[1] = channels[2] = 0;
+    samp_chans = channels;
+  }
+
+  ct = octt_new();
 
-  mm_log((1,"i_count_colors(im 0x%08X,maxc %d)\n"));
+  samp = (i_sample_t *) mymalloc( xsize * 3 * sizeof(i_sample_t));
 
-  xsize=im->xsize; 
-  ysize=im->ysize;
-  ct=octt_new();
-  colorcnt=0;
-  for(y=0;y<ysize;y++) for(x=0;x<xsize;x++) {
-    i_gpix(im,x,y,&val);
-    colorcnt+=octt_add(ct,val.rgb.r,val.rgb.g,val.rgb.b);
-    if (colorcnt > maxc) { octt_delete(ct); return -1; }
+  colorcnt = 0;
+  for(y = 0; y < ysize; ) {
+      i_gsamp(im, 0, xsize, y++, samp, samp_chans, 3);
+      for(x = 0; x < samp_cnt; ) {
+          colorcnt += octt_add(ct, samp[x], samp[x+1], samp[x+2]);
+          x += 3;
+          if (colorcnt > maxc) { 
+              octt_delete(ct); 
+              return -1; 
+          }
+      }
   }
+  myfree(samp);
+  octt_delete(ct);
+  return colorcnt;
+}
+
+/* sorts the array ra[0..n-1] into increasing order using heapsort algorithm 
+ * (adapted from the Numerical Recipes)
+ */
+/* Needed by get_anonymous_color_histo */
+static void
+hpsort(unsigned int n, unsigned *ra) {
+    unsigned int i,
+                 ir,
+                 j,
+                 l, 
+                 rra;
+
+    if (n < 2) return;
+    l = n >> 1;
+    ir = n - 1;
+    for(;;) {
+        if (l > 0) {
+            rra = ra[--l];
+        }
+        else {
+            rra = ra[ir];
+            ra[ir] = ra[0];
+            if (--ir == 0) {
+                ra[0] = rra;
+                break;
+            }
+        }
+        i = l;
+        j = 2 * l + 1;
+        while (j <= ir) {
+            if (j < ir && ra[j] < ra[j+1]) j++;
+            if (rra < ra[j]) {
+                ra[i] = ra[j];
+                i = j;
+                j++; j <<= 1; j--;
+            }
+            else break;
+        }
+        ra[i] = rra;
+    }
+}
+
+/* This function constructs an ordered list which represents how much the
+ * different colors are used. So for instance (100, 100, 500) means that one
+ * color is used for 500 pixels, another for 100 pixels and another for 100
+ * pixels. It's tuned for performance. You might not like the way I've hardcoded
+ * the maxc ;-) and you might want to change the name... */
+/* Uses octt_histo */
+int
+i_get_anonymous_color_histo(i_img *im, unsigned int **col_usage, int maxc) {
+  struct octt *ct;
+  int 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;
+  int samp_cnt = 3 * xsize;
+  ct = octt_new();
+  
+  samp = (i_sample_t *) mymalloc( xsize * 3 * sizeof(i_sample_t));
+  
+  if (im->channels >= 3) {
+    samp_chans = NULL;
+  }
+  else {
+    channels[0] = channels[1] = channels[2] = 0;
+    samp_chans = channels;
+  }
+
+  colorcnt = 0;
+  for(y = 0; y < ysize; ) {
+    i_gsamp(im, 0, xsize, y++, samp, samp_chans, 3);
+    for(x = 0; x < samp_cnt; ) {
+      colorcnt += octt_add(ct, samp[x], samp[x+1], samp[x+2]);
+      x += 3;
+      if (colorcnt > maxc) { 
+       octt_delete(ct); 
+       return -1; 
+      }
+    }
+  }
+  myfree(samp);
+  /* Now that we know the number of colours... */
+  col_usage_it = *col_usage = (unsigned int *) mymalloc(colorcnt * sizeof(unsigned int));
+  octt_histo(ct, &col_usage_it);
+  hpsort(colorcnt, *col_usage);
   octt_delete(ct);
   return colorcnt;
 }
@@ -1573,6 +1671,11 @@ i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps,
       }
     }
     else {
+      if (chan_count <= 0 || chan_count > im->channels) {
+       i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
+                     chan_count);
+       return 0;
+      }
       for (i = 0; i < w; ++i) {
         for (ch = 0; ch < chan_count; ++ch) {
           *samps++ = data[ch];
@@ -1635,6 +1738,11 @@ i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps,
       }
     }
     else {
+      if (chan_count <= 0 || chan_count > im->channels) {
+       i_push_errorf(0, "chan_count %d out of range, must be >0, <= channels", 
+                     chan_count);
+       return 0;
+      }
       for (i = 0; i < w; ++i) {
         for (ch = 0; ch < chan_count; ++ch) {
           *samps++ = Sample8ToF(data[ch]);
@@ -1859,6 +1967,80 @@ int i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry) {
 /*
 =back
 
+=head2 Fallback handler
+
+=over
+
+=item i_gsamp_bits_fb
+
+=cut
+*/
+
+int 
+i_gsamp_bits_fb(i_img *im, int l, int r, int y, unsigned *samps, 
+               const int *chans, int chan_count, int bits) {
+  if (bits < 1 || bits > 32) {
+    i_push_error(0, "Invalid bits, must be 1..32");
+    return -1;
+  }
+
+  if (y >=0 && y < im->ysize && l < im->xsize && l >= 0) {
+    double scale;
+    int ch, count, i, w;
+    
+    if (bits == 32)
+      scale = 4294967295.0;
+    else
+      scale = (double)(1 << bits) - 1;
+
+    if (r > im->xsize)
+      r = im->xsize;
+    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 -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 {
+      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;
+  }
+  else {
+    i_push_error(0, "Image position outside of image");
+    return -1;
+  }
+}
+
+/*
+=back
+
 =head2 Stream reading and writing wrapper functions
 
 =over
@@ -2146,6 +2328,14 @@ 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"),
   };
   static const struct magic_entry more_formats[] = {
     /* these were originally both listed as ico, but cur files can
@@ -2189,10 +2379,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:
 
@@ -2204,12 +2397,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
 */
@@ -2227,7 +2420,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 && 
@@ -2236,19 +2429,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;         
       }
     }
@@ -2258,6 +2451,54 @@ 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, returns black.
+
+=cut
+*/
+
+void
+i_get_file_background(i_img *im, i_color *bg) {
+  if (!i_tags_get_color(&im->tags, "i_background", 0, bg)) {
+    /* black default */
+    bg->channel[0] = bg->channel[1] = bg->channel[2] = 0;
+  }
+  /* always full alpha */
+  bg->channel[3] = 255;
+}
+
+/*
+=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, returns black.
+
+=cut
+*/
+
+void
+i_get_file_backgroundf(i_img *im, i_fcolor *fbg) {
+  i_color bg;
+
+  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;
+}
+
 /*
 =back