static void res_from_exif( VipsImage *im, ExifData *ed ) { double xres, yres; int unit; /* The main image xres/yres are in ifd0. ifd1 has xres/yres of the * image thumbnail, if any. */ if( get_entry_double( ed, 0, EXIF_TAG_X_RESOLUTION, &xres ) || get_entry_double( ed, 0, EXIF_TAG_Y_RESOLUTION, &yres ) || get_entry_int( ed, 0, EXIF_TAG_RESOLUTION_UNIT, &unit ) ) { vips_warn( "VipsJpeg", "%s", _( "error reading resolution" ) ); return; } #ifdef DEBUG printf( "res_from_exif: seen exif tags " "xres = %g, yres = %g, unit = %d\n", xres, yres, unit ); #endif /*DEBUG*/ switch( unit ) { case 2: /* In inches. */ xres /= 25.4; yres /= 25.4; vips_image_set_string( im, VIPS_META_RESOLUTION_UNIT, "in" ); break; case 3: /* In cm. */ xres /= 10.0; yres /= 10.0; vips_image_set_string( im, VIPS_META_RESOLUTION_UNIT, "cm" ); break; default: vips_warn( "VipsJpeg", "%s", _( "unknown EXIF resolution unit" ) ); return; } #ifdef DEBUG printf( "res_from_exif: seen exif resolution %g, %g p/mm\n", xres, yres ); #endif /*DEBUG*/ im->Xres = xres; im->Yres = yres; }
static void attach_exif_entry( ExifEntry *entry, VipsExif *ve ) { char name_txt[256]; VipsBuf name = VIPS_BUF_STATIC( name_txt ); char value_txt[256]; VipsBuf value = VIPS_BUF_STATIC( value_txt ); vips_buf_appendf( &name, "exif-ifd%d-%s", exif_entry_get_ifd( entry ), exif_tag_get_title( entry->tag ) ); vips_exif_to_s( ve->ed, entry, &value ); /* Can't do anything sensible with the error return. */ (void) vips_image_set_string( ve->image, vips_buf_all( &name ), vips_buf_all( &value ) ); }
/* Set the png text data as metadata on the vips image. */ static int vips__set_text( VipsImage *out, int i, const char *key, const char *text ) { char name[256]; if( strcmp( key, "XML:com.adobe.xmp") == 0 ) /* Save as an XMP tag. */ strncpy( name, VIPS_META_XMP_NAME, 256 ); else /* Save as a comment. Some PNGs have EXIF data as * text segments, but the correct way to support this is with * png_get_eXIf_1(). */ vips_snprintf( name, 256, "png-comment-%d-%s", i, key ); vips_image_set_string( out, name, text ); return( 0 ); }
static void vips_exif_attach_entry( ExifEntry *entry, VipsExifParams *params ) { const char *tag_name; char vips_name_txt[256]; VipsBuf vips_name = VIPS_BUF_STATIC( vips_name_txt ); char value_txt[256]; VipsBuf value = VIPS_BUF_STATIC( value_txt ); if( !(tag_name = vips_exif_entry_get_name( entry )) ) return; vips_buf_appendf( &vips_name, "exif-ifd%d-%s", exif_entry_get_ifd( entry ), tag_name ); vips_exif_to_s( params->ed, entry, &value ); /* Can't do anything sensible with the error return. */ (void) vips_image_set_string( params->image, vips_buf_all( &vips_name ), vips_buf_all( &value ) ); }
/* Set the image resolution from the EXIF tags. */ static int vips_image_resolution_from_exif( VipsImage *image, ExifData *ed ) { double xres, yres; int unit; /* The main image xres/yres are in ifd0. ifd1 has xres/yres of the * image thumbnail, if any. * * Don't warn about missing res fields, it's very common, especially for * things like webp. */ if( vips_exif_entry_get_double( ed, 0, EXIF_TAG_X_RESOLUTION, &xres ) || vips_exif_entry_get_double( ed, 0, EXIF_TAG_Y_RESOLUTION, &yres ) || vips_exif_entry_get_int( ed, 0, EXIF_TAG_RESOLUTION_UNIT, &unit ) ) return( -1 ); #ifdef DEBUG printf( "vips_image_resolution_from_exif: seen exif tags " "xres = %g, yres = %g, unit = %d\n", xres, yres, unit ); #endif /*DEBUG*/ switch( unit ) { case 1: /* No unit ... just pass the fields straight to vips. */ vips_image_set_string( image, VIPS_META_RESOLUTION_UNIT, "none" ); break; case 2: /* In inches. */ xres /= 25.4; yres /= 25.4; vips_image_set_string( image, VIPS_META_RESOLUTION_UNIT, "in" ); break; case 3: /* In cm. */ xres /= 10.0; yres /= 10.0; vips_image_set_string( image, VIPS_META_RESOLUTION_UNIT, "cm" ); break; default: g_warning( "%s", _( "unknown EXIF resolution unit" ) ); return( -1 ); } #ifdef DEBUG printf( "vips_image_resolution_from_exif: " "seen exif resolution %g, %g p/mm\n", xres, yres ); #endif /*DEBUG*/ image->Xres = xres; image->Yres = yres; return( 0 ); }
static int vips_fits_get_header( VipsFits *fits, VipsImage *out ) { int status; int bitpix; int width, height, bands, format, type; int keysexist; int i; status = 0; if( fits_get_img_paramll( fits->fptr, 10, &bitpix, &fits->naxis, fits->naxes, &status ) ) { vips_fits_error( status ); return( -1 ); } #ifdef VIPS_DEBUG VIPS_DEBUG_MSG( "naxis = %d\n", fits->naxis ); for( i = 0; i < fits->naxis; i++ ) VIPS_DEBUG_MSG( "%d) %lld\n", i, fits->naxes[i] ); #endif /*VIPS_DEBUG*/ height = 1; bands = 1; switch( fits->naxis ) { /* If you add more dimensions here, adjust data read below. See also * the definition of MAX_DIMENSIONS above. */ case 10: case 9: case 8: case 7: case 6: case 5: case 4: for( i = fits->naxis; i > 3; i-- ) if( fits->naxes[i - 1] != 1 ) { vips_error( "fits", "%s", _( "dimensions above 3 " "must be size 1" ) ); return( -1 ); } case 3: bands = fits->naxes[2]; case 2: height = fits->naxes[1]; case 1: width = fits->naxes[0]; break; default: vips_error( "fits", _( "bad number of axis %d" ), fits->naxis ); return( -1 ); } /* Are we in one-band mode? */ if( fits->band_select != -1 ) bands = 1; /* Get image format. We want the 'raw' format of the image, our caller * can convert using the meta info if they want. */ for( i = 0; i < VIPS_NUMBER( fits2vips_formats ); i++ ) if( fits2vips_formats[i][0] == bitpix ) break; if( i == VIPS_NUMBER( fits2vips_formats ) ) { vips_error( "fits", _( "unsupported bitpix %d\n" ), bitpix ); return( -1 ); } format = fits2vips_formats[i][1]; fits->datatype = fits2vips_formats[i][2]; if( bands == 1 ) { if( format == VIPS_FORMAT_USHORT ) type = VIPS_INTERPRETATION_GREY16; else type = VIPS_INTERPRETATION_B_W; } else if( bands == 3 ) { if( format == VIPS_FORMAT_USHORT ) type = VIPS_INTERPRETATION_RGB16; else type = VIPS_INTERPRETATION_RGB; } else type = VIPS_INTERPRETATION_MULTIBAND; vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); vips_image_init_fields( out, width, height, bands, format, VIPS_CODING_NONE, type, 1.0, 1.0 ); /* Read all keys into meta. */ if( fits_get_hdrspace( fits->fptr, &keysexist, NULL, &status ) ) { vips_fits_error( status ); return( -1 ); } for( i = 0; i < keysexist; i++ ) { char record[81]; char vipsname[100]; if( fits_read_record( fits->fptr, i + 1, record, &status ) ) { vips_fits_error( status ); return( -1 ); } VIPS_DEBUG_MSG( "fits2vips: setting meta on vips image:\n" ); VIPS_DEBUG_MSG( " record == \"%s\"\n", record ); /* FITS lets keys repeat. For example, HISTORY appears many * times, each time with a fresh line of history attached. We * have to include the key index in the vips name we assign. */ vips_snprintf( vipsname, 100, "fits-%d", i ); vips_image_set_string( out, vipsname, record ); } return( 0 ); }
static int parse_header( Read *read ) { VipsImage *im = read->im; Image *image = read->image; Image *p; int i; #ifdef DEBUG printf( "parse_header: filename = %s\n", read->filename ); printf( "GetImageChannelDepth(AllChannels) = %zd\n", GetImageChannelDepth( image, AllChannels, &image->exception ) ); printf( "GetImageDepth() = %zd\n", GetImageDepth( image, &image->exception ) ); printf( "image->depth = %zd\n", image->depth ); printf( "GetImageType() = %d\n", GetImageType( image, &image->exception ) ); printf( "IsGrayImage() = %d\n", IsGrayImage( image, &image->exception ) ); printf( "IsMonochromeImage() = %d\n", IsMonochromeImage( image, &image->exception ) ); printf( "IsOpaqueImage() = %d\n", IsOpaqueImage( image, &image->exception ) ); printf( "image->columns = %zd\n", image->columns ); printf( "image->rows = %zd\n", image->rows ); #endif /*DEBUG*/ im->Xsize = image->columns; im->Ysize = image->rows; read->frame_height = image->rows; if( (im->Bands = get_bands( image )) < 0 ) return( -1 ); /* Depth can be 'fractional'. You'd think we should use * GetImageDepth() but that seems unreliable. 16-bit mono DICOM images * are reported as depth 1, for example. */ im->BandFmt = -1; if( image->depth >= 1 && image->depth <= 8 ) im->BandFmt = VIPS_FORMAT_UCHAR; if( image->depth >= 9 && image->depth <= 16 ) im->BandFmt = VIPS_FORMAT_USHORT; #ifdef UseHDRI if( image->depth == 32 ) im->BandFmt = VIPS_FORMAT_FLOAT; if( image->depth == 64 ) im->BandFmt = VIPS_FORMAT_DOUBLE; #else /*!UseHDRI*/ if( image->depth == 32 ) im->BandFmt = VIPS_FORMAT_UINT; #endif /*UseHDRI*/ if( im->BandFmt == -1 ) { vips_error( "magick2vips", _( "unsupported bit depth %d" ), (int) image->depth ); return( -1 ); } switch( image->colorspace ) { case GRAYColorspace: if( im->BandFmt == VIPS_FORMAT_USHORT ) im->Type = VIPS_INTERPRETATION_GREY16; else im->Type = VIPS_INTERPRETATION_B_W; break; case RGBColorspace: if( im->BandFmt == VIPS_FORMAT_USHORT ) im->Type = VIPS_INTERPRETATION_RGB16; else im->Type = VIPS_INTERPRETATION_RGB; break; case sRGBColorspace: if( im->BandFmt == VIPS_FORMAT_USHORT ) im->Type = VIPS_INTERPRETATION_RGB16; else im->Type = VIPS_INTERPRETATION_sRGB; break; case CMYKColorspace: im->Type = VIPS_INTERPRETATION_CMYK; break; default: vips_error( "magick2vips", _( "unsupported colorspace %d" ), (int) image->colorspace ); return( -1 ); } switch( image->units ) { case PixelsPerInchResolution: im->Xres = image->x_resolution / 25.4; im->Yres = image->y_resolution / 25.4; break; case PixelsPerCentimeterResolution: im->Xres = image->x_resolution / 10.0; im->Yres = image->y_resolution / 10.0; break; default: im->Xres = 1.0; im->Yres = 1.0; break; } /* Other fields. */ im->Coding = VIPS_CODING_NONE; vips_image_pipelinev( im, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Three ways to loop over attributes / properties :-( */ #ifdef HAVE_RESETIMAGEPROPERTYITERATOR { char *key; /* This is the most recent imagemagick API, test for this first. */ ResetImagePropertyIterator( image ); while( (key = GetNextImageProperty( image )) ) { char name_text[256]; VipsBuf name = VIPS_BUF_STATIC( name_text ); vips_buf_appendf( &name, "magick-%s", key ); vips_image_set_string( im, vips_buf_all( &name ), GetImageProperty( image, key ) ); } } #elif defined(HAVE_RESETIMAGEATTRIBUTEITERATOR) { const ImageAttribute *attr; /* magick6.1-ish and later, deprecated in 6.5ish. */ ResetImageAttributeIterator( image ); while( (attr = GetNextImageAttribute( image )) ) { char name_text[256]; VipsBuf name = VIPS_BUF_STATIC( name_text ); vips_buf_appendf( &name, "magick-%s", attr->key ); vips_image_set_string( im, vips_buf_all( &name ), attr->value ); } } #else { const ImageAttribute *attr; /* GraphicsMagick is missing the iterator: we have to loop ourselves. * ->attributes is marked as private in the header, but there's no * getter so we have to access it directly. */ for( attr = image->attributes; attr; attr = attr->next ) { char name_text[256]; VipsBuf name = VIPS_BUF_STATIC( name_text ); vips_buf_appendf( &name, "magick-%s", attr->key ); vips_image_set_string( im, vips_buf_all( &name ), attr->value ); } } #endif /* Do we have a set of equal-sized frames? Append them. FIXME ... there must be an attribute somewhere from dicom read which says this is a volumetric image */ read->n_frames = 0; for( p = image; p; (p = GetNextImageInList( p )) ) { if( p->columns != (unsigned int) im->Xsize || p->rows != (unsigned int) im->Ysize || get_bands( p ) != im->Bands ) break; read->n_frames += 1; } if( p ) /* Nope ... just do the first image in the list. */ read->n_frames = 1; #ifdef DEBUG printf( "image has %d frames\n", read->n_frames ); #endif /*DEBUG*/ /* If all_frames is off, just get the first one. */ if( !read->all_frames ) read->n_frames = 1; /* Record frame pointers. */ im->Ysize *= read->n_frames; if( !(read->frames = VIPS_ARRAY( NULL, read->n_frames, Image * )) ) return( -1 ); p = image; for( i = 0; i < read->n_frames; i++ ) { read->frames[i] = p; p = GetNextImageInList( p ); } return( 0 ); }
static ReadSlide * readslide_new( const char *filename, VipsImage *out, int level, gboolean autocrop, const char *associated ) { ReadSlide *rslide; int64_t w, h; const char *error; const char *background; const char * const *properties; char *associated_names; if( level && associated ) { vips_error( "openslide2vips", "%s", _( "specify only one of level or associated " "image" ) ); return( NULL ); } rslide = VIPS_NEW( out, ReadSlide ); memset( rslide, 0, sizeof( *rslide ) ); g_signal_connect( out, "close", G_CALLBACK( readslide_destroy_cb ), rslide ); rslide->level = level; rslide->autocrop = autocrop; rslide->associated = g_strdup( associated ); /* Non-crazy defaults, override below if we can. */ rslide->tile_width = 256; rslide->tile_height = 256; rslide->osr = openslide_open( filename ); if( rslide->osr == NULL ) { vips_error( "openslide2vips", "%s", _( "unsupported slide format" ) ); return( NULL ); } error = openslide_get_error( rslide->osr ); if( error ) { vips_error( "openslide2vips", _( "opening slide: %s" ), error ); return( NULL ); } if( level < 0 || level >= openslide_get_level_count( rslide->osr ) ) { vips_error( "openslide2vips", "%s", _( "invalid slide level" ) ); return( NULL ); } if( associated && check_associated_image( rslide->osr, associated ) ) return( NULL ); if( associated ) { openslide_get_associated_image_dimensions( rslide->osr, associated, &w, &h ); vips_image_set_string( out, "slide-associated-image", associated ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); } else { char buf[256]; const char *value; openslide_get_level_dimensions( rslide->osr, level, &w, &h ); rslide->downsample = openslide_get_level_downsample( rslide->osr, level ); vips_image_set_int( out, "slide-level", level ); vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL ); /* Try to get tile width/height. An undocumented, experimental * feature. */ vips_snprintf( buf, 256, "openslide.level[%d].tile-width", level ); if( (value = openslide_get_property_value( rslide->osr, buf )) ) rslide->tile_width = atoi( value ); vips_snprintf( buf, 256, "openslide.level[%d].tile-height", level ); if( (value = openslide_get_property_value( rslide->osr, buf )) ) rslide->tile_height = atoi( value ); if( value ) VIPS_DEBUG_MSG( "readslide_new: found tile-size\n" ); /* Some images have a bounds in the header. Crop to * that if autocrop is set. */ if( rslide->autocrop ) if( !get_bounds( rslide->osr, &rslide->bounds ) ) rslide->autocrop = FALSE; if( rslide->autocrop ) { VipsRect image; rslide->bounds.left /= rslide->downsample; rslide->bounds.top /= rslide->downsample; rslide->bounds.width /= rslide->downsample; rslide->bounds.height /= rslide->downsample; /* Clip against image size. */ image.left = 0; image.top = 0; image.width = w; image.height = h; vips_rect_intersectrect( &rslide->bounds, &image, &rslide->bounds ); /* If we've clipped to nothing, ignore bounds. */ if( vips_rect_isempty( &rslide->bounds ) ) rslide->autocrop = FALSE; } if( rslide->autocrop ) { w = rslide->bounds.width; h = rslide->bounds.height; } } rslide->bg = 0xffffff; if( (background = openslide_get_property_value( rslide->osr, OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR )) ) rslide->bg = strtoul( background, NULL, 16 ); if( w <= 0 || h <= 0 || rslide->downsample < 0 ) { vips_error( "openslide2vips", _( "getting dimensions: %s" ), openslide_get_error( rslide->osr ) ); return( NULL ); } if( w > INT_MAX || h > INT_MAX ) { vips_error( "openslide2vips", "%s", _( "image dimensions overflow int" ) ); return( NULL ); } if( !rslide->autocrop ) { rslide->bounds.left = 0; rslide->bounds.top = 0; rslide->bounds.width = w; rslide->bounds.height = h; } vips_image_init_fields( out, w, h, 4, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 ); for( properties = openslide_get_property_names( rslide->osr ); *properties != NULL; properties++ ) vips_image_set_string( out, *properties, openslide_get_property_value( rslide->osr, *properties ) ); associated_names = g_strjoinv( ", ", (char **) openslide_get_associated_image_names( rslide->osr ) ); vips_image_set_string( out, "slide-associated-images", associated_names ); VIPS_FREE( associated_names ); return( rslide ); }
static ReadSlide * readslide_new( const char *filename, VipsImage *out, int layer, const char *associated ) { ReadSlide *rslide; int64_t w, h; const char *background; const char * const *properties; rslide = VIPS_NEW( out, ReadSlide ); memset( rslide, 0, sizeof( *rslide ) ); g_signal_connect( out, "close", G_CALLBACK( readslide_destroy_cb ), rslide ); rslide->layer = layer; rslide->associated = g_strdup( associated ); rslide->osr = openslide_open( filename ); if( rslide->osr == NULL ) { vips_error( "openslide2vips", "%s", _( "failure opening slide" ) ); return( NULL ); } if( layer < 0 || layer >= openslide_get_layer_count( rslide->osr ) ) { vips_error( "openslide2vips", "%s", _( "invalid slide level" ) ); return( NULL ); } if( associated && check_associated_image( rslide->osr, associated ) ) return( NULL ); if( associated ) { openslide_get_associated_image_dimensions( rslide->osr, associated, &w, &h ); vips_image_set_string( out, "slide-associated-image", associated ); vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); } else { openslide_get_layer_dimensions( rslide->osr, layer, &w, &h ); rslide->downsample = openslide_get_layer_downsample( rslide->osr, layer ); vips_image_set_int( out, "slide-level", 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 ) vips_image_set_int( out, VIPS_META_BACKGROUND_RGB, strtoul( background, NULL, 16 ) ); else vips_image_set_int( out, VIPS_META_BACKGROUND_RGB, 0xffffff ); if( w < 0 || h < 0 || rslide->downsample < 0 ) { vips_error( "openslide2vips", _( "getting dimensions: %s" ), openslide_get_error( rslide->osr ) ); return( NULL ); } if( w > INT_MAX || h > INT_MAX ) { vips_error( "openslide2vips", "%s", _( "image dimensions overflow int" ) ); return( NULL ); } vips_image_init_fields( out, w, h, 4, VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 ); for( properties = openslide_get_property_names( rslide->osr ); *properties != NULL; properties++ ) vips_image_set_string( out, *properties, openslide_get_property_value( rslide->osr, *properties ) ); associated = g_strjoinv( ", ", (char **) openslide_get_associated_image_names( rslide->osr ) ); vips_image_set_string( out, "slide-associated-images", associated ); return( rslide ); }