Browse files

convert scale.c to scale.im so we have 8 bit/sample and double/sample

implementations of mixing scaling.

modified imtoc.perl to allow non-conditional #code sections to allow
creation ofr 8 and double/sample versions of support functions.

fixed a bug in an optimization that avoids vertically scaling when the
vertical size stays the same.

The change from double/sample only to both saved about 20% on
scalebench time (which also loads/saves the images)
  • Loading branch information...
1 parent c52cbef commit a10945af5bc182cd109317d19d84a1f04df1cccc Tony Cook committed Aug 30, 2006
Showing with 151 additions and 49 deletions.
  1. +1 −1 MANIFEST
  2. +2 −2 bench/scale.perl
  3. +36 −5 imtoc.perl
  4. +1 −1 lib/Imager/Transformations.pod
  5. +81 −39 scale.c → scale.im
  6. +30 −1 t/t40scale.t
View
2 MANIFEST
@@ -191,7 +191,7 @@ samples/samp-tags.cgi Demonstrate image upload via a HTML form
samples/samp-tags.html Form for samp-tags.cgi
samples/slant_text.pl Using $font->transform() to slant text
samples/tk-photo.pl
-scale.c Newer scaling code
+scale.im Newer scaling code
spot.perl For making an ordered dither matrix from a spot function
stackmach.c
stackmach.h
View
4 bench/scale.perl
@@ -6,8 +6,8 @@
my @allfiles = (@ARGV) x 120;
my $srcdir = '.';
-my %opts1 = (scalefactor=>.333334);
-my %opts2 = (scalefactor=>.25);
+my %opts1 = (scalefactor=>.333334, qtype => 'mixing');
+my %opts2 = (scalefactor=>.25, qtype => 'mixing');
my %exopts=();
for my $file (@allfiles) {
# print STDERR "reading $srcdir/$file\n";
View
41 imtoc.perl
@@ -18,7 +18,7 @@
push @out, "#line 1 \"$src\"\n";
while (defined(my $line = <SRC>)) {
- if ($line =~ /^\#code (.+)$/) {
+ if ($line =~ /^\#code\s+(\S.+)$/) {
$save_code
and do { warn "$src:$code_line:Unclosed #code block\n"; ++$failed; };
@@ -27,18 +27,34 @@
$code_line = $. + 1;
$save_code = 1;
}
+ elsif ($line =~ /^\#code\s*$/) {
+ $save_code
+ and do { warn "$src:$code_line:Unclosed #code block\n"; ++$failed; };
+
+ $cond = '';
+ $cond_line = 0;
+ $code_line = $. + 1;
+ $save_code = 1;
+ }
elsif ($line =~ /^\#\/code$/) {
$save_code
or do { warn "$src:$.:#/code without #code\n"; ++$failed; next; };
- push @out, "#line $cond_line \"$src\"\n";
- push @out, " if ($cond) {\n";
+ if ($cond) {
+ push @out, "#line $cond_line \"$src\"\n";
+ push @out, " if ($cond) {\n";
+ }
+ push @out, "#undef IM_EIGHT_BIT\n",
+ "#define IM_EIGHT_BIT 1\n";
push @out, "#line $code_line \"$src\"\n";
push @out, byte_samples(@code);
- push @out, " }\n", " else {\n";
+ push @out, " }\n", " else {\n"
+ if $cond;
+ push @out, "#undef IM_EIGHT_BIT\n";
push @out, "#line $code_line \"$src\"\n";
push @out, double_samples(@code);
- push @out, " }\n";
+ push @out, " }\n"
+ if $cond;
push @out, "#line $. \"$src\"\n";
@code = ();
$save_code = 0;
@@ -82,6 +98,7 @@ sub byte_samples {
s/\bIM_WORK_T\b/int/g;
s/\bIM_Sf\b/"%d"/g;
s/\bIM_Wf\b/"%d"/g;
+ s/\bIM_SUFFIX\((\w+)\)/$1_8/g;
}
@lines;
@@ -103,6 +120,7 @@ sub double_samples {
s/\bIM_WORK_T\b/double/g;
s/\bIM_Sf\b/"%f"/g;
s/\bIM_Wf\b/"%f"/g;
+ s/\bIM_SUFFIX\((\w+)\)/$1_double/g;
}
@lines;
@@ -137,6 +155,11 @@ =head1 DESCRIPTION
where I<expression> should return true if processing should be done at
8-bits/sample.
+You can also use a #code block around a function definition to produce
+8-bit and double sample versions of a function. In this case #code
+has no expression and you will need to use IM_SUFFIX() to produce
+different function names.
+
The end of a sample-independent section of code is terminated by:
#/code
@@ -196,6 +219,14 @@ =head1 DESCRIPTION
IM_Wf - format string for the work type, C<"%d"> or C<"%f">.
+=item *
+
+IM_SUFFIX(identifier) - adds _8 or _double onto the end of identifier.
+
+=item *
+
+IM_EIGHT_BIT - this is a macro defined only in 8-bit/sample code.
+
=back
Other types, functions and values may be added in the future.
View
2 lib/Imager/Transformations.pod
@@ -8,7 +8,7 @@ Imager::Transformations - Simple transformations of one image into another.
$newimg = $img->copy();
- $newimg = $img->scale(xpixels=>400);
+ $newimg = $img->scale(xpixels=>400, qtype => 'mixing');
$newimg = $img->scale(xpixels=>400, ypixels=>400);
$newimg = $img->scale(xpixels=>400, ypixels=>400, type=>'min');
$newimg = $img->scale(scalefactor=>0.25);
View
120 scale.c → scale.im
@@ -24,13 +24,16 @@
static void
zero_row(i_fcolor *row, int width, int channels);
+
+#code
static void
-accum_output_row(i_fcolor *accum, double fraction, i_fcolor const *in,
+IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
int width, int channels);
static void
-horizontal_scale(i_fcolor *out, int out_width,
- i_fcolor const *in, int in_width,
- int channels);
+IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width,
+ i_fcolor const *in, int in_width,
+ int channels);
+#/code
/*
=item i_scale_mixing
@@ -47,11 +50,9 @@ Adapted from pnmscale.
i_img *
i_scale_mixing(i_img *src, int x_out, int y_out) {
i_img *result;
- i_fcolor *in_row = NULL;
- i_fcolor *xscale_row = NULL;
i_fcolor *accum_row = NULL;
int y;
- int in_row_bytes, out_row_bytes;
+ int accum_row_bytes;
double rowsleft, fracrowtofill;
int rowsread;
double y_scale;
@@ -70,17 +71,6 @@ i_scale_mixing(i_img *src, int x_out, int y_out) {
return NULL;
}
- in_row_bytes = sizeof(i_fcolor) * src->xsize;
- if (in_row_bytes / sizeof(i_fcolor) != src->xsize) {
- i_push_error(0, "integer overflow allocating input row buffer");
- return NULL;
- }
- out_row_bytes = sizeof(i_fcolor) * x_out;
- if (out_row_bytes / sizeof(i_fcolor) != x_out) {
- i_push_error(0, "integer overflow allocating output row buffer");
- return NULL;
- }
-
if (x_out == src->xsize && y_out == src->ysize) {
return i_copy(src);
}
@@ -91,58 +81,102 @@ i_scale_mixing(i_img *src, int x_out, int y_out) {
if (!result)
return NULL;
+ accum_row_bytes = sizeof(i_fcolor) * src->xsize;
+ if (accum_row_bytes / sizeof(i_fcolor) != src->xsize) {
+ i_push_error(0, "integer overflow allocating accumulator row buffer");
+ return NULL;
+ }
+
+ accum_row = mymalloc(accum_row_bytes);
+
+#code src->bits <= 8
+ IM_COLOR *in_row = NULL;
+ IM_COLOR *xscale_row = NULL;
+ int in_row_bytes, out_row_bytes;
+
+ in_row_bytes = sizeof(IM_COLOR) * src->xsize;
+ if (in_row_bytes / sizeof(IM_COLOR) != src->xsize) {
+ i_push_error(0, "integer overflow allocating input row buffer");
+ return NULL;
+ }
+ out_row_bytes = sizeof(IM_COLOR) * x_out;
+ if (out_row_bytes / sizeof(IM_COLOR) != x_out) {
+ i_push_error(0, "integer overflow allocating output row buffer");
+ return NULL;
+ }
+
in_row = mymalloc(in_row_bytes);
- accum_row = mymalloc(in_row_bytes);
xscale_row = mymalloc(out_row_bytes);
rowsread = 0;
rowsleft = 0.0;
for (y = 0; y < y_out; ++y) {
if (y_out == src->ysize) {
- i_glinf(src, 0, src->xsize, y, accum_row);
+ /* no vertical scaling, just load it */
+ int x, ch;
+#ifdef IM_EIGHT_BIT
+ /* load and convert to doubles */
+ IM_GLIN(src, 0, src->xsize, y, in_row);
+ for (x = 0; x < src->xsize; ++x) {
+ for (ch = 0; ch < src->channels; ++ch) {
+ accum_row[x].channel[ch] = in_row[x].channel[ch];
+ }
+ }
+#else
+ IM_GLIN(src, 0, src->xsize, y, accum_row);
+#endif
}
else {
fracrowtofill = 1.0;
zero_row(accum_row, src->xsize, src->channels);
while (fracrowtofill > 0) {
if (rowsleft <= 0) {
if (rowsread < src->ysize) {
- i_glinf(src, 0, src->xsize, rowsread, in_row);
+ IM_GLIN(src, 0, src->xsize, rowsread, in_row);
++rowsread;
}
/* else just use the last row read */
rowsleft = y_scale;
}
if (rowsleft < fracrowtofill) {
- accum_output_row(accum_row, rowsleft, in_row, src->xsize,
- src->channels);
+ IM_SUFFIX(accum_output_row)(accum_row, rowsleft, in_row,
+ src->xsize, src->channels);
fracrowtofill -= rowsleft;
rowsleft = 0;
}
else {
- accum_output_row(accum_row, fracrowtofill, in_row, src->xsize,
- src->channels);
+ IM_SUFFIX(accum_output_row)(accum_row, fracrowtofill, in_row,
+ src->xsize, src->channels);
rowsleft -= fracrowtofill;
fracrowtofill = 0;
}
}
- /* we've accumulated a vertically scaled row */
- if (x_out == src->xsize) {
- /* no need to scale */
- i_plinf(result, 0, x_out, y, accum_row);
- }
- else {
- horizontal_scale(xscale_row, x_out, accum_row, src->xsize,
- src->channels);
- i_plinf(result, 0, x_out, y, xscale_row);
+ }
+ /* we've accumulated a vertically scaled row */
+ if (x_out == src->xsize) {
+ int x, ch;
+#if IM_EIGHT_BIT
+ /* no need to scale, but we need to convert it */
+ for (x = 0; x < x_out; ++x) {
+ for (ch = 0; ch < result->channels; ++ch)
+ xscale_row[x].channel[ch] = accum_row[x].channel[ch];
}
+ IM_PLIN(result, 0, x_out, y, xscale_row);
+#else
+ IM_PLIN(result, 0, x_out, y, accum_row);
+#endif
+ }
+ else {
+ IM_SUFFIX(horizontal_scale)(xscale_row, x_out, accum_row,
+ src->xsize, src->channels);
+ IM_PLIN(result, 0, x_out, y, xscale_row);
}
}
-
myfree(in_row);
- myfree(accum_row);
myfree(xscale_row);
+#/code
+ myfree(accum_row);
return result;
}
@@ -153,18 +187,24 @@ zero_row(i_fcolor *row, int width, int channels) {
int ch;
/* with IEEE floats we could just use memset() but that's not
- safe in general under ANSI C */
+ safe in general under ANSI C.
+ memset() is slightly faster.
+ */
for (x = 0; x < width; ++x) {
for (ch = 0; ch < channels; ++ch)
row[x].channel[ch] = 0.0;
}
}
+#code
+
static void
-accum_output_row(i_fcolor *accum, double fraction, i_fcolor const *in,
+IM_SUFFIX(accum_output_row)(i_fcolor *accum, double fraction, IM_COLOR const *in,
int width, int channels) {
int x, ch;
+ /* it's tempting to change this into a pointer iteration loop but
+ modern CPUs do the indexing as part of the instruction */
for (x = 0; x < width; ++x) {
for (ch = 0; ch < channels; ++ch) {
accum[x].channel[ch] += in[x].channel[ch] * fraction;
@@ -173,7 +213,7 @@ accum_output_row(i_fcolor *accum, double fraction, i_fcolor const *in,
}
static void
-horizontal_scale(i_fcolor *out, int out_width,
+IM_SUFFIX(horizontal_scale)(IM_COLOR *out, int out_width,
i_fcolor const *in, int in_width,
int channels) {
double frac_col_to_fill, frac_col_left;
@@ -219,3 +259,5 @@ horizontal_scale(i_fcolor *out, int out_width,
}
}
}
+
+#/code
View
31 t/t40scale.t
@@ -1,7 +1,7 @@
#!perl -w
use strict;
use lib 't';
-use Test::More tests => 213;
+use Test::More tests => 223;
BEGIN { use_ok(Imager=>':all') }
@@ -31,6 +31,35 @@ ok($scaleimg, "scale it (mixing)") or print "# ", $img->errstr, "\n";
ok($scaleimg->write(file=>'testout/t40scale3.ppm', type=>'pnm'),
"write mixing scaled image") or print "# ", $img->errstr, "\n";
+{ # double image scaling with mixing, since it has code to handle it
+ my $dimg = Imager->new(xsize => $img->getwidth, ysize => $img->getheight,
+ channels => $img->getchannels,
+ bits => 'double');
+ ok($dimg, "create double/sample image");
+ $dimg->paste(src => $img);
+ $scaleimg = $dimg->scale(scalefactor => 0.25, qtype => 'mixing');
+ ok($scaleimg, "scale it (mixing, double)");
+ ok($scaleimg->write(file => 'testout/t40mixdbl.ppm', type => 'pnm'),
+ "write double/mixing scaled image");
+ is($scaleimg->bits, 'double', "got the right image type as output");
+
+ # hscale only, mixing
+ $scaleimg = $dimg->scale(xscalefactor => 0.33, yscalefactor => 1.0,
+ qtype => 'mixing');
+ ok($scaleimg, "scale it (hscale, mixing, double)");
+ is($scaleimg->getheight, $dimg->getheight, "same height");
+ ok($scaleimg->write(file => 'testout/t40hscdmix.ppm', type => 'pnm'),
+ "save it");
+
+ # vscale only, mixing
+ $scaleimg = $dimg->scale(xscalefactor => 1.0, yscalefactor => 0.33,
+ qtype => 'mixing');
+ ok($scaleimg, "scale it (vscale, mixing, double)");
+ is($scaleimg->getwidth, $dimg->getwidth, "same width");
+ ok($scaleimg->write(file => 'testout/t40vscdmix.ppm', type => 'pnm'),
+ "save it");
+}
+
{
# check for a warning when scale() is called in void context
my $warning;

0 comments on commit a10945a

Please sign in to comment.