From c7e0c073ca544e855cb219871ecb9df9f24f1706 Mon Sep 17 00:00:00 2001 From: DarthSim Date: Mon, 5 Aug 2019 20:07:13 +0600 Subject: [PATCH] Add `optimize_gif_frames` and `optimize_gif_transparency` options to `vips_magicksave` --- configure.ac | 2 +- libvips/foreign/magick.c | 62 ++++++++++++++++++++++++++++++++++++ libvips/foreign/magick.h | 4 +++ libvips/foreign/magicksave.c | 49 ++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index e5cac45b..7f2b6b11 100644 --- a/configure.ac +++ b/configure.ac @@ -692,7 +692,7 @@ if test x"$magick6" = x"yes"; then # IM save_LIBS="$LIBS" LIBS="$LIBS $MAGICK_LIBS" - AC_CHECK_FUNCS([InheritException AcquireExceptionInfo SetImageProperty SetImageExtent AcquireImage GetVirtualPixels ResetImageProfileIterator ResetImageAttributeIterator ResetImagePropertyIterator MagickCoreGenesis SetImageOption BlobToStringInfo]) + AC_CHECK_FUNCS([InheritException AcquireExceptionInfo SetImageProperty SetImageExtent AcquireImage GetVirtualPixels ResetImageProfileIterator ResetImageAttributeIterator ResetImagePropertyIterator MagickCoreGenesis SetImageOption BlobToStringInfo OptimizePlusImageLayers OptimizeImageTransparency]) LIBS="$save_LIBS" fi diff --git a/libvips/foreign/magick.c b/libvips/foreign/magick.c index 5d1312e9..304809d7 100644 --- a/libvips/foreign/magick.c +++ b/libvips/foreign/magick.c @@ -205,6 +205,31 @@ magick_set_number_scenes( ImageInfo *image_info, int scene, int number_scenes ) image_info->scenes = strdup( page ); } +int +magick_optimize_image_layers( Image **images, ExceptionInfo *exception ) +{ + Image *tmp; + + tmp = OptimizePlusImageLayers(*images, exception ); + + if ( exception->severity != UndefinedException ) + return MagickFalse; + + VIPS_FREEF( DestroyImageList, *images ); + + *images = tmp; + + return MagickTrue; +} + +int +magick_optimize_image_transparency( const Image *images, + ExceptionInfo *exception ) +{ + OptimizeImageTransparency(images, exception); + return ( exception->severity == UndefinedException ); +} + /* Does a few bytes look like a file IM can handle? */ gboolean @@ -445,6 +470,43 @@ magick_set_number_scenes( ImageInfo *image_info, int scene, int number_scenes ) #endif } +int +magick_optimize_image_layers( Image **images, ExceptionInfo *exception ) +{ +#ifdef HAS_OPTIMIZEPLUSIMAGELAYERS + Image *tmp; + + tmp = OptimizePlusImageLayers(*images, exception ); + + if ( exception->severity != UndefinedException ) + return MagickFalse; + + VIPS_FREEF( DestroyImageList, *images ); + + *images = tmp; + + return MagickTrue; +#else + g_warning( "%s", _( "layers optimization is not supported by your version " + "of libMagick" ) ); + return MagickTrue; +#endif +} + +int +magick_optimize_image_transparency( const Image *images, + ExceptionInfo *exception ) +{ +#ifdef HAS_OPTIMIZEIMAGETRANSPARENCY + OptimizeImageTransparency(images, exception); + return ( exception->severity == UndefinedException ); +#else + g_warning( "%s", _( "transparency optimization is not supported by your " + "version of libMagick" ) ); + return MagickTrue; +#endif +} + /* Does a few bytes look like a file IM can handle? */ gboolean diff --git a/libvips/foreign/magick.h b/libvips/foreign/magick.h index 05304134..21b56bed 100644 --- a/libvips/foreign/magick.h +++ b/libvips/foreign/magick.h @@ -84,6 +84,10 @@ int magick_set_vips_profile( VipsImage *im, Image *image ); int magick_set_magick_profile( Image *image, VipsImage *im, ExceptionInfo *exception ); +int magick_optimize_image_layers( Image **images, ExceptionInfo *exception ); +int magick_optimize_image_transparency( const Image *images, + ExceptionInfo *exception ); + gboolean magick_ismagick( const unsigned char *bytes, size_t length ); #endif /*HAVE_MAGICK6*/ diff --git a/libvips/foreign/magicksave.c b/libvips/foreign/magicksave.c index 68a80e31..78142784 100644 --- a/libvips/foreign/magicksave.c +++ b/libvips/foreign/magicksave.c @@ -10,6 +10,8 @@ * - support "strip" option * 6/7/19 [deftomat] * - support array of delays + * 5/8/19 DarthSim + * - support GIF optimization */ /* @@ -63,6 +65,8 @@ typedef struct _VipsForeignSaveMagick { char *filename; /* NULL during buffer output */ char *format; int quality; + gboolean optimize_gif_frames; + gboolean optimize_gif_transparency; ImageInfo *image_info; ExceptionInfo *exception; @@ -365,6 +369,24 @@ vips_foreign_save_magick_build( VipsObject *object ) vips_foreign_save_magick_write_block, magick ) ) return( -1 ); + if( magick->optimize_gif_frames ) { + if( !magick_optimize_image_layers(&magick->images, magick->exception ) ) { + magick_inherit_exception( magick->exception, magick->images ); + magick_vips_error( class->nickname, magick->exception ); + + return( -1 ); + } + } + + if( magick->optimize_gif_transparency ) { + if( !magick_optimize_image_transparency(magick->images, magick->exception) ) { + magick_inherit_exception( magick->exception, magick->images ); + magick_vips_error( class->nickname, magick->exception ); + + return( -1 ); + } + } + return( 0 ); } @@ -428,6 +450,20 @@ vips_foreign_save_magick_class_init( VipsForeignSaveMagickClass *class ) VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignSaveMagick, quality ), 0, 100, 0 ); + + VIPS_ARG_BOOL( class, "optimize_gif_frames", 4, + _( "Optimize_gif_frames" ), + _( "Apply GIF frames optimization" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveMagick, optimize_gif_frames ), + FALSE ); + + VIPS_ARG_BOOL( class, "optimize_gif_transparency", 5, + _( "Optimize_gif_transparency" ), + _( "Apply GIF transparency optimization" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveMagick, optimize_gif_transparency ), + FALSE ); } static void @@ -586,6 +622,8 @@ vips_foreign_save_magick_buffer_init( VipsForeignSaveMagickBuffer *buffer ) * * * @quality: %gint, quality factor * * @format: %gchararray, format to save as + * * @optimize_gif_frames: %gboolean, apply GIF frames optimization + * * @optimize_gif_transparency: %gboolean, apply GIF transparency optimization * * Write an image using libMagick. * @@ -594,6 +632,15 @@ vips_foreign_save_magick_buffer_init( VipsForeignSaveMagickBuffer *buffer ) * Use @format to explicitly set the save format, for example, "BMP". Otherwise * the format is guessed from the filename suffix. * + * If @optimize_gif_frames is set, GIF frames are cropped to the smallest size + * while preserving the results of the GIF animation. This takes some time for + * computation but saves some time on encoding and produces smaller files in + * some cases. + * + * If @optimize_gif_transparency is set, pixels that don't change the image + * through animation are made transparent. This takes some time for computation + * but saves some time on encoding and produces smaller files in some cases. + * * See also: vips_magicksave_buffer(), vips_magickload(). * * Returns: 0 on success, -1 on error. @@ -622,6 +669,8 @@ vips_magicksave( VipsImage *in, const char *filename, ... ) * * * @quality: %gint, quality factor * * @format: %gchararray, format to save as + * * @optimize_gif_frames: %gboolean, apply GIF frames optimization + * * @optimize_gif_transparency: %gboolean, apply GIF transparency optimization * * As vips_magicksave(), but save to a memory buffer. *