diff --git a/ChangeLog b/ChangeLog index c52f5fa3..4358e66b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -56,6 +56,7 @@ - edvips can change vips header byte ordering - "header" is terse by default - "header" outputs filenames if working on many files +- added openslide support (Benjamin Gilbert) 12/10/11 started 7.26.6 - NOCACHE was not being set correctly on OS X causing performance diff --git a/README b/README index 321cbad3..5952bddd 100644 --- a/README +++ b/README @@ -140,6 +140,10 @@ OpenEXR if available, libvips will directly read (but not write, sadly) OpenEXR images +OpenSlide + if available, libvips can load OpenSlide-supported virtual slide + files: Aperio, Hamamatsu VMS and VMU, MIRAX, and Trestle + swig python python-dev diff --git a/TODO b/TODO index 47af2953..779fdd94 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,3 @@ -- do we need vips_object_unref_outputs() in more places? check all calls to - _build() - - - - - how about vips max add[babe.jpg,babe2.jpg] @@ -15,6 +9,26 @@ +- add ".ndpi" to openslide suffs? though we don't really use suffs for loaders + +- check lazy load hint bug in format-hacking branch + +- everywhere must set dhint in header load + +- 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 + + + + + + + - what should we do? diff --git a/configure.in b/configure.in index d51edd22..a87369f8 100644 --- a/configure.in +++ b/configure.in @@ -438,6 +438,20 @@ if test x"$with_OpenEXR" != "xno"; then ]) fi +# OpenSlide +AC_ARG_WITH([openslide], + AS_HELP_STRING([--without-openslide], [build without OpenSlide (default: test)])) + +if test x"$with_openslide" != "xno"; then + PKG_CHECK_MODULES(OPENSLIDE, openslide >= 3.2.0, + [AC_DEFINE(HAVE_OPENSLIDE,1,[define if you have OpenSlide >= 3.2.0 installed.]) + with_openslide=yes + PACKAGES_USED="$PACKAGES_USED openslide"], + [AC_MSG_WARN([OpenSlide >= 3.2.0 not found; disabling virtual slide support]) + with_openslide=no + ]) +fi + # matio AC_ARG_WITH([matio], AS_HELP_STRING([--without-matio], [build without matio (default: test)])) @@ -598,14 +612,14 @@ fi # Gather all up for VIPS_CFLAGS, VIPS_INCLUDES, VIPS_LIBS and VIPS_CXX_LIBS # sort includes to get longer, more specific dirs first # helps, for example, selecting graphicsmagick over imagemagick -VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $PANGOFT2_CFLAGS $FFTW3_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $OPENEXR_CFLAGS $ORC_CFLAGS +VIPS_CFLAGS=`for i in $VIPS_CFLAGS $GTHREAD_CFLAGS $REQUIRED_CFLAGS $PANGOFT2_CFLAGS $FFTW3_CFLAGS $MAGICK_CFLAGS $PNG_CFLAGS $EXIF_CFLAGS $MATIO_CFLAGS $CFITSIO_CFLAGS $OPENEXR_CFLAGS $OPENSLIDE_CFLAGS $ORC_CFLAGS do echo $i done | sort -ru` VIPS_CFLAGS=`echo $VIPS_CFLAGS` VIPS_CFLAGS="$VIPS_DEBUG_FLAGS $VIPS_CFLAGS" VIPS_INCLUDES="$PNG_INCLUDES $TIFF_INCLUDES $ZIP_INCLUDES $JPEG_INCLUDES $FFTW_INCLUDES $LCMS_INCLUDES" -VIPS_LIBS="$MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $ZIP_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $PANGOFT2_LIBS $FFTW3_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $OPENEXR_LIBS $CFITSIO_LIBS $MATIO_LIBS $EXIF_LIBS -lm" +VIPS_LIBS="$MAGICK_LIBS $PNG_LIBS $TIFF_LIBS $ZIP_LIBS $JPEG_LIBS $GTHREAD_LIBS $REQUIRED_LIBS $PANGOFT2_LIBS $FFTW3_LIBS $FFTW_LIBS $ORC_LIBS $LCMS_LIBS $OPENEXR_LIBS $OPENSLIDE_LIBS $CFITSIO_LIBS $MATIO_LIBS $EXIF_LIBS -lm" # we need this to generate paths in swig/python/setup.py.in AC_SUBST(top_srcdir) @@ -682,6 +696,7 @@ accelerate loops with orc: $with_orc (requires orc-0.4.11 or later) ICC profile support with lcms: $with_lcms (version $with_lcms_ver) file import with OpenEXR: $with_OpenEXR +file import with OpenSlide: $with_openslide file import with matio: $with_matio file import with cfitsio: $with_cfitsio text rendering with pangoft2: $with_pangoft2 diff --git a/libvips/arithmetic/measure.c b/libvips/arithmetic/measure.c index 975e7368..726a5e7b 100644 --- a/libvips/arithmetic/measure.c +++ b/libvips/arithmetic/measure.c @@ -50,8 +50,8 @@ */ /* - */ #define VIPS_DEBUG + */ #ifdef HAVE_CONFIG_H #include @@ -159,7 +159,7 @@ vips_measure_build( VipsObject *object ) * measure on IM_TYPE_LAB images). */ if( dev * 5 > fabs( avg ) && fabs( avg ) > 3 ) - im_warn( "im_measure", + vips_warn( "VipsMeasure", _( "patch %d x %d, band %d: " "avg = %g, sdev = %g" ), i, j, avg, dev ); diff --git a/libvips/arithmetic/stats.c b/libvips/arithmetic/stats.c index eb4357d4..be429d21 100644 --- a/libvips/arithmetic/stats.c +++ b/libvips/arithmetic/stats.c @@ -56,8 +56,8 @@ */ /* - */ #define VIPS_DEBUG + */ #ifdef HAVE_CONFIG_H #include diff --git a/libvips/colour/Makefile.am b/libvips/colour/Makefile.am index 9fe708c6..465fa53b 100644 --- a/libvips/colour/Makefile.am +++ b/libvips/colour/Makefile.am @@ -6,6 +6,7 @@ libcolour_la_SOURCES = \ colour_dispatch.c \ derived.c \ im_icc_transform.c \ + im_argb2rgba.c \ im_LCh2Lab.c \ im_LCh2UCS.c \ im_Lab2LCh.c \ diff --git a/libvips/colour/colour_dispatch.c b/libvips/colour/colour_dispatch.c index 4534bd2d..eb4663ae 100644 --- a/libvips/colour/colour_dispatch.c +++ b/libvips/colour/colour_dispatch.c @@ -640,6 +640,25 @@ static im_function rad2float_desc = { one_in_one_out /* Arg list */ }; +/* Call im_argb2rgba() via arg vector. + */ +static int +argb2rgba_vec( im_object *argv ) +{ + return( im_argb2rgba( argv[0], argv[1] ) ); +} + +/* Description of im_argb2rgba. + */ +static im_function argb2rgba_desc = { + "im_argb2rgba", /* Name */ + "convert pre-multipled argb to png-style rgba", /* Description */ + IM_FN_PIO, /* Flags */ + argb2rgba_vec, /* Dispatch function */ + IM_NUMBER( one_in_one_out ), /* Size of arg list */ + one_in_one_out /* Arg list */ +}; + /* Call im_float2rad() via arg vector. */ static int @@ -1185,6 +1204,7 @@ static im_function *colour_list[] = { &disp2Lab_desc, &disp2XYZ_desc, &float2rad_desc, + &argb2rgba_desc, &icc_ac2rc_desc, &icc_export_depth_desc, &icc_import_desc, diff --git a/libvips/colour/im_argb2rgba.c b/libvips/colour/im_argb2rgba.c new file mode 100644 index 00000000..13d19b2e --- /dev/null +++ b/libvips/colour/im_argb2rgba.c @@ -0,0 +1,110 @@ +/* Convert pre-multipled argb to rgba + * + * 11/12/11 + * - from im_rad2float.c + */ + +/* + + This file is part of VIPS. + + VIPS is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include + +static void +argb2rgba( guint32 *in, PEL *out, int n, void *_bg ) +{ + guint32 bg = GPOINTER_TO_UINT( _bg ); + + int i; + + for( i = 0; i < n; i++ ) { + guint32 x = in[i]; + guint8 a = x >> 24; + + /* Convert from ARGB to RGBA and undo premultiplication. + */ + if( a != 0 ) { + out[0] = 255 * ((x >> 16) & 255) / a; + out[1] = 255 * ((x >> 8) & 255) / a; + out[2] = 255 * (x & 255) / a; + } + else { + /* Use background color. + */ + out[0] = (bg >> 16) & 255; + out[1] = (bg >> 8) & 255; + out[2] = bg & 255; + } + out[3] = a; + + out += 4; + } +} + +/** + * im_argb2rgba: + * @in: input image + * @out: output image + * + * Convert Cairo-style pre-multiplied argb to png-style rgba. Background + * pixels are painted with the metadata item "background-rgb". + * + * See also: im_openslide2vips(). + * + * Returns: 0 on success, -1 on error. + */ +int +im_argb2rgba( IMAGE *in, IMAGE *out ) +{ + guint32 bg; + + /* check for RAD coding + if( in->Coding != IM_CODING_RAD ) { + im_error( "im_rad2float", "%s", _( "not a RAD image" ) ); + return( -1 ); + } + */ + + if( im_cp_desc( out, in ) ) + return( -1 ); + out->Coding = IM_CODING_NONE; + + if( im_meta_get_int( in, "background-rgb", (int *) &bg ) ) + bg = 0xffffff; + + if( im_wrapone( in, out, + (im_wrapone_fn) argb2rgba, GUINT_TO_POINTER( bg ), NULL ) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/conversion/flip.c b/libvips/conversion/flip.c index b26181cd..15567f9b 100644 --- a/libvips/conversion/flip.c +++ b/libvips/conversion/flip.c @@ -133,7 +133,7 @@ vips_flip_horizontal_gen( VipsRegion *or, void *seq, void *a, void *b, VipsRegion *ir = (VipsRegion *) seq; VipsRect *r = &or->valid; VipsRect in; - char *p, *q; + PEL *p, *q; int x, y, z; int le = r->left; diff --git a/libvips/conversion/im_subsample.c b/libvips/conversion/im_subsample.c index 42c94deb..ecaac347 100644 --- a/libvips/conversion/im_subsample.c +++ b/libvips/conversion/im_subsample.c @@ -85,8 +85,8 @@ line_shrink_gen( REGION *or, void *seq, void *a, void *b ) /* Loop down the region. */ for( y = to; y < bo; y++ ) { - char *q = IM_REGION_ADDR( or, le, y ); - char *p; + PEL *q = IM_REGION_ADDR( or, le, y ); + PEL *p; /* Loop across the region, in owidth sized pieces. */ @@ -149,8 +149,8 @@ point_shrink_gen( REGION *or, void *seq, void *a, void *b ) /* Loop down the region. */ for( y = to; y < bo; y++ ) { - char *q = IM_REGION_ADDR( or, le, y ); - char *p; + PEL *q = IM_REGION_ADDR( or, le, y ); + PEL *p; /* Loop across the region, in owidth sized pieces. */ diff --git a/libvips/foreign/openslide.c b/libvips/foreign/openslide.c new file mode 100644 index 00000000..57966eee --- /dev/null +++ b/libvips/foreign/openslide.c @@ -0,0 +1,377 @@ +/* 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 + * - turn into a set of read fns ready to be called from a class + */ + +/* + + 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 + +#include +#include +#include +#include + +#include +#include + +#include + +#include "openslide.h" + +/* 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; + +int +vips__openslide_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( "vips__openslide_isslide: %s - %d\n", filename, ok ); + + return( ok ); +} + +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 ); +} + +int +vips__openslide_read_header( const char *filename, VipsImage *out, + int layer, char *associated ) +{ + ReadSlide *rslide; + + if( !(rslide = readslide_new( filename, out, layer, associated )) ) + return( -1 ); + + return( 0 ); +} + +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 ); +} + +int +vips__openslide_read_file( const char *filename, VipsImage *out, int layer ) +{ + ReadSlide *rslide; + VipsImage *raw; + + VIPS_DEBUG_MSG( "vips__openslide_read_file: %s %d\n", + filename, layer ); + + /* 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, layer, NULL )) ) + return( -1 ); + + 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 a complete row, plus + * 50%. + */ + if( im_tile_cache( raw, out, + TILE_WIDTH, TILE_HEIGHT, + 1.5 * (1 + raw->Xsize / TILE_WIDTH) ) ) + return( -1 ); + + return( 0 ); +} + +int +vips__openslide_read_associated( const char *filename, VipsImage *out, + const char *associated ) +{ + ReadSlide *rslide; + VipsImage *raw; + uint32_t *buf; + int64_t w, h; + int y; + const char *error; + + VIPS_DEBUG_MSG( "vips__openslide_read_associated: %s %s\n", + filename, associated ); + + /* 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, 0, associated )) ) + return( -1 ); + + 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 ); +} + diff --git a/libvips/foreign/openslide.h b/libvips/foreign/openslide.h new file mode 100644 index 00000000..956890f3 --- /dev/null +++ b/libvips/foreign/openslide.h @@ -0,0 +1,47 @@ +/* common defs for jpeg read/write + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef VIPS_OPENSLIDE_H +#define VIPS_OPENSLIDE_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +int vips__openslide_isslide( const char *filename ); +int vips__openslide_read_header( const char *filename, VipsImage *out ); +int vips__openslide_read_file( const char *filename, VipsImage *out ); +int vips__openslide_read_associated( const char *filename, VipsImage *out, + const char *associated ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*VIPS_OPENSLIDE_H*/ diff --git a/libvips/format/Makefile.am b/libvips/format/Makefile.am index 337dcf61..38e1617f 100644 --- a/libvips/format/Makefile.am +++ b/libvips/format/Makefile.am @@ -22,6 +22,7 @@ libformat_la_SOURCES = \ im_vips2raw.c \ matlab.c \ fits.c \ - radiance.c + radiance.c \ + openslide.c INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/format/format.c b/libvips/format/format.c index 72722232..05e569a3 100644 --- a/libvips/format/format.c +++ b/libvips/format/format.c @@ -57,7 +57,7 @@ * transparently supported by im_open(). * * VIPS comes with VipsFormat for TIFF, JPEG, PNG, Analyze, PPM, OpenEXR, CSV, - * Matlab, Radiance, RAW, VIPS and one that wraps libMagick. + * Matlab, Radiance, RAW, VIPS and ones that wrap libMagick and OpenSlide. */ /** @@ -464,6 +464,10 @@ 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 new file mode 100644 index 00000000..2e6c66a1 --- /dev/null +++ b/libvips/format/openslide.c @@ -0,0 +1,469 @@ +/* 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/histograms_lut/im_histnD.c b/libvips/histograms_lut/im_histnD.c index 4f228364..42728e44 100644 --- a/libvips/histograms_lut/im_histnD.c +++ b/libvips/histograms_lut/im_histnD.c @@ -171,7 +171,7 @@ find_hist( REGION *reg, void *seq, void *a, void *b, gboolean *stop ) /* Accumulate! */ for( y = to; y < bo; y++ ) { - char *line = IM_REGION_ADDR( reg, le, y ); + PEL *line = IM_REGION_ADDR( reg, le, y ); switch( im->BandFmt ) { case IM_BANDFMT_UCHAR: diff --git a/libvips/include/vips/colour.h b/libvips/include/vips/colour.h index 9c15e31b..2bd0e3ae 100644 --- a/libvips/include/vips/colour.h +++ b/libvips/include/vips/colour.h @@ -142,6 +142,7 @@ float im_col_dE00( int im_LCh2Lab( VipsImage *in, VipsImage *out ); int im_LabQ2XYZ( VipsImage *in, VipsImage *out ); int im_rad2float( VipsImage *in, VipsImage *out ); +int im_argb2rgba( VipsImage *in, VipsImage *out ); int im_float2rad( VipsImage *in, VipsImage *out ); int im_LCh2UCS( VipsImage *in, VipsImage *out ); int im_Lab2LCh( VipsImage *in, VipsImage *out ); diff --git a/libvips/include/vips/header.h b/libvips/include/vips/header.h index 14ab47da..002fb4e8 100644 --- a/libvips/include/vips/header.h +++ b/libvips/include/vips/header.h @@ -77,7 +77,7 @@ extern "C" { */ #define VIPS_META_RESOLUTION_UNIT "resolution-unit" -int vips_format_sizeof( VipsBandFormat format ); +guint64 vips_format_sizeof( VipsBandFormat format ); int vips_image_get_width( const VipsImage *image ); int vips_image_get_height( const VipsImage *image ); diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index 88a26a29..428daf26 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -282,7 +282,7 @@ typedef struct _VipsImage { */ char *Hist; /* don't use, see vips_image_get_history() */ char *filename; /* pointer to copy of filename */ - char *data; /* start of image data for WIO */ + PEL *data; /* start of image data for WIO */ int kill; /* set to non-zero to block eval */ /* Everything below this private and only used internally by @@ -292,7 +292,7 @@ typedef struct _VipsImage { char *mode; /* mode string passed to _new() */ VipsImageType dtype; /* descriptor type */ int fd; /* file descriptor */ - char *baseaddr; /* pointer to the start of an mmap file */ + void *baseaddr; /* pointer to the start of an mmap file */ size_t length; /* size of mmap area */ guint32 magic; /* magic from header, endian-ness of image */ @@ -316,8 +316,11 @@ typedef struct _VipsImage { /* Part of mmap() read ... the sizeof() the header we skip from the * file start. Usually VIPS_SIZEOF_HEADER, but can be something else * for binary file read. + * + * gint64 so that we can guarantee to work even on systems with + * strange ideas about large files. */ - int sizeof_header; + guint64 sizeof_header; /* If this is a large disc image, don't map the whole thing, instead * have a set of windows shared between the regions active on the @@ -372,7 +375,6 @@ typedef struct _VipsImage { */ gboolean delete_on_close; char *delete_on_close_filename; - } VipsImage; typedef struct _VipsImageClass { @@ -443,7 +445,7 @@ extern const size_t vips__image_sizeof_bandformat[]; (X), (Y), \ 0, 0, \ (I)->Xsize, \ - (I)->Ysize ), abort(), (char *) NULL) \ + (I)->Ysize ), abort(), (PEL *) NULL) \ ) #else /*!VIPS_DEBUG*/ #define VIPS_IMAGE_ADDR( I, X, Y ) \ @@ -468,7 +470,7 @@ VipsImage *vips_image_new( void ); VipsImage *vips_image_new_mode( const char *filename, const char *mode ); VipsImage *vips_image_new_from_file( const char *filename ); VipsImage *vips_image_new_from_file_raw( const char *filename, - int xsize, int ysize, int bands, int offset ); + int xsize, int ysize, int bands, guint64 offset ); VipsImage *vips_image_new_from_memory( void *buffer, int xsize, int ysize, int bands, VipsBandFormat bandfmt ); VipsImage *vips_image_new_array( int xsize, int ysize ); diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index 37a97993..cea14bc5 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -168,6 +168,19 @@ extern int _vips__argument_id; pspec, (FLAGS), (PRIORITY), (OFFSET) ); \ } +#define VIPS_ARG_UINT64( CLASS, NAME, PRIORITY, LONG, DESC, \ + FLAGS, OFFSET, MIN, MAX, VALUE ) { \ + GParamSpec *pspec; \ + \ + pspec = g_param_spec_uint64( (NAME), (LONG), (DESC), \ + (MIN), (MAX), (VALUE), \ + G_PARAM_READWRITE );\ + g_object_class_install_property( G_OBJECT_CLASS( CLASS ), \ + _vips__argument_id++, pspec ); \ + vips_object_class_install_argument( VIPS_OBJECT_CLASS( CLASS ), \ + pspec, (FLAGS), (PRIORITY), (OFFSET) ); \ +} + #define VIPS_ARG_ENUM( CLASS, NAME, PRIORITY, LONG, DESC, \ FLAGS, OFFSET, TYPE, VALUE ) { \ GParamSpec *pspec; \ diff --git a/libvips/include/vips/private.h b/libvips/include/vips/private.h index d9f2c628..2977cdf1 100644 --- a/libvips/include/vips/private.h +++ b/libvips/include/vips/private.h @@ -66,9 +66,9 @@ typedef struct { int top; /* Area of image we have mapped, in pixels */ int height; - char *data; /* First pixel of line 'top' */ + PEL *data; /* First pixel of line 'top' */ - PEL *baseaddr; /* Base of window */ + void *baseaddr; /* Base of window */ size_t length; /* Size of window */ } VipsWindow; @@ -106,7 +106,7 @@ typedef struct { VipsRect area; /* Area this pixel buffer covers */ gboolean done; /* Calculated and in cache */ VipsBufferCache *cache; - char *buf; /* Private malloc() area */ + PEL *buf; /* Private malloc() area */ size_t bsize; /* Size of private malloc() */ } VipsBuffer; diff --git a/libvips/include/vips/region.h b/libvips/include/vips/region.h index 33580cc2..352be997 100644 --- a/libvips/include/vips/region.h +++ b/libvips/include/vips/region.h @@ -69,7 +69,7 @@ typedef struct _VipsRegion { */ /*< private >*/ RegionType type; /* What kind of attachment */ - char *data; /* Off here to get data */ + PEL *data; /* Off here to get data */ int bpl; /* Bytes-per-line for data */ void *seq; /* Sequence we are using to fill region */ @@ -150,7 +150,7 @@ int vips_region_prepare_many( VipsRegion **reg, VipsRect *r ); (R)->valid.left, \ (R)->valid.top, \ (R)->valid.width, \ - (R)->valid.height ), abort(), (char *) NULL) \ + (R)->valid.height ), abort(), (PEL *) NULL) \ ) #else /*DEBUG*/ #define VIPS_REGION_ADDR( R, X, Y ) \ diff --git a/libvips/iofuncs/header.c b/libvips/iofuncs/header.c index 082dcb7d..ed552334 100644 --- a/libvips/iofuncs/header.c +++ b/libvips/iofuncs/header.c @@ -186,8 +186,11 @@ static HeaderField old_double_field[] = { /* This is used by (eg.) VIPS_IMAGE_SIZEOF_ELEMENT() to calculate object * size. + * + * It needs to be guint64 and not size_t since we use this as the basis for + * image address calcs and they have to be 64-bit, even on 32-bit machines. */ -const size_t vips__image_sizeof_bandformat[] = { +const guint64 vips__image_sizeof_bandformat[] = { sizeof( unsigned char ), /* VIPS_FORMAT_UCHAR */ sizeof( signed char ), /* VIPS_FORMAT_CHAR */ sizeof( unsigned short ), /* VIPS_FORMAT_USHORT */ @@ -202,7 +205,7 @@ const size_t vips__image_sizeof_bandformat[] = { /* Return number of bytes for a band format, or -1 on error. */ -int +guint64 vips_format_sizeof( VipsBandFormat format ) { return( (format < 0 || format > VIPS_FORMAT_DPCOMPLEX) ? diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 064e0cb2..f2371c76 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -566,7 +566,7 @@ vips_image_build( VipsObject *object ) const char *mode = image->mode; guint32 magic; - size_t sizeof_image; + guint64 sizeof_image; VIPS_DEBUG_MSG( "vips_image_build: %p\n", image ); @@ -905,7 +905,7 @@ vips_image_class_init( VipsImageClass *class ) G_STRUCT_OFFSET( VipsImage, dhint ), VIPS_TYPE_DEMAND_STYLE, VIPS_DEMAND_STYLE_SMALLTILE ); - VIPS_ARG_INT( class, "sizeof_header", 16, + VIPS_ARG_UINT64( class, "sizeof_header", 16, _( "Size of header" ), _( "Offset in bytes from start of file" ), VIPS_ARGUMENT_SET_ONCE | VIPS_ARGUMENT_CONSTRUCT, @@ -1395,7 +1395,7 @@ vips_image_new_from_file( const char *filename ) */ VipsImage * vips_image_new_from_file_raw( const char *filename, - int xsize, int ysize, int bands, int offset ) + int xsize, int ysize, int bands, guint64 offset ) { VipsImage *image; diff --git a/libvips/iofuncs/region.c b/libvips/iofuncs/region.c index 2f90572d..28810312 100644 --- a/libvips/iofuncs/region.c +++ b/libvips/iofuncs/region.c @@ -934,8 +934,8 @@ vips_region_copy( VipsRegion *reg, VipsRegion *dest, VipsRect *r, int x, int y ) { int z; int len = VIPS_IMAGE_SIZEOF_PEL( reg->im ) * r->width; - char *p = VIPS_REGION_ADDR( reg, r->left, r->top ); - char *q = VIPS_REGION_ADDR( dest, x, y ); + PEL *p = VIPS_REGION_ADDR( reg, r->left, r->top ); + PEL *q = VIPS_REGION_ADDR( dest, x, y ); int plsk = VIPS_REGION_LSKIP( reg ); int qlsk = VIPS_REGION_LSKIP( dest ); @@ -1092,7 +1092,7 @@ vips_region_prepare_to_generate( VipsRegion *reg, VipsRegion *dest, VipsRect *r, int x, int y ) { IMAGE *im = reg->im; - char *p; + PEL *p; if( !im->generate_fn ) { vips_error( "vips_region_prepare_to", diff --git a/libvips/iofuncs/window.c b/libvips/iofuncs/window.c index 25ccb47a..8f7bcad1 100644 --- a/libvips/iofuncs/window.c +++ b/libvips/iofuncs/window.c @@ -245,7 +245,7 @@ vips_window_set( VipsWindow *window, int top, int height ) window->baseaddr = baseaddr; window->length = pagelength; - window->data = (char *) baseaddr + (start - pagestart); + window->data = (PEL *) baseaddr + (start - pagestart); window->top = top; window->height = height;