static VipsAngle get_angle( VipsImage *im ) { VipsAngle angle; const char *orientation; angle = VIPS_ANGLE_0; if( vips_image_get_typeof( im, ORIENTATION ) && !vips_image_get_string( im, ORIENTATION, &orientation ) ) { if( vips_isprefix( "6", orientation ) ) angle = VIPS_ANGLE_90; else if( vips_isprefix( "8", orientation ) ) angle = VIPS_ANGLE_270; else if( vips_isprefix( "3", orientation ) ) angle = VIPS_ANGLE_180; /* Other values do rotate + mirror, don't bother handling them * though, how common can mirroring be. * * See: * * http://www.80sidea.com/archives/2316 */ } return( angle ); }
/* Set IMAGEDESCRIPTION, if it's there. If @properties is TRUE, set from * vips' metadata. */ static int write_embed_imagedescription( Write *write, TIFF *tif ) { if( write->properties ) { char *doc; if( !(doc = vips__make_xml_metadata( "vips2tiff", write->im )) ) return( -1 ); TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, doc ); xmlFree( doc ); } else { const char *imagedescription; if( !vips_image_get_typeof( write->im, VIPS_META_IMAGEDESCRIPTION ) ) return( 0 ); if( vips_image_get_string( write->im, VIPS_META_IMAGEDESCRIPTION, &imagedescription ) ) return( -1 ); TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, imagedescription ); } #ifdef DEBUG printf( "vips2tiff: attached imagedescription from meta\n" ); #endif /*DEBUG*/ return( 0 ); }
static void * write_png_comment( VipsImage *image, const char *field, GValue *value, void *data ) { Write *write = (Write *) data; if( vips_isprefix( "png-comment-", field ) ) { const char *str; int i; char key[80]; if( vips_image_get_string( write->in, field, &str ) ) return( image ); if( sscanf( field, "png-comment-%d-%80s", &i, key ) != 2 ) { vips_error( "vips2png", "%s", _( "bad png comment key" ) ); return( image ); } vips__png_set_text( write->pPng, write->pInfo, key, str ); } return( NULL ); }
/* Set the EXIF resolution from the vips xres/yres tags. */ static int vips_exif_resolution_from_image( ExifData *ed, VipsImage *image ) { double xres, yres; const char *p; int unit; VIPS_DEBUG_MSG( "vips_exif_resolution_from_image: vips res of %g, %g\n", image->Xres, image->Yres ); /* Default to inches, more progs support it. */ unit = 2; if( vips_image_get_typeof( image, VIPS_META_RESOLUTION_UNIT ) && !vips_image_get_string( image, VIPS_META_RESOLUTION_UNIT, &p ) ) { if( vips_isprefix( "cm", p ) ) unit = 3; else if( vips_isprefix( "none", p ) ) unit = 1; } switch( unit ) { case 1: xres = image->Xres; yres = image->Yres; break; case 2: xres = image->Xres * 25.4; yres = image->Yres * 25.4; break; case 3: xres = image->Xres * 10.0; yres = image->Yres * 10.0; break; default: g_warning( "%s", _( "unknown EXIF resolution unit" ) ); return( 0 ); } /* Main image xres/yres/unit are in ifd0. ifd1 has the thumbnail * xres/yres/unit. */ vips_exif_set_tag( ed, 0, EXIF_TAG_X_RESOLUTION, vips_exif_set_double, (void *) &xres ); vips_exif_set_tag( ed, 0, EXIF_TAG_Y_RESOLUTION, vips_exif_set_double, (void *) &yres ); vips_exif_set_tag( ed, 0, EXIF_TAG_RESOLUTION_UNIT, vips_exif_set_int, (void *) &unit ); return( 0 ); }
static int vips_foreign_save_tiff_build( VipsObject *object ) { VipsForeignSave *save = (VipsForeignSave *) object; VipsForeignSaveTiff *tiff = (VipsForeignSaveTiff *) object; const char *p; if( VIPS_OBJECT_CLASS( vips_foreign_save_tiff_parent_class )-> build( object ) ) return( -1 ); /* Default xres/yres to the values from the image. */ if( !vips_object_argument_isset( object, "xres" ) ) tiff->xres = save->ready->Xres * 10.0; if( !vips_object_argument_isset( object, "yres" ) ) tiff->yres = save->ready->Yres * 10.0; /* resunit param overrides resunit metadata. */ if( !vips_object_argument_isset( object, "resunit" ) && vips_image_get_typeof( save->ready, VIPS_META_RESOLUTION_UNIT ) && !vips_image_get_string( save->ready, VIPS_META_RESOLUTION_UNIT, &p ) && vips_isprefix( "in", p ) ) tiff->resunit = VIPS_FOREIGN_TIFF_RESUNIT_INCH; if( tiff->resunit == VIPS_FOREIGN_TIFF_RESUNIT_INCH ) { tiff->xres *= 2.54; tiff->yres *= 2.54; } if( vips__tiff_write( save->ready, tiff->filename, tiff->compression, tiff->Q, tiff->predictor, tiff->profile, tiff->tile, tiff->tile_width, tiff->tile_height, tiff->pyramid, tiff->squash, tiff->miniswhite, tiff->resunit, tiff->xres, tiff->yres, tiff->bigtiff, tiff->rgbjpeg, tiff->properties ) ) return( -1 ); return( 0 ); }
static void * vips_exif_image_field( VipsImage *image, const char *field, GValue *value, void *data ) { ExifData *ed = (ExifData *) data; const char *string; int ifd; const char *p; ExifTag tag; if( !vips_isprefix( "exif-ifd", field ) ) return( NULL ); /* value must be a string. */ if( vips_image_get_string( image, field, &string ) ) { g_warning( _( "bad exif meta \"%s\"" ), field ); return( NULL ); } p = field + strlen( "exif-ifd" ); ifd = atoi( p ); for( ; isdigit( *p ); p++ ) ; if( *p != '-' ) { g_warning( _( "bad exif meta \"%s\"" ), field ); return( NULL ); } if( !(tag = exif_tag_from_name( p + 1 )) ) { g_warning( _( "bad exif meta \"%s\"" ), field ); return( NULL ); } vips_exif_set_tag( ed, ifd, tag, vips_exif_set_entry, (void *) string ); return( NULL ); }
/* 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, ¶ms ); 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 ); }
/* Write a VIPS image to PNG. */ static int write_vips( Write *write, int compress, int interlace, const char *profile, VipsForeignPngFilter filter, gboolean strip, gboolean palette, int colours, int Q, double dither ) { VipsImage *in = write->in; int bit_depth; int color_type; int interlace_type; int i, nb_passes; g_assert( in->BandFmt == VIPS_FORMAT_UCHAR || in->BandFmt == VIPS_FORMAT_USHORT ); g_assert( in->Coding == VIPS_CODING_NONE ); g_assert( in->Bands > 0 && in->Bands < 5 ); /* Catch PNG errors. */ if( setjmp( png_jmpbuf( write->pPng ) ) ) return( -1 ); /* Check input image. If we are writing interlaced, we need to make 7 * passes over the image. We advertise ourselves as seq, so to ensure * we only suck once from upstream, switch to WIO. */ if( interlace ) { if( !(write->memory = vips_image_copy_memory( in )) ) return( -1 ); in = write->memory; } else { if( vips_image_pio_input( in ) ) return( -1 ); } if( compress < 0 || compress > 9 ) { vips_error( "vips2png", "%s", _( "compress should be in [0,9]" ) ); return( -1 ); } /* Set compression parameters. */ png_set_compression_level( write->pPng, compress ); /* Set row filter. */ png_set_filter( write->pPng, 0, filter ); bit_depth = in->BandFmt == VIPS_FORMAT_UCHAR ? 8 : 16; switch( in->Bands ) { case 1: color_type = PNG_COLOR_TYPE_GRAY; break; case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3: color_type = PNG_COLOR_TYPE_RGB; break; case 4: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: vips_error( "vips2png", _( "can't save %d band image as png" ), in->Bands ); return( -1 ); } #ifdef HAVE_IMAGEQUANT /* Enable image quantisation to paletted 8bpp PNG if colours is set. */ if( palette ) { g_assert( colours >= 2 && colours <= 256 ); bit_depth = 8; color_type = PNG_COLOR_TYPE_PALETTE; } #else if( palette ) g_warning( "%s", _( "ignoring palette (no quantisation support)" ) ); #endif /*HAVE_IMAGEQUANT*/ interlace_type = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; png_set_IHDR( write->pPng, write->pInfo, in->Xsize, in->Ysize, bit_depth, color_type, interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); /* Set resolution. libpng uses pixels per meter. */ png_set_pHYs( write->pPng, write->pInfo, VIPS_RINT( in->Xres * 1000 ), VIPS_RINT( in->Yres * 1000 ), PNG_RESOLUTION_METER ); /* Set ICC Profile. */ if( profile && !strip ) { if( strcmp( profile, "none" ) != 0 ) { void *data; size_t length; if( !(data = vips__file_read_name( profile, vips__icc_dir(), &length )) ) return( -1 ); #ifdef DEBUG printf( "write_vips: " "attaching %zd bytes of ICC profile\n", length ); #endif /*DEBUG*/ png_set_iCCP( write->pPng, write->pInfo, "icc", PNG_COMPRESSION_TYPE_BASE, data, length ); } } else if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) && !strip ) { void *data; size_t length; if( vips_image_get_blob( in, VIPS_META_ICC_NAME, &data, &length ) ) return( -1 ); #ifdef DEBUG printf( "write_vips: attaching %zd bytes of ICC profile\n", length ); #endif /*DEBUG*/ png_set_iCCP( write->pPng, write->pInfo, "icc", PNG_COMPRESSION_TYPE_BASE, data, length ); } if( vips_image_get_typeof( in, VIPS_META_XMP_NAME ) ) { const char *str; if( vips_image_get_string( in, VIPS_META_XMP_NAME, &str ) ) return( -1 ); vips__png_set_text( write->pPng, write->pInfo, "XML:com.adobe.xmp", str ); } /* Set any "png-comment-xx-yyy" metadata items. */ if( vips_image_map( in, write_png_comment, write ) ) return( -1 ); #ifdef HAVE_IMAGEQUANT if( palette ) { VipsImage *im_index; VipsImage *im_palette; int palette_count; png_color *png_palette; png_byte *png_trans; int trans_count; if( vips__quantise_image( in, &im_index, &im_palette, colours, Q, dither ) ) return( -1 ); palette_count = im_palette->Xsize; g_assert( palette_count <= PNG_MAX_PALETTE_LENGTH ); png_palette = (png_color *) png_malloc( write->pPng, palette_count * sizeof( png_color ) ); png_trans = (png_byte *) png_malloc( write->pPng, palette_count * sizeof( png_byte ) ); trans_count = 0; for( i = 0; i < palette_count; i++ ) { VipsPel *p = (VipsPel *) VIPS_IMAGE_ADDR( im_palette, i, 0 ); png_color *col = &png_palette[i]; col->red = p[0]; col->green = p[1]; col->blue = p[2]; png_trans[i] = p[3]; if( p[3] != 255 ) trans_count = i + 1; #ifdef DEBUG printf( "write_vips: palette[%d] %d %d %d %d\n", i + 1, p[0], p[1], p[2], p[3] ); #endif /*DEBUG*/ } #ifdef DEBUG printf( "write_vips: attaching %d color palette\n", palette_count ); #endif /*DEBUG*/ png_set_PLTE( write->pPng, write->pInfo, png_palette, palette_count ); if( trans_count ) { #ifdef DEBUG printf( "write_vips: attaching %d alpha values\n", trans_count ); #endif /*DEBUG*/ png_set_tRNS( write->pPng, write->pInfo, png_trans, trans_count, NULL ); } png_free( write->pPng, (void *) png_palette ); png_free( write->pPng, (void *) png_trans ); VIPS_UNREF( im_palette ); VIPS_UNREF( write->memory ); write->memory = im_index; in = write->memory; } #endif /*HAVE_IMAGEQUANT*/ png_write_info( write->pPng, write->pInfo ); /* If we're an intel byte order CPU and this is a 16bit image, we need * to swap bytes. */ if( bit_depth > 8 && !vips_amiMSBfirst() ) png_set_swap( write->pPng ); if( interlace ) nb_passes = png_set_interlace_handling( write->pPng ); else nb_passes = 1; /* Write data. */ for( i = 0; i < nb_passes; i++ ) if( vips_sink_disc( in, write_png_block, write ) ) return( -1 ); /* The setjmp() was held by our background writer: reset it. */ if( setjmp( png_jmpbuf( write->pPng ) ) ) return( -1 ); png_write_end( write->pPng, write->pInfo ); return( 0 ); }