From 47383b5bfc136f7870c319a1edce4848e177425b Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 28 Oct 2021 18:57:24 +0100 Subject: [PATCH] tell buffer and target savers the file format (#2499) tell buffer and target savers the file format Currently, buffer and target savers are not told the format they should write. This is usually OK (the JPEG saver already knows it should write JPEG), but some savers can write several formats, and these currently need an extra parameter. For example: ```ruby buf = x.write_to_buffer ".bmp", format: "bmp" ``` The first ".bmp" gets libvips to pick magicksave, the second `format:` param is necessary to tell magicksave to write BMP. This patch adds stub subclasses so that the savers know the exact format. It also improves PPM save. --- ChangeLog | 2 + libvips/foreign/foreign.c | 13 ++ libvips/foreign/heifload.c | 11 ++ libvips/foreign/heifsave.c | 57 +++++++-- libvips/foreign/pforeign.h | 6 + libvips/foreign/ppmload.c | 6 +- libvips/foreign/ppmsave.c | 203 +++++++++++++++++++++++++++++-- libvips/foreign/vips2magick.c | 72 ++++++++++- libvips/include/vips/enumtypes.h | 2 + libvips/include/vips/foreign.h | 25 ++++ libvips/iofuncs/enumtypes.c | 20 +++ libvips/iofuncs/image.c | 6 +- libvips/module/heif.c | 2 + libvips/module/magick.c | 4 + 14 files changed, 402 insertions(+), 27 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8657826c..2eb8575b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -14,6 +14,8 @@ - added restart_interval option to jpegsave [manthey] - add IIIF3 support to dzsaave [martimpassos] - add atan2 [indus] +- improve buffer and target save file format selection +- added VipsForeignPpmFormat, @format arg to ppm savers 16/8/21 started 8.11.4 - fix off-by-one error in new rank fast path diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 48f22611..07e2199e 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1795,6 +1795,7 @@ vips_foreign_save_class_init( VipsForeignSaveClass *class ) VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignSave, page_height ), 0, VIPS_MAX_COORD, 0 ); + } static void @@ -2782,7 +2783,10 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_load_ppm_file_get_type( void ); extern GType vips_foreign_load_ppm_source_get_type( void ); extern GType vips_foreign_save_ppm_file_get_type( void ); + extern GType vips_foreign_save_pbm_target_get_type( void ); + extern GType vips_foreign_save_pgm_target_get_type( void ); extern GType vips_foreign_save_ppm_target_get_type( void ); + extern GType vips_foreign_save_pfm_target_get_type( void ); extern GType vips_foreign_load_png_file_get_type( void ); extern GType vips_foreign_load_png_buffer_get_type( void ); @@ -2841,7 +2845,9 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_load_magick7_file_get_type( void ); extern GType vips_foreign_load_magick7_buffer_get_type( void ); extern GType vips_foreign_save_magick_file_get_type( void ); + extern GType vips_foreign_save_magick_bmp_file_get_type( void ); extern GType vips_foreign_save_magick_buffer_get_type( void ); + extern GType vips_foreign_save_magick_bmp_buffer_get_type( void ); extern GType vips_foreign_save_dz_file_get_type( void ); extern GType vips_foreign_save_dz_buffer_get_type( void ); @@ -2881,6 +2887,7 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_heif_file_get_type( void ); extern GType vips_foreign_save_heif_buffer_get_type( void ); extern GType vips_foreign_save_heif_target_get_type( void ); + extern GType vips_foreign_save_avif_target_get_type( void ); extern GType vips_foreign_load_nifti_file_get_type( void ); extern GType vips_foreign_load_nifti_source_get_type( void ); @@ -2922,7 +2929,10 @@ vips_foreign_operation_init( void ) vips_foreign_load_ppm_file_get_type(); vips_foreign_load_ppm_source_get_type(); vips_foreign_save_ppm_file_get_type(); + vips_foreign_save_pbm_target_get_type(); + vips_foreign_save_pgm_target_get_type(); vips_foreign_save_ppm_target_get_type(); + vips_foreign_save_pfm_target_get_type(); #endif /*HAVE_PPM*/ #ifdef HAVE_RADIANCE @@ -3052,7 +3062,9 @@ vips_foreign_operation_init( void ) #if defined(ENABLE_MAGICKSAVE) && !defined(MAGICK_MODULE) vips_foreign_save_magick_file_get_type(); + vips_foreign_save_magick_bmp_file_get_type(); vips_foreign_save_magick_buffer_get_type(); + vips_foreign_save_magick_bmp_buffer_get_type(); #endif /*defined(ENABLE_MAGICKSAVE) && !defined(MAGICK_MODULE)*/ #ifdef HAVE_CFITSIO @@ -3081,6 +3093,7 @@ vips_foreign_operation_init( void ) vips_foreign_save_heif_file_get_type(); vips_foreign_save_heif_buffer_get_type(); vips_foreign_save_heif_target_get_type(); + vips_foreign_save_avif_target_get_type(); #endif /*defined(HAVE_HEIF_ENCODER) && !defined(HEIF_MODULE)*/ vips__foreign_load_operation = diff --git a/libvips/foreign/heifload.c b/libvips/foreign/heifload.c index 34916e3a..cd631bd3 100644 --- a/libvips/foreign/heifload.c +++ b/libvips/foreign/heifload.c @@ -76,6 +76,17 @@ #include "pforeign.h" +const char *vips__heic_suffs[] = { + ".heic", + ".heif", + NULL +}; + +const char *vips__avif_suffs[] = { + ".avif", + NULL +}; + const char *vips__heif_suffs[] = { ".heic", ".heif", diff --git a/libvips/foreign/heifsave.c b/libvips/foreign/heifsave.c index 686c9933..3256a8a3 100644 --- a/libvips/foreign/heifsave.c +++ b/libvips/foreign/heifsave.c @@ -335,7 +335,6 @@ vips_foreign_save_heif_build( VipsObject *object ) VipsForeignSave *save = (VipsForeignSave *) object; VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) object; - const char *filename; struct heif_error error; struct heif_writer writer; char *chroma; @@ -357,15 +356,6 @@ vips_foreign_save_heif_build( VipsObject *object ) if( vips_copy( save->ready, &heif->image, NULL ) ) return( -1 ); - /* Compression defaults to VIPS_FOREIGN_HEIF_COMPRESSION_AV1 for .avif - * suffix. - */ - filename = vips_connection_filename( VIPS_CONNECTION( heif->target ) ); - if( !vips_object_argument_isset( object, "compression" ) && - filename && - vips_iscasepostfix( filename, ".avif" ) ) - heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1; - error = heif_context_get_encoder_for_format( heif->ctx, (enum heif_compression_format) heif->compression, &heif->encoder ); @@ -489,7 +479,6 @@ vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; - VipsForeignClass *foreign_class = (VipsForeignClass *) class; VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; gobject_class->dispose = vips_foreign_save_heif_dispose; @@ -500,8 +489,6 @@ vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class ) object_class->description = _( "save image in HEIF format" ); object_class->build = vips_foreign_save_heif_build; - foreign_class->suffs = vips__heif_suffs; - save_class->saveable = VIPS_SAVEABLE_RGBA_ONLY; save_class->format_table = vips_heif_bandfmt; @@ -588,6 +575,9 @@ vips_foreign_save_heif_file_build( VipsObject *object ) if( !(heif->target = vips_target_new_to_file( file->filename )) ) return( -1 ); + if( vips_iscasepostfix( file->filename, ".avif" ) ) + heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1; + if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_file_parent_class )-> build( object ) ) return( -1 ); @@ -600,6 +590,7 @@ vips_foreign_save_heif_file_class_init( VipsForeignSaveHeifFileClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -607,6 +598,8 @@ vips_foreign_save_heif_file_class_init( VipsForeignSaveHeifFileClass *class ) object_class->nickname = "heifsave"; object_class->build = vips_foreign_save_heif_file_build; + foreign_class->suffs = vips__heif_suffs; + VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "Filename to save to" ), @@ -664,6 +657,7 @@ vips_foreign_save_heif_buffer_class_init( { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -671,6 +665,8 @@ vips_foreign_save_heif_buffer_class_init( object_class->nickname = "heifsave_buffer"; object_class->build = vips_foreign_save_heif_buffer_build; + foreign_class->suffs = vips__heic_suffs; + VIPS_ARG_BOXED( class, "buffer", 1, _( "Buffer" ), _( "Buffer to save to" ), @@ -721,6 +717,7 @@ vips_foreign_save_heif_target_class_init( { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -728,6 +725,8 @@ vips_foreign_save_heif_target_class_init( object_class->nickname = "heifsave_target"; object_class->build = vips_foreign_save_heif_target_build; + foreign_class->suffs = vips__heic_suffs; + VIPS_ARG_OBJECT( class, "target", 1, _( "Target" ), _( "Target to save to" ), @@ -742,6 +741,38 @@ vips_foreign_save_heif_target_init( VipsForeignSaveHeifTarget *target ) { } +typedef VipsForeignSaveHeifTarget VipsForeignSaveAvifTarget; +typedef VipsForeignSaveHeifTargetClass VipsForeignSaveAvifTargetClass; + +G_DEFINE_TYPE( VipsForeignSaveAvifTarget, vips_foreign_save_avif_target, + vips_foreign_save_heif_target_get_type() ); + +static void +vips_foreign_save_avif_target_class_init( + VipsForeignSaveAvifTargetClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsOperationClass *operation_class = (VipsOperationClass *) class; + + object_class->nickname = "avifsave_target"; + object_class->description = _( "save image in AVIF format" ); + + foreign_class->suffs = vips__avif_suffs; + + /* Hide from UI. + */ + operation_class->flags = VIPS_OPERATION_DEPRECATED; +} + +static void +vips_foreign_save_avif_target_init( VipsForeignSaveAvifTarget *target ) +{ + VipsForeignSaveHeif *heif = (VipsForeignSaveHeif *) target; + + heif->compression = VIPS_FOREIGN_HEIF_COMPRESSION_AV1; +} + #endif /*HAVE_HEIF_ENCODER*/ /* The C API wrappers are defined in foreign.c. diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index 7017172b..44d955f0 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -158,6 +158,10 @@ int vips__mat_header( const char *filename, VipsImage *out ); int vips__mat_ismat( const char *filename ); extern const char *vips__ppm_suffs[]; +extern const char *vips__save_pbm_suffs[]; +extern const char *vips__save_pgm_suffs[]; +extern const char *vips__save_ppm_suffs[]; +extern const char *vips__save_pfm_suffs[]; int vips__ppm_save_target( VipsImage *in, VipsTarget *target, gboolean ascii, gboolean squash ); @@ -235,6 +239,8 @@ typedef void *(*VipsNiftiMapFn)( const char *name, GValue *value, glong offset, void *a, void *b ); void *vips__foreign_nifti_map( VipsNiftiMapFn fn, void *a, void *b ); +extern const char *vips__heic_suffs[]; +extern const char *vips__avif_suffs[]; extern const char *vips__heif_suffs[]; extern const char *vips__jp2k_suffs[]; diff --git a/libvips/foreign/ppmload.c b/libvips/foreign/ppmload.c index ecdef5cc..130e3cda 100644 --- a/libvips/foreign/ppmload.c +++ b/libvips/foreign/ppmload.c @@ -141,7 +141,11 @@ static char *magic_names[] = { /* Shared with ppmsave. */ -const char *vips__ppm_suffs[] = { ".ppm", ".pgm", ".pbm", ".pfm", NULL }; +const char *vips__ppm_suffs[] = { ".pbm", ".pgm", ".ppm", ".pfm", NULL }; +const char *vips__save_pbm_suffs[] = { ".pbm", NULL }; +const char *vips__save_pgm_suffs[] = { ".pgm", NULL }; +const char *vips__save_ppm_suffs[] = { ".ppm", NULL }; +const char *vips__save_pfm_suffs[] = { ".pfm", NULL }; static gboolean vips_foreign_load_ppm_is_a_source( VipsSource *source ) diff --git a/libvips/foreign/ppmsave.c b/libvips/foreign/ppmsave.c index 8e719c6e..1ac29303 100644 --- a/libvips/foreign/ppmsave.c +++ b/libvips/foreign/ppmsave.c @@ -12,6 +12,8 @@ * - byteswap on save, if necessary [ewelot] * 2/12/20 * - don't add date with @strip [ewelot] + * 28/10/21 + * - add @format, default type by filename */ /* @@ -70,6 +72,7 @@ struct _VipsForeignSavePpm { VipsForeignSave parent_object; VipsTarget *target; + VipsForeignPpmFormat format; gboolean ascii; int bitdepth; @@ -216,16 +219,70 @@ vips_foreign_save_ppm_build( VipsObject *object ) { VipsForeignSave *save = (VipsForeignSave *) object; VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 ); VipsImage *image; char *magic; char *date; + VipsBandFormat target_format; + VipsInterpretation target_interpretation; if( VIPS_OBJECT_CLASS( vips_foreign_save_ppm_parent_class )-> build( object ) ) return( -1 ); image = save->ready; + target_format = image->BandFmt; + target_interpretation = image->Type; + + /* ppm types to set the defaults for bitdepth etc. + * + * pbm ... 1 band 1 bit + * pgm ... 1 band many bit + * ppm ... 3 band many bit + * pfm ... 1 or 3 bands, 32 bit + */ + switch( ppm->format ) { + case VIPS_FOREIGN_PPM_FORMAT_PBM: + if( !vips_object_argument_isset( object, "bitdepth" ) ) + ppm->bitdepth = 1; + target_interpretation = VIPS_INTERPRETATION_B_W; + break; + + case VIPS_FOREIGN_PPM_FORMAT_PGM: + if( target_format == VIPS_FORMAT_USHORT ) + target_interpretation = VIPS_INTERPRETATION_GREY16; + else + target_interpretation = VIPS_INTERPRETATION_B_W; + break; + + case VIPS_FOREIGN_PPM_FORMAT_PPM: + if( target_format == VIPS_FORMAT_USHORT ) + target_interpretation = VIPS_INTERPRETATION_RGB16; + else + target_interpretation = VIPS_INTERPRETATION_sRGB; + break; + + case VIPS_FOREIGN_PPM_FORMAT_PFM: + target_format = VIPS_FORMAT_FLOAT; + break; + + default: + /* Harmless. + */ + break; + } + + if( vips_cast( image, &t[0], target_format, NULL ) ) + return( -1 ); + image = t[0]; + + if( image->Type != target_interpretation ) { + if( vips_colourspace( image, &t[1], + target_interpretation, NULL ) ) + return( -1 ); + image = t[1]; + } /* Handle the deprecated squash parameter. */ @@ -318,9 +375,12 @@ vips_foreign_save_ppm_build( VipsObject *object ) double scale; char buf[G_ASCII_DTOSTR_BUF_SIZE]; - if( vips_image_get_double( image, - "pfm-scale", &scale ) ) - scale = 1; + scale = 1; + if( vips_image_get_typeof( image, "pfm-scale" ) && + !vips_image_get_double( image, + "pfm-scale", &scale ) ) + ; + if( !vips_amiMSBfirst() ) scale *= -1; /* Need to be locale independent. @@ -391,7 +451,6 @@ vips_foreign_save_ppm_class_init( VipsForeignSavePpmClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; - VipsForeignClass *foreign_class = (VipsForeignClass *) class; VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; gobject_class->dispose = vips_foreign_save_ppm_dispose; @@ -402,11 +461,17 @@ vips_foreign_save_ppm_class_init( VipsForeignSavePpmClass *class ) object_class->description = _( "save to ppm" ); object_class->build = vips_foreign_save_ppm_build; - foreign_class->suffs = vips__ppm_suffs; - save_class->saveable = VIPS_SAVEABLE_RGB; save_class->format_table = bandfmt_ppm; + VIPS_ARG_ENUM( class, "format", 2, + _( "Format" ), + _( "Format to save in" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSavePpm, format ), + VIPS_TYPE_FOREIGN_PPM_FORMAT, + VIPS_FOREIGN_PPM_FORMAT_PPM ); + VIPS_ARG_BOOL( class, "ascii", 10, _( "ASCII" ), _( "save as ascii" ), @@ -433,6 +498,7 @@ vips_foreign_save_ppm_class_init( VipsForeignSavePpmClass *class ) static void vips_foreign_save_ppm_init( VipsForeignSavePpm *ppm ) { + ppm->format = VIPS_FOREIGN_PPM_FORMAT_PPM; } typedef struct _VipsForeignSavePpmFile { @@ -456,6 +522,13 @@ vips_foreign_save_ppm_file_build( VipsObject *object ) !(ppm->target = vips_target_new_to_file( file->filename )) ) return( -1 ); + if( vips_iscasepostfix( file->filename, ".pbm" ) ) + ppm->format = VIPS_FOREIGN_PPM_FORMAT_PBM; + else if( vips_iscasepostfix( file->filename, ".pgm" ) ) + ppm->format = VIPS_FOREIGN_PPM_FORMAT_PGM; + else if( vips_iscasepostfix( file->filename, ".pfm" ) ) + ppm->format = VIPS_FOREIGN_PPM_FORMAT_PFM; + return( VIPS_OBJECT_CLASS( vips_foreign_save_ppm_file_parent_class )-> build( object ) ); } @@ -465,6 +538,7 @@ vips_foreign_save_ppm_file_class_init( VipsForeignSavePpmFileClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -473,6 +547,8 @@ vips_foreign_save_ppm_file_class_init( VipsForeignSavePpmFileClass *class ) object_class->description = _( "save image to ppm file" ); object_class->build = vips_foreign_save_ppm_file_build; + foreign_class->suffs = vips__ppm_suffs; + VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "Filename to save to" ), @@ -521,6 +597,7 @@ vips_foreign_save_ppm_target_class_init( { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; @@ -528,6 +605,8 @@ vips_foreign_save_ppm_target_class_init( object_class->nickname = "ppmsave_target"; object_class->build = vips_foreign_save_ppm_target_build; + foreign_class->suffs = vips__save_ppm_suffs; + VIPS_ARG_OBJECT( class, "target", 1, _( "Target" ), _( "Target to save to" ), @@ -542,6 +621,102 @@ vips_foreign_save_ppm_target_init( VipsForeignSavePpmTarget *target ) { } +typedef VipsForeignSavePpmTarget VipsForeignSavePbmTarget; +typedef VipsForeignSavePpmTargetClass VipsForeignSavePbmTargetClass; + +G_DEFINE_TYPE( VipsForeignSavePbmTarget, vips_foreign_save_pbm_target, + vips_foreign_save_ppm_target_get_type() ); + +static void +vips_foreign_save_pbm_target_class_init( + VipsForeignSavePbmTargetClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsOperationClass *operation_class = (VipsOperationClass *) class; + + object_class->nickname = "pbmsave_target"; + object_class->description = _( "save image in pbm format" ); + + foreign_class->suffs = vips__save_pbm_suffs; + + /* Hide from UI. + */ + operation_class->flags = VIPS_OPERATION_DEPRECATED; +} + +static void +vips_foreign_save_pbm_target_init( VipsForeignSavePbmTarget *target ) +{ + VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) target; + + ppm->format = VIPS_FOREIGN_PPM_FORMAT_PBM; +} + +typedef VipsForeignSavePpmTarget VipsForeignSavePgmTarget; +typedef VipsForeignSavePpmTargetClass VipsForeignSavePgmTargetClass; + +G_DEFINE_TYPE( VipsForeignSavePgmTarget, vips_foreign_save_pgm_target, + vips_foreign_save_ppm_target_get_type() ); + +static void +vips_foreign_save_pgm_target_class_init( + VipsForeignSavePgmTargetClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsOperationClass *operation_class = (VipsOperationClass *) class; + + object_class->nickname = "pgmsave_target"; + object_class->description = _( "save image in pgm format" ); + + foreign_class->suffs = vips__save_pgm_suffs; + + /* Hide from UI. + */ + operation_class->flags = VIPS_OPERATION_DEPRECATED; +} + +static void +vips_foreign_save_pgm_target_init( VipsForeignSavePgmTarget *target ) +{ + VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) target; + + ppm->format = VIPS_FOREIGN_PPM_FORMAT_PGM; +} + +typedef VipsForeignSavePpmTarget VipsForeignSavePfmTarget; +typedef VipsForeignSavePpmTargetClass VipsForeignSavePfmTargetClass; + +G_DEFINE_TYPE( VipsForeignSavePfmTarget, vips_foreign_save_pfm_target, + vips_foreign_save_ppm_target_get_type() ); + +static void +vips_foreign_save_pfm_target_class_init( + VipsForeignSavePfmTargetClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsOperationClass *operation_class = (VipsOperationClass *) class; + + object_class->nickname = "pfmsave_target"; + object_class->description = _( "save image in pfm format" ); + + foreign_class->suffs = vips__save_pfm_suffs; + + /* Hide from UI. + */ + operation_class->flags = VIPS_OPERATION_DEPRECATED; +} + +static void +vips_foreign_save_pfm_target_init( VipsForeignSavePfmTarget *target ) +{ + VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) target; + + ppm->format = VIPS_FOREIGN_PPM_FORMAT_PFM; +} + #endif /*HAVE_PPM*/ /** @@ -552,8 +727,9 @@ vips_foreign_save_ppm_target_init( VipsForeignSavePpmTarget *target ) * * Optional arguments: * - * * @ascii: save as ASCII rather than binary - * * @squash: squash 8-bit images down to one bit + * * @format: #VipsForeignPpmFormat, format to save in + * * @ascii: %gboolean, save as ASCII rather than binary + * * @bitdepth: %gint, bitdepth to save at * * Write a VIPS image to a file as PPM. It can write 1, 8, 16 or * 32 bit unsigned integer images, float images, colour or monochrome, @@ -566,8 +742,9 @@ vips_foreign_save_ppm_target_init( VipsForeignSavePpmTarget *target ) * Set @ascii to %TRUE to write as human-readable ASCII. Normally data is * written in binary. * - * Set @squash to %TRUE to squash 8-bit images down to one bit. The saver does - * no dithering, that's up to you. + * Set @bitdepth to 1 to write a one-bit image. + * + * @format defaults to the sub-type for this filename suffix. * * See also: vips_image_write_to_file(). * @@ -592,6 +769,12 @@ vips_ppmsave( VipsImage *in, const char *filename, ... ) * @target: save image to this target * @...: %NULL-terminated list of optional named arguments * + * Optional arguments: + * + * * @format: #VipsForeignPpmFormat, format to save in + * * @ascii: %gboolean, save as ASCII rather than binary + * * @bitdepth: %gint, bitdepth to save at + * * As vips_ppmsave(), but save to a target. * * See also: vips_ppmsave(). diff --git a/libvips/foreign/vips2magick.c b/libvips/foreign/vips2magick.c index 21eedc82..c2b3c305 100644 --- a/libvips/foreign/vips2magick.c +++ b/libvips/foreign/vips2magick.c @@ -102,6 +102,7 @@ vips_foreign_save_magick_dispose( GObject *gobject ) printf( "vips_foreign_save_magick_dispose: %p\n", gobject ); #endif /*DEBUG*/ + VIPS_FREE( magick->filename ); VIPS_FREE( magick->map ); VIPS_FREEF( DestroyImageList, magick->images ); VIPS_FREEF( DestroyImageInfo, magick->image_info ); @@ -414,7 +415,8 @@ vips_foreign_save_magick_build( VipsObject *object ) * Instead, just list the commonly-used formats that all libMagicks support and * that libvips does not. */ -static const char *vips__save_magick_suffs[] = { ".gif", ".bmp", NULL }; +static const char *vips__save_magick_suffs[] = { NULL }; +static const char *vips__save_magick_bmp_suffs[] = { ".bmp", NULL }; /* Save a bit of typing. */ @@ -512,7 +514,7 @@ vips_foreign_save_magick_file_build( VipsObject *object ) VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) object; VipsForeignSaveMagickFile *file = (VipsForeignSaveMagickFile *) object; - magick->filename = file->filename; + magick->filename = g_strdup( file->filename ); if( VIPS_OBJECT_CLASS( vips_foreign_save_magick_file_parent_class )-> build( object ) ) @@ -557,6 +559,38 @@ vips_foreign_save_magick_file_init( VipsForeignSaveMagickFile *file ) { } +typedef VipsForeignSaveMagickFile VipsForeignSaveMagickBmpFile; +typedef VipsForeignSaveMagickFileClass VipsForeignSaveMagickBmpFileClass; + +G_DEFINE_TYPE( VipsForeignSaveMagickBmpFile, vips_foreign_save_magick_bmp_file, + vips_foreign_save_magick_file_get_type() ); + +static void +vips_foreign_save_magick_bmp_file_class_init( + VipsForeignSaveMagickBmpFileClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsOperationClass *operation_class = (VipsOperationClass *) class; + + object_class->nickname = "magicksave_bmp"; + object_class->description = _( "save bmp image with ImageMagick" ); + + foreign_class->suffs = vips__save_magick_bmp_suffs; + + /* Hide from UI. + */ + operation_class->flags = VIPS_OPERATION_DEPRECATED; +} + +static void +vips_foreign_save_magick_bmp_file_init( VipsForeignSaveMagickBmpFile *file ) +{ + VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) file; + + VIPS_SETSTR( magick->format, "bmp" ); +} + typedef struct _VipsForeignSaveMagickBuffer { VipsForeignSaveMagick parent_object; @@ -630,4 +664,38 @@ vips_foreign_save_magick_buffer_init( VipsForeignSaveMagickBuffer *buffer ) { } +typedef VipsForeignSaveMagickBuffer VipsForeignSaveMagickBmpBuffer; +typedef VipsForeignSaveMagickBufferClass VipsForeignSaveMagickBmpBufferClass; + +G_DEFINE_TYPE( VipsForeignSaveMagickBmpBuffer, + vips_foreign_save_magick_bmp_buffer, + vips_foreign_save_magick_buffer_get_type() ); + +static void +vips_foreign_save_magick_bmp_buffer_class_init( + VipsForeignSaveMagickBmpBufferClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsOperationClass *operation_class = (VipsOperationClass *) class; + + object_class->nickname = "magicksave_bmp_buffer"; + object_class->description = _( "save bmp image to magick buffer" ); + + foreign_class->suffs = vips__save_magick_bmp_suffs; + + /* Hide from UI. + */ + operation_class->flags = VIPS_OPERATION_DEPRECATED; +} + +static void +vips_foreign_save_magick_bmp_buffer_init( + VipsForeignSaveMagickBmpBuffer *buffer ) +{ + VipsForeignSaveMagick *magick = (VipsForeignSaveMagick *) buffer; + + VIPS_SETSTR( magick->format, "bmp" ); +} + #endif /*ENABLE_MAGICKSAVE*/ diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h index 8cb85a9e..772e14a1 100644 --- a/libvips/include/vips/enumtypes.h +++ b/libvips/include/vips/enumtypes.h @@ -72,6 +72,8 @@ GType vips_foreign_tiff_resunit_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_FOREIGN_TIFF_RESUNIT (vips_foreign_tiff_resunit_get_type()) GType vips_foreign_png_filter_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_FOREIGN_PNG_FILTER (vips_foreign_png_filter_get_type()) +GType vips_foreign_ppm_format_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_FOREIGN_PPM_FORMAT (vips_foreign_ppm_format_get_type()) GType vips_foreign_dz_layout_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_FOREIGN_DZ_LAYOUT (vips_foreign_dz_layout_get_type()) GType vips_foreign_dz_depth_get_type (void) G_GNUC_CONST; diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index e8d0d1ac..ac58f946 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -612,6 +612,31 @@ int vips_pngsave( VipsImage *in, const char *filename, ... ) int vips_pngsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) __attribute__((sentinel)); +/** + * VipsForeignPpmFormat: + * @VIPS_FOREIGN_PPM_PBM: portable bitmap + * @VIPS_FOREIGN_PPM_PGM: portable greymap + * @VIPS_FOREIGN_PPM_PPM: portable pixmap + * @VIPS_FOREIGN_PPM_PFM: portable float map + * + * The netpbm file format to save as. + * + * #VIPS_FOREIGN_PPM_PBM images are single bit. + * + * #VIPS_FOREIGN_PPM_PGB images are 8, 16, or 32-bits, one band. + * + * #VIPS_FOREIGN_PPM_PPM images are 8, 16, or 32-bits, three bands. + * + * #VIPS_FOREIGN_PPM_PFM images are 32-bit float pixels. + */ +typedef enum { + VIPS_FOREIGN_PPM_FORMAT_PBM, + VIPS_FOREIGN_PPM_FORMAT_PGM, + VIPS_FOREIGN_PPM_FORMAT_PPM, + VIPS_FOREIGN_PPM_FORMAT_PFM, + VIPS_FOREIGN_PPM_FORMAT_LAST +} VipsForeignPpmFormat; + int vips_ppmload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); int vips_ppmload_source( VipsSource *source, VipsImage **out, ... ) diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index a752ae41..0109ed30 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -644,6 +644,26 @@ vips_foreign_png_filter_get_type( void ) return( etype ); } GType +vips_foreign_ppm_format_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_FOREIGN_PPM_FORMAT_PBM, "VIPS_FOREIGN_PPM_FORMAT_PBM", "pbm"}, + {VIPS_FOREIGN_PPM_FORMAT_PGM, "VIPS_FOREIGN_PPM_FORMAT_PGM", "pgm"}, + {VIPS_FOREIGN_PPM_FORMAT_PPM, "VIPS_FOREIGN_PPM_FORMAT_PPM", "ppm"}, + {VIPS_FOREIGN_PPM_FORMAT_PFM, "VIPS_FOREIGN_PPM_FORMAT_PFM", "pfm"}, + {VIPS_FOREIGN_PPM_FORMAT_LAST, "VIPS_FOREIGN_PPM_FORMAT_LAST", "last"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsForeignPpmFormat", values ); + } + + return( etype ); +} +GType vips_foreign_dz_layout_get_type( void ) { static GType etype = 0; diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index c59f28a7..6f93cb8d 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -2750,7 +2750,11 @@ vips_image_write_to_buffer( VipsImage *in, vips__filename_split8( suffix, filename, option_string ); - if( (operation_name = vips_foreign_find_save_target( filename )) ) { + vips_error_freeze(); + operation_name = vips_foreign_find_save_target( filename ); + vips_error_thaw(); + + if( operation_name ) { VipsTarget *target; if( !(target = vips_target_new_to_memory()) ) diff --git a/libvips/module/heif.c b/libvips/module/heif.c index 7d43949f..4eeb722b 100644 --- a/libvips/module/heif.c +++ b/libvips/module/heif.c @@ -64,6 +64,7 @@ g_module_check_init( GModule *module ) extern GType vips_foreign_save_heif_file_get_type( void ); extern GType vips_foreign_save_heif_buffer_get_type( void ); extern GType vips_foreign_save_heif_target_get_type( void ); + extern GType vips_foreign_save_avif_target_get_type( void ); #ifdef HAVE_HEIF_DECODER vips_foreign_load_heif_file_get_type(); @@ -75,6 +76,7 @@ g_module_check_init( GModule *module ) vips_foreign_save_heif_file_get_type(); vips_foreign_save_heif_buffer_get_type(); vips_foreign_save_heif_target_get_type(); + vips_foreign_save_avif_target_get_type(); #endif /*HAVE_HEIF_ENCODER*/ return( NULL ); diff --git a/libvips/module/magick.c b/libvips/module/magick.c index afd63588..e601fb8b 100644 --- a/libvips/module/magick.c +++ b/libvips/module/magick.c @@ -63,7 +63,9 @@ g_module_check_init( GModule *module ) extern GType vips_foreign_load_magick7_file_get_type( void ); extern GType vips_foreign_load_magick7_buffer_get_type( void ); extern GType vips_foreign_save_magick_file_get_type( void ); + extern GType vips_foreign_save_magick_bmp_file_get_type( void ); extern GType vips_foreign_save_magick_buffer_get_type( void ); + extern GType vips_foreign_save_magick_bmp_buffer_get_type( void ); #ifdef ENABLE_MAGICKLOAD #ifdef HAVE_MAGICK6 @@ -79,7 +81,9 @@ g_module_check_init( GModule *module ) #ifdef ENABLE_MAGICKSAVE vips_foreign_save_magick_file_get_type(); + vips_foreign_save_magick_bmp_file_get_type(); vips_foreign_save_magick_buffer_get_type(); + vips_foreign_save_magick_bmp_buffer_get_type(); #endif /*ENABLE_MAGICKSAVE*/ return( NULL );