diff --git a/TODO b/TODO index 48d8211f..27e0d9fa 100644 --- a/TODO +++ b/TODO @@ -1,27 +1,6 @@ -- still some size_t that should be guint64, search for all use of - VIPS_IMAGE_SIZEOF_IMAGE(), vips__parse_size() -- how about - - vips max add[babe.jpg,babe2.jpg] - - does that make any sense? - - vips copy add[babe.jpg,add[babe2.jpg,babe3.jpg]] sum.v - - - - -- openslide sets a g_log() handler, argh, must just set temp - -- make an argb coding type, add to nip2 and known coding - -- "background-rgb" should be a macro - -- nip2 should use zooming support, if possible - @@ -47,11 +26,6 @@ -- see vips_image_cache(): it currently writes to @out allocated by its caller - - should this be called vips_image_write_cache()? or wrapped in the style - of vips_copy()? - @@ -68,6 +42,26 @@ +- how about + + vips max add[babe.jpg,babe2.jpg] + + does that make any sense? + + vips copy add[babe.jpg,add[babe2.jpg,babe3.jpg]] sum.v + + + + +- see vips_image_cache(): it currently writes to @out allocated by its caller + + should this be called vips_image_write_cache()? or wrapped in the style + of vips_copy()? + +- make an argb coding type, add to nip2 and known coding + +- nip2 should use zooming support, if possible + - transform_g_string_array_image() can't handle quoted strings, so filenames with spaces will break diff --git a/libvips/colour/im_argb2rgba.c b/libvips/colour/im_argb2rgba.c index 13d19b2e..aa244580 100644 --- a/libvips/colour/im_argb2rgba.c +++ b/libvips/colour/im_argb2rgba.c @@ -84,7 +84,7 @@ argb2rgba( guint32 *in, PEL *out, int n, void *_bg ) * Returns: 0 on success, -1 on error. */ int -im_argb2rgba( IMAGE *in, IMAGE *out ) +im_argb2rgba( VipsImage *in, IMAGE *out ) { guint32 bg; @@ -99,7 +99,7 @@ im_argb2rgba( IMAGE *in, IMAGE *out ) return( -1 ); out->Coding = IM_CODING_NONE; - if( im_meta_get_int( in, "background-rgb", (int *) &bg ) ) + if( vips_image_get_int( in, VIPS_META_BACKGROUND_RGB, (int *) &bg ) ) bg = 0xffffff; if( im_wrapone( in, out, diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index d079e8b4..0fc8c534 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -59,7 +59,8 @@ * transparently supported by vips_image_new_from_foreign() and friends. * * VIPS comes with VipsForeign for TIFF, JPEG, PNG, Analyze, PPM, OpenEXR, CSV, - * Matlab, Radiance, RAW, VIPS and one that wraps libMagick. + * Matlab, Radiance, RAW and VIPS. It also includes import filters which can + * load with libMagick and with OpenSlide. */ /** diff --git a/libvips/foreign/openslide2vips.c b/libvips/foreign/openslide2vips.c index 04ef3aa4..9d18bb8b 100644 --- a/libvips/foreign/openslide2vips.c +++ b/libvips/foreign/openslide2vips.c @@ -189,9 +189,10 @@ readslide_new( const char *filename, VipsImage *out, OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR ); if( background != NULL ) vips_image_set_int( out, - "background-rgb", strtoul( background, NULL, 16 ) ); + VIPS_META_BACKGROUND_RGB, + strtoul( background, NULL, 16 ) ); else - vips_image_set_int( out, "background-rgb", 0xffffff ); + vips_image_set_int( out, VIPS_META_BACKGROUND_RGB, 0xffffff ); if( w < 0 || h < 0 || rslide->downsample < 0 ) { vips_error( "openslide2vips", _( "getting dimensions: %s" ), diff --git a/libvips/foreign/openslideload.c b/libvips/foreign/openslideload.c index 7ad03ade..66ec0de5 100644 --- a/libvips/foreign/openslideload.c +++ b/libvips/foreign/openslideload.c @@ -116,6 +116,13 @@ vips_foreign_load_openslide_load( VipsForeignLoad *load ) return( 0 ); } +static void +vips_foreign_load_openslide_error_handler( const char *domain, + GLogLevelFlags level, const char *message, void *data ) +{ + vips_error( "openslide", "%s", message ); +} + static void vips_foreign_load_openslide_class_init( VipsForeignLoadOpenslideClass *class ) { @@ -161,6 +168,12 @@ vips_foreign_load_openslide_class_init( VipsForeignLoadOpenslideClass *class ) VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignLoadOpenslide, associated ), NULL ); + + /* Catch just openslide errors. + */ + g_log_set_handler( "Openslide", + G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, + vips_foreign_load_openslide_error_handler, NULL ); } static void @@ -189,10 +202,13 @@ vips_foreign_load_openslide_init( VipsForeignLoadOpenslide *openslide ) * In addition to the slide image itself, virtual slide formats sometimes * include additional images, such as a scan of the slide's barcode. * OpenSlide calls these "associated images". To read an associated image, - * set @associated to the image's. + * set @associated to the image's name. * A slide's associated images are listed in the * "slide-associated-images" metadata item. * + * The output of this operator is in pre-multipled ARGB format. Use + * im_argb2rgba() to decode to png-style RGBA. + * * See also: vips_image_new_from_file(). * * Returns: 0 on success, -1 on error. diff --git a/libvips/format/Makefile.am b/libvips/format/Makefile.am index 38e1617f..337dcf61 100644 --- a/libvips/format/Makefile.am +++ b/libvips/format/Makefile.am @@ -22,7 +22,6 @@ libformat_la_SOURCES = \ im_vips2raw.c \ matlab.c \ fits.c \ - radiance.c \ - openslide.c + radiance.c INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/format/format.c b/libvips/format/format.c index 05e569a3..390b0802 100644 --- a/libvips/format/format.c +++ b/libvips/format/format.c @@ -464,10 +464,6 @@ im__format_init( void ) extern GType vips_format_exr_get_type(); vips_format_exr_get_type(); #endif /*HAVE_OPENEXR*/ -#ifdef HAVE_OPENSLIDE - extern GType vips_format_openslide_get_type(); - vips_format_openslide_get_type(); -#endif /*HAVE_OPENSLIDE*/ #ifdef HAVE_MATIO extern GType vips_format_mat_get_type(); vips_format_mat_get_type(); diff --git a/libvips/format/openslide.c b/libvips/format/openslide.c deleted file mode 100644 index 2e6c66a1..00000000 --- a/libvips/format/openslide.c +++ /dev/null @@ -1,469 +0,0 @@ -/* Read a virtual microscope slide using OpenSlide. - * - * Benjamin Gilbert - * - * Copyright (c) 2011 Carnegie Mellon University - * - * 26/11/11 - * - initial version - * 27/11/11 - * - fix black background in transparent areas - * - no need to set *stop on fill_region() error return - * - add OpenSlide properties to image metadata - * - consolidate setup into one function - * - support reading arbitrary layers - * - use VIPS_ARRAY() - * - add helper to copy a line of pixels - * - support reading associated images - * 7/12/11 - * - redirect OpenSlide error logging to vips_error() - * 8/12/11 - * - add more exposition to documentation - * 9/12/11 - * - unpack to a tile cache - * 11/12/11 - * - move argb->rgba into conversion - */ - -/* - - 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 - - */ - -/* -#define VIPS_DEBUG - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#ifdef HAVE_OPENSLIDE - -#include -#include -#include -#include - -#include -#include - -#include - -/* We run our own tile cache. The OpenSlide one can't always keep enough for a - * complete lines of pixels. - */ -#define TILE_WIDTH (256) -#define TILE_HEIGHT (256) - -typedef struct { - openslide_t *osr; - const char *associated; - - /* Only valid if associated == NULL. - */ - int32_t layer; - double downsample; -} ReadSlide; - -static void -readslide_destroy_cb( VipsImage *image, ReadSlide *rslide ) -{ - VIPS_FREEF( openslide_close, rslide->osr ); -} - -static int -check_associated_image( openslide_t *osr, const char *name ) -{ - const char * const *associated; - - for( associated = openslide_get_associated_image_names( osr ); - *associated != NULL; associated++ ) - if( strcmp( *associated, name ) == 0 ) - return( 0 ); - - vips_error( "im_openslide2vips", - "%s", _( "invalid associated image name" ) ); - - return( -1 ); -} - -static ReadSlide * -readslide_new( const char *filename, VipsImage *out ) -{ - ReadSlide *rslide; - char name[FILENAME_MAX]; - char mode[FILENAME_MAX]; - char *endp; - int64_t w, h; - const char *background; - const char * const *properties; - char *associated; - - rslide = VIPS_NEW( out, ReadSlide ); - memset( rslide, 0, sizeof( *rslide ) ); - g_signal_connect( out, "close", G_CALLBACK( readslide_destroy_cb ), - rslide ); - - vips_filename_split( filename, name, mode ); - rslide->osr = openslide_open( name ); - if( rslide->osr == NULL ) { - vips_error( "im_openslide2vips", - "%s", _( "failure opening slide" ) ); - return( NULL ); - } - - /* Parse optional mode. - */ - rslide->layer = strtol( mode, &endp, 10 ); - if( *mode != 0 && *endp == 0 ) { - /* Mode specifies slide layer. - */ - if( rslide->layer < 0 || rslide->layer >= - openslide_get_layer_count( rslide->osr ) ) { - vips_error( "im_openslide2vips", - "%s", _( "invalid slide layer" ) ); - return( NULL ); - } - } - else if( *mode != 0 ) { - /* Mode specifies associated image. - */ - if ( check_associated_image( rslide->osr, mode ) ) - return( NULL ); - rslide->associated = vips_strdup( VIPS_OBJECT( out ), mode ); - } - - if( rslide->associated ) { - openslide_get_associated_image_dimensions( rslide->osr, - rslide->associated, &w, &h ); - vips_image_set_string( out, "slide-associated-image", - rslide->associated ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); - } - else { - openslide_get_layer_dimensions( rslide->osr, rslide->layer, - &w, &h ); - rslide->downsample = openslide_get_layer_downsample( - rslide->osr, rslide->layer ); - vips_image_set_int( out, "slide-layer", rslide->layer ); - vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); - } - - /* This tag is used by argb2rgba() to paint fully-transparent pixels. - */ - background = openslide_get_property_value( rslide->osr, - OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR ); - if( background != NULL ) - im_meta_set_int( out, - "background-rgb", strtoul( background, NULL, 16 ) ); - else - im_meta_set_int( out, "background-rgb", 0xffffff ); - - if( w < 0 || h < 0 || rslide->downsample < 0 ) { - vips_error( "im_openslide2vips", _( "getting dimensions: %s" ), - openslide_get_error( rslide->osr ) ); - return( NULL ); - } - if( w > INT_MAX || - h > INT_MAX ) { - vips_error( "im_openslide2vips", - "%s", _( "image dimensions overflow int" ) ); - return( NULL ); - } - - vips_image_init_fields( out, (int) w, (int) 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 = g_strjoinv( ", ", (char **) - openslide_get_associated_image_names( rslide->osr ) ); - vips_image_set_string( out, "slide-associated-images", associated ); - g_free( associated ); - - return( rslide ); -} - -static int -fill_region( VipsRegion *out, void *seq, void *_rslide, void *unused, - gboolean *stop ) -{ - ReadSlide *rslide = _rslide; - VipsRect *r = &out->valid; - - const char *error; - int x, y; - - VIPS_DEBUG_MSG( "fill_region: %dx%d @ %dx%d\n", - r->width, r->height, r->left, r->top ); - - /* Fill in tile-sized chunks. Some versions of OpenSlide can fail for - * very large dimensions. - */ - for( y = 0; y < r->height; y += TILE_HEIGHT ) - for( x = 0; x < r->width; x += TILE_WIDTH ) { - int w = VIPS_MIN( TILE_WIDTH, r->width - x ); - int h = VIPS_MIN( TILE_HEIGHT, r->height - y ); - - openslide_read_region( rslide->osr, - (uint32_t *) VIPS_REGION_ADDR( out, - r->left + x, r->top + y ), - (r->left + x) * rslide->downsample, - (r->top + y) * rslide->downsample, - rslide->layer, - w, h ); - } - - error = openslide_get_error( rslide->osr ); - if( error ) { - vips_error( "im_openslide2vips", - _( "reading region: %s" ), error ); - - return( -1 ); - } - - return( 0 ); -} - -static int -fill_associated( VipsImage *out, ReadSlide *rslide ) -{ - uint32_t *buf; - int64_t w, h; - int y; - const char *error; - - openslide_get_associated_image_dimensions( rslide->osr, - rslide->associated, &w, &h ); - if( w == -1 || - h == -1 ) { - vips_error( "im_openslide2vips", - _( "getting dimensions: %s" ), - openslide_get_error( rslide->osr ) ); - - return( -1 ); - } - - buf = VIPS_ARRAY( out, w * h, uint32_t ); - openslide_read_associated_image( rslide->osr, rslide->associated, buf ); - error = openslide_get_error( rslide->osr ); - if( error ) { - vips_error( "im_openslide2vips", - _( "reading associated image: %s" ), error ); - return( -1 ); - } - - if( vips_image_wio_output( out ) ) - return( -1 ); - for( y = 0; y < h; y++ ) - if( vips_image_write_line( out, y, (PEL *) (buf + y * w) ) ) - return( -1 ); - - return( 0 ); -} - -static int -openslide2vips_header( const char *filename, VipsImage *out ) -{ - ReadSlide *rslide; - - if( !(rslide = readslide_new( filename, out )) ) - return( -1 ); - - return( 0 ); -} - -/** - * im_openslide2vips: - * @filename: file to load - * @out: image to write to - * - * Read a virtual slide supported by the OpenSlide library into a VIPS image. - * OpenSlide supports images in Aperio, Hamamatsu VMS, Hamamatsu VMU, MIRAX, - * and Trestle formats. It also supports generic tiled TIFF images, but - * im_openslide2vips() does not. - * - * To facilitate zooming, virtual slide formats include multiple scaled-down - * versions of the high-resolution image. These are typically called - * "levels", though OpenSlide and im_openslide2vips() call them "layers". - * By default, im_openslide2vips() reads the highest-resolution layer - * (layer 0). To read a different layer, specify the layer number as part - * of the filename (for example, "CMU-1.mrxs:3"). - * - * In addition to the slide image itself, virtual slide formats sometimes - * include additional images, such as a scan of the slide's barcode. - * OpenSlide calls these "associated images". To read an associated image, - * specify the image's name as part of the filename (for example, - * "CMU-1.mrxs:label"). A slide's associated images are listed in the - * "slide-associated-images" metadata item. - * - * See also: #VipsFormat - * - * Returns: 0 on success, -1 on error. - */ -static int -im_openslide2vips( const char *filename, VipsImage *out ) -{ - ReadSlide *rslide; - VipsImage *raw; - - VIPS_DEBUG_MSG( "im_openslide2vips: %s\n", filename ); - - /* Tile cache: keep enough for two complete rows of tiles. - * This lets us do (smallish) area ops, like im_conv(), while - * still only hitting each tile once. - */ - if( !(raw = im_open_local( out, "cache", "p" )) ) - return( -1 ); - - if( !(rslide = readslide_new( filename, raw )) ) - return( -1 ); - - if( rslide->associated ) { - VIPS_DEBUG_MSG( "fill_associated:\n" ); - - if( fill_associated( raw, rslide ) ) - return( -1 ); - } - else { - if( vips_image_pio_output( raw ) || - vips_image_generate( raw, - NULL, fill_region, NULL, rslide, NULL ) ) - return( -1 ); - } - - /* Copy to out, adding a cache. Enough tiles for two complete - * rows. - */ - if( im_tile_cache( raw, out, - TILE_WIDTH, TILE_HEIGHT, - 2 * (1 + raw->Xsize / TILE_WIDTH) ) ) - return( -1 ); - - return( 0 ); -} - -static int -isslide( const char *filename ) -{ - openslide_t *osr; - const char *vendor; - int ok; - - ok = 1; - osr = openslide_open( filename ); - if( osr != NULL ) { - /* If this is a generic tiled TIFF image, decline to support - * it, since im_tiff2vips can do better. - */ - vendor = openslide_get_property_value( osr, - OPENSLIDE_PROPERTY_NAME_VENDOR ); - if( vendor == NULL || - strcmp( vendor, "generic-tiff" ) == 0 ) - ok = 0; - openslide_close( osr ); - } - else - ok = 0; - - VIPS_DEBUG_MSG( "isslide: %s - %d\n", filename, ok ); - - return( ok ); -} - -static VipsFormatFlags -slide_flags( const char *filename ) -{ - char name[FILENAME_MAX]; - char mode[FILENAME_MAX]; - char *endp; - - vips_filename_split( filename, name, mode ); - strtol( mode, &endp, 10 ); - if( *mode == 0 || - *endp == 0 ) - /* Slide layer or no mode specified. - */ - return( VIPS_FORMAT_PARTIAL ); - else - /* Associated image specified. - */ - return( 0 ); -} - -static void -error_handler( const char *domain, GLogLevelFlags level, const char *message, - void *data ) -{ - vips_error( "im_openslide2vips", "%s", message ); -} - -/* openslide format adds no new members. - */ -typedef VipsFormat VipsFormatOpenslide; -typedef VipsFormatClass VipsFormatOpenslideClass; - -static const char *slide_suffs[] = { - ".svs", /* Aperio */ - ".vms", ".vmu", /* Hamamatsu */ - ".mrxs", /* MIRAX */ - ".tif", /* Trestle */ - NULL -}; - -static void -vips_format_openslide_class_init( VipsFormatOpenslideClass *class ) -{ - VipsObjectClass *object_class = (VipsObjectClass *) class; - VipsFormatClass *format_class = (VipsFormatClass *) class; - - object_class->nickname = "openslide"; - object_class->description = _( "OpenSlide-supported" ); - - format_class->is_a = isslide; - format_class->header = openslide2vips_header; - format_class->load = im_openslide2vips; - format_class->get_flags = slide_flags; - format_class->suffs = slide_suffs; - - /* Some TIFF files are virtual slides with odd vendor extensions - * (or outright format violations!). Ensure we look at them before - * im_tiff2vips does. OpenSlide tries very hard to reject files it - * doesn't understand, so this should be safe. - */ - format_class->priority = 100; - - g_log_set_handler( "Openslide", - G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, - error_handler, NULL ); -} - -static void -vips_format_openslide_init( VipsFormatOpenslide *object ) -{ -} - -G_DEFINE_TYPE( VipsFormatOpenslide, vips_format_openslide, VIPS_TYPE_FORMAT ); - -#endif /*HAVE_OPENSLIDE*/ diff --git a/libvips/include/vips/header.h b/libvips/include/vips/header.h index 002fb4e8..f5604327 100644 --- a/libvips/include/vips/header.h +++ b/libvips/include/vips/header.h @@ -77,6 +77,14 @@ extern "C" { */ #define VIPS_META_RESOLUTION_UNIT "resolution-unit" +/** + * VIPS_META_BACKGROUND_RGB: + * + * The OpenSlide load operator uses this to note the colour to use to paint + * transparent pixels in pre-multiplied ARGB format. See im_argb2rgba(). + */ +#define VIPS_META_BACKGROUND_RGB "background-rgb" + guint64 vips_format_sizeof( VipsBandFormat format ); int vips_image_get_width( const VipsImage *image ); diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index ae0fc5c1..57ecb752 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -411,7 +411,10 @@ typedef struct _VipsImageClass { GType vips_image_get_type( void ); -extern const size_t vips__image_sizeof_bandformat[]; +/* Has to be guint64 and not size_t/off_t since we have to be able to address + * huge images on platforms with 32-bit files. + */ +extern const guint64 vips__image_sizeof_bandformat[]; /* Pixel address calculation macros. */