better im_shrink()
faster, lower memuse, simpler
This commit is contained in:
parent
9403948bee
commit
24e86ae458
@ -4,6 +4,7 @@
|
|||||||
- delayed write to foreign via a "w" image was not working
|
- delayed write to foreign via a "w" image was not working
|
||||||
- support operations with many returns in Python
|
- support operations with many returns in Python
|
||||||
- sequential read mode for pngload
|
- sequential read mode for pngload
|
||||||
|
- better im_shrink()
|
||||||
|
|
||||||
20/8/11 started 7.27.0
|
20/8/11 started 7.27.0
|
||||||
- version bump for new dev cycle
|
- version bump for new dev cycle
|
||||||
|
@ -27,6 +27,9 @@
|
|||||||
* - fall back to im_copy() for 1/1 shrink
|
* - fall back to im_copy() for 1/1 shrink
|
||||||
* 2/2/11
|
* 2/2/11
|
||||||
* - gtk-doc
|
* - gtk-doc
|
||||||
|
* 10/2/12
|
||||||
|
* - faster, simpler
|
||||||
|
* - shrink in chunks to reduce peak memuse for large shrinks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -69,19 +72,22 @@
|
|||||||
/* Our main parameter struct.
|
/* Our main parameter struct.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
IMAGE *in;
|
||||||
|
IMAGE *out;
|
||||||
double xshrink; /* Shrink factors */
|
double xshrink; /* Shrink factors */
|
||||||
double yshrink;
|
double yshrink;
|
||||||
|
|
||||||
int mw; /* Size of area we average */
|
int mw; /* Size of area we average */
|
||||||
int mh;
|
int mh;
|
||||||
int np; /* Number of pels we average */
|
int np; /* Number of pels we average */
|
||||||
} ShrinkInfo;
|
} ShrinkInfo;
|
||||||
|
|
||||||
/* Our per-sequence parameter struct. We hold an offset for each pel we
|
/* Our per-sequence parameter struct. Somewhere to sum band elements.
|
||||||
* average.
|
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
REGION *ir;
|
REGION *ir;
|
||||||
int *off;
|
|
||||||
|
VipsPel *sum;
|
||||||
} SeqInfo;
|
} SeqInfo;
|
||||||
|
|
||||||
/* Free a sequence value.
|
/* Free a sequence value.
|
||||||
@ -111,10 +117,10 @@ shrink_start( IMAGE *out, void *a, void *b )
|
|||||||
/* Init!
|
/* Init!
|
||||||
*/
|
*/
|
||||||
seq->ir = NULL;
|
seq->ir = NULL;
|
||||||
seq->off = NULL;
|
seq->sum = NULL;
|
||||||
seq->ir = im_region_create( in );
|
seq->ir = im_region_create( in );
|
||||||
seq->off = IM_ARRAY( out, st->np, int );
|
seq->sum = (void *) IM_ARRAY( out, in->Bands, double );
|
||||||
if( !seq->off || !seq->ir ) {
|
if( !seq->sum || !seq->ir ) {
|
||||||
shrink_stop( seq, in, st );
|
shrink_stop( seq, in, st );
|
||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
}
|
||||||
@ -124,51 +130,92 @@ shrink_start( IMAGE *out, void *a, void *b )
|
|||||||
|
|
||||||
/* Integer shrink.
|
/* Integer shrink.
|
||||||
*/
|
*/
|
||||||
#define ishrink(TYPE) \
|
#define ISHRINK( TYPE ) { \
|
||||||
for( y = to; y < bo; y++ ) { \
|
int *sum = (int *) seq->sum; \
|
||||||
TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \
|
TYPE *p = (TYPE *) in; \
|
||||||
\
|
TYPE *q = (TYPE *) out; \
|
||||||
for( x = le; x < ri; x++ ) { \
|
\
|
||||||
int ix = x * st->xshrink; \
|
for( y1 = 0; y1 < st->mh; y1++ ) { \
|
||||||
int iy = y * st->yshrink; \
|
for( i = 0, x1 = 0; x1 < st->mw; x1++ ) \
|
||||||
TYPE *p = (TYPE *) IM_REGION_ADDR( ir, ix, iy ); \
|
for( b = 0; b < bands; b++, i++ ) \
|
||||||
\
|
sum[b] += p[i]; \
|
||||||
for( k = 0; k < ir->im->Bands; k++ ) { \
|
\
|
||||||
int sum = 0; \
|
p += ls; \
|
||||||
int *t = seq->off; \
|
} \
|
||||||
\
|
\
|
||||||
for( z = 0; z < st->np; z++ ) \
|
for( b = 0; b < bands; b++ ) \
|
||||||
sum += p[*t++]; \
|
q[b] = (sum[b] + st->np / 2) / st->np; \
|
||||||
\
|
}
|
||||||
*q++ = sum / st->np; \
|
|
||||||
p++; \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FP shrink.
|
/* Float shrink.
|
||||||
*/
|
*/
|
||||||
#define fshrink(TYPE) \
|
#define FSHRINK( TYPE ) { \
|
||||||
for( y = to; y < bo; y++ ) { \
|
double *sum = (double *) seq->sum; \
|
||||||
TYPE *q = (TYPE *) IM_REGION_ADDR( or, le, y ); \
|
TYPE *p = (TYPE *) in; \
|
||||||
\
|
TYPE *q = (TYPE *) out; \
|
||||||
for( x = le; x < ri; x++ ) { \
|
\
|
||||||
int ix = x * st->xshrink; \
|
for( y1 = 0; y1 < st->mh; y1++ ) { \
|
||||||
int iy = y * st->yshrink; \
|
for( i = 0, x1 = 0; x1 < st->mw; x1++ ) \
|
||||||
TYPE *p = (TYPE *) IM_REGION_ADDR( ir, ix, iy ); \
|
for( b = 0; b < bands; b++, i++ ) \
|
||||||
\
|
sum[b] += p[i]; \
|
||||||
for( k = 0; k < ir->im->Bands; k++ ) { \
|
\
|
||||||
double sum = 0; \
|
p += ls; \
|
||||||
int *t = seq->off; \
|
} \
|
||||||
\
|
\
|
||||||
for( z = 0; z < st->np; z++ ) \
|
for( b = 0; b < bands; b++ ) \
|
||||||
sum += p[*t++]; \
|
q[b] = sum[b] / st->np; \
|
||||||
\
|
}
|
||||||
*q++ = sum / st->np; \
|
|
||||||
p++; \
|
/* Generate an area of @or. @ir is large enough.
|
||||||
} \
|
*/
|
||||||
} \
|
static void
|
||||||
|
shrink_gen2( ShrinkInfo *st, SeqInfo *seq,
|
||||||
|
REGION *or, REGION *ir,
|
||||||
|
int left, int top, int width, int height )
|
||||||
|
{
|
||||||
|
const int bands = st->in->Bands;
|
||||||
|
const int sizeof_pixel = VIPS_IMAGE_SIZEOF_PEL( st->in );
|
||||||
|
const int ls = VIPS_REGION_LSKIP( ir ) /
|
||||||
|
VIPS_IMAGE_SIZEOF_ELEMENT( st->in );
|
||||||
|
|
||||||
|
int x, y, i;
|
||||||
|
int x1, y1, b;
|
||||||
|
|
||||||
|
for( y = 0; y < height; y++ ) {
|
||||||
|
VipsPel *out = IM_REGION_ADDR( or, left, top + y );
|
||||||
|
|
||||||
|
for( x = 0; x < width; x++ ) {
|
||||||
|
int ix = (left + x) * st->xshrink;
|
||||||
|
int iy = (top + y) * st->yshrink;
|
||||||
|
VipsPel *in = IM_REGION_ADDR( ir, ix, iy );
|
||||||
|
|
||||||
|
for( i = 0; i < sizeof_pixel; i++ )
|
||||||
|
seq->sum[i] = 0;
|
||||||
|
|
||||||
|
switch( st->in->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:
|
||||||
|
g_assert( 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Shrink a REGION.
|
/* Shrink a REGION.
|
||||||
*/
|
*/
|
||||||
@ -176,51 +223,44 @@ static int
|
|||||||
shrink_gen( REGION *or, void *vseq, void *a, void *b )
|
shrink_gen( REGION *or, void *vseq, void *a, void *b )
|
||||||
{
|
{
|
||||||
SeqInfo *seq = (SeqInfo *) vseq;
|
SeqInfo *seq = (SeqInfo *) vseq;
|
||||||
|
IMAGE *in = (IMAGE *) a;
|
||||||
ShrinkInfo *st = (ShrinkInfo *) b;
|
ShrinkInfo *st = (ShrinkInfo *) b;
|
||||||
REGION *ir = seq->ir;
|
REGION *ir = seq->ir;
|
||||||
Rect *r = &or->valid;
|
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;
|
/* How do we chunk up the image? We don't want to prepare the whole of
|
||||||
|
* the input region corresponding to *r since it could be huge.
|
||||||
/* What part of the input image do we need? Very careful: round left
|
*
|
||||||
* down, round right up.
|
* Each pixel of *r will depend on roughly mw x mh
|
||||||
|
* pixels, so we walk *r in chunks which map to the tile size.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
s.left = r->left * st->xshrink;
|
int xstep = 1 + VIPS__TILE_WIDTH / st->mw;
|
||||||
s.top = r->top * st->yshrink;
|
int ystep = 1 + VIPS__TILE_HEIGHT / st->mh;
|
||||||
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
|
int x, y;
|
||||||
* 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 ) {
|
for( y = 0; y < r->height; y += ystep )
|
||||||
case IM_BANDFMT_UCHAR: ishrink(unsigned char); break;
|
for( x = 0; x < r->width; x += xstep ) {
|
||||||
case IM_BANDFMT_CHAR: ishrink(char); break;
|
/* Clip the this rect against the image size.
|
||||||
case IM_BANDFMT_USHORT: ishrink(unsigned short); break;
|
*/
|
||||||
case IM_BANDFMT_SHORT: ishrink(short); break;
|
int width = VIPS_MIN( xstep, in->Xsize - r->left - x );
|
||||||
case IM_BANDFMT_UINT: ishrink(unsigned int); break;
|
int height = VIPS_MIN( ystep, in->Ysize - r->top - y );
|
||||||
case IM_BANDFMT_INT: ishrink(int); break;
|
|
||||||
case IM_BANDFMT_FLOAT: fshrink(float); break;
|
Rect s;
|
||||||
case IM_BANDFMT_DOUBLE: fshrink(double); break;
|
|
||||||
|
s.left = (r->left + x) * st->xshrink;
|
||||||
|
s.top = (r->top + y) * st->yshrink;
|
||||||
|
s.width = 1 + ceil( width * st->xshrink );
|
||||||
|
s.height = 1 + ceil( height * st->yshrink );
|
||||||
|
if( im_prepare( ir, &s ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
shrink_gen2( st, seq,
|
||||||
|
or, ir,
|
||||||
|
r->left + x, r->top + y, width, height );
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
|
||||||
im_error( "im_shrink", "%s", _( "unsupported input format" ) );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user