diff --git a/CMakeLists.txt b/CMakeLists.txt index 19ca2eb533..fbfd99e79a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required (VERSION 3.16.3) # Oldest Ubuntu LTS (20.04 currently) -project(libheif LANGUAGES C CXX VERSION 1.17.3) +project(libheif LANGUAGES C CXX VERSION 1.17.5) # compatibility_version is never allowed to be decreased for any specific SONAME. # Libtool in the libheif-1.15.1 release had set it to 17.0.0, so we have to use this for the v1.x.y versions. diff --git a/README.md b/README.md index b85bf7fa4a..faf4d9f387 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ For each codec, there are two configuration variables: * `WITH_{codec}_PLUGIN`: when enabled, the codec is compiled as a separate plugin. In order to use dynamic plugins, also make sure that `ENABLE_PLUGIN_LOADING` is enabled. -The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODER`, `AOM_ENCODER`, `SvtEnc`, `DAV1D`, `FFMPEG_HEVC_DECODER`, `JPEG_DECODER`, `JPEG_ENCODER`, `KVAZAAR`, `OpenJPEG_DECODER`, `OpenJPEG_ENCODER`. +The placeholder `{codec}` can have these values: `LIBDE265`, `X265`, `AOM_DECODER`, `AOM_ENCODER`, `SvtEnc`, `DAV1D`, `FFMPEG_DECODER`, `JPEG_DECODER`, `JPEG_ENCODER`, `KVAZAAR`, `OpenJPEG_DECODER`, `OpenJPEG_ENCODER`. Further options are: diff --git a/gnome/CMakeLists.txt b/gnome/CMakeLists.txt index fc38555df5..c1768cf2ee 100644 --- a/gnome/CMakeLists.txt +++ b/gnome/CMakeLists.txt @@ -1 +1,3 @@ -install(FILES heif.thumbnailer DESTINATION ${CMAKE_INSTALL_DATADIR}/thumbnailers) +if(TARGET heif-thumbnailer) + install(FILES heif.thumbnailer DESTINATION ${CMAKE_INSTALL_DATADIR}/thumbnailers) +endif() diff --git a/libheif/context.cc b/libheif/context.cc index cea1cde2b6..446abe1ab8 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -2553,6 +2553,20 @@ Error HeifContext::encode_image_as_hevc(const std::shared_ptr& i heif_suberror_Invalid_image_size); } + if (encoder->plugin->plugin_api_version >= 3 && + encoder->plugin->query_encoded_size != nullptr) { + uint32_t check_encoded_width = input_width, check_encoded_height = input_height; + + encoder->plugin->query_encoded_size(encoder->encoder, + input_width, input_height, + &check_encoded_width, + &check_encoded_height); + + assert((int)check_encoded_width == encoded_width); + assert((int)check_encoded_height == encoded_height); + } + + // Note: 'ispe' must be before the transformation properties m_heif_file->add_ispe_property(image_id, encoded_width, encoded_height); diff --git a/libheif/plugins/encoder_kvazaar.cc b/libheif/plugins/encoder_kvazaar.cc index 81173ec640..bda1c001b7 100644 --- a/libheif/plugins/encoder_kvazaar.cc +++ b/libheif/plugins/encoder_kvazaar.cc @@ -22,10 +22,11 @@ #include "libheif/heif_plugin.h" #include "encoder_kvazaar.h" #include -#include +#include // apparently, this is a false positive of cpplint #include #include #include +#include extern "C" { #include @@ -302,10 +303,22 @@ static void kvazaar_query_input_colorspace2(void* encoder_raw, heif_colorspace* } else { *colorspace = heif_colorspace_YCbCr; - //*chroma = encoder->chroma; + if (*chroma != heif_chroma_420 && + *chroma != heif_chroma_422 && + *chroma != heif_chroma_444) { + *chroma = heif_chroma_420; + } } } +void kvazaar_query_encoded_size(void* encoder_raw, uint32_t input_width, uint32_t input_height, + uint32_t* encoded_width, uint32_t* encoded_height) +{ + *encoded_width = (input_width + 7) & ~0x7; + *encoded_height = (input_height + 7) & ~0x7; +} + + #if 0 static int rounded_size(int s) { @@ -335,10 +348,15 @@ static void append_chunk_data(kvz_data_chunk* data, std::vector& out) } -static void copy_plane(kvz_pixel* out_p, uint32_t out_stride, const uint8_t* in_p, uint32_t in_stride, int w, int h) +static void copy_plane(kvz_pixel* out_p, uint32_t out_stride, const uint8_t* in_p, uint32_t in_stride, int w, int h, int padded_width, int padded_height) { - for (int y = 0; y < h; y++) { - memcpy(out_p + y * out_stride, in_p + y * in_stride, w); + for (int y = 0; y < padded_height; y++) { + int sy = std::min(y, h - 1); // source y + memcpy(out_p + y * out_stride, in_p + sy * in_stride, w); + + if (padded_width > w) { + memset(out_p + y * out_stride + w, *(in_p + sy * in_stride + w - 1), padded_width - w); + } } } @@ -427,6 +445,14 @@ static struct heif_error kvazaar_encode_image(void* encoder_raw, const struct he (void) ctu; #endif + int input_width = heif_image_get_width(image, heif_channel_Y); + int input_height = heif_image_get_height(image, heif_channel_Y); + int input_chroma_width = 0; + int input_chroma_height = 0; + + uint32_t encoded_width, encoded_height; + kvazaar_query_encoded_size(encoder_raw, input_width, input_height, &encoded_width, &encoded_height); + kvz_chroma_format kvzChroma; int chroma_stride_shift = 0; int chroma_height_shift = 0; @@ -440,18 +466,24 @@ static struct heif_error kvazaar_encode_image(void* encoder_raw, const struct he kvzChroma = KVZ_CSP_420; chroma_stride_shift = 1; chroma_height_shift = 1; + input_chroma_width = (input_width + 1) / 2; + input_chroma_height = (input_height + 1) / 2; } else if (chroma == heif_chroma_422) { config->input_format = KVZ_FORMAT_P422; kvzChroma = KVZ_CSP_422; chroma_stride_shift = 1; chroma_height_shift = 0; + input_chroma_width = (input_width + 1) / 2; + input_chroma_height = input_height; } else if (chroma == heif_chroma_444) { config->input_format = KVZ_FORMAT_P444; kvzChroma = KVZ_CSP_444; chroma_stride_shift = 0; chroma_height_shift = 0; + input_chroma_width = input_width; + input_chroma_height = input_height; } if (chroma != heif_chroma_monochrome) { @@ -468,7 +500,6 @@ static struct heif_error kvazaar_encode_image(void* encoder_raw, const struct he (void) h; } - struct heif_color_profile_nclx* nclx = nullptr; heif_error err = heif_image_get_nclx_color_profile(image, &nclx); if (err.code != heif_error_Ok) { @@ -496,8 +527,8 @@ static struct heif_error kvazaar_encode_image(void* encoder_raw, const struct he config->qp = ((100 - encoder->quality) * 51 + 50) / 100; config->lossless = encoder->lossless ? 1 : 0; - config->width = heif_image_get_width(image, heif_channel_Y); - config->height = heif_image_get_height(image, heif_channel_Y); + config->width = encoded_width; + config->height = encoded_height; // Note: it is ok to cast away the const, as the image content is not changed. // However, we have to guarantee that there are no plane pointers or stride values kept over calling the svt_encode_image() function. @@ -510,7 +541,7 @@ static struct heif_error kvazaar_encode_image(void* encoder_raw, const struct he } */ - kvz_picture* pic = api->picture_alloc_csp(kvzChroma, config->width, config->height); + kvz_picture* pic = api->picture_alloc_csp(kvzChroma, encoded_width, encoded_height); if (!pic) { api->config_destroy(config); return heif_error{ @@ -524,20 +555,22 @@ static struct heif_error kvazaar_encode_image(void* encoder_raw, const struct he int stride; const uint8_t* data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride); - copy_plane(pic->y, pic->stride, data, stride, config->width, config->height); + copy_plane(pic->y, pic->stride, data, stride, input_width, input_height, encoded_width, encoded_height); } else { int stride; const uint8_t* data; data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride); - copy_plane(pic->y, pic->stride, data, stride, config->width, config->height); + copy_plane(pic->y, pic->stride, data, stride, input_width, input_height, encoded_width, encoded_height); data = heif_image_get_plane_readonly(image, heif_channel_Cb, &stride); - copy_plane(pic->u, pic->stride >> chroma_stride_shift, data, stride, config->width >> chroma_stride_shift, config->height >> chroma_height_shift); + copy_plane(pic->u, pic->stride >> chroma_stride_shift, data, stride, input_chroma_width, input_chroma_height, + encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift); data = heif_image_get_plane_readonly(image, heif_channel_Cr, &stride); - copy_plane(pic->v, pic->stride >> chroma_stride_shift, data, stride, config->width >> chroma_stride_shift, config->height >> chroma_height_shift); + copy_plane(pic->v, pic->stride >> chroma_stride_shift, data, stride, input_chroma_width, input_chroma_height, + encoded_width >> chroma_stride_shift, encoded_height >> chroma_height_shift); } kvz_encoder* kvzencoder = api->encoder_open(config); @@ -671,7 +704,7 @@ static struct heif_error kvazaar_get_compressed_data(void* encoder_raw, uint8_t* static const struct heif_encoder_plugin encoder_plugin_kvazaar { - /* plugin_api_version */ 2, + /* plugin_api_version */ 3, /* compression_format */ heif_compression_HEVC, /* id_name */ "kvazaar", /* priority */ kvazaar_PLUGIN_PRIORITY, @@ -698,7 +731,8 @@ static const struct heif_encoder_plugin encoder_plugin_kvazaar /* query_input_colorspace */ kvazaar_query_input_colorspace, /* encode_image */ kvazaar_encode_image, /* get_compressed_data */ kvazaar_get_compressed_data, - /* query_input_colorspace (v2) */ kvazaar_query_input_colorspace2 + /* query_input_colorspace (v2) */ kvazaar_query_input_colorspace2, + /* query_encoded_size (v3) */ kvazaar_query_encoded_size }; const struct heif_encoder_plugin* get_encoder_plugin_kvazaar() diff --git a/libheif/uncompressed_image.cc b/libheif/uncompressed_image.cc index cda5dc3dc2..c91e5dba4f 100644 --- a/libheif/uncompressed_image.cc +++ b/libheif/uncompressed_image.cc @@ -48,7 +48,8 @@ enum heif_uncompressed_component_type component_type_cyan = 13, component_type_magenta = 14, component_type_yellow = 15, - component_type_key_black = 16 + component_type_key_black = 16, + component_type_max_valid = component_type_key_black }; bool is_predefined_component_type(uint16_t type) @@ -467,8 +468,10 @@ static Error get_heif_chroma_uncompressed(std::shared_ptr& uncC, std:: uint16_t component_index = component.component_index; uint16_t component_type = cmpd->get_components()[component_index].component_type; - if (component_type >= 16) { - return { heif_error_Unsupported_feature, heif_suberror_Invalid_parameter_value, "a component_type >= 16 is not supported"}; + if (component_type > component_type_max_valid) { + std::stringstream sstr; + sstr << "a component_type > " << component_type_max_valid << " is not supported"; + return { heif_error_Unsupported_feature, heif_suberror_Invalid_parameter_value, sstr.str()}; } componentSet |= (1 << component_type);