diff --git a/ChangeLog b/ChangeLog index 3dcf3ea7..24aed79b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -26,6 +26,7 @@ master - GIF load supports truncated frames [tlsa] - EXIF support for PNG load and save - deprecate gifsave reoptimise, add reuse +- add "encoder" to heifsave [dloebl] 9/11/22 started 8.13.4 - missing include in mosaic_fuzzer [ServOKio] diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 5fe67441..be318b5c 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -2250,6 +2250,7 @@ vips_heifload_source( VipsSource *source, VipsImage **out, ... ) * * @compression: #VipsForeignHeifCompression, write with this compression * * @effort: %gint, encoding effort * * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode + * * @encoder: #VipsForeignHeifEncoder, select encoder to use * * Write a VIPS image to a file in HEIF format. * @@ -2258,7 +2259,7 @@ vips_heifload_source( VipsSource *source, VipsImage **out, ... ) * * Set @lossless %TRUE to switch to lossless compression. * - * Use @compression to set the encoder e.g. HEVC, AVC, AV1. It defaults to AV1 + * Use @compression to set the compression format e.g. HEVC, AVC, AV1 to use. It defaults to AV1 * if the target filename ends with ".avif", otherwise HEVC. * * Use @effort to control the CPU effort spent improving compression. @@ -2271,6 +2272,8 @@ vips_heifload_source( VipsSource *source, VipsImage **out, ... ) * Use @bitdepth to set the bitdepth of the output file. HEIC supports at * least 8, 10 and 12 bits; other codecs may support more or fewer options. * + * Use @encoder to set the encode library to use, e.g. aom, SVT-AV1, rav1e etc. + * * See also: vips_image_write_to_file(), vips_heifload(). * * Returns: 0 on success, -1 on error. @@ -2303,6 +2306,7 @@ vips_heifsave( VipsImage *in, const char *filename, ... ) * * @compression: #VipsForeignHeifCompression, write with this compression * * @effort: %gint, encoding effort * * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode + * * @encoder: #VipsForeignHeifEncoder, select encoder to use * * As vips_heifsave(), but save to a memory buffer. * @@ -2356,6 +2360,7 @@ vips_heifsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) * * @compression: #VipsForeignHeifCompression, write with this compression * * @effort: %gint, encoding effort * * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode + * * @encoder: #VipsForeignHeifEncoder, select encoder to use * * As vips_heifsave(), but save to a target. * diff --git a/libvips/foreign/heifsave.c b/libvips/foreign/heifsave.c index 304c137a..78fa7d9e 100644 --- a/libvips/foreign/heifsave.c +++ b/libvips/foreign/heifsave.c @@ -99,6 +99,10 @@ typedef struct _VipsForeignSaveHeif { */ VipsForeignSubsample subsample_mode; + /* Encoder to use. For instance: aom, svt etc. + */ + VipsForeignHeifEncoder selected_encoder; + /* The image we save. This is a copy of save->ready since we need to * be able to update the metadata. */ @@ -438,6 +442,7 @@ vips_foreign_save_heif_build( VipsObject *object ) struct heif_error error; struct heif_writer writer; char *chroma; + const struct heif_encoder_descriptor* out_encoder; if( VIPS_OBJECT_CLASS( vips_foreign_save_heif_parent_class )-> build( object ) ) @@ -465,9 +470,36 @@ vips_foreign_save_heif_build( VipsObject *object ) heif->image->Type == VIPS_INTERPRETATION_GREY16 ? 12 : 8; - error = heif_context_get_encoder_for_format( heif->ctx, - (enum heif_compression_format) heif->compression, - &heif->encoder ); + /* Try to find the selected encoder. + */ + if( heif->selected_encoder != VIPS_FOREIGN_HEIF_ENCODER_AUTO ) { + const int count = heif_context_get_encoder_descriptors( + heif->ctx, + (enum heif_compression_format) heif->compression, + vips_enum_nick( VIPS_TYPE_FOREIGN_HEIF_ENCODER, + heif->selected_encoder ), + &out_encoder, 1 ); + + if( count > 0 ) { + error = heif_context_get_encoder( heif->ctx, + out_encoder, &heif->encoder ); + } + else { + g_warning( + "heifsave: could not find selected encoder %s", + vips_enum_nick( VIPS_TYPE_FOREIGN_HEIF_ENCODER, + heif->selected_encoder ) ); + } + } + + /* Fallback to default encoder. + */ + if( !heif->encoder ) { + error = heif_context_get_encoder_for_format( heif->ctx, + (enum heif_compression_format) heif->compression, + &heif->encoder ); + } + if( error.code ) { if( error.code == heif_error_Unsupported_filetype ) vips_error( "heifsave", @@ -654,6 +686,13 @@ vips_foreign_save_heif_class_init( VipsForeignSaveHeifClass *class ) G_STRUCT_OFFSET( VipsForeignSaveHeif, speed ), 0, 9, 5 ); + VIPS_ARG_ENUM( class, "encoder", 18, + _( "Encoder" ), + _( "Select encoder to use" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveHeif, selected_encoder ), + VIPS_TYPE_FOREIGN_HEIF_ENCODER, + VIPS_FOREIGN_HEIF_ENCODER_AUTO ); } static void diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 75dd06ae..32d84a64 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -964,6 +964,28 @@ typedef enum { VIPS_FOREIGN_HEIF_COMPRESSION_LAST } VipsForeignHeifCompression; +/** + * VipsForeignHeifEncoder: + * @VIPS_FOREIGN_HEIF_ENCODER_AUTO: auto + * @VIPS_FOREIGN_HEIF_ENCODER_AOM: aom + * @VIPS_FOREIGN_HEIF_ENCODER_RAV1E: RAV1E + * @VIPS_FOREIGN_HEIF_ENCODER_SVT: SVT-AV1 + * @VIPS_FOREIGN_HEIF_ENCODER_X265: x265 + * + * The selected encoder to use. + * If libheif hasn't been compiled with the selected encoder, + * we will fallback to the default encoder for the compression format. + * + */ +typedef enum { + VIPS_FOREIGN_HEIF_ENCODER_AUTO, + VIPS_FOREIGN_HEIF_ENCODER_AOM, + VIPS_FOREIGN_HEIF_ENCODER_RAV1E, + VIPS_FOREIGN_HEIF_ENCODER_SVT, + VIPS_FOREIGN_HEIF_ENCODER_X265, + VIPS_FOREIGN_HEIF_ENCODER_LAST +} VipsForeignHeifEncoder; + #ifdef __cplusplus } #endif /*__cplusplus*/