add new comparison method rgb_difference that resembles arithmetical difference per...
[imager.git] / image.c
diff --git a/image.c b/image.c
index 10228d1..7da5eca 100644 (file)
--- a/image.c
+++ b/image.c
@@ -289,7 +289,7 @@ i_img_destroy(i_img *im) {
   dIMCTXim(im);
   im_log((aIMCTX, 1,"i_img_destroy(im %p)\n",im));
   i_img_exorcise(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");
 }
 
   im_context_refdec(aIMCTX, "img_destroy");
 }
 
@@ -318,21 +318,15 @@ void
 i_img_info(i_img *im, i_img_dim *info) {
   dIMCTXim(im);
   im_log((aIMCTX,1,"i_img_info(im %p)\n",im));
 i_img_info(i_img *im, i_img_dim *info) {
   dIMCTXim(im);
   im_log((aIMCTX,1,"i_img_info(im %p)\n",im));
-  if (im != NULL) {
-    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;
-  } else {
-    info[0] = 0;
-    info[1] = 0;
-    info[2] = 0;
-    info[3] = 0;
-  }
+
+  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;
 }
 
 /*
 }
 
 /*
@@ -404,6 +398,85 @@ i_img_get_height(i_img *im) {
   return im->ysize;
 }
 
   return im->ysize;
 }
 
+/*
+=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>)
 
 /*
 =item i_copyto_trans(C<im>, C<src>, C<x1>, C<y1>, C<x2>, C<y2>, C<tx>, C<ty>, C<trans>)
 
@@ -1049,7 +1122,7 @@ i_haar(i_img *im) {
   i_img_dim mx,my;
   i_img_dim fx,fy;
   i_img_dim x,y;
   i_img_dim mx,my;
   i_img_dim fx,fy;
   i_img_dim x,y;
-  int ch,c;
+  int ch;
   i_img *new_img,*new_img2;
   i_color val1,val2,dval1,dval2;
   dIMCTXim(im);
   i_img *new_img,*new_img2;
   i_color val1,val2,dval1,dval2;
   dIMCTXim(im);
@@ -1065,7 +1138,6 @@ i_haar(i_img *im) {
   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);
 
   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);
 
-  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(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);
@@ -1132,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;
       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; 
           }
               octt_delete(ct); 
               return -1; 
           }
@@ -1223,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) { 
       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; 
       }
     }
        return -1; 
       }
     }
@@ -1526,15 +1600,8 @@ i_gsamp_bits_fb(i_img *im, i_img_dim l, i_img_dim r, i_img_dim y, unsigned *samp
   }
 }
 
   }
 }
 
-struct magic_entry {
-  unsigned char *magic;
-  size_t magic_size;
-  char *name;
-  unsigned char *mask;  
-};
-
 static int
 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) {
   if (length < magic->magic_size)
     return 0;
   if (magic->mask) {
@@ -1572,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 *
   { (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"),
     FORMAT_ENTRY("\xFF\xD8", "jpeg"),
     FORMAT_ENTRY("GIF87a", "gif"),
     FORMAT_ENTRY("GIF89a", "gif"),
@@ -1636,8 +1703,11 @@ i_test_format_probe(io_glue *data, int length) {
     /* JPEG 2000 
        This might match a little loosely */
     FORMAT_ENTRY("\x00\x00\x00\x0CjP  \x0D\x0A\x87\x0A", "jp2"),
     /* 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 */
     /* these were originally both listed as ico, but cur files can
        include hotspot information */
     FORMAT_ENTRY("\x00\x00\x01\x00", "ico"), /* Windows icon */
@@ -1662,8 +1732,18 @@ i_test_format_probe(io_glue *data, int length) {
   }
 #endif
 
   }
 #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++) { 
   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;
 
     if (test_magic(head, rc, entry)) 
       return entry->name;
@@ -1674,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++) { 
     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;
 
     if (test_magic(head, rc, entry)) 
       return entry->name;
@@ -1719,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];
   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 &&
     if (im->channels == 3) {
       if (colors[0].rgb.r == 255 && 
           colors[0].rgb.g == 255 &&