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
===========
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)
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
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',
--- /dev/null
+#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
+ }
+}
}
-/*
-=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;
-}
-
=cut
*/
+
float
i_img_diff(i_img *im1,i_img *im2) {
int x,y,ch,xb,yb,chb;
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*
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 */
crop Full
difference Full
filter Partial Depends on the filter.
- flip None
+ flip Full
flood_fill Partial [1]
getpixel Full
getsamples Full
is_fcolor4
color_cmp
is_image
+ is_imaged
is_image_similar
image_bounds_checks
mask_tests
$img;
}
-sub is_image_similar($$$$) {
- my ($left, $right, $limit, $comment) = @_;
+sub _low_image_diff_check {
+ my ($left, $right, $comment) = @_;
my $builder = Test::Builder->new;
. $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);
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;
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
#!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;
# 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);