Ejemplo n.º 1
0
/* Scan the exif block on the image, if any, and make a set of vips metadata 
 * tags for what we find.
 */
int
vips__exif_parse( VipsImage *image )
{
	void *data;
	size_t length;
	ExifData *ed;
	VipsExifParams params;
	const char *str;

	if( !vips_image_get_typeof( image, VIPS_META_EXIF_NAME ) )
		return( 0 );
	if( vips_image_get_blob( image, VIPS_META_EXIF_NAME, &data, &length ) )
		return( -1 ); 
	if( !(ed = vips_exif_load_data_without_fix( data, length )) )
		return( -1 );

#ifdef DEBUG_VERBOSE
	show_tags( ed );
	show_values( ed );
#endif /*DEBUG_VERBOSE*/

	/* Look for resolution fields and use them to set the VIPS xres/yres 
	 * fields.
	 *
	 * If the fields are missing, set them from the image, which will have
	 * previously had them set from something like JFIF. 
	 */
	if( vips_image_resolution_from_exif( image, ed ) &&
		vips_exif_resolution_from_image( ed, image ) ) {
		exif_data_free( ed );
		return( -1 ); 
	}

	/* Make sure all required fields are there before we attach the vips
	 * metadata.
	 */
	exif_data_fix( ed );

	/* Attach informational fields for what we find.
	 */
	params.image = image;
	params.ed = ed;
	exif_data_foreach_content( ed, 
		(ExifDataForeachContentFunc) vips_exif_get_content, &params );

	vips_exif_get_thumbnail( image, ed );
	exif_data_free( ed );

	/* Orientation handling. ifd0 has the Orientation tag for the main
	 * image. 
	 */
	if( vips_image_get_typeof( image, "exif-ifd0-Orientation" ) != 0 &&
		!vips_image_get_string( image, 
			"exif-ifd0-Orientation", &str ) ) {
		int orientation;

		orientation = atoi( str );
		orientation = VIPS_CLIP( 1, orientation, 8 );
		vips_image_set_int( image, VIPS_META_ORIENTATION, orientation );
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    jpeg_saved_marker_ptr p;
    VipsInterpretation interpretation;
    double xres, yres;

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

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

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

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

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

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

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

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

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

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

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

    vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );

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

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

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

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

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

            break;

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

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

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

        default:
            break;
        }
    }

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

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

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

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

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

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

	png_uint_32 res_x, res_y;
	int unit_type;

	png_charp name;
	int compression_type;

	png_textp text_ptr;
        int num_text;

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

	png_uint_32 proflen;

	int bands; 
	VipsInterpretation interpretation;
	double Xres, Yres;

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

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

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

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

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

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

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

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

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

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

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

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

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

	/* Uninterlaced images will be read in seq mode. Interlaced images are
	 * read via a huge memory buffer.
	 */
	if( interlace_type == PNG_INTERLACE_NONE ) 
		/* Sequential mode needs thinstrip to work with things like
		 * vips_shrink().
		 */
		vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
	else 
		vips_image_pipelinev( out, VIPS_DEMAND_STYLE_ANY, NULL );

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

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

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

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

	/* Let our caller know. These are very expensive to decode.
	 */
	if( interlace_type != PNG_INTERLACE_NONE ) 
		vips_image_set_int( out, "interlaced", 1 ); 

	if( png_get_text( read->pPng, read->pInfo, 
		&text_ptr, &num_text ) > 0 ) {
		int i;

		for( i = 0; i < num_text; i++ ) 
			/* .text is always a null-terminated C string.
			 */
			if( vips__set_text( out, i, 
				text_ptr[i].key, text_ptr[i].text ) ) 
				return( -1 ); 
	}

	return( 0 );
}