Exemplo n.º 1
0
/* sizealike by expanding in just one dimension and copying the final element. 
 */
static int
vips__hist_sizealike_vec( VipsImage **in, VipsImage **out, int n )
{
	int i;
	int max_size;

	g_assert( n >= 1 );

	max_size = VIPS_MAX( in[0]->Xsize, in[0]->Ysize );
	for( i = 1; i < n; i++ ) 
		max_size = VIPS_MAX( max_size, 
			VIPS_MAX( in[0]->Xsize, in[0]->Ysize ) );

	for( i = 0; i < n; i++ ) 
		if( in[i]->Ysize == 1 ) {
			if( vips_embed( in[i], &out[i], 0, 0, max_size, 1, 
				"extend", VIPS_EXTEND_COPY,
				NULL ) )
				return( -1 );
		}
		else {
			if( vips_embed( in[i], &out[i], 0, 0, 1, max_size, 
				"extend", VIPS_EXTEND_COPY,
				NULL ) )
				return( -1 );
		}

	return( 0 );
}
Exemplo n.º 2
0
static int
vips_unary_const_build( VipsObject *object )
{
	VipsArithmetic *arithmetic = VIPS_ARITHMETIC( object );
	VipsUnary *unary = (VipsUnary *) object;
	VipsUnaryConst *uconst = (VipsUnaryConst *) object;

	/* If we have a three-element vector we need to bandup the image to
	 * match.
	 */
	uconst->n = 1;
	if( uconst->c )
		uconst->n = VIPS_MAX( uconst->n, uconst->c->n );
	if( unary->in )
		uconst->n = VIPS_MAX( uconst->n, unary->in->Bands );
	arithmetic->base_bands = uconst->n;

	if( unary->in && uconst->c ) {
		if( vips_check_vector( "VipsRelationalConst", 
			uconst->c->n, unary->in ) )
		return( -1 );
	}

	/* Some operations need the vector in the input type (eg.
	 * im_equal_vec() where the output type is always uchar and is useless
	 * for comparisons), some need it in the output type (eg.
	 * im_andimage_vec() where we want to get the double to an int so we
	 * can do bitwise-and without having to cast for each pixel), some
	 * need a fixed type (eg. im_powtra_vec(), where we want to keep it as
	 * double).
	 *
	 * Therefore pass in the desired vector type as a param.
	 */

	if( uconst->c ) 
		uconst->c_ready = make_pixel( (VipsObject *) uconst, 
			uconst->n, uconst->const_format,
			uconst->c->n, (double *) uconst->c->data );

	if( VIPS_OBJECT_CLASS( vips_unary_const_parent_class )->
		build( object ) )
		return( -1 );

	return( 0 );
}
Exemplo n.º 3
0
int
vips__sizealike_vec( VipsImage **in, VipsImage **out, int n )
{
    int i;
    int width_max;
    int height_max;

    g_assert( n >= 1 );

    width_max = in[0]->Xsize;
    height_max = in[0]->Ysize;
    for( i = 1; i < n; i++ ) {
        width_max = VIPS_MAX( width_max, in[i]->Xsize );
        height_max = VIPS_MAX( height_max, in[i]->Ysize );
    }

    for( i = 0; i < n; i++ )
        if( vips_embed( in[i], &out[i],
                        0, 0, width_max, height_max, NULL ) )
            return( -1 );

    return( 0 );
}
Exemplo n.º 4
0
/* base_bands is the default minimum.
 *
 * Handy for example, if you have VipsLinear with
 * a 3-element vector of constants and a 1-band input image, you need to cast
 * the image up to three bands.
 */
int
vips__bandalike_vec( const char *domain,
                     VipsImage **in, VipsImage **out, int n, int base_bands )
{
    int i;
    int max_bands;

    g_assert( n >= 1 );

    max_bands = base_bands;
    for( i = 0; i < n; i++ )
        max_bands = VIPS_MAX( max_bands, in[i]->Bands );
    for( i = 0; i < n; i++ )
        if( vips__bandup( domain, in[i], &out[i], max_bands ) )
            return( -1 );

    return( 0 );
}
Exemplo n.º 5
0
static void
write_grow( Write *write, size_t grow_len )
{
	size_t new_len = write->len + grow_len;

	if( new_len > write->alloc ) {
		size_t proposed_alloc = (16 + write->alloc) * 3 / 2;

		write->alloc = VIPS_MAX( proposed_alloc, new_len );

		/* Our result mujst be freedd with g_free(), so it's OK to use
		 * g_realloc(). 
		 */
	 	write->buf = g_realloc( write->buf, write->alloc );

		VIPS_DEBUG_MSG( "write_buf_grow: grown to %zd bytes\n",
			write->alloc );
	}
}
Exemplo n.º 6
0
static void
write_buf_grow( WriteBuf *wbuf, size_t grow_len )
{
	size_t new_len = wbuf->len + grow_len;

	if( new_len > wbuf->alloc ) {
		size_t proposed_alloc = (16 + wbuf->alloc) * 3 / 2;

		wbuf->alloc = VIPS_MAX( proposed_alloc, new_len );

		/* There's no vips_realloc(), so we call g_realloc() directly.
		 * This is safe, since vips_malloc() / vips_free() are wrappers 
		 * over g_malloc() / g_free().
		 *
		 * FIXME: add vips_realloc().
		 */
	 	wbuf->buf = g_realloc( wbuf->buf, wbuf->alloc );

		VIPS_DEBUG_MSG( "write_buf_grow: grown to %zd bytes\n",
			wbuf->alloc );
	}
}
Exemplo n.º 7
0
/* Calculate the shrink factors. 
 *
 * We shrink in two stages: first, a shrink with a block average. This can
 * only accurately shrink by integer factors. We then do a second shrink with
 * a supplied interpolator to get the exact size we want.
 */
static int
calculate_shrink( int width, int height, double *residual )
{
	/* Calculate the horizontal and vertical shrink we'd need to fit the
	 * image to the bounding box, and pick the biggest.
	 *
	 * In crop mode we aim to fill the bounding box, so we must use the
	 * smaller axis.
	 */
	double horizontal = (double) width / thumbnail_width;
	double vertical = (double) height / thumbnail_height;
	double factor = crop_image ?
		VIPS_MIN( horizontal, vertical ) : 
		VIPS_MAX( horizontal, vertical ); 

	/* If the shrink factor is <= 1.0, we need to zoom rather than shrink.
	 * Just set the factor to 1 in this case.
	 */
	double factor2 = factor < 1.0 ? 1.0 : factor;

	/* Int component of shrink.
	 */
	int shrink = floor( factor2 );

	if( residual ) {
		/* Width after int shrink.
		 */
		int iwidth = width / shrink;

		/* Therefore residual scale factor is.
		 */
		*residual = (width / factor) / iwidth; 
	}

	return( shrink );
}
Exemplo n.º 8
0
/* Zoom a VipsRegion.
 */
static int
vips_zoom_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
{
	VipsRegion *ir = (VipsRegion *) seq;
	VipsZoom *zoom = (VipsZoom *) b;

	/* Output area we are building.
	 */
	const VipsRect *r = &or->valid;
	const int ri = VIPS_RECT_RIGHT( r );
	const int bo = VIPS_RECT_BOTTOM(r);

	VipsRect s;
	int left, right, top, bottom;
	int width, height;

	/* Area of input we need. We have to round out, as we may have
	 * part-pixels all around the edges.
	 */
	left = ROUND_DOWN( r->left, zoom->xfac );
	right = ROUND_UP( ri, zoom->xfac );
	top = ROUND_DOWN( r->top, zoom->yfac );
	bottom = ROUND_UP( bo, zoom->yfac );
	width = right - left;
	height = bottom - top;
	s.left = left / zoom->xfac;
	s.top = top / zoom->yfac;
	s.width = width / zoom->xfac;
	s.height = height / zoom->yfac;
	if( vips_region_prepare( ir, &s ) )
		return( -1 );
	
	/* Find the part of the output (if any) which uses only whole pels.
	 */
	left = ROUND_UP( r->left, zoom->xfac );
	right = ROUND_DOWN( ri, zoom->xfac );
	top = ROUND_UP( r->top, zoom->yfac );
	bottom = ROUND_DOWN( bo, zoom->yfac );
	width = right - left;
	height = bottom - top;

	/* Stage 1: we just paint the whole pels in the centre of the region.
	 * As we know they are not clipped, we can do it quickly.
	 */
	if( width > 0 && height > 0 ) 
		vips_zoom_paint_whole( or, ir, zoom, left, right, top, bottom );

	/* Just fractional pixels left. Paint in the top, left, right and
	 * bottom parts.
	 */
	if( top - r->top > 0 ) 
		/* Some top pixels.
		 */
		vips_zoom_paint_part( or, ir, zoom, 
			r->left, ri, r->top, VIPS_MIN( top, bo ) );
	if( left - r->left > 0 && height > 0 )
		/* Left pixels.
		 */
		vips_zoom_paint_part( or, ir, zoom, 
			r->left, VIPS_MIN( left, ri ), top, bottom );
	if( ri - right > 0 && height > 0 )
		/* Right pixels.
		 */
		vips_zoom_paint_part( or, ir, zoom, 
			VIPS_MAX( right, r->left ), ri, top, bottom );
	if( bo - bottom > 0 && height >= 0 )
		/* Bottom pixels.
		 */
		vips_zoom_paint_part( or, ir, zoom, 
			r->left, ri, VIPS_MAX( bottom, r->top ), bo );

	return( 0 );
}
Exemplo n.º 9
0
static int
vips_resize_build( VipsObject *object )
{
	VipsResample *resample = VIPS_RESAMPLE( object );
	VipsResize *resize = (VipsResize *) object;

	VipsImage **t = (VipsImage **) 
		vips_object_local_array( object, 7 );

	VipsImage *in;
	int window_size;
	int int_shrink;
	int int_shrink_width;
	double residual;
	double sigma;

	if( VIPS_OBJECT_CLASS( vips_resize_parent_class )->build( object ) )
		return( -1 );

	if( !vips_object_argument_isset( object, "interpolate" ) ) {
		VipsInterpolate *interpolate;
		char *nick;

		if( vips_type_find( "VipsInterpolate", "bicubic" ) )
			nick = "bicubic";
		else
			nick = "bilinear";
		interpolate = vips_interpolate_new( nick );
		g_object_set( object, "interpolate", interpolate, NULL ); 
		VIPS_UNREF( interpolate ); 
	}

	in = resample->in;

	window_size = resize->interpolate ? 
		vips_interpolate_get_window_size( resize->interpolate ) : 2;

	/* If the factor is > 1.0, we need to zoom rather than shrink.
	 * Just set the int part to 1 in this case.
	 */
	int_shrink = resize->scale > 1.0 ? 1 : floor( 1.0 / resize->scale );

	/* We want to shrink by less for interpolators with larger windows.
	 */
	int_shrink = VIPS_MAX( 1,
		int_shrink / VIPS_MAX( 1, window_size / 2 ) );

	/* Size after int shrink.
	 */
	int_shrink_width = in->Xsize / int_shrink;

	/* Therefore residual scale factor is.
	 */
	residual = (in->Xsize * resize->scale) / int_shrink_width;

	/* A copy for enlarge resize.
	 */
	if( vips_shrink( in, &t[0], int_shrink, int_shrink, NULL ) )
		return( -1 );
	in = t[0];

	/* We want to make sure we read the image sequentially.
	 * However, the convolution we may be doing later will force us 
	 * into SMALLTILE or maybe FATSTRIP mode and that will break
	 * sequentiality.
	 *
	 * So ... read into a cache where tiles are scanlines, and make sure
	 * we keep enough scanlines to be able to serve a line of tiles.
	 *
	 * We use a threaded tilecache to avoid a deadlock: suppose thread1,
	 * evaluating the top block of the output, is delayed, and thread2, 
	 * evaluating the second block, gets here first (this can happen on 
	 * a heavily-loaded system). 
	 *
	 * With an unthreaded tilecache (as we had before), thread2 will get
	 * the cache lock and start evaling the second block of the shrink. 
	 * When it reaches the png reader it will stall until the first block 
	 * has been used ... but it never will, since thread1 will block on 
	 * this cache lock. 
	 */
	if( int_shrink > 1 ) { 
		int tile_width;
		int tile_height;
		int nlines;

		vips_get_tile_size( in, 
			&tile_width, &tile_height, &nlines );
		if( vips_tilecache( in, &t[6], 
			"tile_width", in->Xsize,
			"tile_height", 10,
			"max_tiles", 1 + (nlines * 2) / 10,
			"access", VIPS_ACCESS_SEQUENTIAL,
			"threaded", TRUE, 
			NULL ) )
			return( -1 );
		in = t[6];
	}

	/* If the final affine will be doing a large downsample, we can get 
	 * nasty aliasing on hard edges. Blur before affine to smooth this out.
	 *
	 * Don't blur for very small shrinks, blur with radius 1 for x1.5
	 * shrinks, blur radius 2 for x2.5 shrinks and above, etc.
	 */
	sigma = ((1.0 / residual) - 0.5) / 1.5;
	if( residual < 1.0 &&
		sigma > 0.1 ) { 
		if( vips_gaussblur( in, &t[2], sigma, NULL ) )
			return( -1 );
		in = t[2];
	}

	if( vips_affine( in, &t[3], residual, 0, 0, residual, 
		"interpolate", resize->interpolate,
		"idx", resize->idx,
		"idy", resize->idy,
		NULL ) )  
		return( -1 );
	in = t[3];

	/* If we are upsampling, don't sharpen.
	 */
	if( int_shrink > 1 ) { 
		t[5] = vips_image_new_matrixv( 3, 3,
			-1.0, -1.0, -1.0,
			-1.0, 32.0, -1.0,
			-1.0, -1.0, -1.0 );
		vips_image_set_double( t[5], "scale", 24 );

		if( vips_conv( in, &t[4], t[5], NULL ) ) 
			return( -1 );
		in = t[4];
	}

	if( vips_image_write( in, resample->out ) )
		return( -1 ); 

	return( 0 );
}
Exemplo n.º 10
0
static int
vips_join_build( VipsObject *object )
{
	VipsConversion *conversion = VIPS_CONVERSION( object );
	VipsJoin *join = (VipsJoin *) object;

	int x, y;
	VipsImage *t;

	if( VIPS_OBJECT_CLASS( vips_join_parent_class )->build( object ) )
		return( -1 );

	switch( join->direction ) {
	case VIPS_DIRECTION_HORIZONTAL:
		x = join->in1->Xsize + join->shim;

		switch( join->align ) {
		case VIPS_ALIGN_LOW:
			y = 0;
			break;

		case VIPS_ALIGN_CENTRE:
			y = join->in1->Ysize / 2 - join->in2->Ysize / 2;
			break;

		case VIPS_ALIGN_HIGH:
			y = join->in1->Ysize - join->in2->Ysize;
			break;

		default:
			g_assert( 0 );

			/* Keep -Wall happy.
			 */
			return( 0 );
		}

		break;

	case VIPS_DIRECTION_VERTICAL:
		y = join->in1->Ysize + join->shim;

		switch( join->align ) {
		case VIPS_ALIGN_LOW:
			x = 0;
			break;

		case VIPS_ALIGN_CENTRE:
			x = join->in1->Xsize / 2 - join->in2->Xsize / 2;
			break;

		case VIPS_ALIGN_HIGH:
			x = join->in1->Xsize - join->in2->Xsize;
			break;

		default:
			g_assert( 0 );

			/* Keep -Wall happy.
			 */
			return( 0 );
		}

		break;

	default:
		g_assert( 0 );

		/* Keep -Wall happy.
		 */
		return( 0 );
	}

	if( vips_insert( join->in1, join->in2, &t, x, y,
		"expand", TRUE,
		"background", join->background,
		NULL ) )
		return( -1 );

	if( !join->expand ) {
		VipsImage *t2;
		int left, top, width, height;

		switch( join->direction ) {
		case VIPS_DIRECTION_HORIZONTAL:
			left = 0;
			top = VIPS_MAX( 0, y ) - y;
			width = t->Xsize;
			height = VIPS_MIN( join->in1->Ysize, join->in2->Ysize );
			break;

		case VIPS_DIRECTION_VERTICAL:
			left = VIPS_MAX( 0, x ) - x;
			top = 0;
			width = VIPS_MIN( join->in1->Xsize, join->in2->Xsize );
			height = t->Ysize; 
			break;

		default:
			g_assert( 0 );

			/* Keep -Wall happy.
			 */
			return( 0 );
		}

		if( vips_extract_area( t, &t2, 
			left, top, width, height, NULL ) ) {
			g_object_unref( t );
			return( -1 );
		}
		g_object_unref( t );

		t = t2;
	}

	if( vips_image_write( t, conversion->out ) ) {
		g_object_unref( t );
		return( -1 );
	}
	g_object_unref( t );

	return( 0 );
}
Exemplo n.º 11
0
/* Entropy-style smartcrop. Repeatedly discard low interest areas. This should
 * be faster for very large images. 
 */
static int
vips_smartcrop_entropy( VipsSmartcrop *smartcrop, 
	VipsImage *in, int *left, int *top )
{
	int max_slice_size;
	int width;
	int height;

	*left = 0;
	*top = 0;
	width = in->Xsize;
	height = in->Ysize;

	/* How much do we trim by each iteration? Aim for 8 steps in the axis
	 * that needs trimming most.
	 */
	max_slice_size = VIPS_MAX( 
		ceil( (width - smartcrop->width) / 8.0 ),
		ceil( (height - smartcrop->height) / 8.0 ) );

	/* Repeatedly take a slice off width and height until we 
	 * reach the target.
	 */
	while( width > smartcrop->width || 
		height > smartcrop->height ) {
		const int slice_width = 
			VIPS_MIN( width - smartcrop->width, max_slice_size );
		const int slice_height = 
			VIPS_MIN( height - smartcrop->height, max_slice_size );

		if( slice_width > 0 ) { 
			double left_score;
			double right_score;

			if( vips_smartcrop_score( smartcrop, in, 
				*left, *top, 
				slice_width, height, &left_score ) )
				return( -1 );

			if( vips_smartcrop_score( smartcrop, in, 
				*left + width - slice_width, *top, 
				slice_width, height, &right_score ) )
				return( -1 ); 

			width -= slice_width;
			if( left_score < right_score ) 
				*left += slice_width;
		}

		if( slice_height > 0 ) { 
			double top_score;
			double bottom_score;

			if( vips_smartcrop_score( smartcrop, in, 
				*left, *top, 
				width, slice_height, &top_score ) )
				return( -1 );

			if( vips_smartcrop_score( smartcrop, in, 
				*left, *top + height - slice_height, 
				width, slice_height, &bottom_score ) )
				return( -1 ); 

			height -= slice_height;
			if( top_score < bottom_score ) 
				*top += slice_height;
		}
	}

	return( 0 );
}
Exemplo n.º 12
0
static int
vips_smartcrop_attention( VipsSmartcrop *smartcrop, 
	VipsImage *in, int *left, int *top )
{
	/* From smartcrop.js.
	 */
	static double skin_vector[] = {-0.78, -0.57, -0.44};
	static double ones[] = {1.0, 1.0, 1.0};

	VipsImage **t = (VipsImage **) 
		vips_object_local_array( VIPS_OBJECT( smartcrop ), 24 );

	double hscale;
	double vscale;
	double sigma;
	double max;
	int x_pos;
	int y_pos;

	/* The size we shrink to gives the precision with which we can place
	 * the crop
	 */
	hscale = 32.0 / in->Xsize;
	vscale = 32.0 / in->Ysize;
	sigma = VIPS_MAX( sqrt( pow( smartcrop->width * hscale, 2 ) +
		pow( smartcrop->height * vscale, 2 ) ) / 10, 1.0 );
	if ( vips_resize( in, &t[17], hscale,
		"vscale", vscale,
		NULL ) )
		return( -1 );

	/* Simple edge detect.
	 */
	if( !(t[21] = vips_image_new_matrixv( 3, 3,
		 0.0, -1.0,  0.0, 
		-1.0,  4.0, -1.0, 
		 0.0, -1.0,  0.0 )) )
		return( -1 );

	/* Convert to XYZ and just use the first three bands.
	 */
	if( vips_colourspace( t[17], &t[0], VIPS_INTERPRETATION_XYZ, NULL ) ||
		vips_extract_band( t[0], &t[1], 0, "n", 3, NULL ) )
		return( -1 );

	/* Edge detect on Y. 
	 */
	if( vips_extract_band( t[1], &t[2], 1, NULL ) ||
		vips_conv( t[2], &t[3], t[21], 
			"precision", VIPS_PRECISION_INTEGER,
			NULL ) ||
		vips_linear1( t[3], &t[4], 5.0, 0.0, NULL ) ||
		vips_abs( t[4], &t[14], NULL ) )
		return( -1 );

	/* Look for skin colours. Taken from smartcrop.js.
	 */
	if( 
		/* Normalise to magnitude of colour in XYZ.
		 */
		pythagoras( smartcrop, t[1], &t[5] ) ||
		vips_divide( t[1], t[5], &t[6], NULL ) ||

		/* Distance from skin point.
		 */
		vips_linear( t[6], &t[7], ones, skin_vector, 3, NULL ) ||
		pythagoras( smartcrop, t[7], &t[8] ) ||

		/* Rescale to 100 - 0 score.
		 */
		vips_linear1( t[8], &t[9], -100.0, 100.0, NULL ) ||

		/* Ignore dark areas.
		 */
		vips_more_const1( t[2], &t[10], 5.0, NULL ) ||
		!(t[11] = vips_image_new_from_image1( t[10], 0.0 )) ||
		vips_ifthenelse( t[10], t[9], t[11], &t[15], NULL ) )
		return( -1 );

	/* Look for saturated areas.
	 */
	if( vips_colourspace( t[1], &t[12], 
		VIPS_INTERPRETATION_LAB, NULL ) ||
		vips_extract_band( t[12], &t[13], 1, NULL ) ||
		vips_ifthenelse( t[10], t[13], t[11], &t[16], NULL ) )
		return( -1 );

	/* Sum, blur and find maxpos.
	 *
	 * The amount of blur is related to the size of the crop
	 * area: how large an area we want to consider for the scoring
	 * function.
	 */

	if( vips_sum( &t[14], &t[18], 3, NULL ) ||
		vips_gaussblur( t[18], &t[19], sigma, NULL ) ||
		vips_max( t[19], &max, "x", &x_pos, "y", &y_pos, NULL ) )
		return( -1 ); 

	/* Centre the crop over the max.
	 */
	*left = VIPS_CLIP( 0, 
		x_pos / hscale - smartcrop->width / 2, 
		in->Xsize - smartcrop->width );
	*top = VIPS_CLIP( 0, 
		y_pos / vscale - smartcrop->height / 2, 
		in->Ysize - smartcrop->height ); 

	return( 0 ); 
}
Exemplo n.º 13
0
/* Build a pyramid. 
 *
 * width/height is the size of this layer, real_* the subsection of the layer
 * which is real pixels (as opposed to background). 
 */
static Layer *
pyramid_build( VipsForeignSaveDz *dz, Layer *above, 
	int width, int height, VipsRect *real_pixels )
{
	VipsForeignSave *save = VIPS_FOREIGN_SAVE( dz );
	Layer *layer = VIPS_NEW( dz, Layer );

	VipsRect strip;
	int limit; 

	layer->dz = dz;
	layer->width = width;
	layer->height = height;

	layer->tiles_across = ROUND_UP( width, dz->tile_size ) / dz->tile_size;
	layer->tiles_down = ROUND_UP( height, dz->tile_size ) / dz->tile_size;

	layer->real_pixels = *real_pixels; 

	layer->image = NULL;
	layer->strip = NULL;
	layer->copy = NULL;

	if( !above )
		/* Top of pyramid.
		 */
		layer->sub = 1;	
	else
		layer->sub = above->sub * 2;

	layer->below = NULL;
	layer->above = above;

	/* We round the image size up to an even number to make x2 shrink
	 * easy.
	 */
	layer->image = vips_image_new();
	if( vips_image_pipelinev( layer->image, 
		VIPS_DEMAND_STYLE_ANY, save->ready, NULL ) ) {
		layer_free( layer );
		return( NULL );
	}
	layer->image->Xsize = width + (width & 1);
	layer->image->Ysize = height + (height & 1);

	layer->strip = vips_region_new( layer->image );
	layer->copy = vips_region_new( layer->image );

	/* The regions will get used in the bg thread callback, so make sure
	 * we don't own them.
	 */
	vips__region_no_ownership( layer->strip );
	vips__region_no_ownership( layer->copy );

	/* Build a line of tiles here. Normally strips are height + 2 *
	 * overlap, but the first row is missing the top edge.
	 *
	 * Expand the strip if necessary to make sure we have an even 
	 * number of lines. 
	 */
	layer->y = 0;
	layer->write_y = 0;
	strip.left = 0;
	strip.top = 0;
	strip.width = layer->image->Xsize;
	strip.height = dz->tile_size + dz->overlap;
	if( (strip.height & 1) == 1 )
		strip.height += 1;
	if( vips_region_buffer( layer->strip, &strip ) ) {
		layer_free( layer );
		return( NULL );
	}

	switch( dz->depth ) {
	case VIPS_FOREIGN_DZ_DEPTH_ONEPIXEL:
		limit = 1;
		break;

	case VIPS_FOREIGN_DZ_DEPTH_ONETILE:
		limit = dz->tile_size;
		break;

	case VIPS_FOREIGN_DZ_DEPTH_ONE:
		limit = VIPS_MAX( width, height );
		break;

	default:
		g_assert( 0 );
		limit = dz->tile_size;
		break;
	}

	if( width > limit || 
		height > limit ) {
		/* Round up, so eg. a 5 pixel wide image becomes 3 a layer
		 * down.
		 *
		 * For the rect, round left/top down, round bottom/right up,
		 * so we get all possible pixels. 
		 */
		VipsRect halfrect;

		halfrect.left = real_pixels->left / 2;
		halfrect.top = real_pixels->top / 2;
		halfrect.width = (VIPS_RECT_RIGHT( real_pixels ) + 1) / 2 - 
			halfrect.left;
		halfrect.height = (VIPS_RECT_BOTTOM( real_pixels ) + 1) / 2 - 
			halfrect.top;

		if( !(layer->below = pyramid_build( dz, layer, 
			(width + 1) / 2, (height + 1) / 2,
			&halfrect )) ) { 
			layer_free( layer );
			return( NULL );
		}
		layer->n = layer->below->n + 1;
	}
	else
		layer->n = 0;

#ifdef DEBUG
	printf( "pyramid_build:\n" );
	printf( "\tn = %d\n", layer->n );
	printf( "\twidth = %d, height = %d\n", width, height );
	printf( "\tXsize = %d, Ysize = %d\n", 
		layer->image->Xsize, layer->image->Ysize );
	printf( "\treal_pixels.left = %d, real_pixels.top = %d\n", 
		real_pixels->left, real_pixels->top ); 
	printf( "\treal_pixels.width = %d, real_pixels.height = %d\n", 
		real_pixels->width, real_pixels->height ); 
#endif

	return( layer );
}