/* @(#) Shrink any non-complex image by some x, y, factor. No interpolation! * @(#) Just average an area. Suitable for making quicklooks only! * @(#) * @(#) int * @(#) im_shrink( in, out, xshrink, yshrink ) * @(#) IMAGE *in, *out; * @(#) double xshrink, yshrink; * @(#) * @(#) Returns either 0 (success) or -1 (fail) * @(#) * * Copyright: 1990, N. Dessipris. * * Authors: Nicos Dessipris and Kirk Martinez * Written on: 29/04/1991 * Modified on: 2/11/92, 22/2/93 Kirk Martinez - Xres Yres & cleanup incredibly inefficient for box filters as LUTs are used instead of + Needs converting to a smoother filter: eg Gaussian! KM * 15/7/93 JC * - rewritten for partial v2 * - ANSIfied * - now shrinks any non-complex type * - no longer cloned from im_convsub() * - could be much better! see km comments above * 3/8/93 JC * - rounding bug fixed * 11/1/94 JC * - problems with .000001 and round up/down ignored! Try shrink 3738 * pixel image by 9.345000000001 * 7/10/94 JC * - IM_NEW and IM_ARRAY added * - more typedef * 3/7/95 JC * - IM_CODING_LABQ handling added here * 20/12/08 * - fall back to im_copy() for 1/1 shrink */ /* 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 #endif /*HAVE_CONFIG_H*/ #include #include #include #include #include #ifdef WITH_DMALLOC #include #endif /*WITH_DMALLOC*/ /* Our main parameter struct. */ typedef struct { double xshrink; /* Shrink factors */ double yshrink; int mw; /* Size of area we average */ int mh; int np; /* Number of pels we average */ } ShrinkInfo; /* Our per-sequence parameter struct. We hold an offset for each pel we * average. */ typedef struct { REGION *ir; int *off; } SeqInfo; /* Free a sequence value. */ static int shrink_stop( void *vseq, void *a, void *b ) { SeqInfo *seq = (SeqInfo *) vseq; IM_FREEF( im_region_free, seq->ir ); return( 0 ); } /* Make a sequence value. */ static void * shrink_start( IMAGE *out, void *a, void *b ) { IMAGE *in = (IMAGE *) a; ShrinkInfo *st = (ShrinkInfo *) b; SeqInfo *seq; if( !(seq = IM_NEW( out, SeqInfo )) ) return( NULL ); /* Init! */ seq->ir = NULL; seq->off = NULL; seq->ir = im_region_create( in ); seq->off = IM_ARRAY( out, st->np, int ); if( !seq->off || !seq->ir ) { shrink_stop( seq, in, st ); return( NULL ); } return( (void *) seq ); } /* Integer shrink. */ #define ishrink(TYPE) \ for( y = to; y < bo; y++ ) { \ TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ \ for( x = le; x < ri; x++ ) { \ int ix = x * st->xshrink; \ int iy = y * st->yshrink; \ TYPE *p = (TYPE *) IM_REGION_ADDR( ir, ix, iy ); \ \ for( k = 0; k < ir->im->Bands; k++ ) { \ int sum = 0; \ int *t = seq->off; \ \ for( z = 0; z < st->np; z++ ) \ sum += p[*t++]; \ \ *q++ = sum / st->np; \ p++; \ } \ } \ } /* FP shrink. */ #define fshrink(TYPE) \ for( y = to; y < bo; y++ ) { \ TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \ \ for( x = le; x < ri; x++ ) { \ int ix = x * st->xshrink; \ int iy = y * st->yshrink; \ TYPE *p = (TYPE *) IM_REGION_ADDR( ir, ix, iy ); \ \ for( k = 0; k < ir->im->Bands; k++ ) { \ double sum = 0; \ int *t = seq->off; \ \ for( z = 0; z < st->np; z++ ) \ sum += p[*t++]; \ \ *q++ = sum / st->np; \ p++; \ } \ } \ } /* Shrink a REGION. */ static int shrink_gen( REGION *or, void *vseq, void *a, void *b ) { SeqInfo *seq = (SeqInfo *) vseq; ShrinkInfo *st = (ShrinkInfo *) b; REGION *ir = seq->ir; Rect *r = &or->valid; Rect s; int le = r->left; int ri = IM_RECT_RIGHT( r ); int to = r->top; int bo = IM_RECT_BOTTOM(r); int x, y, z, k; /* What part of the input image do we need? Very careful: round left * down, round right up. */ s.left = r->left * st->xshrink; s.top = r->top * st->yshrink; s.width = ceil( IM_RECT_RIGHT( r ) * st->xshrink ) - s.left; s.height = ceil( IM_RECT_BOTTOM( r ) * st->yshrink ) - s.top; if( im_prepare( ir, &s ) ) return( -1 ); /* Init offsets for pel addressing. Note that offsets must be for the * type we will address the memory array with. */ for( z = 0, y = 0; y < st->mh; y++ ) for( x = 0; x < st->mw; x++ ) seq->off[z++] = (IM_REGION_ADDR( ir, x, y ) - IM_REGION_ADDR( ir, 0, 0 )) / IM_IMAGE_SIZEOF_ELEMENT( ir->im ); switch( ir->im->BandFmt ) { case IM_BANDFMT_UCHAR: ishrink(unsigned char); break; case IM_BANDFMT_CHAR: ishrink(char); break; case IM_BANDFMT_USHORT: ishrink(unsigned short); break; case IM_BANDFMT_SHORT: ishrink(short); break; case IM_BANDFMT_UINT: ishrink(unsigned int); break; case IM_BANDFMT_INT: ishrink(int); break; case IM_BANDFMT_FLOAT: fshrink(float); break; case IM_BANDFMT_DOUBLE: fshrink(double); break; default: im_error( "im_shrink", "%s", _( "unsupported input format" ) ); return( -1 ); } return( 0 ); } static int shrink( IMAGE *in, IMAGE *out, double xshrink, double yshrink ) { ShrinkInfo *st; /* Check parameters. */ if( !in || im_iscomplex( in ) ) { im_error( "im_shrink", "%s", _( "non-complex input only" ) ); return( -1 ); } if( xshrink < 1.0 || yshrink < 1.0 ) { im_error( "im_shrink", "%s", _( "shrink factors should both be >1" ) ); return( -1 ); } if( im_piocheck( in, out ) ) return( -1 ); /* Prepare output. Note: we round the output width down! */ if( im_cp_desc( out, in ) ) return( -1 ); out->Xsize = in->Xsize / xshrink; out->Ysize = in->Ysize / yshrink; out->Xres = in->Xres / xshrink; out->Yres = in->Yres / yshrink; if( out->Xsize <= 0 || out->Ysize <= 0 ) { im_error( "im_shrink", "%s", _( "image has shrunk to nothing" ) ); return( -1 ); } /* Build and attach state struct. */ if( !(st = IM_NEW( out, ShrinkInfo )) ) return( -1 ); st->xshrink = xshrink; st->yshrink = yshrink; st->mw = ceil( xshrink ); st->mh = ceil( yshrink ); st->np = st->mw * st->mh; /* Set demand hints. We want THINSTRIP, as we will be demanding a * large area of input for each output line. */ if( im_demand_hint( out, IM_THINSTRIP, in, NULL ) ) return( -1 ); /* Generate! */ if( im_generate( out, shrink_start, shrink_gen, shrink_stop, in, st ) ) return( -1 ); return( 0 ); } /* Wrap up the above: do IM_CODING_LABQ as well. */ int im_shrink( IMAGE *in, IMAGE *out, double xshrink, double yshrink ) { if( xshrink == 1 && yshrink == 1 ) { return( im_copy( in, out ) ); } else if( in->Coding == IM_CODING_LABQ ) { IMAGE *t[2]; if( im_open_local_array( out, t, 2, "im_shrink:1", "p" ) || im_LabQ2LabS( in, t[0] ) || shrink( t[0], t[1], xshrink, yshrink ) || im_LabS2LabQ( t[1], out ) ) return( -1 ); } else if( in->Coding == IM_CODING_NONE ) { if( shrink( in, out, xshrink, yshrink ) ) return( -1 ); } else { im_error( "im_shrink", "%s", _( "unknown coding type" ) ); return( -1 ); } return( 0 ); }