]> git.imager.perl.org - imager.git/blobdiff - image.c
report library version numbers where we already have the XS for it
[imager.git] / image.c
diff --git a/image.c b/image.c
index dfef965b204016b8fa2d053a26a6ae56883fe2c2..5dba629f32ba0b8430775ce4698009de2cfe414c 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(img)
+=category Image Implementation
+
+Imager interal 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)
+=item i_img_destroy(img)
 
-=category Image
-
-Destroy image and free data via exorcise.
+=category Image creation/destruction
+=synopsis i_img_destroy(img)
 
-   im - Image pointer
+Destroy an image object
 
 =cut
 */
@@ -586,75 +643,6 @@ 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)
 
@@ -704,20 +692,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);
@@ -877,6 +853,7 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
   i_color val,val1,val2;
   i_img *new_img;
 
+  i_clear_error();
   mm_log((1,"i_scaleaxis(im %p,Value %.2f,Axis %d)\n",im,Value,Axis));
 
 
@@ -904,6 +881,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); 
@@ -1041,7 +1022,8 @@ i_scale_nn(i_img *im, float scx, float scy) {
 /*
 =item i_sametype(i_img *im, int xsize, int 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).
 
@@ -1083,7 +1065,8 @@ 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
+=category Image creation/destruction
+=synopsis i_img *img = i_sametype_chans(src, width, height, channels);
 
 Returns an image of the same type (sample size).
 
@@ -1264,9 +1247,10 @@ i_count_colors(i_img *im,int maxc) {
   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;
   }
@@ -1274,7 +1258,7 @@ i_count_colors(i_img *im,int maxc) {
     channels[0] = channels[1] = channels[2] = 0;
     samp_chans = channels;
   }
-  int samp_cnt = 3 * xsize;
+
   ct = octt_new();
 
   samp = (i_sample_t *) mymalloc( xsize * 3 * sizeof(i_sample_t));
@@ -1300,7 +1284,8 @@ i_count_colors(i_img *im,int maxc) {
  * (adapted from the Numerical Recipes)
  */
 /* Needed by get_anonymous_color_histo */
-void hpsort(unsigned int n, int *ra) {
+static void
+hpsort(unsigned int n, unsigned *ra) {
     unsigned int i,
                  ir,
                  j,
@@ -1344,44 +1329,51 @@ void hpsort(unsigned int n, int *ra) {
  * the maxc ;-) and you might want to change the name... */
 /* Uses octt_histo */
 int
-get_anonymous_color_histo(i_img *im, unsigned int **col_usage) {
-    struct octt *ct;
-    int x,y,i;
-    int colorcnt;
-    int maxc = 10000000;
-    unsigned int *col_usage_it;
-    i_sample_t * samp;
-
-    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));
-
-    colorcnt = 0;
-    for(y = 0; y < ysize; ) {
-        i_gsamp(im, 0, xsize, y++, samp, NULL, 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; 
-            }
-        }
+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 * 2 * sizeof(unsigned int));
-    octt_histo(ct, &col_usage_it);
-    hpsort(colorcnt, *col_usage);
-    octt_delete(ct);
-    return colorcnt;
+  }
+  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;
 }
 
-
-
 /*
 =back
 
@@ -1677,6 +1669,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];
@@ -1739,6 +1736,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]);
@@ -1963,6 +1965,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
@@ -2331,7 +2407,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 && 
@@ -2340,19 +2416,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;         
       }
     }
@@ -2362,6 +2438,50 @@ i_img_is_monochrome(i_img *im, int *zero_is_white) {
   return 0;
 }
 
+/*
+=item i_get_file_background(im, &bg)
+
+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)
+
+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