blob: 5ff459a111315eff4271b74d3563e8ff6401759b [file] [log] [blame]
/* @(#) 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 <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*/
/* 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 || vips_bandfmt_iscomplex( in->BandFmt ) ) {
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 );
}