diff --git a/TODO b/TODO index 41761e89..f65f2522 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,17 @@ +- remove uses of vips_image_pio_output(), this is done for you by generate + now + +- magickload is broken + + $ vips magickload healthygirl.jpg x.v + VipsRegion: inappropriate region type + VipsRegion: inappropriate region type + VipsImage: file has been truncated + + seems to be a magick bug: ping jpg reports 1 band, load jpg reports 3 band + + + - "header fred.png" does not work, since header uses im_open() which uses VipsForeign diff --git a/configure.in b/configure.in index 26b276b4..2bedc94d 100644 --- a/configure.in +++ b/configure.in @@ -388,6 +388,12 @@ if test x"$with_magick" != "xno"; then LIBS=$save_LIBS fi +if test x"$with_magick" = x"yes"; then + AM_CONDITIONAL(HAVE_MAGICK, true) +else + AM_CONDITIONAL(HAVE_MAGICK, false) +fi + # orc AC_ARG_WITH([orc], AS_HELP_STRING([--without-orc], [build without orc (default: test)])) diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index 7a1f2646..61435dac 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -16,6 +16,18 @@ libforeign_la_SOURCES = \ EXTRA_DIST = +if HAVE_MAGICK +libforeign_la_SOURCES += \ + magick.h \ + magick2vips.c \ + magickload.c +else +EXTRA_DIST += \ + magick.h \ + magick2vips.c \ + magickload.c +endif + if HAVE_OPENEXR libforeign_la_SOURCES += \ openexr2vips.h \ diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 85e7b5e8..9b1f891e 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -528,6 +528,7 @@ static void * vips_foreign_load_start( VipsImage *out, void *a, void *dummy ) { VipsForeignLoad *load = VIPS_FOREIGN_LOAD( a ); + VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( a ); VipsForeignLoadClass *class = VIPS_FOREIGN_LOAD_GET_CLASS( a ); if( !load->real ) { @@ -559,6 +560,23 @@ vips_foreign_load_start( VipsImage *out, void *a, void *dummy ) if( class->load( load ) || vips_image_pio_input( load->real ) ) return( NULL ); + + /* ->header() read the header into @out, load has read the + * image into @real. They must match exactly in size, bands, + * format and coding for the copy to work. + * + * Someversions of ImageMagick give different results between + * Ping and Load for some formats, for example. + */ + if( load->real->Xsize != out->Xsize || + load->real->Ysize != out->Ysize || + load->real->Bands != out->Bands || + load->real->Coding != out->Coding || + load->real->BandFmt != out->BandFmt ) { + vips_error( object_class->nickname, + "%s", _( "header() and load() differ" ) ); + return( NULL ); + } } return( vips_region_new( load->real ) ); @@ -1195,6 +1213,7 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_load_raw_get_type( void ); extern GType vips_foreign_save_raw_get_type( void ); extern GType vips_foreign_save_rawfd_get_type( void ); + extern GType vips_foreign_load_magick_get_type( void ); vips_foreign_load_csv_get_type(); vips_foreign_save_csv_get_type(); @@ -1222,6 +1241,10 @@ vips_foreign_operation_init( void ) vips_foreign_load_openslide_get_type(); #endif /*HAVE_OPENSLIDE*/ +#ifdef HAVE_MAGICK + vips_foreign_load_magick_get_type(); +#endif /*HAVE_MAGICK*/ + #ifdef HAVE_CFITSIO vips_foreign_load_fits_get_type(); vips_foreign_save_fits_get_type(); @@ -1232,6 +1255,39 @@ vips_foreign_operation_init( void ) #endif /*HAVE_OPENEXR*/ } +/** + * vips_magickload: + * @filename: file to load + * @out: decompressed image + * @...: %NULL-terminated list of optional named arguments + * + * Read in an image using libMagick, the ImageMagick library. This library can + * read more than 80 file formats, including SVG, BMP, EPS, DICOM and many + * others. + * The reader can handle any ImageMagick image, including the float and double + * formats. It will work with any quantum size, including HDR. Any metadata + * attached to the libMagick image is copied on to the VIPS image. + * + * The reader should also work with most versions of GraphicsMagick. See the + * "--with-magickpackage" configure option. + * + * See also: vips_image_new_from_file(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_magickload( const char *filename, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "magickload", ap, filename, out ); + va_end( ap ); + + return( result ); +} + /** * vips_tiffload: * @filename: file to load diff --git a/libvips/foreign/magick.h b/libvips/foreign/magick.h new file mode 100644 index 00000000..3fb4ee1e --- /dev/null +++ b/libvips/foreign/magick.h @@ -0,0 +1,44 @@ +/* common defs for libMagick 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_MAGICK_H +#define VIPS_MAGICK_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +int vips__magick_read( const char *filename, VipsImage *out ); +int vips__magick_read_header( const char *filename, VipsImage *out ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*VIPS_MAGICK_H*/ diff --git a/libvips/foreign/magick2vips.c b/libvips/foreign/magick2vips.c new file mode 100644 index 00000000..79a2ca4e --- /dev/null +++ b/libvips/foreign/magick2vips.c @@ -0,0 +1,708 @@ +/* Read a file using libMagick + * + * 7/1/03 JC + * - from im_tiff2vips + * 3/2/03 JC + * - some InitializeMagick() fail with NULL arg + * 2/11/04 + * - im_magick2vips_header() also checks sensible width/height + * 28/10/05 + * - copy attributes to meta + * - write many-frame images as a big column if all frames have identical + * width/height/bands/depth + * 31/3/06 + * - test for magick attr support + * 8/5/06 + * - set RGB16/GREY16 if appropriate + * 10/8/07 + * - support 32/64 bit imagemagick too + * 21/2/08 + * - use MaxRGB if QuantumRange is missing (thanks Bob) + * - look for MAGICKCORE_HDRI_SUPPORT (thanks Marcel) + * - use image->attributes if GetNextImageAttribute() is missing + * 3/3/09 + * - allow funky bit depths, like 14 (thanks Mikkel) + * 17/3/09 + * - reset dcm:display-range to help DICOM read + * 20/4/09 + * - argh libMagick uses 255 == transparent ... we must invert all + * alpha channels + * 12/5/09 + * - fix signed/unsigned warnings + * 23/7/09 + * - SetImageOption() is optional (to help GM) + * 4/2/10 + * - gtkdoc + * 30/4/10 + * - better number of bands detection with GetImageType() + * - use new API stuff, argh + * 17/12/11 + * - 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +/* Turn on debugging output. + */ +#define DEBUG + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#include + +#include + +#include "magick.h" + +/* pre-float Magick used to call this MaxRGB. + */ +#if !defined(QuantumRange) +# define QuantumRange MaxRGB +#endif + +/* And this used to be UseHDRI. + */ +#if MAGICKCORE_HDRI_SUPPORT +# define UseHDRI=1 +#endif + +/* What we track during a read call. + */ +typedef struct _Read { + char *filename; + VipsImage *im; + + Image *image; + ImageInfo *image_info; + ExceptionInfo exception; + + int n_frames; + Image **frames; + int frame_height; + + /* Mutex to serialise calls to libMagick during threaded read. + */ + GMutex *lock; +} Read; + +static int +read_destroy( VipsImage *im, Read *read ) +{ +#ifdef DEBUG + printf( "magick2vips: read_destroy: %s\n", read->filename ); +#endif /*DEBUG*/ + + VIPS_FREE( read->filename ); + VIPS_FREEF( DestroyImage, read->image ); + VIPS_FREEF( DestroyImageInfo, read->image_info ); + VIPS_FREE( read->frames ); + DestroyExceptionInfo( &read->exception ); + VIPS_FREEF( g_mutex_free, read->lock ); + + return( 0 ); +} + +static Read * +read_new( const char *filename, VipsImage *im ) +{ + Read *read; + static int inited = 0; + + if( !inited ) { +#ifdef HAVE_MAGICKCOREGENESIS + MagickCoreGenesis( vips_get_argv0(), MagickFalse ); +#else /*!HAVE_MAGICKCOREGENESIS*/ + InitializeMagick( "" ); +#endif /*HAVE_MAGICKCOREGENESIS*/ + inited = 1; + } + + if( !(read = VIPS_NEW( im, Read )) ) + return( NULL ); + read->filename = g_strdup( filename ); + read->im = im; + read->image = NULL; + read->image_info = CloneImageInfo( NULL ); + GetExceptionInfo( &read->exception ); + read->n_frames = 0; + read->frames = NULL; + read->frame_height = 0; + read->lock = g_mutex_new(); + + g_signal_connect( im, "close", G_CALLBACK( read_destroy ), read ); + + if( !read->image_info ) + return( NULL ); + + vips_strncpy( read->image_info->filename, filename, MaxTextExtent ); + +#ifdef DEBUG + printf( "magick2vips: read_new: %s\n", read->filename ); +#endif /*DEBUG*/ + + return( read ); +} + +static int +get_bands( Image *image ) +{ + int bands; + ImageType type = GetImageType( image, &image->exception ); + + switch( type ) { + case BilevelType: + case GrayscaleType: + bands = 1; + break; + + case GrayscaleMatteType: + /* ImageMagick also has PaletteBilevelMatteType, but GraphicsMagick + * does not. Skip for portability. + */ + bands = 2; + break; + + case PaletteType: + case TrueColorType: + bands = 3; + break; + + case PaletteMatteType: + case TrueColorMatteType: + case ColorSeparationType: + bands = 4; + break; + + case ColorSeparationMatteType: + bands = 5; + break; + + default: + vips_error( "magick2vips", _( "unsupported image type %d" ), + (int) type ); + return( -1 ); + } + + return( bands ); +} + +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(DefaultChannels) = %ld\n", + GetImageChannelDepth( image, DefaultChannels, + &image->exception ) ); + printf( "GetImageChannelDepth(AllChannels) = %ld\n", + GetImageChannelDepth( image, AllChannels, &image->exception ) ); + printf( "GetImageDepth() = %ld\n", + GetImageDepth( image, &image->exception ) ); + printf( "image->depth = %lu\n", image->depth ); + printf( "GetImageQuantumDepth(MagickFalse) = %ld\n", + GetImageQuantumDepth( image, MagickFalse ) ); + 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 ) ); +#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_demand_hint( 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; + + /* 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 ); +} + +/* Divide by this to get 0 - MAX from a Quantum. Eg. consider QuantumRange == + * 65535, MAX == 255 (a Q16 ImageMagic representing an 8-bit image). Make sure + * this can't be zero (if QuantumRange < MAX) .. can happen if we have a Q8 + * ImageMagick trying to represent a 16-bit image. + */ +#define SCALE( MAX ) \ + (QuantumRange < (MAX) ? \ + 1 : \ + ((QuantumRange + 1) / ((MAX) + 1))) + +#define GRAY_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) \ + q[x] = pixels[x].green / SCALE( MAX ); \ +} + +#define GRAYA_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) { \ + q[0] = pixels[x].green / SCALE( MAX ); \ + q[1] = MAX - pixels[x].opacity / SCALE( MAX ); \ + \ + q += 2; \ + } \ +} + +#define RGB_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) { \ + q[0] = pixels[x].red / SCALE( MAX ); \ + q[1] = pixels[x].green / SCALE( MAX ); \ + q[2] = pixels[x].blue / SCALE( MAX ); \ + \ + q += 3; \ + } \ +} + +#define RGBA_LOOP( TYPE, MAX ) { \ + TYPE *q = (TYPE *) q8; \ + \ + for( x = 0; x < n; x++ ) { \ + q[0] = pixels[x].red / SCALE( MAX ); \ + q[1] = pixels[x].green / SCALE( MAX ); \ + q[2] = pixels[x].blue / SCALE( MAX ); \ + q[3] = MAX - pixels[x].opacity / SCALE( MAX ); \ + \ + q += 4; \ + } \ +} + +static void +unpack_pixels( VipsImage *im, PEL *q8, PixelPacket *pixels, int n ) +{ + int x; + + switch( im->Bands ) { + case 1: + /* Gray. + */ + switch( im->BandFmt ) { + case VIPS_FORMAT_UCHAR: + GRAY_LOOP( unsigned char, 255 ); break; + case VIPS_FORMAT_USHORT: + GRAY_LOOP( unsigned short, 65535 ); break; + case VIPS_FORMAT_UINT: + GRAY_LOOP( unsigned int, 4294967295UL ); break; + case VIPS_FORMAT_DOUBLE: + GRAY_LOOP( double, QuantumRange ); break; + + default: + g_assert( 0 ); + } + break; + + case 2: + /* Gray plus alpha. + */ + switch( im->BandFmt ) { + case VIPS_FORMAT_UCHAR: + GRAYA_LOOP( unsigned char, 255 ); break; + case VIPS_FORMAT_USHORT: + GRAYA_LOOP( unsigned short, 65535 ); break; + case VIPS_FORMAT_UINT: + GRAYA_LOOP( unsigned int, 4294967295UL ); break; + case VIPS_FORMAT_DOUBLE: + GRAYA_LOOP( double, QuantumRange ); break; + + default: + g_assert( 0 ); + } + break; + + case 3: + /* RGB. + */ + switch( im->BandFmt ) { + case VIPS_FORMAT_UCHAR: + RGB_LOOP( unsigned char, 255 ); break; + case VIPS_FORMAT_USHORT: + RGB_LOOP( unsigned short, 65535 ); break; + case VIPS_FORMAT_UINT: + RGB_LOOP( unsigned int, 4294967295UL ); break; + case VIPS_FORMAT_DOUBLE: + RGB_LOOP( double, QuantumRange ); break; + + default: + g_assert( 0 ); + } + break; + + case 4: + /* RGBA or CMYK. + */ + switch( im->BandFmt ) { + case VIPS_FORMAT_UCHAR: + RGBA_LOOP( unsigned char, 255 ); break; + case VIPS_FORMAT_USHORT: + RGBA_LOOP( unsigned short, 65535 ); break; + case VIPS_FORMAT_UINT: + RGBA_LOOP( unsigned int, 4294967295UL ); break; + case VIPS_FORMAT_DOUBLE: + RGBA_LOOP( double, QuantumRange ); break; + + default: + g_assert( 0 ); + } + break; + + default: + g_assert( 0 ); + } +} + +static PixelPacket * +get_pixels( Image *image, int left, int top, int width, int height ) +{ + PixelPacket *pixels; + +#ifdef HAVE_GETVIRTUALPIXELS + if( !(pixels = (PixelPacket *) GetVirtualPixels( image, + left, top, width, height, &image->exception )) ) +#else + if( !(pixels = GetImagePixels( image, left, top, width, height )) ) +#endif + return( NULL ); + +/* Can't happen if red/green/blue are doubles. + */ +#ifndef UseHDRI + /* Unpack palette. + */ + if( image->storage_class == PseudoClass ) { +#ifdef HAVE_GETVIRTUALPIXELS + IndexPacket *indexes = (IndexPacket *) + GetVirtualIndexQueue( image ); +#else + IndexPacket *indexes = GetIndexes( image ); +#endif + + int i; + + for( i = 0; i < width * height; i++ ) { + IndexPacket x = indexes[i]; + + if( x < image->colors ) { + pixels[i].red = image->colormap[x].red; + pixels[i].green = image->colormap[x].green; + pixels[i].blue = image->colormap[x].blue; + } + } + } +#endif /*UseHDRI*/ + + return( pixels ); +} + +static int +magick_fill_region( VipsRegion *out, + void *seq, void *a, void *b, gboolean *stop ) +{ + Read *read = (Read *) a; + VipsRect *r = &out->valid; + int y; + + for( y = 0; y < r->height; y++ ) { + int top = r->top + y; + int frame = top / read->frame_height; + int line = top % read->frame_height; + + PixelPacket *pixels; + + g_mutex_lock( read->lock ); + pixels = get_pixels( read->frames[frame], + r->left, line, r->width, 1 ); + g_mutex_unlock( read->lock ); + + if( !pixels ) { + vips_error( "magick2vips", + "%s", _( "unable to read pixels" ) ); + return( -1 ); + } + + unpack_pixels( read->im, VIPS_REGION_ADDR( out, r->left, top ), + pixels, r->width ); + } + + return( 0 ); +} + +int +vips__magick_read( const char *filename, VipsImage *out ) +{ + Read *read; + +#ifdef DEBUG + printf( "magick2vips: vips__magick_read: %s\n", filename ); +#endif /*DEBUG*/ + + if( !(read = read_new( filename, out )) ) + return( -1 ); + +#ifdef HAVE_SETIMAGEOPTION + /* When reading DICOM images, we want to ignore any + * window_center/_width setting, since it may put pixels outside the + * 0-65535 range and lose data. + * + * These window settings are attached as vips metadata, so our caller + * can interpret them if it wants. + */ + SetImageOption( read->image_info, "dcm:display-range", "reset" ); +#endif /*HAVE_SETIMAGEOPTION*/ + + read->image = ReadImage( read->image_info, &read->exception ); + if( !read->image ) { + vips_error( "magick2vips", _( "unable to read file \"%s\"\n" + "libMagick error: %s %s" ), + filename, + read->exception.reason, read->exception.description ); + return( -1 ); + } + + if( parse_header( read ) ) + return( -1 ); + if( vips_image_generate( out, + NULL, magick_fill_region, NULL, read, NULL ) ) + return( -1 ); + + return( 0 ); +} + +int +vips__magick_read_header( const char *filename, VipsImage *im ) +{ + Read *read; + +#ifdef DEBUG + printf( "magick2vips: vips__magick_read_header: %s\n", filename ); +#endif /*DEBUG*/ + + if( !(read = read_new( filename, im )) ) + return( -1 ); + + read->image = PingImage( read->image_info, &read->exception ); + if( !read->image ) { + vips_error( "magick2vips", _( "unable to ping file " + "\"%s\"\nlibMagick error: %s %s" ), + filename, + read->exception.reason, read->exception.description ); + return( -1 ); + } + + if( parse_header( read ) ) + return( -1 ); + + if( im->Xsize <= 0 || im->Ysize <= 0 ) { + vips_error( "magick2vips", "%s", _( "bad image size" ) ); + return( -1 ); + } + + return( 0 ); +} + diff --git a/libvips/foreign/magickload.c b/libvips/foreign/magickload.c new file mode 100644 index 00000000..a053a667 --- /dev/null +++ b/libvips/foreign/magickload.c @@ -0,0 +1,153 @@ +/* load with libMagick + * + * 5/12/11 + * - from openslideload.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 + + */ + +/* +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include "magick.h" + +typedef struct _VipsForeignLoadMagick { + VipsForeignLoad parent_object; + + /* Filename for load. + */ + char *filename; + +} VipsForeignLoadMagick; + +typedef VipsForeignLoadClass VipsForeignLoadMagickClass; + +G_DEFINE_TYPE( VipsForeignLoadMagick, vips_foreign_load_magick, + VIPS_TYPE_FOREIGN_LOAD ); + +static gboolean +ismagick( const char *filename ) +{ + VipsImage *t; + + t = vips_image_new(); + if( vips__magick_read_header( filename, t ) ) { + g_object_unref( t ); + return( FALSE ); + } + g_object_unref( t ); + + return( TRUE ); +} + +static VipsForeignFlags +vips_foreign_load_magick_get_flags_filename( const char *filename ) +{ + return( VIPS_FOREIGN_PARTIAL ); +} + +static VipsForeignFlags +vips_foreign_load_magick_get_flags( VipsForeignLoad *load ) +{ + VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load; + + return( vips_foreign_load_magick_get_flags_filename( + magick->filename ) ); +} + +static int +vips_foreign_load_magick_header( VipsForeignLoad *load ) +{ + VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load; + + if( vips__magick_read_header( magick->filename, load->out ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_foreign_load_magick_load( VipsForeignLoad *load ) +{ + VipsForeignLoadMagick *magick = (VipsForeignLoadMagick *) load; + + if( vips__magick_read( magick->filename, load->real ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_load_magick_class_init( VipsForeignLoadMagickClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "magickload"; + object_class->description = _( "load file with ImageMagick" ); + + /* We need to be well to the back of the queue since the vips's + * dedicated loaders are usually preferable, if possible. + */ + foreign_class->priority = -100; + + load_class->is_a = ismagick; + load_class->get_flags = vips_foreign_load_magick_get_flags; + load_class->header = vips_foreign_load_magick_header; + load_class->load = vips_foreign_load_magick_load; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadMagick, filename ), + NULL ); +} + +static void +vips_foreign_load_magick_init( VipsForeignLoadMagick *magick ) +{ +} diff --git a/libvips/foreign/openslide2vips.c b/libvips/foreign/openslide2vips.c index 9d18bb8b..68df2cfb 100644 --- a/libvips/foreign/openslide2vips.c +++ b/libvips/foreign/openslide2vips.c @@ -295,9 +295,8 @@ vips__openslide_read( const char *filename, VipsImage *out, int layer ) if( !(rslide = readslide_new( filename, raw, layer, NULL )) ) return( -1 ); - if( vips_image_pio_output( raw ) || - vips_image_generate( raw, - NULL, vips__openslide_generate, NULL, rslide, NULL ) ) + if( vips_image_generate( raw, + NULL, vips__openslide_generate, NULL, rslide, NULL ) ) return( -1 ); /* Copy to out, adding a cache. Enough tiles for a complete row, plus diff --git a/libvips/format/im_exr2vips.c b/libvips/format/im_exr2vips.c index e8305c6a..64956cfd 100644 --- a/libvips/format/im_exr2vips.c +++ b/libvips/format/im_exr2vips.c @@ -1,30 +1,7 @@ /* Convert OpenEXR to VIPS * - * 1/5/06 - * - from im_png2vips.c - * 17/5/06 - * - oops, buffer calcs were wrong - * 19/5/06 - * - added tiled read, with a separate cache - * - removed *255 we had before, better to do something clever with - * chromaticities - * 4/2/10 - * - gtkdoc - - TODO - - - colour management - - attributes - - more of OpenEXR's pixel formats - - more than just RGBA channels - - the openexr C API is very limited ... it seems RGBA half pixels is - all you can do - - openexr lets you have different formats in different channels :-( - - there's no API to read the "chromaticities" attribute :-( - + * 17/12/11 + * - just a stub */ /* diff --git a/libvips/format/im_magick2vips.c b/libvips/format/im_magick2vips.c index 0948d524..9206b917 100644 --- a/libvips/format/im_magick2vips.c +++ b/libvips/format/im_magick2vips.c @@ -1,41 +1,7 @@ /* Read a file using libMagick * - * 7/1/03 JC - * - from im_tiff2vips - * 3/2/03 JC - * - some InitializeMagick() fail with NULL arg - * 2/11/04 - * - im_magick2vips_header() also checks sensible width/height - * 28/10/05 - * - copy attributes to meta - * - write many-frame images as a big column if all frames have identical - * width/height/bands/depth - * 31/3/06 - * - test for magick attr support - * 8/5/06 - * - set RGB16/GREY16 if appropriate - * 10/8/07 - * - support 32/64 bit imagemagick too - * 21/2/08 - * - use MaxRGB if QuantumRange is missing (thanks Bob) - * - look for MAGICKCORE_HDRI_SUPPORT (thanks Marcel) - * - use image->attributes if GetNextImageAttribute() is missing - * 3/3/09 - * - allow funky bit depths, like 14 (thanks Mikkel) - * 17/3/09 - * - reset dcm:display-range to help DICOM read - * 20/4/09 - * - argh libMagick uses 255 == transparent ... we must invert all - * alpha channels - * 12/5/09 - * - fix signed/unsigned warnings - * 23/7/09 - * - SetImageOption() is optional (to help GM) - * 4/2/10 - * - gtkdoc - * 30/4/10 - * - better number of bands detection with GetImageType() - * - use new API stuff, argh + * 17/12/11 + * - just a stub */ /* @@ -73,661 +39,20 @@ #endif /*HAVE_CONFIG_H*/ #include -#ifndef HAVE_MAGICK - #include -int -im_magick2vips( const char *filename, IMAGE *im ) -{ - im_error( "im_magick2vips", "%s", - _( "libMagick support disabled" ) ); - return( -1 ); -} - -#else /*HAVE_MAGICK*/ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -/* pre-float Magick used to call this MaxRGB. - */ -#if !defined(QuantumRange) -# define QuantumRange MaxRGB -#endif - -/* And this used to be UseHDRI. - */ -#if MAGICKCORE_HDRI_SUPPORT -# define UseHDRI=1 -#endif - -/* What we track during a read call. - */ -typedef struct _Read { - char *filename; - IMAGE *im; - - Image *image; - ImageInfo *image_info; - ExceptionInfo exception; - - int n_frames; - Image **frames; - int frame_height; - - /* Mutex to serialise calls to libMagick during threaded read. - */ - GMutex *lock; -} Read; - -static int -read_destroy( Read *read ) -{ -#ifdef DEBUG - printf( "im_magick2vips: read_destroy: %s\n", read->filename ); -#endif /*DEBUG*/ - - IM_FREEF( DestroyImage, read->image ); - IM_FREEF( DestroyImageInfo, read->image_info ); - IM_FREE( read->frames ); - IM_FREE( read->filename ); - DestroyExceptionInfo( &read->exception ); - IM_FREEF( g_mutex_free, read->lock ); - im_free( read ); - - return( 0 ); -} - -static Read * -read_new( const char *filename, IMAGE *im ) -{ - Read *read; - static int inited = 0; - - if( !inited ) { -#ifdef HAVE_MAGICKCOREGENESIS - MagickCoreGenesis( im_get_argv0(), MagickFalse ); -#else /*!HAVE_MAGICKCOREGENESIS*/ - InitializeMagick( "" ); -#endif /*HAVE_MAGICKCOREGENESIS*/ - inited = 1; - } - - if( !(read = IM_NEW( NULL, Read )) ) - return( NULL ); - read->filename = im_strdup( NULL, filename ); - read->im = im; - read->image = NULL; - read->image_info = CloneImageInfo( NULL ); - GetExceptionInfo( &read->exception ); - read->n_frames = 0; - read->frames = NULL; - read->frame_height = 0; - read->lock = g_mutex_new(); - - if( im_add_close_callback( im, - (im_callback_fn) read_destroy, read, NULL ) ) { - read_destroy( read ); - return( NULL ); - } - - if( !read->filename || !read->image_info ) - return( NULL ); - - im_strncpy( read->image_info->filename, filename, MaxTextExtent ); - -#ifdef DEBUG - printf( "im_magick2vips: read_new: %s\n", read->filename ); -#endif /*DEBUG*/ - - return( read ); -} - -static int -get_bands( Image *image ) -{ - int bands; - ImageType type = GetImageType( image, &image->exception ); - - switch( type ) { - case BilevelType: - case GrayscaleType: - bands = 1; - break; - - case GrayscaleMatteType: - /* ImageMagick also has PaletteBilevelMatteType, but GraphicsMagick - * does not. Skip for portability. - */ - bands = 2; - break; - - case PaletteType: - case TrueColorType: - bands = 3; - break; - - case PaletteMatteType: - case TrueColorMatteType: - case ColorSeparationType: - bands = 4; - break; - - case ColorSeparationMatteType: - bands = 5; - break; - - default: - im_error( "im_magick2vips", _( "unsupported image type %d" ), - (int) type ); - return( -1 ); - } - - return( bands ); -} - -static int -parse_header( Read *read ) -{ - IMAGE *im = read->im; - Image *image = read->image; - - Image *p; - int i; - - /* Handy for testimng IM/GM - printf( "parse_header: filename = %s\n", read->filename ); - printf( "GetImageChannelDepth(DefaultChannels) = %ld\n", - GetImageChannelDepth( image, DefaultChannels, - &image->exception ) ); - printf( "GetImageChannelDepth(AllChannels) = %ld\n", - GetImageChannelDepth( image, AllChannels, &image->exception ) ); - printf( "GetImageDepth() = %ld\n", - GetImageDepth( image, &image->exception ) ); - printf( "image->depth = %lu\n", image->depth ); - printf( "GetImageQuantumDepth(MagickFalse) = %ld\n", - GetImageQuantumDepth( image, MagickFalse ) ); - 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 ) ); - */ - - 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 = IM_BANDFMT_UCHAR; - if( image->depth >= 9 && image->depth <= 16 ) - im->BandFmt = IM_BANDFMT_USHORT; -#ifdef UseHDRI - if( image->depth == 32 ) - im->BandFmt = IM_BANDFMT_FLOAT; - if( image->depth == 64 ) - im->BandFmt = IM_BANDFMT_DOUBLE; -#else /*!UseHDRI*/ - if( image->depth == 32 ) - im->BandFmt = IM_BANDFMT_UINT; -#endif /*UseHDRI*/ - - if( im->BandFmt == -1 ) { - im_error( "im_magick2vips", _( "unsupported bit depth %d" ), - (int) image->depth ); - return( -1 ); - } - - switch( image->colorspace ) { - case GRAYColorspace: - if( im->BandFmt == IM_BANDFMT_USHORT ) - im->Type = IM_TYPE_GREY16; - else - im->Type = IM_TYPE_B_W; - break; - - case RGBColorspace: - if( im->BandFmt == IM_BANDFMT_USHORT ) - im->Type = IM_TYPE_RGB16; - else - im->Type = IM_TYPE_RGB; - break; - - case sRGBColorspace: - if( im->BandFmt == IM_BANDFMT_USHORT ) - im->Type = IM_TYPE_RGB16; - else - im->Type = IM_TYPE_sRGB; - break; - - case CMYKColorspace: - im->Type = IM_TYPE_CMYK; - break; - - default: - im_error( "im_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 = IM_CODING_NONE; - - /* 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 ); - im_meta_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 ); - im_meta_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 ); - im_meta_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; - - /* Record frame pointers. - */ - im->Ysize *= read->n_frames; - if( !(read->frames = IM_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 ); -} - -/* Divide by this to get 0 - MAX from a Quantum. Eg. consider QuantumRange == - * 65535, MAX == 255 (a Q16 ImageMagic representing an 8-bit image). Make sure - * this can't be zero (if QuantumRange < MAX) .. can happen if we have a Q8 - * ImageMagick trying to represent a 16-bit image. - */ -#define SCALE( MAX ) \ - (QuantumRange < (MAX) ? \ - 1 : \ - ((QuantumRange + 1) / ((MAX) + 1))) - -#define GRAY_LOOP( TYPE, MAX ) { \ - TYPE *q = (TYPE *) q8; \ - \ - for( x = 0; x < n; x++ ) \ - q[x] = pixels[x].green / SCALE( MAX ); \ -} - -#define GRAYA_LOOP( TYPE, MAX ) { \ - TYPE *q = (TYPE *) q8; \ - \ - for( x = 0; x < n; x++ ) { \ - q[0] = pixels[x].green / SCALE( MAX ); \ - q[1] = MAX - pixels[x].opacity / SCALE( MAX ); \ - \ - q += 2; \ - } \ -} - -#define RGB_LOOP( TYPE, MAX ) { \ - TYPE *q = (TYPE *) q8; \ - \ - for( x = 0; x < n; x++ ) { \ - q[0] = pixels[x].red / SCALE( MAX ); \ - q[1] = pixels[x].green / SCALE( MAX ); \ - q[2] = pixels[x].blue / SCALE( MAX ); \ - \ - q += 3; \ - } \ -} - -#define RGBA_LOOP( TYPE, MAX ) { \ - TYPE *q = (TYPE *) q8; \ - \ - for( x = 0; x < n; x++ ) { \ - q[0] = pixels[x].red / SCALE( MAX ); \ - q[1] = pixels[x].green / SCALE( MAX ); \ - q[2] = pixels[x].blue / SCALE( MAX ); \ - q[3] = MAX - pixels[x].opacity / SCALE( MAX ); \ - \ - q += 4; \ - } \ -} - -static void -unpack_pixels( IMAGE *im, PEL *q8, PixelPacket *pixels, int n ) -{ - int x; - - switch( im->Bands ) { - case 1: - /* Gray. - */ - switch( im->BandFmt ) { - case IM_BANDFMT_UCHAR: - GRAY_LOOP( unsigned char, 255 ); break; - case IM_BANDFMT_USHORT: - GRAY_LOOP( unsigned short, 65535 ); break; - case IM_BANDFMT_UINT: - GRAY_LOOP( unsigned int, 4294967295UL ); break; - case IM_BANDFMT_DOUBLE: - GRAY_LOOP( double, QuantumRange ); break; - - default: - assert( 0 ); - } - break; - - case 2: - /* Gray plus alpha. - */ - switch( im->BandFmt ) { - case IM_BANDFMT_UCHAR: - GRAYA_LOOP( unsigned char, 255 ); break; - case IM_BANDFMT_USHORT: - GRAYA_LOOP( unsigned short, 65535 ); break; - case IM_BANDFMT_UINT: - GRAYA_LOOP( unsigned int, 4294967295UL ); break; - case IM_BANDFMT_DOUBLE: - GRAYA_LOOP( double, QuantumRange ); break; - - default: - assert( 0 ); - } - break; - - case 3: - /* RGB. - */ - switch( im->BandFmt ) { - case IM_BANDFMT_UCHAR: - RGB_LOOP( unsigned char, 255 ); break; - case IM_BANDFMT_USHORT: - RGB_LOOP( unsigned short, 65535 ); break; - case IM_BANDFMT_UINT: - RGB_LOOP( unsigned int, 4294967295UL ); break; - case IM_BANDFMT_DOUBLE: - RGB_LOOP( double, QuantumRange ); break; - - default: - assert( 0 ); - } - break; - - case 4: - /* RGBA or CMYK. - */ - switch( im->BandFmt ) { - case IM_BANDFMT_UCHAR: - RGBA_LOOP( unsigned char, 255 ); break; - case IM_BANDFMT_USHORT: - RGBA_LOOP( unsigned short, 65535 ); break; - case IM_BANDFMT_UINT: - RGBA_LOOP( unsigned int, 4294967295UL ); break; - case IM_BANDFMT_DOUBLE: - RGBA_LOOP( double, QuantumRange ); break; - - default: - assert( 0 ); - } - break; - - default: - assert( 0 ); - } -} - -static PixelPacket * -get_pixels( Image *image, int left, int top, int width, int height ) -{ - PixelPacket *pixels; - -#ifdef HAVE_GETVIRTUALPIXELS - if( !(pixels = (PixelPacket *) GetVirtualPixels( image, - left, top, width, height, &image->exception )) ) -#else - if( !(pixels = GetImagePixels( image, left, top, width, height )) ) -#endif - return( NULL ); - -/* Can't happen if red/green/blue are doubles. - */ -#ifndef UseHDRI - /* Unpack palette. - */ - if( image->storage_class == PseudoClass ) { -#ifdef HAVE_GETVIRTUALPIXELS - IndexPacket *indexes = (IndexPacket *) - GetVirtualIndexQueue( image ); -#else - IndexPacket *indexes = GetIndexes( image ); -#endif - - int i; - - for( i = 0; i < width * height; i++ ) { - IndexPacket x = indexes[i]; - - if( x < image->colors ) { - pixels[i].red = image->colormap[x].red; - pixels[i].green = image->colormap[x].green; - pixels[i].blue = image->colormap[x].blue; - } - } - } -#endif /*UseHDRI*/ - - return( pixels ); -} - -static int -magick_fill_region( REGION *out, void *seq, void *a, void *b ) -{ - Read *read = (Read *) a; - Rect *r = &out->valid; - int y; - - for( y = 0; y < r->height; y++ ) { - int top = r->top + y; - int frame = top / read->frame_height; - int line = top % read->frame_height; - - PixelPacket *pixels; - - g_mutex_lock( read->lock ); - pixels = get_pixels( read->frames[frame], - r->left, line, r->width, 1 ); - g_mutex_unlock( read->lock ); - - if( !pixels ) { - im_error( "im_magick2vips", - "%s", _( "unable to read pixels" ) ); - return( -1 ); - } - - unpack_pixels( read->im, - (PEL *) IM_REGION_ADDR( out, r->left, top ), - pixels, r->width ); - } - - return( 0 ); -} - -/** - * im_magick2vips: - * @filename: file to load - * @out: image to write to - * - * Read in an image using libMagick, the ImageMagick library. This library can - * read more than 80 file formats, including SVG, BMP, EPS, DICOM and many - * others. - * The reader can handle any ImageMagick image, including the float and double - * formats. It will work with any quantum size, including HDR. Any metadata - * attached to the libMagick image is copied on to the VIPS image. - * - * The reader should also work with most versions of GraphicsMagick. - * - * See also: #VipsFormat. - * - * Returns: 0 on success, -1 on error. - */ int im_magick2vips( const char *filename, IMAGE *out ) { - Read *read; + VipsImage *t; - if( !(read = read_new( filename, out )) ) + if( vips_magickload( filename, &t, NULL ) ) return( -1 ); - -#ifdef HAVE_SETIMAGEOPTION - /* When reading DICOM images, we want to ignore any - * window_center/_width setting, since it may put pixels outside the - * 0-65535 range and lose data. - * - * These window settings are attached as vips metadata, so our caller - * can interpret them if it wants. - */ - SetImageOption( read->image_info, "dcm:display-range", "reset" ); -#endif /*HAVE_SETIMAGEOPTION*/ - - read->image = ReadImage( read->image_info, &read->exception ); - if( !read->image ) { - im_error( "im_magick2vips", _( "unable to read file \"%s\"\n" - "libMagick error: %s %s" ), - filename, - read->exception.reason, read->exception.description ); - return( -1 ); - } - - if( parse_header( read ) || - im_poutcheck( out ) || - im_demand_hint( out, IM_SMALLTILE, NULL ) || - im_generate( out, NULL, magick_fill_region, NULL, read, NULL ) ) - return( -1 ); - - return( 0 ); -} - -static int -magick2vips_header( const char *filename, IMAGE *im ) -{ - Read *read; - - if( !(read = read_new( filename, im )) ) - return( -1 ); - - read->image = PingImage( read->image_info, &read->exception ); - if( !read->image ) { - im_error( "im_magick2vips", _( "unable to ping file " - "\"%s\"\nlibMagick error: %s %s" ), - filename, - read->exception.reason, read->exception.description ); - return( -1 ); - } - - if( parse_header( read ) ) - return( -1 ); - - if( im->Xsize <= 0 || im->Ysize <= 0 ) { - im_error( "im_magick2vips", "%s", _( "bad image size" ) ); + if( vips_image_write( t, out ) ) { + g_object_unref( t ); return( -1 ); } + g_object_unref( t ); return( 0 ); } @@ -735,16 +60,7 @@ magick2vips_header( const char *filename, IMAGE *im ) static int ismagick( const char *filename ) { - IMAGE *im; - int result; - - if( !(im = im_open( "dummy", "p" )) ) - return( -1 ); - result = magick2vips_header( filename, im ); - im_error_clear(); - im_close( im ); - - return( result == 0 ); + return( vips_foreign_is_a( "magickload", filename ) ); } static const char *magick_suffs[] = { NULL }; @@ -764,7 +80,6 @@ vips_format_magick_class_init( VipsFormatMagickClass *class ) object_class->description = _( "libMagick-supported" ); format_class->is_a = ismagick; - format_class->header = magick2vips_header; format_class->load = im_magick2vips; format_class->suffs = magick_suffs; @@ -781,4 +96,3 @@ vips_format_magick_init( VipsFormatMagick *object ) G_DEFINE_TYPE( VipsFormatMagick, vips_format_magick, VIPS_TYPE_FORMAT ); -#endif /*HAVE_MAGICK*/ diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 5d156caf..906a67af 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -346,6 +346,9 @@ int vips_csvload( const char *filename, VipsImage **out, ... ) int vips_csvsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); +int vips_magickload( const char *filename, VipsImage **out, ... ) + __attribute__((sentinel)); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/iofuncs/generate.c b/libvips/iofuncs/generate.c index a5ee7210..488f2f79 100644 --- a/libvips/iofuncs/generate.c +++ b/libvips/iofuncs/generate.c @@ -601,6 +601,9 @@ vips_image_generate( VipsImage *image, g_assert( generate_fn ); g_assert( vips_object_sanity( VIPS_OBJECT( image ) ) ); + if( vips_image_pio_output( image ) ) + return( -1 ); + if( !image->hint_set ) { vips_error( "vips_image_generate", "%s", _( "demand hint not set" ) ); diff --git a/libvips/iofuncs/region.c b/libvips/iofuncs/region.c index 96efdce2..e6877dca 100644 --- a/libvips/iofuncs/region.c +++ b/libvips/iofuncs/region.c @@ -676,11 +676,15 @@ vips_region_region( VipsRegion *reg, /* Sanity check. */ - if( !dest->data || - VIPS_IMAGE_SIZEOF_PEL( dest->im ) != - VIPS_IMAGE_SIZEOF_PEL( reg->im ) ) { + if( !dest->data ) { vips_error( "VipsRegion", - "%s", _( "inappropriate region type" ) ); + "%s", _( "no pixel data on attached image" ) ); + return( -1 ); + } + if( VIPS_IMAGE_SIZEOF_PEL( dest->im ) != + VIPS_IMAGE_SIZEOF_PEL( reg->im ) ) { + vips_error( "VipsRegion", + "%s", _( "images do not match in pixel size" ) ); return( -1 ); } vips__region_check_ownership( reg );