- use scanline oriented operations to flip images instead of pixel
authorTony Cook <tony@develop=help.com>
Tue, 24 Nov 2009 08:02:16 +0000 (08:02 +0000)
committerTony Cook <tony@develop=help.com>
Tue, 24 Nov 2009 08:02:16 +0000 (08:02 +0000)
   operations
   https://rt.cpan.org/Ticket/Display.html?id=39278

 - use double/sample operations to flip large sample images instead of
   8-bit sample operations.
   https://rt.cpan.org/Ticket/Display.html?id=39280

Changes
Imager.xs
MANIFEST
Makefile.PL
flip.im [new file with mode: 0644]
image.c
imager.h
lib/Imager/LargeSamples.pod
lib/Imager/Test.pm
t/t64copyflip.t

diff --git a/Changes b/Changes
index ae33a08..c9bdfbe 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,18 @@
 Imager release history.  Older releases can be found in Changes.old
 
+Imager 0.72 - unreleased
+===========
+
+Bug fixes:
+
+ - use scanline oriented operations to flip images instead of pixel
+   operations
+   https://rt.cpan.org/Ticket/Display.html?id=39278
+
+ - use double/sample operations to flip large sample images instead of
+   8-bit sample operations.
+   https://rt.cpan.org/Ticket/Display.html?id=39280
+
 Imager 0.71 - 16 Nov 2009
 ===========
 
index 539836a..f6733ed 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -1868,7 +1868,10 @@ i_img_diff(im1,im2)
     Imager::ImgRaw     im1
     Imager::ImgRaw     im2
 
-
+double
+i_img_diffd(im1,im2)
+    Imager::ImgRaw     im1
+    Imager::ImgRaw     im2
 
 undef_int        
 i_init_fonts(t1log=0)
index eda895f..b0f0077 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -105,6 +105,7 @@ feat.h
 fills.c         Generic fills
 filterlist.perl
 filters.im
+flip.im
 font.c
 fontfiles/ExistenceTest.afm     please edit ExistenceTest.sfd in CVS
 fontfiles/ExistenceTest.pfb     to change these files, edited and
index 327c2a9..0b71b80 100644 (file)
@@ -168,7 +168,7 @@ my @objs = qw(Imager.o draw.o polygon.o image.o io.o iolayer.o
               regmach.o trans2.o quant.o error.o convert.o
               map.o tags.o palimg.o maskimg.o img16.o rotate.o
               bmp.o tga.o color.o fills.o imgdouble.o limits.o hlines.o
-              imext.o scale.o rubthru.o render.o paste.o compose.o);
+              imext.o scale.o rubthru.o render.o paste.o compose.o flip.o);
 
 my %opts=(
           'NAME'         => 'Imager',
diff --git a/flip.im b/flip.im
new file mode 100644 (file)
index 0000000..c3df8f7
--- /dev/null
+++ b/flip.im
@@ -0,0 +1,192 @@
+#include "imager.h"
+
+static void flip_h(i_img *im);
+static void flip_v(i_img *im);
+static void flip_hv(i_img *im);
+
+#define XAXIS 0
+#define YAXIS 1
+#define XYAXIS 2
+
+/*
+=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) {
+  i_clear_error();
+
+  mm_log((1, "i_flipxy(im %p, direction %d)\n", im, direction ));
+
+  if (!im)
+    return 0;
+
+  switch (direction) {
+  case XAXIS: /* Horizontal flip */
+    flip_h(im);
+    break;
+
+  case YAXIS: /* Vertical flip */
+    flip_v(im);
+    break;
+
+  case XYAXIS: /* Horizontal and Vertical flip */
+    flip_hv(im);
+    break;
+
+  default:
+    mm_log((1, "i_flipxy: direction is invalid\n" ));
+    i_push_errorf(0, "direction %d invalid", direction);
+    return 0;
+
+  }
+  return 1;
+}
+
+static void
+flip_row_pal(i_palidx *row, i_img_dim width) {
+  i_palidx tmp;
+  i_palidx *leftp = row;
+  i_palidx *rightp = row + width - 1;
+  
+  while (leftp < rightp) {
+    tmp = *leftp;
+    *leftp = *rightp;
+    *rightp = tmp;
+    ++leftp;
+    --rightp;
+  }
+}
+
+#code
+
+static void
+IM_SUFFIX(flip_row)(IM_COLOR *row, i_img_dim width) {
+  IM_COLOR tmp;
+  IM_COLOR *leftp = row;
+  IM_COLOR *rightp = row + width - 1;
+  
+  while (leftp < rightp) {
+    tmp = *leftp;
+    *leftp = *rightp;
+    *rightp = tmp;
+    ++leftp;
+    --rightp;
+  }
+}
+
+#/code
+
+static void
+flip_h(i_img *im) {
+  int y;
+  if (im->type == i_palette_type) {
+    i_palidx *line = mymalloc(im->xsize * sizeof(i_palidx));
+    for (y = 0; y < im->ysize; ++y) {
+      i_gpal(im, 0, im->xsize, y, line);
+      flip_row_pal(line, im->xsize);
+      i_ppal(im, 0, im->xsize, y, line);
+    }
+    myfree(line);
+  }
+  else {
+#code im->bits == i_8_bits
+    IM_COLOR *line = mymalloc(im->xsize * sizeof(IM_COLOR));
+    for (y = 0; y < im->ysize; ++y) {
+      IM_GLIN(im, 0, im->xsize, y, line);
+      IM_SUFFIX(flip_row)(line, im->xsize);
+      IM_PLIN(im, 0, im->xsize, y, line);
+    }
+    myfree(line);
+#/code
+  }
+}
+
+static void
+flip_v(i_img *im) {
+  int topy = 0;
+  int boty = im->ysize - 1;
+  if (im->type == i_palette_type) {
+    i_palidx *top_line = mymalloc(im->xsize * sizeof(i_palidx));
+    i_palidx *bot_line = mymalloc(im->xsize * sizeof(i_palidx));
+    while (topy < boty) {
+      i_gpal(im, 0, im->xsize, topy, top_line);
+      i_gpal(im, 0, im->xsize, boty, bot_line);
+      i_ppal(im, 0, im->xsize, topy, bot_line);
+      i_ppal(im, 0, im->xsize, boty, top_line);
+      ++topy;
+      --boty;
+    }
+    myfree(bot_line);
+    myfree(top_line);
+  }
+  else {
+#code im->bits == i_8_bits
+    IM_COLOR *top_line = mymalloc(im->xsize * sizeof(IM_COLOR));
+    IM_COLOR *bot_line = mymalloc(im->xsize * sizeof(IM_COLOR));
+    while (topy < boty) {
+      IM_GLIN(im, 0, im->xsize, topy, top_line);
+      IM_GLIN(im, 0, im->xsize, boty, bot_line);
+      IM_PLIN(im, 0, im->xsize, topy, bot_line);
+      IM_PLIN(im, 0, im->xsize, boty, top_line);
+      ++topy;
+      --boty;
+    }
+    myfree(top_line);
+    myfree(bot_line);
+#/code 
+  }
+}
+
+static void
+flip_hv(i_img *im) {
+  int topy = 0;
+  int boty = im->ysize - 1;
+  if (im->type == i_palette_type) {
+    i_palidx *top_line = mymalloc(im->xsize * sizeof(i_palidx));
+    i_palidx *bot_line = mymalloc(im->xsize * sizeof(i_palidx));
+    while (topy < boty) {
+      i_gpal(im, 0, im->xsize, topy, top_line);
+      i_gpal(im, 0, im->xsize, boty, bot_line);
+      flip_row_pal(top_line, im->xsize);
+      flip_row_pal(bot_line, im->xsize);
+      i_ppal(im, 0, im->xsize, topy, bot_line);
+      i_ppal(im, 0, im->xsize, boty, top_line);
+      ++topy;
+      --boty;
+    }
+    myfree(bot_line);
+    myfree(top_line);
+  }
+  else {
+#code im->bits == i_8_bits
+    IM_COLOR *top_line = mymalloc(im->xsize * sizeof(IM_COLOR));
+    IM_COLOR *bot_line = mymalloc(im->xsize * sizeof(IM_COLOR));
+    while (topy < boty) {
+      IM_GLIN(im, 0, im->xsize, topy, top_line);
+      IM_GLIN(im, 0, im->xsize, boty, bot_line);
+      IM_SUFFIX(flip_row)(top_line, im->xsize);
+      IM_SUFFIX(flip_row)(bot_line, im->xsize);
+      IM_PLIN(im, 0, im->xsize, topy, bot_line);
+      IM_PLIN(im, 0, im->xsize, boty, top_line);
+      ++topy;
+      --boty;
+    }
+    if (topy == boty) {
+      IM_GLIN(im, 0, im->xsize, topy, top_line);
+      IM_SUFFIX(flip_row)(top_line, im->xsize);
+      IM_PLIN(im, 0, im->xsize, topy, top_line);
+    }
+    myfree(top_line);
+    myfree(bot_line);
+#/code 
+  }
+}
diff --git a/image.c b/image.c
index e6d5345..55c0864 100644 (file)
--- a/image.c
+++ b/image.c
@@ -709,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;
-}
-
 
 
 
@@ -1159,6 +1051,7 @@ can return zero.
 
 =cut
 */
+
 float
 i_img_diff(i_img *im1,i_img *im2) {
   int x,y,ch,xb,yb,chb;
@@ -1184,6 +1077,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*
index 7c3d22f..e44311c 100644 (file)
--- a/imager.h
+++ b/imager.h
@@ -214,6 +214,7 @@ extern i_img *i_convert(i_img *src, const float *coeff, int outchan, int inchan)
 extern void i_map(i_img *im, unsigned char (*maps)[256], unsigned int mask);
 
 float i_img_diff   (i_img *im1,i_img *im2);
+double i_img_diffd(i_img *im1,i_img *im2);
 
 /* font routines */
 
index 0445bea..fe2f8f0 100644 (file)
@@ -33,7 +33,7 @@ don't, for future improvements.
   crop         Full
   difference   Full
   filter       Partial    Depends on the filter.
-  flip         None
+  flip         Full
   flood_fill   Partial    [1]
   getpixel     Full
   getsamples   Full
index 2fd6408..6e11b51 100644 (file)
@@ -18,6 +18,7 @@ use vars qw(@ISA @EXPORT_OK);
      is_fcolor4
      color_cmp
      is_image
+     is_imaged
      is_image_similar 
      image_bounds_checks
      mask_tests
@@ -261,8 +262,8 @@ sub test_image_double {
   $img;
 }
 
-sub is_image_similar($$$$) {
-  my ($left, $right, $limit, $comment) = @_;
+sub _low_image_diff_check {
+  my ($left, $right, $comment) = @_;
 
   my $builder = Test::Builder->new;
 
@@ -304,6 +305,22 @@ sub is_image_similar($$$$) {
                    . $right->getchannels);
     return;
   }
+
+  return 1;
+}
+
+sub is_image_similar($$$$) {
+  my ($left, $right, $limit, $comment) = @_;
+
+  {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+    _low_image_diff_check($left, $right, $comment)
+      or return;
+  }
+
+  my $builder = Test::Builder->new;
+
   my $diff = Imager::i_img_diff($left->{IMG}, $right->{IMG});
   if ($diff > $limit) {
     $builder->ok(0, $comment);
@@ -338,6 +355,42 @@ sub is_image($$$) {
   return is_image_similar($left, $right, 0, $comment);
 }
 
+sub is_imaged($$$) {
+  my ($left, $right, $comment) = @_;
+
+  {
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+    _low_image_diff_check($left, $right, $comment)
+      or return;
+  }
+
+  my $builder = Test::Builder->new;
+
+  my $diff = Imager::i_img_diffd($left->{IMG}, $right->{IMG});
+  if ($diff > 0) {
+    $builder->ok(0, $comment);
+    $builder->diag("image data difference: $diff");
+   
+    # find the first mismatch
+  PIXELS:
+    for my $y (0 .. $left->getheight()-1) {
+      for my $x (0.. $left->getwidth()-1) {
+       my @lsamples = $left->getsamples(x => $x, y => $y, width => 1);
+       my @rsamples = $right->getsamples(x => $x, y => $y, width => 1);
+       if ("@lsamples" ne "@rsamples") {
+         $builder->diag("first mismatch at ($x, $y) - @lsamples vs @rsamples");
+         last PIXELS;
+       }
+      }
+    }
+
+    return;
+  }
+  
+  return $builder->ok(1, $comment);
+}
+
 sub image_bounds_checks {
   my $im = shift;
 
@@ -554,6 +607,11 @@ each pixel.  The color comparison is done at 8-bits per pixel.  The
 color representation such as direct vs paletted, bits per sample are
 not checked.  Equivalent to is_image_similar($im1, $im2, 0, $comment).
 
+=item is_imaged($im, $im2, $comment)
+
+Tests if the two images have the same content at the double/sample
+level.
+
 =item is_image_similar($im1, $im2, $maxdiff, $comment)
 
 Tests if the 2 images have similar content.  Both images must be
index bc9a65d..8714984 100644 (file)
@@ -1,8 +1,8 @@
 #!perl -w
 use strict;
-use Test::More tests => 65;
+use Test::More tests => 69;
 use Imager;
-use Imager::Test qw(is_color3);
+use Imager::Test qw(is_color3 is_image is_imaged test_image_double);
 
 #$Imager::DEBUG=1;
 
@@ -17,27 +17,32 @@ ok($nimg, "copy returned something");
 # test if ->copy() works
 
 my $diff = Imager::i_img_diff($img->{IMG}, $nimg->{IMG});
-is($diff, 0, "copy matches source");
-
+is_image($img, $nimg, "copy matches source");
 
 # test if ->flip(dir=>'h')->flip(dir=>'h') doesn't alter the image
-
 $nimg->flip(dir=>"h")->flip(dir=>"h");
-$diff = Imager::i_img_diff($img->{IMG}, $nimg->{IMG});
-is($diff, 0, "double horiz flipped matches original");
+is_image($nimg, $img, "double horiz flipped matches original");
 
 # test if ->flip(dir=>'v')->flip(dir=>'v') doesn't alter the image
-
 $nimg->flip(dir=>"v")->flip(dir=>"v");
-$diff = Imager::i_img_diff($img->{IMG}, $nimg->{IMG});
-is($diff, 0, "double vertically flipped image matches original");
+is_image($nimg, $img, "double vertically flipped image matches original");
 
 
 # test if ->flip(dir=>'h')->flip(dir=>'v') is same as ->flip(dir=>'hv')
-
 $nimg->flip(dir=>"v")->flip(dir=>"h")->flip(dir=>"hv");;
-$diff = Imager::i_img_diff($img->{IMG}, $nimg->{IMG});
-is($diff, 0, "check flip with hv matches flip v then flip h");
+is_image($img, $nimg, "check flip with hv matches flip v then flip h");
+
+{
+  my $imsrc = test_image_double;
+  my $imcp = $imsrc->copy;
+  is_imaged($imsrc, $imcp, "copy double image");
+  $imcp->flip(dir=>"v")->flip(dir=>"v");
+  is_imaged($imsrc, $imcp, "flip v twice");
+  $imcp->flip(dir=>"h")->flip(dir=>"h");
+  is_imaged($imsrc, $imcp, "flip h twice");
+  $imcp->flip(dir=>"h")->flip(dir=>"v")->flip(dir=>"hv");
+  is_imaged($imsrc, $imcp, "flip h,v,hv twice");
+}
 
 rot_test($img, 90, 4);
 rot_test($img, 180, 2);