]> git.imager.perl.org - imager.git/blobdiff - image.c
[RT #65864] eliminate calls to Test::More's note()
[imager.git] / image.c
diff --git a/image.c b/image.c
index 5dba629f32ba0b8430775ce4698009de2cfe414c..bf25b7d253b70e872e4858b337d2c7feab0acd8b 100644 (file)
--- a/image.c
+++ b/image.c
@@ -86,10 +86,10 @@ i_img_alloc(void) {
 }
 
 /*
 }
 
 /*
-=item i_img_init(img)
+=item i_img_init(C<img>)
 =category Image Implementation
 
 =category Image Implementation
 
-Imager interal initialization of images.
+Imager internal initialization of images.
 
 Currently this does very little, in the future it may be used to
 support threads, or color profiles.
 
 Currently this does very little, in the future it may be used to
 support threads, or color profiles.
@@ -479,8 +479,8 @@ i_img_exorcise(i_img *im) {
 }
 
 /* 
 }
 
 /* 
-=item i_img_destroy(img)
-
+=item i_img_destroy(C<img>)
+=order 90
 =category Image creation/destruction
 =synopsis i_img_destroy(img)
 
 =category Image creation/destruction
 =synopsis i_img_destroy(img)
 
@@ -536,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);
 
 =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
 */
 
 =cut
 */
@@ -550,11 +553,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
 */
 
 =cut
 */
@@ -562,11 +565,11 @@ int
 i_img_getmask(i_img *im) { return im->ch_mask; }
 
 /*
 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
 */
 
 =cut
 */
@@ -574,9 +577,9 @@ int
 i_img_getchannels(i_img *im) { return im->channels; }
 
 /*
 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.
 
 
 Returns the width in pixels of the image.
 
@@ -588,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.
 
 
 Returns the height in pixels of the image.
 
@@ -602,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
 
 
 =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
 */
 
 =cut
 */
@@ -644,11 +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_copy(src)
+=item i_copy(source)
 
 =category Image
 
 
 =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.
 
 
 Tags are not copied, only the image data.
 
@@ -706,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;
-}
-
 
 
 
 
 
 
@@ -852,11 +747,12 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
   short psave;
   i_color val,val1,val2;
   i_img *new_img;
   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));
 
 
   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) {
   if (Axis == XAXIS) {
     hsize = (int)(0.5 + im->xsize * Value);
     if (hsize < 1) {
@@ -928,15 +824,46 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
          
          i_gpix(im, Mx, i, &val1);
          i_gpix(im, mx, i, &val2);
          
          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];
+           }
+         }
+       }
+
+       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;
          }
        }
          }
        }
-       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, j, i, &val);
       }
        }
        i_ppix(new_img, j, i, &val);
       }
@@ -953,14 +880,43 @@ i_scaleaxis(i_img *im, float Value, int Axis) {
 
          i_gpix(im, i, Mx, &val1);
          i_gpix(im, i, mx, &val2);
 
          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);
       }
        }
        i_ppix(new_img, i, j, &val);
       }
@@ -999,13 +955,14 @@ i_scale_nn(i_img *im, float scx, float scy) {
   nxsize = (int) ((float) im->xsize * scx);
   if (nxsize < 1) {
     nxsize = 1;
   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;
   }
   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);
   
     
   new_img=i_img_empty_ch(NULL,nxsize,nysize,im->channels);
   
@@ -1020,7 +977,7 @@ 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/destruction
 =synopsis i_img *img = i_sametype(src, width, height);
 
 =category Image creation/destruction
 =synopsis i_img *img = i_sametype(src, width, height);
@@ -1063,7 +1020,7 @@ 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/destruction
 =synopsis i_img *img = i_sametype_chans(src, width, height, channels);
 
 =category Image creation/destruction
 =synopsis i_img *img = i_sametype_chans(src, width, height, channels);
@@ -1155,6 +1112,7 @@ can return zero.
 
 =cut
 */
 
 =cut
 */
+
 float
 i_img_diff(i_img *im1,i_img *im2) {
   int x,y,ch,xb,yb,chb;
 float
 i_img_diff(i_img *im1,i_img *im2) {
   int x,y,ch,xb,yb,chb;
@@ -1180,6 +1138,50 @@ i_img_diff(i_img *im1,i_img *im2) {
   return tdiff;
 }
 
   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*
 /* just a tiny demo of haar wavelets */
 
 i_img*
@@ -2326,6 +2328,14 @@ i_test_format_probe(io_glue *data, int length) {
 
     /* bzip2 compressed */
     FORMAT_ENTRY("BZh", "bzip2"),
 
     /* 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
   };
   static const struct magic_entry more_formats[] = {
     /* these were originally both listed as ico, but cur files can
@@ -2369,10 +2379,13 @@ i_test_format_probe(io_glue *data, int length) {
 /*
 =item i_img_is_monochrome(img, &zero_is_white)
 
 /*
 =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
 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:
 
 
 For performance of encoders we require monochrome images:
 
@@ -2384,12 +2397,12 @@ be paletted
 
 =item *
 
 
 =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
 
 
 =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
 */
 
 =cut
 */
@@ -2441,6 +2454,8 @@ i_img_is_monochrome(i_img *im, int *zero_is_white) {
 /*
 =item i_get_file_background(im, &bg)
 
 /*
 =item i_get_file_background(im, &bg)
 
+=category Files
+
 Retrieve the file write background color tag from the image.
 
 If not present, returns black.
 Retrieve the file write background color tag from the image.
 
 If not present, returns black.
@@ -2461,6 +2476,8 @@ i_get_file_background(i_img *im, i_color *bg) {
 /*
 =item i_get_file_backgroundf(im, &bg)
 
 /*
 =item i_get_file_backgroundf(im, &bg)
 
+=category Files
+
 Retrieve the file write background color tag from the image as a
 floating point color.
 
 Retrieve the file write background color tag from the image as a
 floating point color.