libheif autorotate is back

Using the new heif_image_handle_get_ispe_width() to fetch the untransformed
dimensions.

See:

https://github.com/strukturag/libheif/issues/106#issuecomment-459766087

https://github.com/libvips/libvips/issues/1247
This commit is contained in:
John Cupitt 2019-03-06 20:32:34 +00:00
parent 3010a4a865
commit 533ddbc8b3
2 changed files with 88 additions and 18 deletions

View File

@ -939,6 +939,18 @@ if test x"$with_heif" = x"yes"; then
LIBS="$save_LIBS" LIBS="$save_LIBS"
fi fi
# fetch untransformed width/height added in 1.3.4
if test x"$with_heif" = x"yes"; then
save_LIBS="$LIBS"
LIBS="$LIBS $HEIF_LIBS"
AC_CHECK_FUNCS(heif_image_handle_get_ispe_width,[
AC_DEFINE(HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH,1,
[define if you have heif_image_handle_get_ispe_width.])
],[]
)
LIBS="$save_LIBS"
fi
# pdfium # pdfium
AC_ARG_WITH([pdfium], AC_ARG_WITH([pdfium],
AS_HELP_STRING([--without-pdfium], [build without pdfium (default: test)])) AS_HELP_STRING([--without-pdfium], [build without pdfium (default: test)]))

View File

@ -56,17 +56,6 @@
#include "pforeign.h" #include "pforeign.h"
/* FIXME ... autorotate
*
* This is very hard to support properly with libheif. There's a thing to
* disable autorot on decode, but the dimensions you get from querying the
* image handle sometimes do and sometimes don't apply this rotation for you,
* depending on the image. And the only way to discover if an image has an
* orientation is to query the exif, which is not reliable.
*
* Perhaps we'll be able to support this in a future libheif.
*/
typedef struct _VipsForeignLoadHeif { typedef struct _VipsForeignLoadHeif {
VipsForeignLoad parent_object; VipsForeignLoad parent_object;
@ -80,6 +69,10 @@ typedef struct _VipsForeignLoadHeif {
*/ */
gboolean thumbnail; gboolean thumbnail;
/* Apply any orientation tags in the header.
*/
gboolean autorotate;
/* Context for this image. /* Context for this image.
*/ */
struct heif_context *ctx; struct heif_context *ctx;
@ -380,9 +373,12 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
} }
#endif /*HAVE_HEIF_COLOR_PROFILE*/ #endif /*HAVE_HEIF_COLOR_PROFILE*/
/* We always use libheif's autorotate, so remove the exif one. /* If we are using libheif's autorotate, remove the exif one.
*/ */
vips_autorot_remove_angle( out ); #ifdef HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH
if( heif->autorotate )
vips_autorot_remove_angle( out );
#endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/
vips_image_set_int( out, "heif-primary", heif->primary_page ); vips_image_set_int( out, "heif-primary", heif->primary_page );
vips_image_set_int( out, "n-pages", heif->n_top ); vips_image_set_int( out, "n-pages", heif->n_top );
@ -400,6 +396,38 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
return( 0 ); return( 0 );
} }
static int
vips_foreign_load_heif_get_width( VipsForeignLoadHeif *heif )
{
int width;
/* _get_ipse_width() fetches the untransformed dimension, but was only
* added in 1.3.4. Without it, we just use the transformed dimension
* and have to autorotate.
*/
width = heif_image_handle_get_width( heif->handle );
#ifdef HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH
if( !heif->autorotate )
width = heif_image_handle_get_ispe_width( heif->handle );
#endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/
return( width );
}
static int
vips_foreign_load_heif_get_height( VipsForeignLoadHeif *heif )
{
int height;
height = heif_image_handle_get_height( heif->handle );
#ifdef HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH
if( !heif->autorotate )
height = heif_image_handle_get_ispe_height( heif->handle );
#endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/
return( height );
}
static int static int
vips_foreign_load_heif_header( VipsForeignLoad *load ) vips_foreign_load_heif_header( VipsForeignLoad *load )
{ {
@ -485,15 +513,15 @@ vips_foreign_load_heif_header( VipsForeignLoad *load )
if( vips_foreign_load_heif_set_page( heif, if( vips_foreign_load_heif_set_page( heif,
heif->page, heif->thumbnail ) ) heif->page, heif->thumbnail ) )
return( -1 ); return( -1 );
heif->page_width = heif_image_handle_get_width( heif->handle ); heif->page_width = vips_foreign_load_heif_get_width( heif );
heif->page_height = heif_image_handle_get_height( heif->handle ); heif->page_height = vips_foreign_load_heif_get_height( heif );
for( i = heif->page + 1; i < heif->page + heif->n; i++ ) { for( i = heif->page + 1; i < heif->page + heif->n; i++ ) {
if( vips_foreign_load_heif_set_page( heif, if( vips_foreign_load_heif_set_page( heif,
i, heif->thumbnail ) ) i, heif->thumbnail ) )
return( -1 ); return( -1 );
if( heif_image_handle_get_width( heif->handle ) != if( vips_foreign_load_heif_get_width( heif ) !=
heif->page_width || heif->page_width ||
heif_image_handle_get_height( heif->handle ) != vips_foreign_load_heif_get_height( heif ) !=
heif->page_height ) { heif->page_height ) {
vips_error( class->nickname, "%s", vips_error( class->nickname, "%s",
_( "not all pages are the same size" ) ); _( "not all pages are the same size" ) );
@ -560,8 +588,14 @@ vips_foreign_load_heif_generate( VipsRegion *or,
* *
* FIXME What will this do for RGBA? Or is alpha always * FIXME What will this do for RGBA? Or is alpha always
* separate? * separate?
*
* Only disable transforms if we have something to fetch the
* untransformed size.
*/ */
options = heif_decoding_options_alloc(); options = heif_decoding_options_alloc();
#ifdef HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH
options->ignore_transformations = !heif->autorotate;
#endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/
error = heif_decode_image( heif->handle, &heif->img, error = heif_decode_image( heif->handle, &heif->img,
heif_colorspace_RGB, heif_chroma_interleaved_RGB, heif_colorspace_RGB, heif_chroma_interleaved_RGB,
options ); options );
@ -617,13 +651,30 @@ vips_foreign_load_heif_generate( VipsRegion *or,
#endif /*DEBUG*/ #endif /*DEBUG*/
} }
if( !heif->data ) if( !heif->data ) {
int image_width = heif_image_get_width( heif->img,
heif_channel_interleaved );
int image_height = heif_image_get_height( heif->img,
heif_channel_interleaved );
/* We can sometimes get inconsistency between the dimensions
* reported on the handle, and the final image we fetch. Error
* out to prevent a segv.
*/
if( image_width != heif->page_width ||
image_height != heif->page_height ) {
vips_error( class->nickname,
"%s", _( "bad image dimensions on decode" ) );
return( -1 );
}
if( !(heif->data = heif_image_get_plane_readonly( heif->img, if( !(heif->data = heif_image_get_plane_readonly( heif->img,
heif_channel_interleaved, &heif->stride )) ) { heif_channel_interleaved, &heif->stride )) ) {
vips_error( class->nickname, vips_error( class->nickname,
"%s", _( "unable to get image data" ) ); "%s", _( "unable to get image data" ) );
return( -1 ); return( -1 );
} }
}
memcpy( VIPS_REGION_ADDR( or, 0, r->top ), memcpy( VIPS_REGION_ADDR( or, 0, r->top ),
heif->data + heif->stride * line, heif->data + heif->stride * line,
@ -695,6 +746,13 @@ vips_foreign_load_heif_class_init( VipsForeignLoadHeifClass *class )
G_STRUCT_OFFSET( VipsForeignLoadHeif, thumbnail ), G_STRUCT_OFFSET( VipsForeignLoadHeif, thumbnail ),
FALSE ); FALSE );
VIPS_ARG_BOOL( class, "autorotate", 21,
_( "Autorotate" ),
_( "Rotate image using exif orientation" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadHeif, autorotate ),
FALSE );
} }
static void static void