| /* 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 |
| stretch_stop( void *vseq, void *a, void *b ) |
| { |
| SeqInfo *seq = (SeqInfo *) vseq; |
| |
| IM_FREEF( im_region_free, seq->ir ); |
| |
| return( 0 ); |
| } |
| |
| static void * |
| stretch_start( IMAGE *out, void *a, void *b ) |
| { |
| IMAGE *in = (IMAGE *) a; |
| StretchInfo *sin = (StretchInfo *) b; |
| SeqInfo *seq; |
| |
| if( !(seq = IM_NEW( out, SeqInfo )) ) |
| 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 ) { |
| stretch_stop( seq, NULL, NULL ); |
| 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 |
| stretch_gen( REGION *or, void *vseq, void *a, void *b ) |
| { |
| SeqInfo *seq = (SeqInfo *) vseq; |
| StretchInfo *sin = (StretchInfo *) b; |
| 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 ) { |
| im_error( "im_stretch3", |
| "%s", _( "not uncoded unsigned short" ) ); |
| return( -1 ); |
| } |
| if( dx < 0 || dx >= 1.0 || dy < 0 || dy >= 1.0 ) { |
| im_error( "im_stretch3", |
| "%s", _( "displacements out of range [0,1)" ) ); |
| 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, |
| stretch_start, stretch_gen, stretch_stop, in, sin ) ) |
| return( -1 ); |
| |
| return( 0 ); |
| } |