revise autorot system

- deprecate vips_autorot_get_angle() since orientation is no longer a
simple rotate
- add vips_image_get_orientation() and vips_image_get_orientation_swap()
- revise tiff and jpeg loader autorotate to just call vips_autorot(),
but only if necessary
- revise thumbnail autorotate too
This commit is contained in:
John Cupitt 2020-06-06 17:25:46 +01:00
parent 30386db775
commit 981d5c4b16
11 changed files with 213 additions and 299 deletions

View File

@ -24,6 +24,8 @@
behaviour with alpha channels behaviour with alpha channels
- improve bioformats support with read and write of tiff subifd pyramids - improve bioformats support with read and write of tiff subifd pyramids
- thumbnail exploits 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 24/4/20 started 8.9.3
- better iiif tile naming [IllyaMoskvin] - better iiif tile naming [IllyaMoskvin]

View File

@ -8,6 +8,7 @@
* - don't remove orientation if it's one of the cases we don't handle * - don't remove orientation if it's one of the cases we don't handle
* 10/5/20 * 10/5/20
* - handle mirrored images * - handle mirrored images
* - deprecate vips_autorot_get_angle()
*/ */
/* /*
@ -56,8 +57,7 @@ typedef struct _VipsAutorot {
VipsImage *in; VipsImage *in;
VipsAngle angle; VipsAngle angle;
gboolean flip;
gboolean flip;
} VipsAutorot; } VipsAutorot;
@ -65,51 +65,6 @@ typedef VipsConversionClass VipsAutorotClass;
G_DEFINE_TYPE( VipsAutorot, vips_autorot, VIPS_TYPE_CONVERSION ); 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 * static void *
vips_autorot_remove_angle_sub( VipsImage *image, vips_autorot_remove_angle_sub( VipsImage *image,
const char *field, GValue *value, void *my_data ) const char *field, GValue *value, void *my_data )
@ -130,14 +85,14 @@ vips_autorot_remove_angle_sub( VipsImage *image,
* vips_autorot_remove_angle: (method) * vips_autorot_remove_angle: (method)
* @image: image to remove orientation from * @image: image to remove orientation from
* *
* Remove the orientation tag on @image. Also remove any exif orientation tags. * Remove the orientation tag on @image. Also remove any exif orientation tags.
* * You must vips_copy() the image before calling this function since it
* See also: vips_autorot_get_angle(). * modifies metadata.
*/ */
void void
vips_autorot_remove_angle( VipsImage *image ) 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 ); (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 ) ) if( VIPS_OBJECT_CLASS( vips_autorot_parent_class )->build( object ) )
return( -1 ); return( -1 );
int orientation = 0; VipsAngle angle;
VipsAngle angle; gboolean flip;
gboolean flip = FALSE; VipsImage *in;
if( !vips_image_get_typeof( autorot->in, VIPS_META_ORIENTATION ) ||
vips_image_get_int( autorot->in, VIPS_META_ORIENTATION, &orientation ) )
orientation = 1;
switch( orientation ) { in = autorot->in;
case 2: switch( vips_image_get_orientation( in ) ) {
angle = VIPS_ANGLE_D0; case 2:
flip = TRUE; angle = VIPS_ANGLE_D0;
break; flip = TRUE;
break;
case 4: case 3:
flip = TRUE; angle = VIPS_ANGLE_D180;
flip = FALSE;
break;
case 3: case 4:
angle = VIPS_ANGLE_D180; angle = VIPS_ANGLE_D180;
break; flip = TRUE;
break;
case 5:
flip = TRUE;
case 6: case 5:
angle = VIPS_ANGLE_D90; angle = VIPS_ANGLE_D90;
break; flip = TRUE;
break;
case 7:
flip = TRUE;
case 8: case 6:
angle = VIPS_ANGLE_D270; angle = VIPS_ANGLE_D90;
break; flip = FALSE;
break;
default:
angle = VIPS_ANGLE_D0;
flip = FALSE;
break;
}
g_object_set( object, case 7:
"angle", angle, angle = VIPS_ANGLE_D270;
NULL ); flip = TRUE;
break;
g_object_set( object, case 8:
"flip", flip, angle = VIPS_ANGLE_D270;
NULL ); flip = FALSE;
break;
if( angle != VIPS_ANGLE_D0 && flip) { case 1:
if( vips_rot( autorot->in, &t[0], angle, NULL ) ) default:
return( -1 ); 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 ) ) g_object_set( object,
return( -1 ); "angle", angle,
} "flip", flip,
else if( angle != VIPS_ANGLE_D0 ) { NULL );
if( vips_rot( autorot->in, &t[0], angle, NULL))
return ( -1 );
if( vips_copy( t[0], &t[2], NULL)) if( angle != VIPS_ANGLE_D0 ) {
return ( -1 ); if( vips_rot( in, &t[0], angle, NULL ) )
} return( -1 );
else if( flip ) { in = t[0];
if( vips_flip( autorot->in, &t[0], VIPS_DIRECTION_HORIZONTAL, NULL ) ) }
return ( -1 );
if( vips_copy( t[0], &t[2], NULL ) ) if( flip ) {
return( -1 ); if( vips_flip( in, &t[1], VIPS_DIRECTION_HORIZONTAL, NULL ) )
} else { return( -1 );
if( vips_copy( autorot->in, &t[2], NULL ) ) in = t[1];
return( -1 ); }
}
vips_autorot_remove_angle( t[2] ); /* We must copy before modifying metadata.
*/
if( vips_image_write( t[2], conversion->out ) ) 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( -1 );
return( 0 ); return( 0 );
@ -265,11 +214,11 @@ vips_autorot_class_init( VipsAutorotClass *class )
VIPS_TYPE_ANGLE, VIPS_ANGLE_D0 ); VIPS_TYPE_ANGLE, VIPS_ANGLE_D0 );
VIPS_ARG_BOOL( class, "flip", 7, VIPS_ARG_BOOL( class, "flip", 7,
_( "Flip" ), _( "Flip" ),
_( "Whether the image was flipped or not" ), _( "Whether the image was flipped or not" ),
VIPS_ARGUMENT_OPTIONAL_OUTPUT, VIPS_ARGUMENT_OPTIONAL_OUTPUT,
G_STRUCT_OFFSET( VipsAutorot, flip ), G_STRUCT_OFFSET( VipsAutorot, flip ),
FALSE); FALSE );
} }
static void static void
@ -288,15 +237,14 @@ vips_autorot_init( VipsAutorot *autorot )
* Optional arguments: * Optional arguments:
* *
* * @angle: output #VipsAngle the image was rotated by * * @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 * Look at the image metadata and rotate and flip the image to make it
* #VIPS_META_ORIENTATION tag is removed from @out to prevent accidental * upright. The #VIPS_META_ORIENTATION tag is removed from @out to prevent
* double rotation. * accidental double rotation.
* *
* Read @angle to find the amount the image was rotated by. * Read @angle to find the amount the image was rotated by. Read @flip to
* * see if the image was also flipped.
* See also: vips_autorot_get_angle(), vips_autorot_remove_angle(), vips_rot().
* *
* Returns: 0 on success, -1 on error * Returns: 0 on success, -1 on error
*/ */

View File

@ -801,3 +801,15 @@ vips_warn( const char *domain, const char *fmt, ... )
va_end( ap ); 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 );
}

View File

@ -523,7 +523,8 @@ vips__exif_parse( VipsImage *image )
int orientation; int orientation;
orientation = atoi( str ); orientation = atoi( str );
if( orientation < 1 || orientation > 8 ) if( orientation < 1 ||
orientation > 8 )
orientation = 1; orientation = 1;
vips_image_set_int( image, VIPS_META_ORIENTATION, orientation ); vips_image_set_int( image, VIPS_META_ORIENTATION, orientation );
} }

View File

@ -830,40 +830,6 @@ read_jpeg_generate( VipsRegion *or,
return( 0 ); 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. /* Read a cinfo to a VIPS image.
*/ */
static int static int
@ -871,7 +837,7 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out )
{ {
struct jpeg_decompress_struct *cinfo = &jpeg->cinfo; struct jpeg_decompress_struct *cinfo = &jpeg->cinfo;
VipsImage **t = (VipsImage **) VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 3 ); vips_object_local_array( VIPS_OBJECT( out ), 4 );
VipsImage *im; VipsImage *im;
@ -902,10 +868,16 @@ read_jpeg_image( ReadJpeg *jpeg, VipsImage *out )
vips_extract_area( t[1], &t[2], vips_extract_area( t[1], &t[2],
0, 0, jpeg->output_width, jpeg->output_height, NULL ) ) 0, 0, jpeg->output_width, jpeg->output_height, NULL ) )
return( -1 ); return( -1 );
im = t[2]; 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 ) ) if( vips_image_write( im, out ) )
return( -1 ); 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. /* Swap width and height if we're going to rotate this image.
*/ */
if( jpeg->autorotate ) { if( jpeg->autorotate &&
VipsAngle angle = vips_autorot_get_angle( out ); vips_image_get_orientation_swap( out ) ) {
VIPS_SWAP( int, out->Xsize, out->Ysize );
if( angle == VIPS_ANGLE_D90 ||
angle == VIPS_ANGLE_D270 )
VIPS_SWAP( int, out->Xsize, out->Ysize );
/* We won't be returning an orientation tag.
*/
vips_autorot_remove_angle( out ); vips_autorot_remove_angle( out );
} }
} }

View File

@ -1862,59 +1862,6 @@ rtiff_seq_stop( void *seq, void *a, void *b )
return( 0 ); 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. /* Unpremultiply associative alpha, if any.
*/ */
static int static int
@ -1952,6 +1899,8 @@ rtiff_read_tilewise( Rtiff *rtiff, VipsImage *out )
VipsImage **t = (VipsImage **) VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 4 ); vips_object_local_array( VIPS_OBJECT( out ), 4 );
VipsImage *in;
#ifdef DEBUG #ifdef DEBUG
printf( "tiff2vips: rtiff_read_tilewise\n" ); printf( "tiff2vips: rtiff_read_tilewise\n" );
#endif /*DEBUG*/ #endif /*DEBUG*/
@ -1991,21 +1940,31 @@ rtiff_read_tilewise( Rtiff *rtiff, VipsImage *out )
*/ */
vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_THINSTRIP, NULL ); vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_THINSTRIP, NULL );
if( vips_image_generate( t[0], /* Generate to out, adding a cache. Enough tiles for two complete rows.
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.
*/ */
if( vips_tilecache( t[0], &t[1], if(
"tile_width", tile_width, vips_image_generate( t[0],
"tile_height", tile_height, rtiff_seq_start, rtiff_fill_region, rtiff_seq_stop,
"max_tiles", 2 * (1 + t[0]->Xsize / tile_width), rtiff, NULL ) ||
NULL ) || vips_tilecache( t[0], &t[1],
rtiff_autorotate( rtiff, t[1], &t[2] ) || "tile_width", tile_width,
rtiff_unpremultiply( rtiff, t[2], &t[3] ) || "tile_height", tile_height,
vips_image_write( t[3], out ) ) "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( -1 );
return( 0 ); return( 0 );
@ -2226,6 +2185,8 @@ rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out )
VipsImage **t = (VipsImage **) VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 4 ); vips_object_local_array( VIPS_OBJECT( out ), 4 );
VipsImage *in;
#ifdef DEBUG #ifdef DEBUG
printf( "tiff2vips: rtiff_read_stripwise\n" ); printf( "tiff2vips: rtiff_read_stripwise\n" );
#endif /*DEBUG*/ #endif /*DEBUG*/
@ -2301,9 +2262,20 @@ rtiff_read_stripwise( Rtiff *rtiff, VipsImage *out )
vips_sequential( t[0], &t[1], vips_sequential( t[0], &t[1],
"tile_height", rtiff->header.read_height, "tile_height", rtiff->header.read_height,
NULL ) || NULL ) ||
rtiff_autorotate( rtiff, t[1], &t[2] ) || rtiff_unpremultiply( rtiff, t[1], &t[2] ) )
rtiff_unpremultiply( rtiff, t[2], &t[3] ) || return( -1 );
vips_image_write( t[3], out ) ) 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( -1 );
return( 0 ); return( 0 );
@ -2683,29 +2655,6 @@ rtiff_header_read_all( Rtiff *rtiff )
return( 0 ); 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 ); typedef gboolean (*TiffPropertyFn)( TIFF *tif );
static gboolean static gboolean
@ -2755,7 +2704,11 @@ vips__tiff_read_header_source( VipsSource *source, VipsImage *out,
if( rtiff_set_header( rtiff, out ) ) if( rtiff_set_header( rtiff, out ) )
return( -1 ); 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 /* We never call vips_source_decode() since we need to be able to
* seek() the whole way through the file. Just minimise instead, * seek() the whole way through the file. Just minimise instead,

View File

@ -292,6 +292,8 @@ void vips_info( const char *domain, const char *fmt, ... )
__attribute__((format(printf, 2, 3))); __attribute__((format(printf, 2, 3)));
void vips_vinfo( const char *domain, const char *fmt, va_list ap ); void vips_vinfo( const char *domain, const char *fmt, va_list ap );
VipsAngle vips_autorot_get_angle( VipsImage *image );
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /*__cplusplus*/ #endif /*__cplusplus*/

View File

@ -192,7 +192,6 @@ int vips_rot270( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel)); __attribute__((sentinel));
int vips_rot45( VipsImage *in, VipsImage **out, ... ) int vips_rot45( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel)); __attribute__((sentinel));
VipsAngle vips_autorot_get_angle( VipsImage *image );
void vips_autorot_remove_angle( VipsImage *image ); void vips_autorot_remove_angle( VipsImage *image );
int vips_autorot( VipsImage *in, VipsImage **out, ... ) int vips_autorot( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel)); __attribute__((sentinel));

View File

@ -179,6 +179,8 @@ double vips_image_get_offset( const VipsImage *image );
int vips_image_get_page_height( VipsImage *image ); int vips_image_get_page_height( VipsImage *image );
int vips_image_get_n_pages( VipsImage *image ); int vips_image_get_n_pages( VipsImage *image );
int vips_image_get_n_subifds( 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 ); const void *vips_image_get_data( VipsImage *image );
void vips_image_init_fields( VipsImage *image, void vips_image_init_fields( VipsImage *image,

View File

@ -863,6 +863,47 @@ vips_image_get_n_subifds( VipsImage *image )
return( 0 ); 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) * vips_image_get_data: (method)
* @image: image to get data for * @image: image to get data for
@ -957,14 +998,11 @@ static int
meta_cp( VipsImage *dst, const VipsImage *src ) meta_cp( VipsImage *dst, const VipsImage *src )
{ {
if( src->meta ) { if( src->meta ) {
/* Loop, copying fields.
*/
meta_init( dst );
/* We lock with vips_image_set() to stop races in highly- /* We lock with vips_image_set() to stop races in highly-
* threaded applications. * threaded applications.
*/ */
g_mutex_lock( vips__meta_lock ); g_mutex_lock( vips__meta_lock );
meta_init( dst );
vips_slist_map2( src->meta_traverse, vips_slist_map2( src->meta_traverse,
(VipsSListMap2Fn) meta_cp_field, dst, NULL ); (VipsSListMap2Fn) meta_cp_field, dst, NULL );
g_mutex_unlock( vips__meta_lock ); 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( name );
g_assert( value ); g_assert( value );
meta_init( image );
/* We lock between modifying metadata and copying metadata between /* We lock between modifying metadata and copying metadata between
* images, see meta_cp(). * images, see meta_cp().
* *
@ -1065,6 +1101,7 @@ vips_image_set( VipsImage *image, const char *name, GValue *value )
* highly-threaded applications. * highly-threaded applications.
*/ */
g_mutex_lock( vips__meta_lock ); g_mutex_lock( vips__meta_lock );
meta_init( image );
(void) meta_new( image, name, value ); (void) meta_new( image, name, value );
g_mutex_unlock( vips__meta_lock ); g_mutex_unlock( vips__meta_lock );

View File

@ -116,7 +116,8 @@ typedef struct _VipsThumbnail {
int input_width; int input_width;
int input_height; int input_height;
int page_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_pages; /* Pages in file */
int n_loaded_pages; /* Pages we've loaded from file */ int n_loaded_pages; /* Pages we've loaded from file */
int n_subifds; /* Number of subifds */ 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_width = image->Xsize;
thumbnail->input_height = image->Ysize; 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->page_height = vips_image_get_page_height( image );
thumbnail->n_pages = vips_image_get_n_pages( image ); thumbnail->n_pages = vips_image_get_n_pages( image );
thumbnail->n_subifds = vips_image_get_n_subifds( image ); thumbnail->n_subifds = vips_image_get_n_subifds( image );
@ -390,11 +392,10 @@ static void
vips_thumbnail_calculate_shrink( VipsThumbnail *thumbnail, vips_thumbnail_calculate_shrink( VipsThumbnail *thumbnail,
int input_width, int input_height, double *hshrink, double *vshrink ) 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 = gboolean rotate =
(thumbnail->angle == VIPS_ANGLE_D90 || thumbnail->swap &&
thumbnail->angle == VIPS_ANGLE_D270) &&
thumbnail->auto_rotate; thumbnail->auto_rotate;
int target_width = rotate ? int target_width = rotate ?
thumbnail->height : thumbnail->width; thumbnail->height : thumbnail->width;
@ -831,21 +832,12 @@ vips_thumbnail_build( VipsObject *object )
} }
if( thumbnail->auto_rotate && if( thumbnail->auto_rotate &&
thumbnail->angle != VIPS_ANGLE_D0 ) { thumbnail->orientation != 1 ) {
VipsAngle angle = vips_autorot_get_angle( in ); g_info( "rotating by EXIF orientation %d",
thumbnail->orientation );
g_info( "rotating by %s", if( vips_autorot( in, &t[14], NULL ) )
vips_enum_nick( VIPS_TYPE_ANGLE, angle ) ); return( -1 );
/* 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 );
in = t[14]; in = t[14];
vips_autorot_remove_angle( in );
} }
/* Crop after rotate so we don't need to rotate the crop box. /* Crop after rotate so we don't need to rotate the crop box.