diff --git a/ChangeLog b/ChangeLog index 7bd10158..20144c67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,6 +24,8 @@ behaviour with alpha channels - improve bioformats support with read and write of tiff subifd pyramids - thumbnail exploits subifd pyramids +- handle all EXIF orientation cases, deprecate + vips_autorot_get_angle() [Elad-Laufer] 24/4/20 started 8.9.3 - better iiif tile naming [IllyaMoskvin] diff --git a/libvips/conversion/autorot.c b/libvips/conversion/autorot.c index 344165b2..b297dc95 100644 --- a/libvips/conversion/autorot.c +++ b/libvips/conversion/autorot.c @@ -8,6 +8,7 @@ * - don't remove orientation if it's one of the cases we don't handle * 10/5/20 * - handle mirrored images + * - deprecate vips_autorot_get_angle() */ /* @@ -56,8 +57,7 @@ typedef struct _VipsAutorot { VipsImage *in; VipsAngle angle; - - gboolean flip; + gboolean flip; } VipsAutorot; @@ -65,51 +65,6 @@ typedef VipsConversionClass VipsAutorotClass; G_DEFINE_TYPE( VipsAutorot, vips_autorot, VIPS_TYPE_CONVERSION ); -/** - * vips_autorot_get_angle: - * @image: image to fetch orientation from - * - * Examine the metadata on @im and return the #VipsAngle to rotate by to turn - * the image upright. - * - * See also: vips_autorot(). - * - * Returns: the #VipsAngle to rotate by to make the image upright. - */ -VipsAngle -vips_autorot_get_angle( VipsImage *im ) -{ - int orientation; - VipsAngle angle; - - if( !vips_image_get_typeof( im, VIPS_META_ORIENTATION ) || - vips_image_get_int( im, VIPS_META_ORIENTATION, &orientation ) ) - orientation = 1; - - switch( orientation ) { - case 6: - angle = VIPS_ANGLE_D90; - break; - - case 8: - angle = VIPS_ANGLE_D270; - break; - - case 3: - angle = VIPS_ANGLE_D180; - break; - - default: - /* Other values do rotate + mirror, don't bother handling them - * though, how common can mirroring be. - */ - angle = VIPS_ANGLE_D0; - break; - } - - return( angle ); -} - static void * vips_autorot_remove_angle_sub( VipsImage *image, const char *field, GValue *value, void *my_data ) @@ -130,14 +85,14 @@ vips_autorot_remove_angle_sub( VipsImage *image, * vips_autorot_remove_angle: (method) * @image: image to remove orientation from * - * Remove the orientation tag on @image. Also remove any exif orientation tags. - * - * See also: vips_autorot_get_angle(). + * Remove the orientation tag on @image. Also remove any exif orientation tags. + * You must vips_copy() the image before calling this function since it + * modifies metadata. */ void vips_autorot_remove_angle( VipsImage *image ) { - (void) vips_image_remove( image, VIPS_META_ORIENTATION ); + (void) vips_image_remove( image, VIPS_META_ORIENTATION ); (void) vips_image_map( image, vips_autorot_remove_angle_sub, NULL ); } @@ -151,88 +106,82 @@ vips_autorot_build( VipsObject *object ) if( VIPS_OBJECT_CLASS( vips_autorot_parent_class )->build( object ) ) return( -1 ); - int orientation = 0; - VipsAngle angle; - gboolean flip = FALSE; - - if( !vips_image_get_typeof( autorot->in, VIPS_META_ORIENTATION ) || - vips_image_get_int( autorot->in, VIPS_META_ORIENTATION, &orientation ) ) - orientation = 1; + VipsAngle angle; + gboolean flip; + VipsImage *in; - switch( orientation ) { + in = autorot->in; - case 2: - angle = VIPS_ANGLE_D0; - flip = TRUE; - break; + switch( vips_image_get_orientation( in ) ) { + case 2: + angle = VIPS_ANGLE_D0; + flip = TRUE; + break; - case 4: - flip = TRUE; + case 3: + angle = VIPS_ANGLE_D180; + flip = FALSE; + break; - case 3: - angle = VIPS_ANGLE_D180; - break; - - case 5: - flip = TRUE; + case 4: + angle = VIPS_ANGLE_D180; + flip = TRUE; + break; - case 6: - angle = VIPS_ANGLE_D90; - break; - - case 7: - flip = TRUE; + case 5: + angle = VIPS_ANGLE_D90; + flip = TRUE; + break; - case 8: - angle = VIPS_ANGLE_D270; - break; - - default: - angle = VIPS_ANGLE_D0; - flip = FALSE; - break; - - } + case 6: + angle = VIPS_ANGLE_D90; + flip = FALSE; + break; - g_object_set( object, - "angle", angle, - NULL ); + case 7: + angle = VIPS_ANGLE_D270; + flip = TRUE; + break; - g_object_set( object, - "flip", flip, - NULL ); + case 8: + angle = VIPS_ANGLE_D270; + flip = FALSE; + break; - if( angle != VIPS_ANGLE_D0 && flip) { - if( vips_rot( autorot->in, &t[0], angle, NULL ) ) - return( -1 ); + case 1: + default: + angle = VIPS_ANGLE_D0; + flip = FALSE; + break; - if( vips_flip( t[0], &t[1], VIPS_DIRECTION_HORIZONTAL, NULL ) ) - return ( -1 ); + } - if( vips_copy( t[1], &t[2], NULL ) ) - return( -1 ); - } - else if( angle != VIPS_ANGLE_D0 ) { - if( vips_rot( autorot->in, &t[0], angle, NULL)) - return ( -1 ); + g_object_set( object, + "angle", angle, + "flip", flip, + NULL ); - if( vips_copy( t[0], &t[2], NULL)) - return ( -1 ); - } - else if( flip ) { - if( vips_flip( autorot->in, &t[0], VIPS_DIRECTION_HORIZONTAL, NULL ) ) - return ( -1 ); + if( angle != VIPS_ANGLE_D0 ) { + if( vips_rot( in, &t[0], angle, NULL ) ) + return( -1 ); + in = t[0]; + } - if( vips_copy( t[0], &t[2], NULL ) ) - return( -1 ); - } else { - if( vips_copy( autorot->in, &t[2], NULL ) ) - return( -1 ); - } + if( flip ) { + if( vips_flip( in, &t[1], VIPS_DIRECTION_HORIZONTAL, NULL ) ) + return( -1 ); + in = t[1]; + } - vips_autorot_remove_angle( t[2] ); - - if( vips_image_write( t[2], conversion->out ) ) + /* We must copy before modifying metadata. + */ + if( vips_copy( in, &t[2], NULL ) ) + return( -1 ); + in = t[2]; + + vips_autorot_remove_angle( in ); + + if( vips_image_write( in, conversion->out ) ) return( -1 ); return( 0 ); @@ -265,11 +214,11 @@ vips_autorot_class_init( VipsAutorotClass *class ) VIPS_TYPE_ANGLE, VIPS_ANGLE_D0 ); VIPS_ARG_BOOL( class, "flip", 7, - _( "Flip" ), - _( "Whether the image was flipped or not" ), - VIPS_ARGUMENT_OPTIONAL_OUTPUT, - G_STRUCT_OFFSET( VipsAutorot, flip ), - FALSE); + _( "Flip" ), + _( "Whether the image was flipped or not" ), + VIPS_ARGUMENT_OPTIONAL_OUTPUT, + G_STRUCT_OFFSET( VipsAutorot, flip ), + FALSE ); } static void @@ -288,15 +237,14 @@ vips_autorot_init( VipsAutorot *autorot ) * Optional arguments: * * * @angle: output #VipsAngle the image was rotated by - * * @flip: output whether the image was flipped + * * @flip: output %gboolean whether the image was flipped * - * Look at the image metadata and rotate the image to make it upright. The - * #VIPS_META_ORIENTATION tag is removed from @out to prevent accidental - * double rotation. + * Look at the image metadata and rotate and flip the image to make it + * upright. The #VIPS_META_ORIENTATION tag is removed from @out to prevent + * accidental double rotation. * - * Read @angle to find the amount the image was rotated by. - * - * See also: vips_autorot_get_angle(), vips_autorot_remove_angle(), vips_rot(). + * Read @angle to find the amount the image was rotated by. Read @flip to + * see if the image was also flipped. * * Returns: 0 on success, -1 on error */ diff --git a/libvips/deprecated/rename.c b/libvips/deprecated/rename.c index 28146b94..ad3eaa22 100644 --- a/libvips/deprecated/rename.c +++ b/libvips/deprecated/rename.c @@ -801,3 +801,15 @@ vips_warn( const char *domain, const char *fmt, ... ) va_end( ap ); } +/** + * vips_autorot_get_angle: + * @image: image to fetch orientation from + * + * This function is deprecated. Use vips_autorot() instead. + */ +VipsAngle +vips_autorot_get_angle( VipsImage *im ) +{ + return( VIPS_ANGLE_D0 ); +} + diff --git a/libvips/foreign/exif.c b/libvips/foreign/exif.c index ced41bec..6e6c442d 100644 --- a/libvips/foreign/exif.c +++ b/libvips/foreign/exif.c @@ -523,7 +523,8 @@ vips__exif_parse( VipsImage *image ) int orientation; orientation = atoi( str ); - if( orientation < 1 || orientation > 8 ) + if( orientation < 1 || + orientation > 8 ) orientation = 1; vips_image_set_int( image, VIPS_META_ORIENTATION, orientation ); } diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index a4d53fd8..26accc40 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -830,40 +830,6 @@ read_jpeg_generate( VipsRegion *or, return( 0 ); } -/* Auto-rotate, if rotate_image is set. - */ -static VipsImage * -read_jpeg_rotate( VipsObject *process, VipsImage *im ) -{ - VipsImage **t = (VipsImage **) vips_object_local_array( process, 3 ); - VipsAngle angle = vips_autorot_get_angle( im ); - - if( angle != VIPS_ANGLE_D0 ) { - /* Need to copy to memory or disc, we have to stay seq. - */ - const guint64 image_size = VIPS_IMAGE_SIZEOF_IMAGE( im ); - const guint64 disc_threshold = vips_get_disc_threshold(); - - if( image_size > disc_threshold ) - t[0] = vips_image_new_temp_file( "%s.v" ); - else - t[0] = vips_image_new_memory(); - - if( vips_image_write( im, t[0] ) || - vips_rot( t[0], &t[1], angle, NULL ) ) - return( NULL ); - im = t[1]; - - if( vips_copy( im, &t[2], NULL ) ) - return( NULL ); - im = t[2]; - - vips_autorot_remove_angle( im ); - } - - return( im ); -} - /* Read a cinfo to a VIPS image. */ static int @@ -871,7 +837,7 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out ) { struct jpeg_decompress_struct *cinfo = &jpeg->cinfo; VipsImage **t = (VipsImage **) - vips_object_local_array( VIPS_OBJECT( out ), 3 ); + vips_object_local_array( VIPS_OBJECT( out ), 4 ); VipsImage *im; @@ -902,10 +868,16 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out ) vips_extract_area( t[1], &t[2], 0, 0, jpeg->output_width, jpeg->output_height, NULL ) ) return( -1 ); - im = t[2]; - if( jpeg->autorotate ) - im = read_jpeg_rotate( VIPS_OBJECT( out ), im ); + + if( jpeg->autorotate && + vips_image_get_orientation( im ) != 1 ) { + /* This will go via a huge memory buffer :-( + */ + if( vips_autorot( im, &t[3], NULL ) ) + return( -1 ); + im = t[3]; + } if( vips_image_write( im, out ) ) return( -1 ); @@ -948,15 +920,9 @@ vips__jpeg_read( ReadJpeg *jpeg, VipsImage *out, gboolean header_only ) /* Swap width and height if we're going to rotate this image. */ - if( jpeg->autorotate ) { - VipsAngle angle = vips_autorot_get_angle( out ); - - if( angle == VIPS_ANGLE_D90 || - angle == VIPS_ANGLE_D270 ) - VIPS_SWAP( int, out->Xsize, out->Ysize ); - - /* We won't be returning an orientation tag. - */ + if( jpeg->autorotate && + vips_image_get_orientation_swap( out ) ) { + VIPS_SWAP( int, out->Xsize, out->Ysize ); vips_autorot_remove_angle( out ); } } diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index b622d4e2..34fefd75 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -1862,59 +1862,6 @@ rtiff_seq_stop( void *seq, void *a, void *b ) return( 0 ); } -/* Auto-rotate handling. - */ -static int -rtiff_autorotate( Rtiff *rtiff, VipsImage *in, VipsImage **out ) -{ - VipsAngle angle = vips_autorot_get_angle( in ); - - if( rtiff->autorotate && - angle != VIPS_ANGLE_D0 ) { - /* Need to copy to memory or disc, we have to stay seq. - */ - const guint64 image_size = VIPS_IMAGE_SIZEOF_IMAGE( in ); - const guint64 disc_threshold = vips_get_disc_threshold(); - - VipsImage *im; - VipsImage *x; - - if( image_size > disc_threshold ) - im = vips_image_new_temp_file( "%s.v" ); - else - im = vips_image_new_memory(); - - if( vips_image_write( in, im ) ) { - g_object_unref( im ); - return( -1 ); - } - - if( vips_rot( im, &x, angle, NULL ) ) { - g_object_unref( im ); - return( -1 ); - } - g_object_unref( im ); - im = x; - - if( vips_copy( im, out, NULL ) ) { - g_object_unref( im ); - return( -1 ); - } - g_object_unref( im ); - - /* We must remove the tag to prevent accidental - * double rotations. - */ - vips_autorot_remove_angle( *out ); - } - else { - *out = in; - g_object_ref( in ); - } - - return( 0 ); -} - /* Unpremultiply associative alpha, if any. */ static int @@ -1952,6 +1899,8 @@ rtiff_read_tilewise( Rtiff *rtiff, VipsImage *out ) VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( out ), 4 ); + VipsImage *in; + #ifdef DEBUG printf( "tiff2vips: rtiff_read_tilewise\n" ); #endif /*DEBUG*/ @@ -1991,21 +1940,31 @@ rtiff_read_tilewise( Rtiff *rtiff, VipsImage *out ) */ vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_THINSTRIP, NULL ); - if( vips_image_generate( t[0], - rtiff_seq_start, rtiff_fill_region, rtiff_seq_stop, - rtiff, NULL ) ) - return( -1 ); - - /* Copy to out, adding a cache. Enough tiles for two complete rows. + /* Generate to out, adding a cache. Enough tiles for two complete rows. */ - if( vips_tilecache( t[0], &t[1], - "tile_width", tile_width, - "tile_height", tile_height, - "max_tiles", 2 * (1 + t[0]->Xsize / tile_width), - NULL ) || - rtiff_autorotate( rtiff, t[1], &t[2] ) || - rtiff_unpremultiply( rtiff, t[2], &t[3] ) || - vips_image_write( t[3], out ) ) + if( + vips_image_generate( t[0], + rtiff_seq_start, rtiff_fill_region, rtiff_seq_stop, + rtiff, NULL ) || + vips_tilecache( t[0], &t[1], + "tile_width", tile_width, + "tile_height", tile_height, + "max_tiles", 2 * (1 + t[0]->Xsize / tile_width), + NULL ) || + rtiff_unpremultiply( rtiff, t[1], &t[2] ) ) + return( -1 ); + in = t[2]; + + /* Only do this if we have to. + */ + if( rtiff->autorotate && + vips_image_get_orientation( in ) != 1 ) { + if( vips_autorot( in, &t[3], NULL ) ) + return( -1 ); + in = t[3]; + } + + if( vips_image_write( in, out ) ) return( -1 ); return( 0 ); @@ -2226,6 +2185,8 @@ rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out ) VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( out ), 4 ); + VipsImage *in; + #ifdef DEBUG printf( "tiff2vips: rtiff_read_stripwise\n" ); #endif /*DEBUG*/ @@ -2301,9 +2262,20 @@ rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out ) vips_sequential( t[0], &t[1], "tile_height", rtiff->header.read_height, NULL ) || - rtiff_autorotate( rtiff, t[1], &t[2] ) || - rtiff_unpremultiply( rtiff, t[2], &t[3] ) || - vips_image_write( t[3], out ) ) + rtiff_unpremultiply( rtiff, t[1], &t[2] ) ) + return( -1 ); + in = t[2]; + + /* Only do this if we have to. + */ + if( rtiff->autorotate && + vips_image_get_orientation( in ) != 1 ) { + if( vips_autorot( in, &t[3], NULL ) ) + return( -1 ); + in = t[3]; + } + + if( vips_image_write( in, out ) ) return( -1 ); return( 0 ); @@ -2683,29 +2655,6 @@ rtiff_header_read_all( Rtiff *rtiff ) return( 0 ); } -/* On a header-only read, we can just swap width/height if orientation is 6 or - * 8. - */ -static void -vips__tiff_read_header_orientation( Rtiff *rtiff, VipsImage *out ) -{ - int orientation; - - if( rtiff->autorotate && - vips_image_get_typeof( out, VIPS_META_ORIENTATION ) && - !vips_image_get_int( out, - VIPS_META_ORIENTATION, &orientation ) ) { - if( orientation == 3 || - orientation == 6 ) - VIPS_SWAP( int, out->Xsize, out->Ysize ); - - /* We must remove VIPS_META_ORIENTATION to prevent accidental - * double rotations. - */ - vips_image_remove( out, VIPS_META_ORIENTATION ); - } -} - typedef gboolean (*TiffPropertyFn)( TIFF *tif ); static gboolean @@ -2755,7 +2704,11 @@ vips__tiff_read_header_source( VipsSource *source, VipsImage *out, if( rtiff_set_header( rtiff, out ) ) return( -1 ); - vips__tiff_read_header_orientation( rtiff, out ); + if( rtiff->autorotate && + vips_image_get_orientation_swap( out ) ) { + VIPS_SWAP( int, out->Xsize, out->Ysize ); + vips_autorot_remove_angle( out ); + } /* We never call vips_source_decode() since we need to be able to * seek() the whole way through the file. Just minimise instead, diff --git a/libvips/include/vips/almostdeprecated.h b/libvips/include/vips/almostdeprecated.h index 26d90937..1bde7c84 100644 --- a/libvips/include/vips/almostdeprecated.h +++ b/libvips/include/vips/almostdeprecated.h @@ -292,6 +292,8 @@ void vips_info( const char *domain, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); void vips_vinfo( const char *domain, const char *fmt, va_list ap ); +VipsAngle vips_autorot_get_angle( VipsImage *image ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/include/vips/conversion.h b/libvips/include/vips/conversion.h index 86643e9f..9dfaa521 100644 --- a/libvips/include/vips/conversion.h +++ b/libvips/include/vips/conversion.h @@ -192,7 +192,6 @@ int vips_rot270( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); int vips_rot45( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); -VipsAngle vips_autorot_get_angle( VipsImage *image ); void vips_autorot_remove_angle( VipsImage *image ); int vips_autorot( VipsImage *in, VipsImage **out, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/header.h b/libvips/include/vips/header.h index d4d09037..11e9ea3f 100644 --- a/libvips/include/vips/header.h +++ b/libvips/include/vips/header.h @@ -179,6 +179,8 @@ double vips_image_get_offset( const VipsImage *image ); int vips_image_get_page_height( VipsImage *image ); int vips_image_get_n_pages( VipsImage *image ); int vips_image_get_n_subifds( VipsImage *image ); +int vips_image_get_orientation( VipsImage *image ); +gboolean vips_image_get_orientation_swap( VipsImage *image ); const void *vips_image_get_data( VipsImage *image ); void vips_image_init_fields( VipsImage *image, diff --git a/libvips/iofuncs/header.c b/libvips/iofuncs/header.c index 230dfeb7..4109b5d4 100644 --- a/libvips/iofuncs/header.c +++ b/libvips/iofuncs/header.c @@ -863,6 +863,47 @@ vips_image_get_n_subifds( VipsImage *image ) return( 0 ); } +/** + * vips_image_get_orientation: (method) + * @image: image to get from + * + * Fetch and sanity-check #VIPS_META_ORIENTATION. Default to 1 (no rotate, + * no flip) if not present or crazy. + * + * Returns: the image orientation. + */ +int +vips_image_get_orientation( VipsImage *image ) +{ + int orientation; + + if( vips_image_get_typeof( image, VIPS_META_ORIENTATION ) && + !vips_image_get_int( image, VIPS_META_ORIENTATION, + &orientation ) && + orientation > 0 && + orientation < 9 ) + return( orientation ); + + return( 1 ); +} + +/** + * vips_image_get_orientation_swap: (method) + * @image: image to get from + * + * Return %TRUE if applying the orientation would swap width and height. + * + * Returns: if width/height will swap + */ +gboolean +vips_image_get_orientation_swap( VipsImage *image ) +{ + int orientation = vips_image_get_orientation( image ); + + return( orientation >= 5 && + orientation <= 8 ); +} + /** * vips_image_get_data: (method) * @image: image to get data for @@ -957,14 +998,11 @@ static int meta_cp( VipsImage *dst, const VipsImage *src ) { if( src->meta ) { - /* Loop, copying fields. - */ - meta_init( dst ); - /* We lock with vips_image_set() to stop races in highly- * threaded applications. */ g_mutex_lock( vips__meta_lock ); + meta_init( dst ); vips_slist_map2( src->meta_traverse, (VipsSListMap2Fn) meta_cp_field, dst, NULL ); g_mutex_unlock( vips__meta_lock ); @@ -1055,8 +1093,6 @@ vips_image_set( VipsImage *image, const char *name, GValue *value ) g_assert( name ); g_assert( value ); - meta_init( image ); - /* We lock between modifying metadata and copying metadata between * images, see meta_cp(). * @@ -1065,6 +1101,7 @@ vips_image_set( VipsImage *image, const char *name, GValue *value ) * highly-threaded applications. */ g_mutex_lock( vips__meta_lock ); + meta_init( image ); (void) meta_new( image, name, value ); g_mutex_unlock( vips__meta_lock ); diff --git a/libvips/resample/thumbnail.c b/libvips/resample/thumbnail.c index 25e5beda..25a75135 100644 --- a/libvips/resample/thumbnail.c +++ b/libvips/resample/thumbnail.c @@ -116,7 +116,8 @@ typedef struct _VipsThumbnail { int input_width; int input_height; int page_height; - VipsAngle angle; /* From vips_autorot_get_angle() */ + int orientation; /* From vips_image_get_orientation() */ + gboolean swap; /* If we must swap width / height */ int n_pages; /* Pages in file */ int n_loaded_pages; /* Pages we've loaded from file */ int n_subifds; /* Number of subifds */ @@ -203,7 +204,8 @@ vips_thumbnail_read_header( VipsThumbnail *thumbnail, VipsImage *image ) { thumbnail->input_width = image->Xsize; thumbnail->input_height = image->Ysize; - thumbnail->angle = vips_autorot_get_angle( image ); + thumbnail->orientation = vips_image_get_orientation( image ); + thumbnail->swap = vips_image_get_orientation_swap( image ); thumbnail->page_height = vips_image_get_page_height( image ); thumbnail->n_pages = vips_image_get_n_pages( image ); thumbnail->n_subifds = vips_image_get_n_subifds( image ); @@ -390,11 +392,10 @@ static void vips_thumbnail_calculate_shrink( VipsThumbnail *thumbnail, int input_width, int input_height, double *hshrink, double *vshrink ) { - /* If we will be rotating, swap the target width and height. + /* If we will be rotating, swap the target width and height. */ gboolean rotate = - (thumbnail->angle == VIPS_ANGLE_D90 || - thumbnail->angle == VIPS_ANGLE_D270) && + thumbnail->swap && thumbnail->auto_rotate; int target_width = rotate ? thumbnail->height : thumbnail->width; @@ -831,21 +832,12 @@ vips_thumbnail_build( VipsObject *object ) } if( thumbnail->auto_rotate && - thumbnail->angle != VIPS_ANGLE_D0 ) { - VipsAngle angle = vips_autorot_get_angle( in ); - - g_info( "rotating by %s", - vips_enum_nick( VIPS_TYPE_ANGLE, angle ) ); - - /* Need to copy to memory, we have to stay seq. - */ - if( !(t[9] = vips_image_copy_memory( in )) || - vips_rot( t[9], &t[10], angle, NULL ) || - vips_copy( t[10], &t[14], NULL ) ) - return( -1 ); + thumbnail->orientation != 1 ) { + g_info( "rotating by EXIF orientation %d", + thumbnail->orientation ); + if( vips_autorot( in, &t[14], NULL ) ) + return( -1 ); in = t[14]; - - vips_autorot_remove_angle( in ); } /* Crop after rotate so we don't need to rotate the crop box.