From ed79cfba7ea839ac9c31545b21f4301786c7ac4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20L=C3=B6bl?= Date: Fri, 27 May 2022 12:41:01 +0200 Subject: [PATCH] magicksave: Add bitdepth option (#2819) * flag to save bmp with bitdepth 1 * magicksave: add bitdepth option * update comments * update documentation * revised based on review comments Co-authored-by: Thomas Barton --- ChangeLog | 1 + libvips/foreign/magick.c | 24 ++++++++++++++++++++++++ libvips/foreign/magick.h | 2 ++ libvips/foreign/magicksave.c | 5 +++++ libvips/foreign/vips2magick.c | 24 ++++++++++++++++++++++++ 5 files changed, 56 insertions(+) diff --git a/ChangeLog b/ChangeLog index 6cfc1363..7d4c566a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -28,6 +28,7 @@ - add vips_target_end(), deprecate vips_target_finish() - add "mixed" to webpsave [dloebl] - add "reoptimise" to gifsave [dloebl] +- add "bitdepth" to magicksave [dloebl] 26/11/21 started 8.12.3 - better arg checking for vips_hist_find_ndim() [travisbell] diff --git a/libvips/foreign/magick.c b/libvips/foreign/magick.c index 177cc4e4..5d1d856f 100644 --- a/libvips/foreign/magick.c +++ b/libvips/foreign/magick.c @@ -286,6 +286,19 @@ magick_ismagick( const unsigned char *bytes, size_t length ) GetImageMagick( bytes, length, format ) ); } +int +magick_quantize_images( Image *images, + const size_t depth, ExceptionInfo *exception ) +{ + QuantizeInfo info; + + GetQuantizeInfo( &info ); + info.number_colors = 1 << depth; + QuantizeImages( &info, images, exception ); + + return( exception->severity == UndefinedException ); +} + #endif /*HAVE_MAGICK7*/ #ifdef HAVE_MAGICK6 @@ -616,6 +629,17 @@ magick_ismagick( const unsigned char *bytes, size_t length ) #endif } +int +magick_quantize_images( Image *images, + const size_t depth, ExceptionInfo *exception ) +{ + QuantizeInfo info; + + GetQuantizeInfo( &info ); + info.number_colors = (1 << depth); + return QuantizeImages( &info, images ); +} + #endif /*HAVE_MAGICK6*/ #if defined(HAVE_MAGICK6) || defined(HAVE_MAGICK7) diff --git a/libvips/foreign/magick.h b/libvips/foreign/magick.h index 21b56bed..f4aecb4c 100644 --- a/libvips/foreign/magick.h +++ b/libvips/foreign/magick.h @@ -88,6 +88,8 @@ int magick_optimize_image_layers( Image **images, ExceptionInfo *exception ); int magick_optimize_image_transparency( const Image *images, ExceptionInfo *exception ); +int magick_quantize_images( Image *images, const size_t depth, 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 7f12a367..f55bafae 100644 --- a/libvips/foreign/magicksave.c +++ b/libvips/foreign/magicksave.c @@ -66,6 +66,7 @@ * * @format: %gchararray, format to save as * * @optimize_gif_frames: %gboolean, apply GIF frames optimization * * @optimize_gif_transparency: %gboolean, apply GIF transparency optimization + * * @bitdepth: %gint, number of bits per pixel * * Write an image using libMagick. * @@ -83,6 +84,9 @@ * through animation are made transparent. This takes some time for computation * but saves some time on encoding and produces smaller files in some cases. * + * @bitdepth specifies the number of bits per pixel. The image will be quantized + * and dithered if the value is within the valid range (1 to 8). + * * See also: vips_magicksave_buffer(), vips_magickload(). * * Returns: 0 on success, -1 on error. @@ -113,6 +117,7 @@ vips_magicksave( VipsImage *in, const char *filename, ... ) * * @format: %gchararray, format to save as * * @optimize_gif_frames: %gboolean, apply GIF frames optimization * * @optimize_gif_transparency: %gboolean, apply GIF transparency optimization + * * @bitdepth: %gint, number of bits per pixel * * As vips_magicksave(), but save to a memory buffer. * diff --git a/libvips/foreign/vips2magick.c b/libvips/foreign/vips2magick.c index 96cdcf6c..beb81625 100644 --- a/libvips/foreign/vips2magick.c +++ b/libvips/foreign/vips2magick.c @@ -69,6 +69,7 @@ typedef struct _VipsForeignSaveMagick { char *filename; /* NULL during buffer output */ char *format; int quality; + int bitdepth; gboolean optimize_gif_frames; gboolean optimize_gif_transparency; @@ -406,6 +407,21 @@ vips_foreign_save_magick_build( VipsObject *object ) return( -1 ); } } + + /* Bitdepth <= 8 requested? Quantize/Dither images. + * ImageMagick then selects the appropriate bit depth when writing + * the actual image (e.g. BMP or GIF). + */ + if( magick->bitdepth ) { + if ( !magick_quantize_images( magick->images, + magick->bitdepth, magick->exception ) ) { + magick_inherit_exception( magick->exception, + magick->images ); + magick_vips_error( class->nickname, magick->exception ); + + return( -1 ); + } + } return( 0 ); } @@ -493,6 +509,13 @@ vips_foreign_save_magick_class_init( VipsForeignSaveMagickClass *class ) G_STRUCT_OFFSET( VipsForeignSaveMagick, optimize_gif_transparency ), FALSE ); + + VIPS_ARG_INT( class, "bitdepth", 6, + _( "Image bitdepth" ), + _( "Image bitdepth. Default is auto bitdepth." ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveMagick, bitdepth ), + 0, 8, 0); } static void @@ -502,6 +525,7 @@ vips_foreign_save_magick_init( VipsForeignSaveMagick *magick ) * int array later. */ g_value_init( &magick->delay_gvalue, G_TYPE_INT ); + magick->bitdepth = 0; } typedef struct _VipsForeignSaveMagickFile {