2007-08-29 18:23:50 +02:00
|
|
|
/* Function to stretch an image by 3%, and displace in x and y. Cubic
|
|
|
|
* interpolation with a seperable mask. Displacements are:
|
|
|
|
*
|
|
|
|
* 0 <= xdisp < 1.0.
|
|
|
|
* 0 <= ydisp < 1.0.
|
|
|
|
*
|
|
|
|
* Each horizontal block of 33 pixels is stretched to 34.
|
|
|
|
*
|
|
|
|
* Written by Ahmed Abbood
|
|
|
|
* August-1994
|
|
|
|
*
|
|
|
|
* Any unsigned short image. Output image is 3 pixels smaller because of
|
|
|
|
* convolution, but x is larger by 3%:
|
|
|
|
*
|
|
|
|
* out->Xsize = 34*(in->Xsize / 33) + in->Xsize%33 - 3;
|
|
|
|
* out->Ysize = in->Ysize - 3;
|
|
|
|
*
|
|
|
|
* 20/10/95 JC
|
|
|
|
* - was not freeing regions correctly
|
|
|
|
* - tidied up
|
|
|
|
* 29/3/96 JC
|
|
|
|
* - completely rewritten ... now produces correct result, and is 2x
|
|
|
|
* faster
|
|
|
|
* 18/9/97 JC
|
|
|
|
* - added to VIPS library as im_stretch3
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
This file is part of VIPS.
|
|
|
|
|
|
|
|
VIPS is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif /*HAVE_CONFIG_H*/
|
|
|
|
#include <vips/intl.h>
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include <vips/vips.h>
|
|
|
|
|
|
|
|
#ifdef WITH_DMALLOC
|
|
|
|
#include <dmalloc.h>
|
|
|
|
#endif /*WITH_DMALLOC*/
|
|
|
|
|
|
|
|
/* Data for the cubic interpolation function.
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
IMAGE *in;
|
|
|
|
double dx, dy;
|
|
|
|
|
|
|
|
int xoff, yoff; /* Mask we start with for this disp. */
|
|
|
|
int mask[34][4]; /* Fixed-point masks for each output pixel */
|
|
|
|
} StretchInfo;
|
|
|
|
|
|
|
|
/* Per-thread info.
|
|
|
|
*/
|
|
|
|
typedef struct seq_info {
|
|
|
|
StretchInfo *sin;
|
|
|
|
REGION *ir;
|
|
|
|
unsigned short *buf;
|
|
|
|
int lsk;
|
|
|
|
} SeqInfo;
|
|
|
|
|
|
|
|
static int
|
2007-10-20 23:12:50 +02:00
|
|
|
stretch_stop( void *vseq, void *a, void *b )
|
2007-08-29 18:23:50 +02:00
|
|
|
{
|
2007-10-20 23:12:50 +02:00
|
|
|
SeqInfo *seq = (SeqInfo *) vseq;
|
|
|
|
|
|
|
|
IM_FREEF( im_region_free, seq->ir );
|
2007-08-29 18:23:50 +02:00
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *
|
2007-10-20 23:12:50 +02:00
|
|
|
stretch_start( IMAGE *out, void *a, void *b )
|
2007-08-29 18:23:50 +02:00
|
|
|
{
|
2007-10-20 23:12:50 +02:00
|
|
|
IMAGE *in = (IMAGE *) a;
|
|
|
|
StretchInfo *sin = (StretchInfo *) b;
|
|
|
|
SeqInfo *seq;
|
2007-08-29 18:23:50 +02:00
|
|
|
|
2007-10-20 23:12:50 +02:00
|
|
|
if( !(seq = IM_NEW( out, SeqInfo )) )
|
2007-08-29 18:23:50 +02:00
|
|
|
return( NULL );
|
|
|
|
|
|
|
|
seq->sin = sin;
|
|
|
|
seq->ir = im_region_create( in );
|
|
|
|
seq->lsk = IM_IMAGE_N_ELEMENTS( out );
|
|
|
|
seq->buf = IM_ARRAY( out, 4*seq->lsk, unsigned short );
|
|
|
|
|
|
|
|
if( !seq->buf || !seq->ir ) {
|
2007-10-20 23:12:50 +02:00
|
|
|
stretch_stop( seq, NULL, NULL );
|
2007-08-29 18:23:50 +02:00
|
|
|
return( NULL );
|
|
|
|
}
|
|
|
|
|
|
|
|
return( (void *)seq );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Stretch a line of pels into a line in the buffer.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
make_xline( StretchInfo *sin,
|
|
|
|
unsigned short *p, unsigned short *q, int w, int m )
|
|
|
|
{
|
|
|
|
int bands = sin->in->Bands;
|
|
|
|
int tot;
|
|
|
|
int x, b;
|
|
|
|
|
|
|
|
/* Offsets for subsequent pixels.
|
|
|
|
*/
|
|
|
|
int o1 = 1*bands;
|
|
|
|
int o2 = 2*bands;
|
|
|
|
int o3 = 3*bands;
|
|
|
|
|
|
|
|
for( x = 0; x < w; x++ ) {
|
|
|
|
int *mask = &sin->mask[m][0];
|
|
|
|
unsigned short *p1 = p;
|
|
|
|
|
|
|
|
/* Loop for this pel.
|
|
|
|
*/
|
|
|
|
for( b = 0; b < bands; b++ ) {
|
|
|
|
tot = p1[0]*mask[0] + p1[o1]*mask[1] +
|
|
|
|
p1[o2]*mask[2] + p1[o3]*mask[3];
|
|
|
|
tot = IM_MAX( 0, tot );
|
|
|
|
p1++;
|
|
|
|
*q++ = (tot + 16384) >> 15;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Move to next mask.
|
|
|
|
*/
|
|
|
|
m++;
|
|
|
|
if( m == 34 )
|
|
|
|
/* Back to mask 0, reuse this input pel.
|
|
|
|
*/
|
|
|
|
m = 0;
|
|
|
|
else
|
|
|
|
/* Move to next input pel.
|
|
|
|
*/
|
|
|
|
p += bands;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* As above, but do the vertical resample. lsk is how much we add to move down
|
|
|
|
* a line in p, boff is [0,1,2,3] for which buffer line is mask[3].
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
make_yline( StretchInfo *sin, int lsk, int boff,
|
|
|
|
unsigned short *p, unsigned short *q, int w, int m )
|
|
|
|
{
|
|
|
|
int bands = sin->in->Bands;
|
|
|
|
int we = w * bands;
|
|
|
|
int *mask = &sin->mask[m][0];
|
|
|
|
int tot;
|
|
|
|
int x;
|
|
|
|
|
|
|
|
/* Offsets for subsequent pixels. Down a line each time.
|
|
|
|
*/
|
|
|
|
int o0 = lsk*boff;
|
|
|
|
int o1 = lsk*((boff + 1) % 4);
|
|
|
|
int o2 = lsk*((boff + 2) % 4);
|
|
|
|
int o3 = lsk*((boff + 3) % 4);
|
|
|
|
|
|
|
|
for( x = 0; x < we; x++ ) {
|
|
|
|
tot = p[o0]*mask[0] + p[o1]*mask[1] +
|
|
|
|
p[o2]*mask[2] + p[o3]*mask[3];
|
|
|
|
tot = IM_MAX( 0, tot );
|
|
|
|
p++;
|
|
|
|
*q++ = (tot + 16384) >> 15;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2007-10-20 23:12:50 +02:00
|
|
|
stretch_gen( REGION *or, void *vseq, void *a, void *b )
|
2007-08-29 18:23:50 +02:00
|
|
|
{
|
2007-10-20 23:12:50 +02:00
|
|
|
SeqInfo *seq = (SeqInfo *) vseq;
|
|
|
|
StretchInfo *sin = (StretchInfo *) b;
|
2007-08-29 18:23:50 +02:00
|
|
|
REGION *ir = seq->ir;
|
|
|
|
Rect *r = &or->valid;
|
|
|
|
Rect r1;
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
/* What mask do we start with?
|
|
|
|
*/
|
|
|
|
int xstart = (r->left + sin->xoff) % 34;
|
|
|
|
|
|
|
|
/* What part of input do we need for this output?
|
|
|
|
*/
|
|
|
|
r1.left = r->left - (r->left + sin->xoff) / 34;
|
|
|
|
r1.top = r->top;
|
|
|
|
x = IM_RECT_RIGHT( r );
|
|
|
|
x = x - (x + sin->xoff) / 34 + 3;
|
|
|
|
r1.width = x - r1.left;
|
|
|
|
r1.height = r->height + 3;
|
|
|
|
if( im_prepare( ir, &r1 ) )
|
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
/* Fill the first three lines of the buffer.
|
|
|
|
*/
|
|
|
|
for( y = 0; y < 3; y++ ) {
|
|
|
|
unsigned short *p = (unsigned short *)
|
|
|
|
IM_REGION_ADDR( ir, r1.left, y + r1.top );
|
|
|
|
unsigned short *q = seq->buf + seq->lsk*y;
|
|
|
|
|
|
|
|
make_xline( sin, p, q, r->width, xstart );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Loop for subsequent lines: stretch a new line of x pels, and
|
|
|
|
* interpolate a line of output from the 3 previous xes plus this new
|
|
|
|
* one.
|
|
|
|
*/
|
|
|
|
for( y = 0; y < r->height; y++ ) {
|
|
|
|
/* Next line of fresh input pels.
|
|
|
|
*/
|
|
|
|
unsigned short *p = (unsigned short *)
|
|
|
|
IM_REGION_ADDR( ir, r1.left, y + r1.top + 3 );
|
|
|
|
|
|
|
|
/* Next line we fill in the buffer.
|
|
|
|
*/
|
|
|
|
int boff = (y + 3)%4;
|
|
|
|
unsigned short *q = seq->buf + boff*seq->lsk;
|
|
|
|
|
|
|
|
/* Line we write in output.
|
|
|
|
*/
|
|
|
|
unsigned short *q1 = (unsigned short *)
|
|
|
|
IM_REGION_ADDR( or, r->left, y + r->top );
|
|
|
|
|
|
|
|
/* Process this new xline.
|
|
|
|
*/
|
|
|
|
make_xline( sin, p, q, r->width, xstart );
|
|
|
|
|
|
|
|
/* Generate new output line.
|
|
|
|
*/
|
|
|
|
make_yline( sin, seq->lsk, boff,
|
|
|
|
seq->buf, q1, r->width, sin->yoff );
|
|
|
|
}
|
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
im_stretch3( IMAGE *in, IMAGE *out, double dx, double dy )
|
|
|
|
{
|
|
|
|
StretchInfo *sin;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Check our args.
|
|
|
|
*/
|
|
|
|
if( in->Coding != IM_CODING_NONE || in->BandFmt != IM_BANDFMT_USHORT ) {
|
2008-11-02 23:11:01 +01:00
|
|
|
im_error( "im_stretch3",
|
|
|
|
"%s", _( "not uncoded unsigned short" ) );
|
2007-08-29 18:23:50 +02:00
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
if( dx < 0 || dx >= 1.0 || dy < 0 || dy >= 1.0 ) {
|
2007-10-20 23:12:50 +02:00
|
|
|
im_error( "im_stretch3",
|
2008-11-02 23:11:01 +01:00
|
|
|
"%s", _( "displacements out of range [0,1)" ) );
|
2007-08-29 18:23:50 +02:00
|
|
|
return( -1 );
|
|
|
|
}
|
|
|
|
if( im_piocheck( in, out ) )
|
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
/* Prepare the output image.
|
|
|
|
*/
|
|
|
|
if( im_cp_desc( out, in ) )
|
|
|
|
return( -1 );
|
|
|
|
out->Xsize = 34*(in->Xsize / 33) + in->Xsize%33 - 3;
|
|
|
|
out->Ysize = in->Ysize - 3;
|
|
|
|
|
|
|
|
if( im_demand_hint( out, IM_FATSTRIP, in, NULL ) )
|
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
if( !(sin = IM_NEW( out, StretchInfo )) )
|
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
/* Save parameters.
|
|
|
|
*/
|
|
|
|
sin->in = in;
|
|
|
|
sin->dx = dx;
|
|
|
|
sin->dy = dy;
|
|
|
|
|
|
|
|
/* Generate masks.
|
|
|
|
*/
|
|
|
|
for( i = 0; i < 34; i++ ) {
|
|
|
|
double d = (34.0 - i)/34.0;
|
|
|
|
|
|
|
|
double y0 = 2.0*d*d - d - d*d*d;
|
|
|
|
double y1 = 1.0 - 2.0*d*d + d*d*d;
|
|
|
|
double y2 = d + d*d - d*d*d;
|
|
|
|
double y3 = -d*d + d*d*d;
|
|
|
|
|
|
|
|
sin->mask[i][0] = IM_RINT( y0 * 32768 );
|
|
|
|
sin->mask[i][1] = IM_RINT( y1 * 32768 );
|
|
|
|
sin->mask[i][2] = IM_RINT( y2 * 32768 );
|
|
|
|
sin->mask[i][3] = IM_RINT( y3 * 32768 );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Which mask do we start with to apply these offsets?
|
|
|
|
*/
|
|
|
|
sin->xoff = (dx * 33.0) + 0.5;
|
|
|
|
sin->yoff = (dy * 33.0) + 0.5;
|
|
|
|
|
|
|
|
if( im_generate( out,
|
2007-10-20 23:12:50 +02:00
|
|
|
stretch_start, stretch_gen, stretch_stop, in, sin ) )
|
2007-08-29 18:23:50 +02:00
|
|
|
return( -1 );
|
|
|
|
|
|
|
|
return( 0 );
|
|
|
|
}
|