Ejemplo n.º 1
0
static int
vips_sequential_build( VipsObject *object )
{
	VipsConversion *conversion = VIPS_CONVERSION( object );
	VipsSequential *sequential = (VipsSequential *) object;

	VipsImage *t;

	VIPS_DEBUG_MSG( "vips_sequential_build\n" );

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

	if( vips_image_pio_input( sequential->in ) )
		return( -1 );

	if( vips_linecache( sequential->in, &t, 
		"tile_height", sequential->tile_height,
		"access", sequential->access,
		NULL ) )
		return( -1 );

	vips_object_local( object, t ); 

	if( vips_image_pipelinev( conversion->out, 
		VIPS_DEMAND_STYLE_THINSTRIP, t, NULL ) )
		return( -1 );
	if( vips_image_generate( conversion->out,
		vips_start_one, vips_sequential_generate, vips_stop_one, 
		t, sequential ) )
		return( -1 );

	return( 0 );
}
Ejemplo n.º 2
0
static int
vips_worley_build( VipsObject *object )
{
	VipsCreate *create = VIPS_CREATE( object );
	VipsWorley *worley = (VipsWorley *) object;

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

	/* Be careful if width is a multiple of cell_size.
	 */
	worley->cells_across = ROUND_UP( worley->width, worley->cell_size ) / 
		worley->cell_size;
	worley->cells_down = ROUND_UP( worley->height, worley->cell_size ) / 
		worley->cell_size;

	worley->seed = g_random_double() * 0xffffffffu;

	vips_image_init_fields( create->out,
		worley->width, worley->height, 1,
		VIPS_FORMAT_INT, VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W,
		1.0, 1.0 );
	vips_image_pipelinev( create->out,
		VIPS_DEMAND_STYLE_ANY, NULL );
	if( vips_image_generate( create->out,
		vips_worley_start, vips_worley_gen, vips_worley_stop, 
		worley, NULL ) )
		return( -1 );

	return( 0 );
}
Ejemplo n.º 3
0
static void
vips_foreign_load_svg_parse( VipsForeignLoadSvg *svg, VipsImage *out )
{
	RsvgDimensionData dimensions;
	int width;
	int height;
	double scale;
	double res;

	/* Calculate dimensions at default dpi/scale.
	 */
	rsvg_handle_set_dpi( svg->page, 72.0 );
	rsvg_handle_get_dimensions( svg->page, &dimensions );
	width = dimensions.width;
	height = dimensions.height;

	/* Calculate dimensions at required dpi/scale.
	 */
	scale = svg->scale * svg->dpi / 72.0;
	if( scale != 1.0 ) {
		rsvg_handle_set_dpi( svg->page, svg->dpi * svg->scale );
		rsvg_handle_get_dimensions( svg->page, &dimensions );

		if( width == dimensions.width && 
			height == dimensions.height ) {
			/* SVG without width and height always reports the same 
			 * dimensions regardless of dpi. Apply dpi/scale using 
			 * cairo instead.
			 */
			svg->cairo_scale = scale;
			width = width * scale;
			height = height * scale;
		} else {
			/* SVG with width and height reports correctly scaled 
			 * dimensions.
			 */
			width = dimensions.width;
			height = dimensions.height;
		}
	}

	/* We need pixels/mm for vips.
	 */
	res = svg->dpi / 25.4;

	vips_image_init_fields( out, 
		width, height,
		4, VIPS_FORMAT_UCHAR,
		VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, res, res );

	/* We render to a linecache, so fat strips work well.
	 */
        vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );

}
Ejemplo n.º 4
0
static int
read_header( Read *read, VipsImage *out )
{
	vips_image_init_fields( out,
		read->config.input.width, read->config.input.height,
		read->config.input.has_alpha ? 4 : 3,
		VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
		VIPS_INTERPRETATION_sRGB,
		1.0, 1.0 );

	vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );

	return( 0 );
}
Ejemplo n.º 5
0
static Read *
read_new( const char *filename, VipsImage *out )
{
	Read *read;
	int64_t w, h;

	read = VIPS_NEW( out, Read );
	memset( read, 0, sizeof( *read ) );
	g_signal_connect( out, "close", G_CALLBACK( read_destroy_cb ),
		read );

  GDALAllRegister();
  read->hDataset = GDALOpen( filename, GA_ReadOnly );
	if( read->hDataset == NULL ) {
		vips_error( "gdal2vips", 
			"%s", _( "unsupported gdal format" ) );
		return( NULL );
	}

	vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );

	w = GDALGetRasterXSize (read->hDataset);
	h = GDALGetRasterYSize (read->hDataset);

	GDALRasterBandH hBand;
	hBand = GDALGetRasterBand( read->hDataset, 1 );
	if( hBand == NULL ) {
		vips_error( "gdal2vips", 
			"%s", _( "can not open raster band" ) );
		return( NULL );
	}

	GDALGetBlockSize( hBand, &read->tile_width, &read->tile_height );

	read->data_type = GDALGetRasterDataType( hBand );
	// int vipsFormat = VIPS_FORMAT_UCHAR;

	// switch( dataType ) {
	// 	case GDT_Byte: vipsFormat = VIPS_FORMAT_UCHAR; break;
	// 	case GDT_UInt16: vipsFormat = VIPS_FORMAT_USHORT; break;

	// }

	vips_image_init_fields( out, w, h, 3, VIPS_FORMAT_UCHAR,
		VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 );

	return( read );
}
Ejemplo n.º 6
0
/* Walk the pyramid allocating resources. 
 */
static int
pyramid_fill( Write *write )
{
	Layer *layer;

	for( layer = write->layer; layer; layer = layer->below ) {
		VipsRect strip_size;

		layer->image = vips_image_new();
		if( vips_image_pipelinev( layer->image, 
			VIPS_DEMAND_STYLE_ANY, write->im, NULL ) ) 
			return( -1 );
		layer->image->Xsize = layer->width;
		layer->image->Ysize = layer->height;

		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. 
		 *
		 * Expand the strip if necessary to make sure we have an even 
		 * number of lines. 
		 */
		strip_size.left = 0;
		strip_size.top = 0;
		strip_size.width = layer->image->Xsize;
		strip_size.height = write->tileh;
		if( (strip_size.height & 1) == 1 )
			strip_size.height += 1;
		if( vips_region_buffer( layer->strip, &strip_size ) ) 
			return( -1 );

		if( !(layer->tif = 
			vips__tiff_openout( layer->lname, write->bigtiff )) ||
			write_tiff_header( write, layer ) )  
			return( -1 );
	}

	return( 0 );
}
Ejemplo n.º 7
0
static int
vips_point_build( VipsObject *object )
{
	VipsCreate *create = VIPS_CREATE( object );
	VipsPoint *point = VIPS_POINT( object );
	VipsPointClass *class = VIPS_POINT_GET_CLASS( point );
	VipsImage **t = (VipsImage **) vips_object_local_array( object, 4 );

	VipsImage *in;

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

	t[0] = vips_image_new();
	vips_image_init_fields( t[0],
		point->width, point->height, 1,
		VIPS_FORMAT_FLOAT, VIPS_CODING_NONE, class->interpretation,
		1.0, 1.0 );
	vips_image_pipelinev( t[0], 
		VIPS_DEMAND_STYLE_ANY, NULL );
	if( vips_image_generate( t[0], 
		NULL, vips_point_gen, NULL, point, NULL ) )
		return( -1 );
	in = t[0];

	if( point->uchar ) {
		float min = class->min;
		float max = class->max;
		float range = max - min;

		if( vips_linear1( in, &t[2], 
			255.0 / range, -min * 255.0 / range, 
			"uchar", TRUE,
			NULL ) )
			return( -1 );
		in = t[2];

		/* We don't want FOURIER or whatever in this case.
		 */
		in->Type = VIPS_INTERPRETATION_MULTIBAND;
	}
Ejemplo n.º 8
0
static int
vips_gaussnoise_build( VipsObject *object )
{
	VipsCreate *create = VIPS_CREATE( object );
	VipsGaussnoise *gaussnoise = (VipsGaussnoise *) object;

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

	vips_image_init_fields( create->out,
		gaussnoise->width, gaussnoise->height, 1,
		VIPS_FORMAT_FLOAT, VIPS_CODING_NONE,
		VIPS_INTERPRETATION_B_W, 1.0, 1.0 );
	vips_image_pipelinev( create->out, 
		VIPS_DEMAND_STYLE_ANY, NULL );

	if( vips_image_generate( create->out, 
		NULL, vips_gaussnoise_gen, NULL, gaussnoise, NULL ) )
		return( -1 );

	return( 0 );
}
Ejemplo n.º 9
0
static int
vips_byteswap_build( VipsObject *object )
{
	VipsConversion *conversion = VIPS_CONVERSION( object );
	VipsByteswap *byteswap = (VipsByteswap *) object;

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

	if( vips_image_pio_input( byteswap->in ) )
		return( -1 );

	if( vips_image_pipelinev( conversion->out, 
		VIPS_DEMAND_STYLE_THINSTRIP, byteswap->in, NULL ) )
		return( -1 );

	if( vips_image_generate( conversion->out,
		vips_start_one, vips_byteswap_gen, vips_stop_one, 
		byteswap->in, byteswap ) )
		return( -1 );

	return( 0 );
}
Ejemplo n.º 10
0
static void
vips_foreign_load_svg_parse( VipsForeignLoadSvg *svg, 
	VipsImage *out )
{
	RsvgDimensionData dimensions;
	double res;

	rsvg_handle_get_dimensions( svg->page, &dimensions ); 

	/* We need pixels/mm for vips.
	 */
	res = svg->dpi / 25.4;

	vips_image_init_fields( out, 
		dimensions.width * svg->scale, dimensions.height * svg->scale, 
		4, VIPS_FORMAT_UCHAR,
		VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, res, res );

	/* We render to a linecache, so fat strips work well.
	 */
        vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );

}
Ejemplo n.º 11
0
static int
vips_black_build( VipsObject *object )
{
	VipsCreate *create = VIPS_CREATE( object );
	VipsBlack *black = (VipsBlack *) object;

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

	vips_image_init_fields( create->out,
		black->width, black->height, black->bands, 
		VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
                black->bands == 1 ? 
			VIPS_INTERPRETATION_B_W : VIPS_INTERPRETATION_MULTIBAND,
		1.0, 1.0 );
	vips_image_pipelinev( create->out, 
		VIPS_DEMAND_STYLE_ANY, NULL );

	if( vips_image_generate( create->out, 
		NULL, vips_black_gen, NULL, NULL, NULL ) )
		return( -1 );

	return( 0 );
}
Ejemplo n.º 12
0
static int
vips_flip_build( VipsObject *object )
{
	VipsConversion *conversion = VIPS_CONVERSION( object );
	VipsFlip *flip = (VipsFlip *) object;

	VipsGenerateFn generate_fn;

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

	if( vips_image_pio_input( flip->in ) )
		return( -1 );

	if( vips_image_pipelinev( conversion->out, 
		VIPS_DEMAND_STYLE_THINSTRIP, flip->in, NULL ) )
		return( -1 );

	if( flip->direction == VIPS_DIRECTION_HORIZONTAL ) {
		generate_fn = vips_flip_horizontal_gen;
		conversion->out->Xoffset = flip->in->Xsize;
		conversion->out->Yoffset = 0;
	}
	else {
		generate_fn = vips_flip_vertical_gen;
		conversion->out->Xoffset = 0;
		conversion->out->Yoffset = flip->in->Ysize;
	}

	if( vips_image_generate( conversion->out,
		vips_start_one, generate_fn, vips_stop_one, 
		flip->in, flip ) )
		return( -1 );

	return( 0 );
}
Ejemplo n.º 13
0
static int
read_header( Read *read, VipsImage *out )
{
	vips_image_init_fields( out,
		read->width, read->height,
		read->config.input.has_alpha ? 4 : 3,
		VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
		VIPS_INTERPRETATION_sRGB,
		1.0, 1.0 );

	vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );

#ifdef HAVE_LIBWEBPMUX
{
	WebPData bitstream;
	WebPMux *mux;
	int i;

	/* We have to parse the whole file again to get the metadata out.
	 *
	 * Don't make parse failure an error. We don't want to refuse to read
	 * any pixels because of some malformed metadata.
	 */
	bitstream.bytes = read->data;
	bitstream.size = read->length;
	if( !(mux = WebPMuxCreate( &bitstream, 0 )) ) {
		vips_warn( "webp", "%s", _( "unable to read image metadata" ) ); 
		return( 0 ); 
	}

	for( i = 0; i < vips__n_webp_names; i++ ) { 
		const char *vips = vips__webp_names[i].vips;
		const char *webp = vips__webp_names[i].webp;

		WebPData data;

		if( WebPMuxGetChunk( mux, webp, &data ) == WEBP_MUX_OK ) { 
			void *blob;

			if( !(blob = vips_malloc( NULL, data.size )) ) {
				WebPMuxDelete( mux ); 
				return( -1 ); 
			}

			memcpy( blob, data.bytes, data.size );
			vips_image_set_blob( out, vips, 
				(VipsCallbackFn) vips_free, blob, data.size );
		}
	}

	WebPMuxDelete( mux ); 

	/* We may have read some exif ... parse into the header.
	 */
	if( vips__exif_parse( out ) )
		return( -1 ); 
}
#endif /*HAVE_LIBWEBPMUX*/

	return( 0 );
}
Ejemplo n.º 14
0
Archivo: copy.c Proyecto: kjell/libvips
static int
vips_copy_build( VipsObject *object )
{
    VipsConversion *conversion = VIPS_CONVERSION( object );
    VipsCopy *copy = (VipsCopy *) object;

    int i;

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

    if( vips_image_pio_input( copy->in ) )
        return( -1 );

    if( vips_image_pipelinev( conversion->out,
                              VIPS_DEMAND_STYLE_THINSTRIP, copy->in, NULL ) )
        return( -1 );

    /* Use props to adjust header fields.
     */
    for( i = 0; i < VIPS_NUMBER( vips_copy_names ); i++ ) {
        const char *name = vips_copy_names[i];

        GParamSpec *pspec;
        VipsArgumentClass *argument_class;
        VipsArgumentInstance *argument_instance;

        if( vips_object_get_argument( object, name,
                                      &pspec, &argument_class, &argument_instance ) )
            return( -1 );

        if( argument_instance->assigned ) {
            GType type = G_PARAM_SPEC_VALUE_TYPE( pspec );
            GValue value = { 0, };

            g_value_init( &value, type );
            g_object_get_property( G_OBJECT( object ),
                                   name, &value );

#ifdef VIPS_DEBUG
            {
                char *str;

                str = g_strdup_value_contents( &value );
                printf( "vips_copy_build: %s = %s\n", name, str );
                g_free( str );
            }
#endif /* VIPS_DEBUG */

            g_object_set_property( G_OBJECT( conversion->out ),
                                   name, &value );
            g_value_unset( &value );
        }
    }

    if( vips_image_generate( conversion->out,
                             vips_start_one, vips_copy_gen, vips_stop_one,
                             copy->in, copy ) )
        return( -1 );

    return( 0 );
}
Ejemplo n.º 15
0
static int
vips_fits_get_header( VipsFits *fits, VipsImage *out )
{
	int status;
	int bitpix;

	int width, height, bands, format, type;
	int keysexist;
	int i;

	status = 0;

	if( fits_get_img_paramll( fits->fptr, 
		10, &bitpix, &fits->naxis, fits->naxes, &status ) ) {
		vips_fits_error( status );
		return( -1 );
	}

#ifdef VIPS_DEBUG
	VIPS_DEBUG_MSG( "naxis = %d\n", fits->naxis );
	for( i = 0; i < fits->naxis; i++ )
		VIPS_DEBUG_MSG( "%d) %lld\n", i, fits->naxes[i] );
#endif /*VIPS_DEBUG*/

	height = 1;
	bands = 1;
	switch( fits->naxis ) {
	/* If you add more dimensions here, adjust data read below. See also
	 * the definition of MAX_DIMENSIONS above.
	 */
	case 10:
	case 9:
	case 8:
	case 7:
	case 6:
	case 5:
	case 4:
		for( i = fits->naxis; i > 3; i-- )
			if( fits->naxes[i - 1] != 1 ) {
				vips_error( "fits", 
					"%s", _( "dimensions above 3 "
					"must be size 1" ) );
				return( -1 );
			}

	case 3:
		bands = fits->naxes[2];

	case 2:
		height = fits->naxes[1];

	case 1:
		width = fits->naxes[0];
		break;

	default:
		vips_error( "fits", _( "bad number of axis %d" ), fits->naxis );
		return( -1 );
	}

	/* Are we in one-band mode?
	 */
	if( fits->band_select != -1 )
		bands = 1;

	/* Get image format. We want the 'raw' format of the image, our caller
	 * can convert using the meta info if they want.
	 */
	for( i = 0; i < VIPS_NUMBER( fits2vips_formats ); i++ )
		if( fits2vips_formats[i][0] == bitpix )
			break;
	if( i == VIPS_NUMBER( fits2vips_formats ) ) {
		vips_error( "fits", _( "unsupported bitpix %d\n" ),
			bitpix );
		return( -1 );
	}
	format = fits2vips_formats[i][1];
	fits->datatype = fits2vips_formats[i][2];

	if( bands == 1 ) {
		if( format == VIPS_FORMAT_USHORT )
			type = VIPS_INTERPRETATION_GREY16;
		else
			type = VIPS_INTERPRETATION_B_W;
	}
	else if( bands == 3 ) {
		if( format == VIPS_FORMAT_USHORT )
			type = VIPS_INTERPRETATION_RGB16;
		else
			type = VIPS_INTERPRETATION_RGB;
	}
	else
		type = VIPS_INTERPRETATION_MULTIBAND;

	vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );
	vips_image_init_fields( out,
		 width, height, bands,
		 format,
		 VIPS_CODING_NONE, type, 1.0, 1.0 );

	/* Read all keys into meta.
	 */
	if( fits_get_hdrspace( fits->fptr, &keysexist, NULL, &status ) ) {
		vips_fits_error( status );
		return( -1 );
	}

	for( i = 0; i < keysexist; i++ ) {
		char record[81];
		char vipsname[100];

		if( fits_read_record( fits->fptr, i + 1, record, &status ) ) {
			vips_fits_error( status );
			return( -1 );
		}

		VIPS_DEBUG_MSG( "fits2vips: setting meta on vips image:\n" );
		VIPS_DEBUG_MSG( " record == \"%s\"\n", record );

		/* FITS lets keys repeat. For example, HISTORY appears many
		 * times, each time with a fresh line of history attached. We
		 * have to include the key index in the vips name we assign.
		 */

		vips_snprintf( vipsname, 100, "fits-%d", i );
		vips_image_set_string( out, vipsname, record );
	}

	return( 0 );
}
Ejemplo n.º 16
0
/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to
 * do 255-pel.
 */
static int
read_jpeg_header( ReadJpeg *jpeg, VipsImage *out )
{
	struct jpeg_decompress_struct *cinfo = &jpeg->cinfo;

	jpeg_saved_marker_ptr p;
	VipsInterpretation interpretation;
	double xres, yres;

	/* Capture app2 sections here for assembly.
	 */
	void *app2_data[MAX_APP2_SECTIONS] = { 0 };
	size_t app2_data_length[MAX_APP2_SECTIONS] = { 0 };
	size_t data_length;
	int i;

	/* Read JPEG header. libjpeg will set out_color_space sanely for us 
	 * for YUV YCCK etc.
	 */
	jpeg_read_header( cinfo, TRUE );
	cinfo->scale_denom = jpeg->shrink;
	cinfo->scale_num = 1;
	jpeg_calc_output_dimensions( cinfo );

	jpeg->invert_pels = FALSE;
	switch( cinfo->out_color_space ) {
	case JCS_GRAYSCALE:
		interpretation = VIPS_INTERPRETATION_B_W;
		break;

	case JCS_CMYK:
		interpretation = VIPS_INTERPRETATION_CMYK;
		/* Photoshop writes CMYK JPEG inverted :-( Maybe this is a
		 * way to spot photoshop CMYK JPGs.
		 */
		if( cinfo->saw_Adobe_marker ) 
			jpeg->invert_pels = TRUE;
		break;

	case JCS_RGB:
	default:
		interpretation = VIPS_INTERPRETATION_sRGB;
		break;
	}

	/* Get the jfif resolution. exif may overwrite this later.
	 */
	xres = 1.0;
	yres = 1.0;
	if( cinfo->saw_JFIF_marker &&
		cinfo->X_density != 1U && 
		cinfo->Y_density != 1U ) {
#ifdef DEBUG
		printf( "read_jpeg_header: seen jfif _density %d, %d\n",
			cinfo->X_density, cinfo->Y_density );
#endif /*DEBUG*/

		switch( cinfo->density_unit ) {
		case 0:
			/* None. Just set.
			 */
			xres = cinfo->X_density;
			yres = cinfo->Y_density;
			break;

		case 1:
			/* Pixels per inch.
			 */
			xres = cinfo->X_density / 25.4;
			yres = cinfo->Y_density / 25.4;
			break;

		case 2:
			/* Pixels per cm.
			 */
			xres = cinfo->X_density / 10.0;
			yres = cinfo->Y_density / 10.0;
			break;

		default:
			vips_warn( "VipsJpeg", 
				"%s", _( "unknown JFIF resolution unit" ) );
			break;
		}

#ifdef DEBUG
		printf( "read_jpeg_header: seen jfif resolution %g, %g p/mm\n",
			       xres, yres );
#endif /*DEBUG*/
	}

	/* Set VIPS header.
	 */
	vips_image_init_fields( out,
		cinfo->output_width, cinfo->output_height,
		cinfo->output_components,
		VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
		interpretation,
		xres, yres );

	vips_image_pipelinev( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );

	/* Interlaced jpegs need lots of memory to read, so our caller needs
	 * to know.
	 */
	(void) vips_image_set_int( out, "jpeg-multiscan", 
		jpeg_has_multiple_scans( cinfo ) );

	/* Look for EXIF and ICC profile.
	 */
	for( p = cinfo->marker_list; p; p = p->next ) {
#ifdef DEBUG
{
		printf( "read_jpeg_header: seen %d bytes of APP%d\n",
			p->data_length,
			p->marker - JPEG_APP0 );

		for( i = 0; i < 10; i++ ) 
			printf( "\t%d) '%c' (%d)\n", 
				i, p->data[i], p->data[i] );
}
#endif /*DEBUG*/

		switch( p->marker ) {
		case JPEG_APP0 + 1:
			/* Possible EXIF or XMP data.
			 */
			if( p->data_length > 4 &&
				vips_isprefix( "Exif", (char *) p->data ) ) {
				if( parse_exif( out, 
					p->data, p->data_length ) ||
					attach_blob( out, VIPS_META_EXIF_NAME, 
						p->data, p->data_length ) )
				return( -1 );
			}

			if( p->data_length > 4 &&
				vips_isprefix( "http", (char *) p->data ) &&
				attach_blob( out, VIPS_META_XMP_NAME, 
					p->data, p->data_length ) )
				return( -1 );

			break;

		case JPEG_APP0 + 2:
			/* Possible ICC profile.
			 */
			if( p->data_length > 14 &&
				vips_isprefix( "ICC_PROFILE", 
					(char *) p->data ) ) {
				/* cur_marker numbers from 1, according to
				 * spec.
				 */
				int cur_marker = p->data[12] - 1;

				if( cur_marker >= 0 &&
					cur_marker < MAX_APP2_SECTIONS ) {
					app2_data[cur_marker] = p->data + 14;
					app2_data_length[cur_marker] = 
						p->data_length - 14;
				}
			}
			break;

		case JPEG_APP0 + 13:
			/* Possible IPCT data block.
			 */
			if( p->data_length > 5 &&
				vips_isprefix( "Photo", (char *) p->data ) &&
				attach_blob( out, VIPS_META_IPCT_NAME,
					p->data, p->data_length ) )
				return( -1 );
			break;

		default:
			break;
		}
	}

	/* Assemble ICC sections.
	 */
	data_length = 0;
	for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ )
		data_length += app2_data_length[i];
	if( data_length ) {
		unsigned char *data;
		int p;

#ifdef DEBUG
		printf( "read_jpeg_header: assembled %zd byte ICC profile\n",
			data_length );
#endif /*DEBUG*/

		if( !(data = vips_malloc( NULL, data_length )) ) 
			return( -1 );

		p = 0;
		for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) {
			memcpy( data + p, app2_data[i], app2_data_length[i] );
			p += app2_data_length[i];
		}

		vips_image_set_blob( out, VIPS_META_ICC_NAME, 
			(VipsCallbackFn) vips_free, data, data_length );
	}

	return( 0 );
}
Ejemplo n.º 17
0
static int
parse_header( Read *read )
{
	VipsImage *im = read->im;
	Image *image = read->image;

	Image *p;
	int i;

#ifdef DEBUG
	printf( "parse_header: filename = %s\n", read->filename );
	printf( "GetImageChannelDepth(AllChannels) = %zd\n",
		GetImageChannelDepth( image, AllChannels, &image->exception ) );
	printf( "GetImageDepth() = %zd\n",
		GetImageDepth( image, &image->exception ) );
	printf( "image->depth = %zd\n", image->depth );
	printf( "GetImageType() = %d\n",
		GetImageType( image, &image->exception ) );
	printf( "IsGrayImage() = %d\n",
		IsGrayImage( image, &image->exception ) );
	printf( "IsMonochromeImage() = %d\n",
		IsMonochromeImage( image, &image->exception ) );
	printf( "IsOpaqueImage() = %d\n",
		IsOpaqueImage( image, &image->exception ) );
	printf( "image->columns = %zd\n", image->columns ); 
	printf( "image->rows = %zd\n", image->rows ); 
#endif /*DEBUG*/

	im->Xsize = image->columns;
	im->Ysize = image->rows;
	read->frame_height = image->rows;
	if( (im->Bands = get_bands( image )) < 0 )
		return( -1 );

	/* Depth can be 'fractional'. You'd think we should use
	 * GetImageDepth() but that seems unreliable. 16-bit mono DICOM images 
	 * are reported as depth 1, for example.
	 */
	im->BandFmt = -1;
	if( image->depth >= 1 && image->depth <= 8 ) 
		im->BandFmt = VIPS_FORMAT_UCHAR;
	if( image->depth >= 9 && image->depth <= 16 ) 
		im->BandFmt = VIPS_FORMAT_USHORT;
#ifdef UseHDRI
	if( image->depth == 32 )
		im->BandFmt = VIPS_FORMAT_FLOAT;
	if( image->depth == 64 )
		im->BandFmt = VIPS_FORMAT_DOUBLE;
#else /*!UseHDRI*/
	if( image->depth == 32 )
		im->BandFmt = VIPS_FORMAT_UINT;
#endif /*UseHDRI*/

	if( im->BandFmt == -1 ) {
		vips_error( "magick2vips", _( "unsupported bit depth %d" ),
			(int) image->depth );
		return( -1 );
	}

	switch( image->colorspace ) {
	case GRAYColorspace:
		if( im->BandFmt == VIPS_FORMAT_USHORT )
			im->Type = VIPS_INTERPRETATION_GREY16;
		else
			im->Type = VIPS_INTERPRETATION_B_W;
		break;

	case RGBColorspace:
		if( im->BandFmt == VIPS_FORMAT_USHORT )
			im->Type = VIPS_INTERPRETATION_RGB16;
		else
			im->Type = VIPS_INTERPRETATION_RGB;
		break;

	case sRGBColorspace:
		if( im->BandFmt == VIPS_FORMAT_USHORT )
			im->Type = VIPS_INTERPRETATION_RGB16;
		else
			im->Type = VIPS_INTERPRETATION_sRGB;
		break;

	case CMYKColorspace:
		im->Type = VIPS_INTERPRETATION_CMYK;
		break;

	default:
		vips_error( "magick2vips", _( "unsupported colorspace %d" ),
			(int) image->colorspace );
		return( -1 );
	}

	switch( image->units ) {
	case PixelsPerInchResolution:
		im->Xres = image->x_resolution / 25.4;
		im->Yres = image->y_resolution / 25.4;
		break;

	case PixelsPerCentimeterResolution:
		im->Xres = image->x_resolution / 10.0;
		im->Yres = image->y_resolution / 10.0;
		break;

	default:
		im->Xres = 1.0;
		im->Yres = 1.0;
		break;
	}

	/* Other fields.
	 */
	im->Coding = VIPS_CODING_NONE;

	vips_image_pipelinev( im, VIPS_DEMAND_STYLE_SMALLTILE, NULL );

	/* Three ways to loop over attributes / properties :-(
	 */

#ifdef HAVE_RESETIMAGEPROPERTYITERATOR
{
	char *key;

	/* This is the most recent imagemagick API, test for this first.
	 */
	ResetImagePropertyIterator( image );
	while( (key = GetNextImageProperty( image )) ) {
		char name_text[256];
		VipsBuf name = VIPS_BUF_STATIC( name_text );

		vips_buf_appendf( &name, "magick-%s", key );
		vips_image_set_string( im, 
			vips_buf_all( &name ), GetImageProperty( image, key ) );
	}
}
#elif defined(HAVE_RESETIMAGEATTRIBUTEITERATOR)
{
	const ImageAttribute *attr;

	/* magick6.1-ish and later, deprecated in 6.5ish.
	 */
	ResetImageAttributeIterator( image );
	while( (attr = GetNextImageAttribute( image )) ) {
		char name_text[256];
		VipsBuf name = VIPS_BUF_STATIC( name_text );

		vips_buf_appendf( &name, "magick-%s", attr->key );
		vips_image_set_string( im, vips_buf_all( &name ), attr->value );
	}
}
#else
{
	const ImageAttribute *attr;

	/* GraphicsMagick is missing the iterator: we have to loop ourselves.
	 * ->attributes is marked as private in the header, but there's no
	 * getter so we have to access it directly.
	 */
	for( attr = image->attributes; attr; attr = attr->next ) {
		char name_text[256];
		VipsBuf name = VIPS_BUF_STATIC( name_text );

		vips_buf_appendf( &name, "magick-%s", attr->key );
		vips_image_set_string( im, vips_buf_all( &name ), attr->value );
	}
}
#endif 

	/* Do we have a set of equal-sized frames? Append them.

	   	FIXME ... there must be an attribute somewhere from dicom read 
		which says this is a volumetric image

	 */
	read->n_frames = 0;
	for( p = image; p; (p = GetNextImageInList( p )) ) {
		if( p->columns != (unsigned int) im->Xsize ||
			p->rows != (unsigned int) im->Ysize ||
			get_bands( p ) != im->Bands )
			break;

		read->n_frames += 1;
	}
	if( p ) 
		/* Nope ... just do the first image in the list.
		 */
		read->n_frames = 1;

#ifdef DEBUG
	printf( "image has %d frames\n", read->n_frames );
#endif /*DEBUG*/

	/* If all_frames is off, just get the first one.
	 */
	if( !read->all_frames )
		read->n_frames = 1;

	/* Record frame pointers.
	 */
	im->Ysize *= read->n_frames;
	if( !(read->frames = VIPS_ARRAY( NULL, read->n_frames, Image * )) )
		return( -1 );
	p = image;
	for( i = 0; i < read->n_frames; i++ ) {
		read->frames[i] = p;
		p = GetNextImageInList( p );
	}

	return( 0 );
}
Ejemplo n.º 18
0
static ReadSlide *
readslide_new( const char *filename, VipsImage *out, 
	int level, gboolean autocrop, const char *associated )
{
	ReadSlide *rslide;
	int64_t w, h;
	const char *error;
	const char *background;
	const char * const *properties;
	char *associated_names;

	if( level && 
		associated ) {
		vips_error( "openslide2vips",
			"%s", _( "specify only one of level or associated "
			"image" ) );
		return( NULL );
	}

	rslide = VIPS_NEW( out, ReadSlide );
	memset( rslide, 0, sizeof( *rslide ) );
	g_signal_connect( out, "close", G_CALLBACK( readslide_destroy_cb ),
		rslide );

	rslide->level = level;
	rslide->autocrop = autocrop;
	rslide->associated = g_strdup( associated );

	/* Non-crazy defaults, override below if we can.
	 */
	rslide->tile_width = 256;
	rslide->tile_height = 256;

	rslide->osr = openslide_open( filename );
	if( rslide->osr == NULL ) {
		vips_error( "openslide2vips", 
			"%s", _( "unsupported slide format" ) );
		return( NULL );
	}

	error = openslide_get_error( rslide->osr );
	if( error ) {
		vips_error( "openslide2vips",
			_( "opening slide: %s" ), error );
		return( NULL );
	}

	if( level < 0 || 
		level >= openslide_get_level_count( rslide->osr ) ) {
		vips_error( "openslide2vips",
			"%s", _( "invalid slide level" ) );
		return( NULL );
	}

	if( associated &&
		check_associated_image( rslide->osr, associated ) )
		return( NULL );

	if( associated ) {
		openslide_get_associated_image_dimensions( rslide->osr,
			associated, &w, &h );
		vips_image_set_string( out, "slide-associated-image",
			associated );
		vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
	} 
	else {
		char buf[256];
		const char *value;

		openslide_get_level_dimensions( rslide->osr,
			level, &w, &h );
		rslide->downsample = openslide_get_level_downsample(
			rslide->osr, level );
		vips_image_set_int( out, "slide-level", level );
		vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );

		/* Try to get tile width/height. An undocumented, experimental
		 * feature.
		 */
		vips_snprintf( buf, 256, 
			"openslide.level[%d].tile-width", level );
		if( (value = openslide_get_property_value( rslide->osr, buf )) )
			rslide->tile_width = atoi( value );
		vips_snprintf( buf, 256, 
			"openslide.level[%d].tile-height", level );
		if( (value = openslide_get_property_value( rslide->osr, buf )) )
			rslide->tile_height = atoi( value );
		if( value )
			VIPS_DEBUG_MSG( "readslide_new: found tile-size\n" );

		/* Some images have a bounds in the header. Crop to 
		 * that if autocrop is set. 
		 */
		if( rslide->autocrop ) 
			if( !get_bounds( rslide->osr, &rslide->bounds ) )
				rslide->autocrop = FALSE; 
		if( rslide->autocrop ) {
			VipsRect image;

			rslide->bounds.left /= rslide->downsample;
			rslide->bounds.top /= rslide->downsample;
			rslide->bounds.width /= rslide->downsample;
			rslide->bounds.height /= rslide->downsample;

			/* Clip against image size.
			 */
			image.left = 0;
			image.top = 0;
			image.width = w;
			image.height = h;
			vips_rect_intersectrect( &rslide->bounds, &image, 
				&rslide->bounds );

			/* If we've clipped to nothing, ignore bounds.
			 */
			if( vips_rect_isempty( &rslide->bounds ) )
				rslide->autocrop = FALSE;
		}
		if( rslide->autocrop ) {
			w = rslide->bounds.width;
			h = rslide->bounds.height;
		}
	}

	rslide->bg = 0xffffff;
	if( (background = openslide_get_property_value( rslide->osr,
		OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR )) )
		rslide->bg = strtoul( background, NULL, 16 );

	if( w <= 0 || 
		h <= 0 || 
		rslide->downsample < 0 ) {
		vips_error( "openslide2vips", _( "getting dimensions: %s" ),
			openslide_get_error( rslide->osr ) );
		return( NULL );
	}
	if( w > INT_MAX || 
		h > INT_MAX ) {
		vips_error( "openslide2vips",
			"%s", _( "image dimensions overflow int" ) );
		return( NULL );
	}

	if( !rslide->autocrop ) {
		rslide->bounds.left = 0;
		rslide->bounds.top = 0;
		rslide->bounds.width = w;
		rslide->bounds.height = h;
	}

	vips_image_init_fields( out, w, h, 4, VIPS_FORMAT_UCHAR,
		VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 );

	for( properties = openslide_get_property_names( rslide->osr );
		*properties != NULL; properties++ )
		vips_image_set_string( out, *properties,
			openslide_get_property_value( rslide->osr,
			*properties ) );

	associated_names = g_strjoinv( ", ", (char **)
		openslide_get_associated_image_names( rslide->osr ) );
	vips_image_set_string( out, 
		"slide-associated-images", associated_names );
	VIPS_FREE( associated_names );

	return( rslide );
}
Ejemplo n.º 19
0
/* Read a png header.
 */
static int
png2vips_header( Read *read, VipsImage *out )
{
	png_uint_32 width, height;
	int bit_depth, color_type;
	int interlace_type;

	png_uint_32 res_x, res_y;
	int unit_type;

	png_charp name;
	int compression_type;

	/* Well thank you, libpng.
	 */
#if PNG_LIBPNG_VER < 10400
	png_charp profile;
#else
	png_bytep profile;
#endif

	png_uint_32 proflen;

	int bands; 
	VipsInterpretation interpretation;
	double Xres, Yres;

	if( setjmp( png_jmpbuf( read->pPng ) ) ) 
		return( -1 );

	png_get_IHDR( read->pPng, read->pInfo, 
		&width, &height, &bit_depth, &color_type,
		&interlace_type, NULL, NULL );

	/* png_get_channels() gives us 1 band for palette images ... so look
	 * at colour_type for output bands.
	 *
	 * Ignore alpha, we detect that separately below.
	 */
	switch( color_type ) {
	case PNG_COLOR_TYPE_PALETTE: 
		bands = 3; 
		break;

	case PNG_COLOR_TYPE_GRAY_ALPHA: 
	case PNG_COLOR_TYPE_GRAY: 
		bands = 1; 
		break;

	case PNG_COLOR_TYPE_RGB: 
	case PNG_COLOR_TYPE_RGB_ALPHA: 
		bands = 3; 
		break;

	default:
		vips_error( "png2vips", "%s", _( "unsupported color type" ) );
		return( -1 );
	}

	if( bit_depth > 8 ) {
		if( bands < 3 )
			interpretation = VIPS_INTERPRETATION_GREY16;
		else
			interpretation = VIPS_INTERPRETATION_RGB16;
	}
	else {
		if( bands < 3 )
			interpretation = VIPS_INTERPRETATION_B_W;
		else
			interpretation = VIPS_INTERPRETATION_sRGB;
	}

	/* Expand palette images.
	 */
	if( color_type == PNG_COLOR_TYPE_PALETTE )
		png_set_palette_to_rgb( read->pPng );

	/* Expand transparency.
	 */
	if( png_get_valid( read->pPng, read->pInfo, PNG_INFO_tRNS ) ) {
		png_set_tRNS_to_alpha( read->pPng );
		bands += 1;
	}
	else if( color_type == PNG_COLOR_TYPE_GRAY_ALPHA || 
		color_type == PNG_COLOR_TYPE_RGB_ALPHA ) {
		/* Some images have no transparency chunk, but still set
		 * color_type to alpha.
		 */
		bands += 1;
	}

	/* Expand <8 bit images to full bytes.
	 */
	if( color_type == PNG_COLOR_TYPE_GRAY &&
		bit_depth < 8 ) 
		png_set_expand_gray_1_2_4_to_8( read->pPng );

	/* If we're an INTEL byte order machine and this is 16bits, we need
	 * to swap bytes.
	 */
	if( bit_depth > 8 && 
		!vips_amiMSBfirst() )
		png_set_swap( read->pPng );

	/* Get resolution. Default to 72 pixels per inch, the usual png value. 
	 */
	unit_type = PNG_RESOLUTION_METER;
	res_x = (72 / 2.54 * 100);
	res_y = (72 / 2.54 * 100);
	png_get_pHYs( read->pPng, read->pInfo, &res_x, &res_y, &unit_type );
	switch( unit_type ) {
	case PNG_RESOLUTION_METER:
		Xres = res_x / 1000.0;
		Yres = res_y / 1000.0;
		break;
	
	default:
		Xres = res_x;
		Yres = res_y;
		break;
	}

	/* Set VIPS header.
	 */
	vips_image_init_fields( out,
		width, height, bands,
		bit_depth > 8 ? 
			VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR,
		VIPS_CODING_NONE, interpretation, 
		Xres, Yres );

	/* Sequential mode needs thinstrip to work with things like
	 * vips_shrink().
	 */
        vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );

	/* Fetch the ICC profile. @name is useless, something like "icc" or
	 * "ICC Profile" etc.  Ignore it.
	 *
	 * @profile was png_charpp in libpngs < 1.5, png_bytepp is the
	 * modern one. Ignore the warning, if any.
	 */
	if( png_get_iCCP( read->pPng, read->pInfo, 
		&name, &compression_type, &profile, &proflen ) ) {
		void *profile_copy;

#ifdef DEBUG
		printf( "png2vips_header: attaching %zd bytes of ICC profile\n",
			proflen );
		printf( "png2vips_header: name = \"%s\"\n", name );
#endif /*DEBUG*/

		if( !(profile_copy = vips_malloc( NULL, proflen )) ) 
			return( -1 );
		memcpy( profile_copy, profile, proflen );
		vips_image_set_blob( out, VIPS_META_ICC_NAME, 
			(VipsCallbackFn) vips_free, profile_copy, proflen );
	}

	/* Sanity-check line size.
	 */
	png_read_update_info( read->pPng, read->pInfo );
	if( png_get_rowbytes( read->pPng, read->pInfo ) != 
		VIPS_IMAGE_SIZEOF_LINE( out ) ) {
		vips_error( "vipspng", 
			"%s", _( "unable to read PNG header" ) );
		return( -1 );
	}

	return( 0 );
}
Ejemplo n.º 20
0
Archivo: csv.c Proyecto: songfj/libvips
static int
read_csv( FILE *fp, VipsImage *out,
          int skip,
          int lines,
          const char *whitespace, const char *separator,
          gboolean read_image )
{
    int i;
    char whitemap[256];
    char sepmap[256];
    const char *p;
    fpos_t pos;
    int columns;
    int ch;
    double d;
    double *buf;
    int y;

    /* Make our char maps.
     */
    for( i = 0; i < 256; i++ ) {
        whitemap[i] = 0;
        sepmap[i] = 0;
    }
    for( p = whitespace; *p; p++ )
        whitemap[(int) *p] = 1;
    for( p = separator; *p; p++ )
        sepmap[(int) *p] = 1;

    /* Skip first few lines.
     */
    for( i = 0; i < skip; i++ )
        if( !skip_line( fp ) ) {
            vips_error( "csv2vips",
                        "%s", _( "end of file while skipping start" ) );
            return( -1 );
        }

    /* Parse the first line to get number of columns. Only bother checking
     * fgetpos() the first time we use it: assume it's working after this.
     */
    if( fgetpos( fp, &pos ) ) {
        vips_error_system( errno, "csv2vips",
                           "%s", _( "unable to seek" ) );
        return( -1 );
    }
    for( columns = 0;
            (ch = read_double( fp, whitemap, sepmap,
                               skip + 1, columns + 1, &d )) == 0;
            columns++ )
        ;
    (void) fsetpos( fp, &pos );

    if( columns == 0 ) {
        vips_error( "csv2vips", "%s", _( "empty line" ) );
        return( -1 );
    }

    /* If lines is -1, we have to scan the whole file to get the
     * number of lines out.
     */
    if( lines == -1 ) {
        (void) fgetpos( fp, &pos );
        for( lines = 0; skip_line( fp ); lines++ )
            ;
        (void) fsetpos( fp, &pos );
    }

    vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
    vips_image_init_fields( out,
                            columns, lines, 1,
                            VIPS_FORMAT_DOUBLE,
                            VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 );

    /* Just reading the header? We are done.
     */
    if( !read_image )
        return( 0 );

    if( !(buf = VIPS_ARRAY( out,
                            VIPS_IMAGE_N_ELEMENTS( out ), double )) )
        return( -1 );

    for( y = 0; y < lines; y++ ) {
        int x;

        for( x = 0; x < columns; x++ ) {
            int lineno = y + skip + 1;
            int colno = x + 1;

            ch = read_double( fp, whitemap, sepmap,
                              lineno, colno, &d );
            if( ch == EOF ) {
                vips_error( "csv2vips",
                            _( "unexpected EOF, line %d col %d" ),
                            lineno, colno );
                return( -1 );
            }
            else if( ch == '\n' ) {
                vips_error( "csv2vips",
                            _( "unexpected EOL, line %d col %d" ),
                            lineno, colno );
                return( -1 );
            }
            else if( ch )
                /* Parse error.
                 */
                return( -1 );

            buf[x] = d;
        }

        if( vips_image_write_line( out, y, (VipsPel *) buf ) )
            return( -1 );

        /* Skip over the '\n' to the next line.
         */
        skip_line( fp );
    }

    return( 0 );
}
Ejemplo n.º 21
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 );
}