]> git.imager.perl.org - imager.git/blobdiff - image.c
the rubthrough() method now supports destination images with an alpha
[imager.git] / image.c
diff --git a/image.c b/image.c
index 8c6149d72dca75f11704a67a04ae1aaa6f7fa147..316982afdc0d841e211f901167af904ae1c7b01f 100644 (file)
--- a/image.c
+++ b/image.c
@@ -1,6 +1,5 @@
-#include "image.h"
-#include "imagei.h"
-#include "io.h"
+#include "imager.h"
+#include "imageri.h"
 
 /*
 =head1 NAME
@@ -26,7 +25,7 @@ color objects for Imager.
 
 Some of these functions are internal.
 
-=over 4
+=over
 
 =cut
 */
@@ -38,18 +37,20 @@ 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 */
-void fake(void) { ceil(1); }
+static void fake(void) { ceil(1); }
 
-static int i_ppix_d(i_img *im, int x, int y, i_color *val);
+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, i_color *vals);
-static int i_ppixf_d(i_img *im, int x, int y, i_fcolor *val);
+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, i_fcolor *vals);
-static int i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, int *chans, int chan_count);
-static int i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, int *chans, int chan_count);
+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 ICL_new_internal(r, g, b, a)
@@ -70,7 +71,7 @@ ICL_new_internal(unsigned char r,unsigned char g,unsigned char b,unsigned char a
 
   mm_log((1,"ICL_new_internal(r %d,g %d,b %d,a %d)\n", r, g, b, a));
 
-  if ( (cl=mymalloc(sizeof(i_color))) == NULL) m_fatal(2,"malloc() error\n");
+  if ( (cl=mymalloc(sizeof(i_color))) == NULL) i_fatal(2,"malloc() error\n");
   cl->rgba.r = r;
   cl->rgba.g = g;
   cl->rgba.b = b;
@@ -99,7 +100,7 @@ ICL_set_internal(i_color *cl,unsigned char r,unsigned char g,unsigned char b,uns
   mm_log((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)
-      m_fatal(2,"malloc() error\n");
+      i_fatal(2,"malloc() error\n");
   cl->rgba.r=r;
   cl->rgba.g=g;
   cl->rgba.b=b;
@@ -141,7 +142,7 @@ Dump color information to log - strictly for debugging.
 */
 
 void
-ICL_info(i_color *cl) {
+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));
 }
@@ -172,7 +173,7 @@ i_fcolor *i_fcolor_new(double r, double g, double b, double a) {
 
   mm_log((1,"i_fcolor_new(r %g,g %g,b %g,a %g)\n", r, g, b, a));
 
-  if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) m_fatal(2,"malloc() error\n");
+  if ( (cl=mymalloc(sizeof(i_fcolor))) == NULL) i_fatal(2,"malloc() error\n");
   cl->rgba.r = r;
   cl->rgba.g = g;
   cl->rgba.b = b;
@@ -202,7 +203,7 @@ static i_img IIM_base_8bit_direct =
 {
   0, /* channels set */
   0, 0, 0, /* xsize, ysize, bytes */
-  ~0, /* ch_mask */
+  ~0U, /* ch_mask */
   i_8_bits, /* bits */
   i_direct_type, /* type */
   0, /* virtual */
@@ -253,7 +254,12 @@ static i_img IIM_base_8bit_direct =
 /*
 =item IIM_new(x, y, ch)
 
-Creates a new image object I<x> pixels wide, and I<y> pixels high with I<ch> channels.
+=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
 */
@@ -278,8 +284,6 @@ IIM_DESTROY(i_img *im) {
   /*   myfree(cl); */
 }
 
-
-
 /* 
 =item i_img_new()
 
@@ -296,7 +300,7 @@ i_img_new() {
   
   mm_log((1,"i_img_struct()\n"));
   if ( (im=mymalloc(sizeof(i_img))) == NULL)
-    m_fatal(2,"malloc() error\n");
+    i_fatal(2,"malloc() error\n");
   
   *im = IIM_base_8bit_direct;
   im->xsize=0;
@@ -347,10 +351,28 @@ Re-new image reference
 
 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)
-      m_fatal(2,"malloc() error\n");
+      i_fatal(2,"malloc() error\n");
 
   memcpy(im, &IIM_base_8bit_direct, sizeof(i_img));
   i_tags_new(&im->tags);
@@ -358,8 +380,9 @@ i_img_empty_ch(i_img *im,int x,int y,int ch) {
   im->ysize    = y;
   im->channels = ch;
   im->ch_mask  = MAXINT;
-  im->bytes=x*y*im->channels;
-  if ( (im->idata=mymalloc(im->bytes)) == NULL) m_fatal(2,"malloc() error\n"); 
+  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;
@@ -400,6 +423,8 @@ i_img_exorcise(i_img *im) {
 /* 
 =item i_img_destroy(im)
 
+=category Image
+
 Destroy image and free data via exorcise.
 
    im - Image pointer
@@ -417,6 +442,8 @@ i_img_destroy(i_img *im) {
 /* 
 =item i_img_info(im, info)
 
+=category Image
+
 Return image information
 
    im - Image pointer
@@ -483,54 +510,12 @@ int
 i_img_getchannels(i_img *im) { return im->channels; }
 
 
-/*
-=item i_ppix(im, x, y, col)
-
-Sets the pixel at (I<x>,I<y>) in I<im> to I<col>.
-
-Returns true if the pixel could be set, false if x or y is out of
-range.
-
-=cut
-*/
-int
-(i_ppix)(i_img *im, int x, int y, i_color *val) { return im->i_f_ppix(im, x, y, val); }
-
-/*
-=item i_gpix(im, x, y, &col)
-
-Get the pixel at (I<x>,I<y>) in I<im> into I<col>.
-
-Returns true if the pixel could be retrieved, false otherwise.
-
-=cut
-*/
-int
-(i_gpix)(i_img *im, int x, int y, i_color *val) { return im->i_f_gpix(im, x, y, val); }
-
-/*
-=item i_ppix_pch(im, x, y, ch)
-
-Get the value from the channel I<ch> for pixel (I<x>,I<y>) from I<im>
-scaled to [0,1].
-
-Returns zero if x or y is out of range.
-
-Warning: this ignores the vptr interface for images.
-
-=cut
-*/
-float
-i_gpix_pch(i_img *im,int x,int y,int ch) {
-  /* FIXME */
-  if (x>-1 && x<im->xsize && y>-1 && y<im->ysize) return ((float)im->idata[(x+y*im->xsize)*im->channels+ch]/255);
-  else return 0;
-}
-
 
 /*
 =item i_copyto_trans(im, src, x1, y1, x2, y2, tx, ty, 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.
@@ -539,7 +524,7 @@ pass NULL in trans for non transparent i_colors.
 */
 
 void
-i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,i_color *trans) {
+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_color pv;
   int x,y,t,ttx,tty,tt,ch;
 
@@ -571,6 +556,8 @@ i_copyto_trans(i_img *im,i_img *src,int x1,int y1,int x2,int y2,int tx,int ty,i_
 /*
 =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.
@@ -586,22 +573,39 @@ i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty)
   
   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 pv;
+    i_color *row = mymalloc(sizeof(i_color) * (x2-x1));
     tty = ty;
     for(y=y1; y<y2; y++) {
       ttx = tx;
-      for(x=x1; x<x2; x++) {
-        i_gpix(src, x,   y,   &pv);
-        i_ppix(im,  ttx, tty, &pv);
-        ttx++;
-      }
+      i_glin(src, x1, x2, y, row);
+      i_plin(im, tx, tx+x2-x1, tty, row);
       tty++;
     }
+    myfree(row);
   }
   else {
     i_fcolor pv;
@@ -619,25 +623,34 @@ i_copyto(i_img *im, i_img *src, int x1, int y1, int x2, int y2, int tx, int ty)
 }
 
 /*
-=item i_copy(im, src)
+=item i_copy(src)
+
+=category Image
 
-Copies the contents of the image I<src> over the image I<im>.
+Creates a new image that is a copy of src.
+
+Tags are not copied, only the image data.
+
+Returns: i_img *
 
 =cut
 */
 
-void
-i_copy(i_img *im, i_img *src) {
+i_img *
+i_copy(i_img *src) {
   int y, y1, x1;
+  i_img *im = i_sametype(src, src->xsize, src->ysize);
+
+  mm_log((1,"i_copy(src %p)\n", src));
 
-  mm_log((1,"i_copy(im* %p,src %p)\n", im, src));
+  if (!im)
+    return NULL;
 
   x1 = src->xsize;
   y1 = src->ysize;
   if (src->type == i_direct_type) {
     if (src->bits == i_8_bits) {
       i_color *pv;
-      i_img_empty_ch(im, x1, y1, src->channels);
       pv = mymalloc(sizeof(i_color) * x1);
       
       for (y = 0; y < y1; ++y) {
@@ -648,14 +661,6 @@ i_copy(i_img *im, i_img *src) {
     }
     else {
       i_fcolor *pv;
-      if (src->bits == i_16_bits)
-       i_img_16_new_low(im, x1, y1, src->channels);
-      else if (src->bits == i_double_bits)
-       i_img_double_new_low(im, x1, y1, src->channels);
-      else {
-       fprintf(stderr, "i_copy(): Unknown image bit size %d\n", src->bits);
-       return; /* I dunno */
-      }
 
       pv = mymalloc(sizeof(i_fcolor) * x1);
       for (y = 0; y < y1; ++y) {
@@ -687,100 +692,8 @@ i_copy(i_img *im, i_img *src) {
     }
     myfree(vals);
   }
-}
-
-
-/*
-=item i_rubthru(im, src, tx, ty)
-
-Takes the image I<src> and applies it at an original (I<tx>,I<ty>) in I<im>.
 
-The alpha channel of each pixel in I<src> is used to control how much
-the existing colour in I<im> is replaced, if it is 255 then the colour
-is completely replaced, if it is 0 then the original colour is left 
-unmodified.
-
-=cut
-*/
-
-int
-i_rubthru(i_img *im,i_img *src,int tx,int ty) {
-  int x, y, ttx, tty;
-  int chancount;
-  int chans[3];
-  int alphachan;
-  int ch;
-
-  mm_log((1,"i_rubthru(im %p, src %p, tx %d, ty %d)\n", im, src, tx, ty));
-  i_clear_error();
-
-  if (im->channels == 3 && src->channels == 4) {
-    chancount = 3;
-    chans[0] = 0; chans[1] = 1; chans[2] = 2;
-    alphachan = 3;
-  }
-  else if (im->channels == 3 && src->channels == 2) {
-    chancount = 3;
-    chans[0] = chans[1] = chans[2] = 0;
-    alphachan = 1;
-  }
-  else if (im->channels == 1 && src->channels == 2) {
-    chancount = 1;
-    chans[0] = 0;
-    alphachan = 1;
-  }
-  else {
-    i_push_error(0, "rubthru can only work where (dest, src) channels are (3,4), (3,2) or (1,2)");
-    return 0;
-  }
-
-  if (im->bits <= 8) {
-    /* if you change this code, please make sure the else branch is
-       changed in a similar fashion - TC */
-    int alpha;
-    i_color pv, orig, dest;
-    ttx = tx;
-    for(x=0; x<src->xsize; x++) {
-      tty=ty;
-      for(y=0;y<src->ysize;y++) {
-        /* fprintf(stderr,"reading (%d,%d) writing (%d,%d).\n",x,y,ttx,tty); */
-        i_gpix(src, x,   y,   &pv);
-        i_gpix(im,  ttx, tty, &orig);
-        alpha = pv.channel[alphachan];
-        for (ch = 0; ch < chancount; ++ch) {
-          dest.channel[ch] = (alpha * pv.channel[chans[ch]]
-                              + (255 - alpha) * orig.channel[ch])/255;
-        }
-        i_ppix(im, ttx, tty, &dest);
-        tty++;
-      }
-      ttx++;
-    }
-  }
-  else {
-    double alpha;
-    i_fcolor pv, orig, dest;
-
-    ttx = tx;
-    for(x=0; x<src->xsize; x++) {
-      tty=ty;
-      for(y=0;y<src->ysize;y++) {
-        /* fprintf(stderr,"reading (%d,%d) writing (%d,%d).\n",x,y,ttx,tty); */
-        i_gpixf(src, x,   y,   &pv);
-        i_gpixf(im,  ttx, tty, &orig);
-        alpha = pv.channel[alphachan];
-        for (ch = 0; ch < chancount; ++ch) {
-          dest.channel[ch] = alpha * pv.channel[chans[ch]]
-                              + (1 - alpha) * orig.channel[ch];
-        }
-        i_ppixf(im, ttx, tty, &dest);
-        tty++;
-      }
-      ttx++;
-    }
-  }
-
-  return 1;
+  return im;
 }
 
 
@@ -909,6 +822,7 @@ Lanczos(float x) {
   else return(sin(PIx) / PIx * sin(PIx2) / PIx2);
 }
 
+
 /*
 =item i_scaleaxis(im, value, axis)
 
@@ -932,8 +846,13 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
 
   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) {
+      hsize = 1;
+      Value = 1.0 / im->xsize;
+    }
     vsize = im->ysize;
     
     jEnd = hsize;
@@ -942,13 +861,18 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
     hsize = im->xsize;
     vsize = (int)(0.5 + im->ysize * Value);
 
+    if (vsize < 1) {
+      vsize = 1;
+      Value = 1.0 / im->ysize;
+    }
+
     jEnd = vsize;
     iEnd = hsize;
   }
   
   new_img = i_img_empty_ch(NULL, hsize, vsize, im->channels);
   
-  /* 1.5 is a magic number, setting it to 2 will cause rather blurred images */
+  /* 1.4 is a magic number, setting it to 2 will cause rather blurred images */
   LanczosWidthFactor = (Value >= 1) ? 1 : (int) (1.4/Value); 
   lMax = LanczosWidthFactor << 1;
   
@@ -982,9 +906,15 @@ 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++) {
-         i_gpix(im, T+l+1,      i, &val1);
-         i_gpix(im, T-lMax+l+1, i, &val2);
+       for (l=0; l<lMax; l++) {
+         int mx = T-lMax+l+1;
+         int 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];
@@ -1002,15 +932,20 @@ 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++) {
-         i_gpix(im, i, T+l+1, &val1);
-         i_gpix(im, i, T-lMax+l+1, &val2);
+         int mx = T-lMax+l+1;
+         int 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] += l1[l]        * val1.channel[k];
            PictureValue[k] += l0[lMax-l-1] * val2.channel[k]; 
          }
        }
        for (k=0; k<im->channels; k++) {
-         psave = (short)( PictureValue[k] / LanczosWidthFactor);
+         psave = (short)(0.5+(PictureValue[k] / LanczosWidthFactor));
          val.channel[k] = minmax(0, 255, psave);
        }
        i_ppix(new_img, i, j, &val);
@@ -1048,7 +983,15 @@ i_scale_nn(i_img *im, float scx, float scy) {
   mm_log((1,"i_scale_nn(im 0x%x,scx %.2f,scy %.2f)\n",im,scx,scy));
 
   nxsize = (int) ((float) im->xsize * scx);
+  if (nxsize < 1) {
+    nxsize = 1;
+    scx = 1 / im->xsize;
+  }
   nysize = (int) ((float) im->ysize * scy);
+  if (nysize < 1) {
+    nysize = 1;
+    scy = 1 / im->ysize;
+  }
     
   new_img=i_img_empty_ch(NULL,nxsize,nysize,im->channels);
   
@@ -1065,6 +1008,8 @@ i_scale_nn(i_img *im, float scx, float scy) {
 /*
 =item i_sametype(i_img *im, int xsize, int ysize)
 
+=category Image creation
+
 Returns an image of the same type (sample size, channels, paletted/direct).
 
 For paletted images the palette is copied from the source.
@@ -1102,6 +1047,34 @@ i_img *i_sametype(i_img *src, int xsize, int ysize) {
   }
 }
 
+/*
+=item i_sametype_chans(i_img *im, int xsize, int ysize, int channels)
+
+=category Image creation
+
+Returns an image of the same type (sample size).
+
+For paletted images the equivalent direct type is returned.
+
+=cut
+*/
+
+i_img *i_sametype_chans(i_img *src, int xsize, int ysize, int channels) {
+  if (src->bits == 8) {
+    return i_img_empty_ch(NULL, xsize, ysize, channels);
+  }
+  else if (src->bits == i_16_bits) {
+    return i_img_16_new(xsize, ysize, channels);
+  }
+  else if (src->bits == i_double_bits) {
+    return i_img_double_new(xsize, ysize, channels);
+  }
+  else {
+    i_push_error(0, "Unknown image bits");
+    return NULL;
+  }
+}
+
 /*
 =item i_transform(im, opx, opxl, opy, opyl, parm, parmlen)
 
@@ -1143,8 +1116,8 @@ i_transform(i_img *im, int *opx,int opxl,int *opy,int opyl,double parm[],int par
     parm[1]=(double)ny;
 
     /*     fprintf(stderr,"(%d,%d) ->",nx,ny);  */
-    rx=op_run(opx,opxl,parm,parmlen);
-    ry=op_run(opy,opyl,parm,parmlen);
+    rx=i_op_run(opx,opxl,parm,parmlen);
+    ry=i_op_run(opy,opyl,parm,parmlen);
     /*    fprintf(stderr,"(%f,%f)\n",rx,ry); */
     i_gpix(im,rx,ry,&val);
     i_ppix(new_img,nx,ny,&val);
@@ -1272,13 +1245,6 @@ i_count_colors(i_img *im,int maxc) {
   return colorcnt;
 }
 
-
-symbol_table_t symbol_table={i_has_format,ICL_set_internal,ICL_info,
-                            i_img_new,i_img_empty,i_img_empty_ch,i_img_exorcise,
-                            i_img_info,i_img_setmask,i_img_getmask,i_ppix,i_gpix,
-                            i_box,i_draw,i_arc,i_copyto,i_copyto_trans,i_rubthru};
-
-
 /*
 =back
 
@@ -1299,8 +1265,9 @@ Returns 0 if the pixel could be set, -1 otherwise.
 
 =cut
 */
+static
 int
-i_ppix_d(i_img *im, int x, int y, i_color *val) {
+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 ) {
@@ -1324,14 +1291,16 @@ 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];
+      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 */
 }
 
@@ -1351,6 +1320,7 @@ 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;
@@ -1387,8 +1357,9 @@ 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, i_color *vals) {
+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) {
@@ -1415,8 +1386,9 @@ i_plin_d(i_img *im, int l, int r, int y, i_color *vals) {
 
 =cut
 */
+static
 int
-i_ppixf_d(i_img *im, int x, int y, i_fcolor *val) {
+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 ) {
@@ -1435,6 +1407,7 @@ i_ppixf_d(i_img *im, int x, int y, i_fcolor *val) {
 
 =cut
 */
+static
 int
 i_gpixf_d(i_img *im, int x, int y, i_fcolor *val) {
   int ch;
@@ -1464,6 +1437,7 @@ 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;
@@ -1500,8 +1474,9 @@ 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, i_fcolor *vals) {
+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) {
@@ -1534,8 +1509,10 @@ Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
 
 =cut
 */
-int i_gsamp_d(i_img *im, int l, int r, int y, i_sample_t *samps, 
-              int *chans, int chan_count) {
+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;
 
@@ -1590,8 +1567,10 @@ Returns the number of samples read (which should be (r-l) * bits_set(chan_mask)
 
 =cut
 */
-int i_gsampf_d(i_img *im, int l, int r, int y, i_fsample_t *samps, 
-               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) {
   int ch, count, i, w;
   unsigned char *data;
   for (ch = 0; ch < chan_count; ++ch) {
@@ -1653,7 +1632,7 @@ i_sample_t versions.
 =cut
 */
 
-int i_ppixf_fp(i_img *im, int x, int y, i_fcolor *pix) {
+int i_ppixf_fp(i_img *im, int x, int y, const i_fcolor *pix) {
   i_color temp;
   int ch;
 
@@ -1686,7 +1665,7 @@ int i_gpixf_fp(i_img *im, int x, int y, i_fcolor *pix) {
 
 =cut
 */
-int i_plinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix) {
+int i_plinf_fp(i_img *im, int l, int r, int y, const i_fcolor *pix) {
   i_color *work;
 
   if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
@@ -1753,7 +1732,7 @@ int i_glinf_fp(i_img *im, int l, int r, int y, i_fcolor *pix) {
 =cut
 */
 int i_gsampf_fp(i_img *im, int l, int r, int y, i_fsample_t *samp, 
-                int *chans, int chan_count) {
+                int const *chans, int chan_count) {
   i_sample_t *work;
 
   if (y >= 0 && y < im->ysize && l < im->xsize && l >= 0) {
@@ -1791,11 +1770,11 @@ im->ext_data points at.
 
 =over
 
-=item i_addcolors_forward(i_img *im, i_color *colors, int count)
+=item i_addcolors_forward(i_img *im, const i_color *colors, int count)
 
 =cut
 */
-int i_addcolors_forward(i_img *im, i_color *colors, int count) {
+int i_addcolors_forward(i_img *im, const i_color *colors, int count) {
   return i_addcolors(*(i_img **)im->ext_data, colors, count);
 }
 
@@ -1809,11 +1788,11 @@ int i_getcolors_forward(i_img *im, int i, i_color *color, int count) {
 }
 
 /*
-=item i_setcolors_forward(i_img *im, int i, i_color *color, int count)
+=item i_setcolors_forward(i_img *im, int i, const i_color *color, int count)
 
 =cut
 */
-int i_setcolors_forward(i_img *im, int i, i_color *color, int count) {
+int i_setcolors_forward(i_img *im, int i, const i_color *color, int count) {
   return i_setcolors(*(i_img **)im->ext_data, i, color, count);
 }
 
@@ -1836,11 +1815,11 @@ int i_maxcolors_forward(i_img *im) {
 }
 
 /*
-=item i_findcolor_forward(i_img *im, i_color *color, i_palidx *entry)
+=item i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry)
 
 =cut
 */
-int i_findcolor_forward(i_img *im, i_color *color, i_palidx *entry) {
+int i_findcolor_forward(i_img *im, const i_color *color, i_palidx *entry) {
   return i_findcolor(*(i_img **)im->ext_data, color, entry);
 }
 
@@ -1896,7 +1875,7 @@ i_gen_reader(i_gen_read_data *gci, char *buf, int length) {
       gci->cpos = 0;
       gci->length = did_read;
 
-      copy_size = min(length, gci->length);
+      copy_size = i_min(length, gci->length);
       memcpy(buf, gci->buffer, copy_size);
       gci->cpos += copy_size;
       buf += copy_size;
@@ -1939,13 +1918,13 @@ i_gen_read_data_new(i_read_callback_t cb, char *userdata) {
 }
 
 /*
-=item free_gen_read_data(i_gen_read_data *)
+=item i_free_gen_read_data(i_gen_read_data *)
 
 Cleans up.
 
 =cut
 */
-void free_gen_read_data(i_gen_read_data *self) {
+void i_free_gen_read_data(i_gen_read_data *self) {
   myfree(self);
 }
 
@@ -1989,7 +1968,7 @@ int size)
 
 Allocates and initializes the data structure used by i_gen_writer.
 
-This should be released with L<image.c/free_gen_write_data>
+This should be released with L<image.c/i_free_gen_write_data>
 
 =cut
 */
@@ -1999,7 +1978,7 @@ i_gen_write_data *i_gen_write_data_new(i_write_callback_t cb,
   i_gen_write_data *self = mymalloc(sizeof(i_gen_write_data));
   self->cb = cb;
   self->userdata = userdata;
-  self->maxlength = min(max_length, sizeof(self->buffer));
+  self->maxlength = i_min(max_length, sizeof(self->buffer));
   if (self->maxlength < 0)
     self->maxlength = sizeof(self->buffer);
   self->filledto = 0;
@@ -2008,7 +1987,7 @@ i_gen_write_data *i_gen_write_data_new(i_write_callback_t cb,
 }
 
 /*
-=item free_gen_write_data(i_gen_write_data *info, int flush)
+=item i_free_gen_write_data(i_gen_write_data *info, int flush)
 
 Cleans up the write buffer.
 
@@ -2022,7 +2001,7 @@ ie. if it fails.
 =cut
 */
 
-int free_gen_write_data(i_gen_write_data *info, int flush)
+int i_free_gen_write_data(i_gen_write_data *info, int flush)
 {
   int result = !flush || 
     info->filledto == 0 ||
@@ -2032,9 +2011,160 @@ int free_gen_write_data(i_gen_write_data *info, int flush)
   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) {
+  if (length < magic->magic_size)
+    return 0;
+  if (magic->mask) {
+    int i;
+    unsigned char *bufp = buffer, 
+      *maskp = magic->mask, 
+      *magicp = magic->magic;
+
+    for (i = 0; i < magic->magic_size; ++i) {
+      int mask = *maskp == 'x' ? 0xFF : *maskp == ' ' ? 0 : *maskp;
+      ++maskp;
+
+      if ((*bufp++ & mask) != (*magicp++ & mask)) 
+       return 0;
+    }
+
+    return 1;
+  }
+  else {
+    return !memcmp(magic->magic, buffer, magic->magic_size);
+  }
+}
+
+/*
+=item i_test_format_probe(io_glue *data, int length)
+
+Check the beginning of the supplied file for a 'magic number'
+
+=cut
+*/
+
+#define FORMAT_ENTRY(magic, type) \
+  { (unsigned char *)(magic ""), sizeof(magic)-1, type }
+#define FORMAT_ENTRY2(magic, type, mask) \
+  { (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[] = {
+    FORMAT_ENTRY("\xFF\xD8", "jpeg"),
+    FORMAT_ENTRY("GIF87a", "gif"),
+    FORMAT_ENTRY("GIF89a", "gif"),
+    FORMAT_ENTRY("MM\0*", "tiff"),
+    FORMAT_ENTRY("II*\0", "tiff"),
+    FORMAT_ENTRY("BM", "bmp"),
+    FORMAT_ENTRY("\x89PNG\x0d\x0a\x1a\x0a", "png"),
+    FORMAT_ENTRY("P1", "pnm"),
+    FORMAT_ENTRY("P2", "pnm"),
+    FORMAT_ENTRY("P3", "pnm"),
+    FORMAT_ENTRY("P4", "pnm"),
+    FORMAT_ENTRY("P5", "pnm"),
+    FORMAT_ENTRY("P6", "pnm"),
+    FORMAT_ENTRY("/* XPM", "xpm"),
+    FORMAT_ENTRY("\x8aMNG", "mng"),
+    FORMAT_ENTRY("\x8aJNG", "jng"),
+    /* SGI RGB - with various possible parameters to avoid false positives
+       on similar files 
+       values are: 2 byte magic, rle flags (0 or 1), bytes/sample (1 or 2)
+    */
+    FORMAT_ENTRY("\x01\xDA\x00\x01", "rgb"),
+    FORMAT_ENTRY("\x01\xDA\x00\x02", "rgb"),
+    FORMAT_ENTRY("\x01\xDA\x01\x01", "rgb"),
+    FORMAT_ENTRY("\x01\xDA\x01\x02", "rgb"),
+    
+    FORMAT_ENTRY2("FORM    ILBM", "ilbm", "xxxx    xxxx"),
+
+    /* different versions of PCX format 
+       http://www.fileformat.info/format/pcx/
+    */
+    FORMAT_ENTRY("\x0A\x00\x01", "pcx"),
+    FORMAT_ENTRY("\x0A\x02\x01", "pcx"),
+    FORMAT_ENTRY("\x0A\x03\x01", "pcx"),
+    FORMAT_ENTRY("\x0A\x04\x01", "pcx"),
+    FORMAT_ENTRY("\x0A\x05\x01", "pcx"),
+
+    /* FITS - http://fits.gsfc.nasa.gov/ */
+    FORMAT_ENTRY("SIMPLE  =", "fits"),
+
+    /* PSD - Photoshop */
+    FORMAT_ENTRY("8BPS\x00\x01", "psd"),
+    
+    /* EPS - Encapsulated Postscript */
+    /* only reading 18 chars, so we don't include the F in EPSF */
+    FORMAT_ENTRY("%!PS-Adobe-2.0 EPS", "eps"),
+
+    /* Utah RLE */
+    FORMAT_ENTRY("\x52\xCC", "utah"),
+
+    /* GZIP compressed, only matching deflate for now */
+    FORMAT_ENTRY("\x1F\x8B\x08", "gzip"),
+
+    /* bzip2 compressed */
+    FORMAT_ENTRY("BZh", "bzip2"),
+  };
+  static const struct 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 */
+    FORMAT_ENTRY("\x00\x00\x02\x00", "cur"), /* Windows cursor */
+    FORMAT_ENTRY2("\x00\x00\x00\x00\x00\x00\x00\x07", 
+                 "xwd", "    xxxx"), /* X Windows Dump */
+  };
+
+  unsigned int i;
+  unsigned char head[18];
+  ssize_t rc;
+
+  io_glue_commit_types(data);
+  rc = data->readcb(data, head, 18);
+  if (rc == -1) return NULL;
+  data->seekcb(data, -rc, SEEK_CUR);
+
+  for(i=0; i<sizeof(formats)/sizeof(formats[0]); i++) { 
+    struct magic_entry const *entry = formats + i;
+
+    if (test_magic(head, rc, entry)) 
+      return entry->name;
+  }
+
+  if ((rc == 18) &&
+      tga_header_verify(head))
+    return "tga";
+
+  for(i=0; i<sizeof(more_formats)/sizeof(more_formats[0]); i++) { 
+    struct magic_entry const *entry = more_formats + i;
+
+    if (test_magic(head, rc, entry)) 
+      return entry->name;
+  }
+
+  return NULL;
+}
+
+
+
+
 /*
 =back
 
+=head1 AUTHOR
+
+Arnar M. Hrafnkelsson <addi@umich.edu>
+
+Tony Cook <tony@develop-help.com>
+
 =head1 SEE ALSO
 
 L<Imager>, L<gif.c>