diff --git a/CMakeLists.txt b/CMakeLists.txt index 255ed61aaf..c26ea68453 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.19.1) +project(libheif LANGUAGES C CXX VERSION 1.19.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. @@ -416,6 +416,9 @@ endif() if (KVAZAAR_FOUND AND NOT (PLUGIN_LOADING_SUPPORTED_AND_ENABLED AND WITH_KVAZAAR_PLUGIN)) list(APPEND REQUIRES_PRIVATE "kvazaar") endif() +if (OpenH264_FOUND AND (WITH_OpenH264_DECODER AND NOT (PLUGIN_LOADING_SUPPORTED_AND_ENABLED AND WITH_OpenH264_DECODER_PLUGIN))) + list(APPEND REQUIRES_PRIVATE "openh264") +endif() if ((AOM_DECODER_FOUND AND NOT (PLUGIN_LOADING_SUPPORTED_AND_ENABLED AND WITH_AOM_DECODER_PLUGIN)) OR (AOM_ENCODER_FOUND AND NOT (PLUGIN_LOADING_SUPPORTED_AND_ENABLED AND WITH_AOM_ENCODER_PLUGIN))) list(APPEND REQUIRES_PRIVATE "aom") @@ -438,6 +441,9 @@ endif() if (OpenJPEG_FOUND AND ((WITH_OpenJPEG_DECODER AND NOT (PLUGIN_LOADING_SUPPORTED_AND_ENABLED AND WITH_OpenJPEG_DECODER_PLUGIN)) OR (WITH_OpenJPEG_ENCODER AND NOT (PLUGIN_LOADING_SUPPORTED_AND_ENABLED AND WITH_OpenJPEG_ENCODER_PLUGIN)))) list(APPEND REQUIRES_PRIVATE "libopenjp2") endif() +if (OPENJPH_FOUND AND (WITH_OPENJPH_ENCODER AND NOT (PLUGIN_LOADING_SUPPORTED_AND_ENABLED AND WITH_OPENJPH_ENCODER_PLUGIN))) + list(APPEND REQUIRES_PRIVATE "openjph") +endif() if (LIBSHARPYUV_FOUND) list(APPEND REQUIRES_PRIVATE "libsharpyuv") endif() @@ -543,6 +549,7 @@ if(ENABLE_COVERAGE) endif() option(BUILD_TESTING "" ON) +set(CMAKE_CTEST_ARGUMENTS "--output-on-failure") include(CTest) if(BUILD_TESTING) # TODO: fix tests on windows. diff --git a/appveyor.yml b/appveyor.yml index 031fa958d1..5f1707c68c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,8 +27,10 @@ before_build: - mkdir build - cd build - cmake .. -A %arch% -DCMAKE_TOOLCHAIN_FILE=c:/tools/vcpkg/scripts/buildsystems/vcpkg.cmake -DWITH_DAV1D=ON -DWITH_AOM_DECODER=ON -DWITH_AOM_ENCODER=ON -DWITH_JPEG_DECODER=ON -DWITH_JPEG_ENCODER=ON -DWITH_UNCOMPRESSED_CODEC=ON -DWITH_HEADER_COMPRESSION=ON -DWITH_FFMPEG_DECODER=ON + - dir build: + project: build\libheif.sln parallel: true verbosity: normal diff --git a/examples/heif_dec.cc b/examples/heif_dec.cc index 4fd7422706..b85f6b7338 100644 --- a/examples/heif_dec.cc +++ b/examples/heif_dec.cc @@ -858,9 +858,12 @@ int main(int argc, char** argv) decode_options->strict_decoding = strict_decoding; decode_options->decoder_id = decoder_id; - decode_options->start_progress = start_progress; - decode_options->on_progress = on_progress; - decode_options->end_progress = end_progress; + + if (!option_quiet) { + decode_options->start_progress = start_progress; + decode_options->on_progress = on_progress; + decode_options->end_progress = end_progress; + } if (chroma_upsampling=="nearest-neighbor") { decode_options->color_conversion_options.preferred_chroma_upsampling_algorithm = heif_chroma_upsampling_nearest_neighbor; diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc index f7808bd2f3..516fb2d25c 100644 --- a/examples/heif_enc.cc +++ b/examples/heif_enc.cc @@ -210,7 +210,7 @@ void show_help(const char* argv0) << " --tiled-image-height # override image height of tiled image\n" << " --tiled-input-x-y usually, the first number in the input tile filename should be the y position.\n" << " With this option, this can be swapped so that the first number is x, the second number y.\n" -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES << " --tiling-method METHOD choose one of these methods: grid, tili, unci. The default is 'grid'.\n" << " --add-pyramid-group when several images are given, put them into a multi-resolution pyramid group.\n" #endif @@ -749,7 +749,7 @@ heif_image_handle* encode_tiled(heif_context* ctx, heif_encoder* encoder, heif_e return nullptr; } } -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES else if (tiling_method == "tili") { heif_tiled_image_parameters tiled_params{}; tiled_params.version = 1; @@ -776,7 +776,7 @@ heif_image_handle* encode_tiled(heif_context* ctx, heif_encoder* encoder, heif_e params.tile_height = tiling.tile_height; params.compression = unci_compression; - std::string input_filename = tile_generator.filename(0, 0); + std::string input_filename = tile_generator.filename(0, 0).string(); InputImage prototype_image = load_image(input_filename, output_bit_depth); heif_error error = heif_context_add_unci_image(ctx, ¶ms, options, prototype_image.image.get(), &tiled_image); @@ -974,7 +974,7 @@ int main(int argc, char** argv) case OPTION_TILING_METHOD: tiling_method = optarg; if (tiling_method != "grid" -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES && tiling_method != "tili" && tiling_method != "unci" #endif ) { @@ -1393,7 +1393,7 @@ int main(int argc, char** argv) heif_image_handle_release(primary_image_handle); } -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES if (add_pyramid_group && encoded_image_ids.size() > 1) { error = heif_context_add_pyramid_entity_group(context.get(), encoded_image_ids.data(), encoded_image_ids.size(), nullptr); if (error.code) { diff --git a/examples/heif_info.cc b/examples/heif_info.cc index 5ea8baa307..751273e509 100644 --- a/examples/heif_info.cc +++ b/examples/heif_info.cc @@ -77,7 +77,8 @@ static struct option long_options[] = { {0, 0, 0, 0} }; -const char* fourcc_to_string(uint32_t fourcc) +// Note: the same function is also exists in common_utils.h, but is not in the public API. +static const char* fourcc_to_string(uint32_t fourcc) { static char fcc[5]; fcc[0] = (char) ((fourcc >> 24) & 0xFF); @@ -272,7 +273,7 @@ int main(int argc, char** argv) if (err.code) { std::cerr << "Error while trying to get image tiling information: " << err.message << "\n"; } - else if (tiling.num_columns > 0) { + else if (tiling.num_columns != 1 || tiling.num_rows != 1) { std::cout << " tiles: " << tiling.num_columns << "x" << tiling.num_rows << ", tile size: " << tiling.tile_width << "x" << tiling.tile_height << "\n"; } diff --git a/fuzzing/color_conversion_fuzzer.cc b/fuzzing/color_conversion_fuzzer.cc index d73ccaf58f..b75a99ffa9 100644 --- a/fuzzing/color_conversion_fuzzer.cc +++ b/fuzzing/color_conversion_fuzzer.cc @@ -70,7 +70,7 @@ static bool read_plane(BitstreamRange* range, if (!range->prepare_read(static_cast(width) * height)) { return false; } - if (!image->add_plane(channel, width, height, bit_depth)) { + if (auto err = image->add_plane(channel, width, height, bit_depth, heif_get_disabled_security_limits())) { return false; } uint32_t stride; @@ -96,7 +96,7 @@ static bool read_plane_interleaved(BitstreamRange* range, if (!range->prepare_read(static_cast(width) * height * comps)) { return false; } - if (!image->add_plane(channel, width, height, bit_depth)) { + if (auto err = image->add_plane(channel, width, height, bit_depth, heif_get_disabled_security_limits())) { return false; } uint32_t stride; @@ -250,19 +250,23 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) int output_bpp = 0; // Same as input. heif_encoding_options* options = heif_encoding_options_alloc(); - auto out_image = convert_colorspace(in_image, - static_cast(out_colorspace), - static_cast(out_chroma), - nullptr, - output_bpp, - options->color_conversion_options); + auto out_image_result = convert_colorspace(in_image, + static_cast(out_colorspace), + static_cast(out_chroma), + nullptr, + output_bpp, + options->color_conversion_options, + heif_get_disabled_security_limits()); + heif_encoding_options_free(options); - if (!out_image) { + if (out_image_result.error) { // Conversion is not supported. return 0; } + auto out_image = *out_image_result; + assert(out_image->get_width() == width); assert(out_image->get_height() == height); assert(out_image->get_chroma_format() == diff --git a/fuzzing/file_fuzzer.cc b/fuzzing/file_fuzzer.cc index 5608686f6b..c43c1630b8 100644 --- a/fuzzing/file_fuzzer.cc +++ b/fuzzing/file_fuzzer.cc @@ -51,7 +51,11 @@ static void TestDecodeImage(struct heif_context* ctx, heif_image_handle_get_metadata_type(handle, metadata_ids[i]); heif_image_handle_get_metadata_content_type(handle, metadata_ids[i]); size_t metadata_size = heif_image_handle_get_metadata_size(handle, metadata_ids[i]); - assert(metadata_size < filesize); + + // This assertion is invalid. Metadata can in fact be larger than the file if there are several + // overlapping iloc extents. Does not make much sense, but it is technically valid. + //assert(metadata_size < filesize); + uint8_t* metadata_data = static_cast(malloc(metadata_size)); assert(metadata_data); heif_image_handle_get_metadata(handle, metadata_ids[i], metadata_data); diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 0af5797370..6860624d36 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -140,7 +140,7 @@ if (ENABLE_EXPERIMENTAL_FEATURES) list(APPEND libheif_headers api/libheif/heif_experimental.h) - target_compile_definitions(heif PUBLIC ENABLE_EXPERIMENTAL_FEATURES) + target_compile_definitions(heif PUBLIC HEIF_ENABLE_EXPERIMENTAL_FEATURES) endif() # Needed to find libheif/heif_version.h while compiling the library diff --git a/libheif/api/libheif/heif.cc b/libheif/api/libheif/heif.cc index 5a10ab64c7..3b7ce8532f 100644 --- a/libheif/api/libheif/heif.cc +++ b/libheif/api/libheif/heif.cc @@ -1020,7 +1020,8 @@ struct heif_error heif_image_handle_get_tile_size(const struct heif_image_handle struct heif_entity_group* heif_context_get_entity_groups(const struct heif_context* ctx, - uint32_t type_filter, uint32_t item_filter, + uint32_t type_filter, + heif_item_id item_filter, int* out_num_groups) { std::shared_ptr grplBox = ctx->context->get_heif_file()->get_grpl_box(); @@ -1557,7 +1558,7 @@ void heif_image_get_content_light_level(const struct heif_image* image, struct h int heif_image_handle_get_content_light_level(const struct heif_image_handle* handle, struct heif_content_light_level* out) { - auto clli = handle->image->get_file()->get_property(handle->image->get_id()); + auto clli = handle->image->get_property(); if (out && clli) { *out = clli->clli; } @@ -1587,7 +1588,7 @@ void heif_image_get_mastering_display_colour_volume(const struct heif_image* ima int heif_image_handle_get_mastering_display_colour_volume(const struct heif_image_handle* handle, struct heif_mastering_display_colour_volume* out) { - auto mdcv = handle->image->get_file()->get_property(handle->image->get_id()); + auto mdcv = handle->image->get_property(); if (out && mdcv) { *out = mdcv->mdcv; } @@ -1664,7 +1665,7 @@ void heif_image_get_pixel_aspect_ratio(const struct heif_image* image, uint32_t* int heif_image_handle_get_pixel_aspect_ratio(const struct heif_image_handle* handle, uint32_t* aspect_h, uint32_t* aspect_v) { - auto pasp = handle->image->get_file()->get_property(handle->image->get_id()); + auto pasp = handle->image->get_property(); if (pasp) { *aspect_h = pasp->hSpacing; *aspect_v = pasp->vSpacing; @@ -1773,7 +1774,7 @@ heif_error heif_image_crop(struct heif_image* img, "Image size exceeds maximum supported size"}; } - auto cropResult = img->image->crop(left, static_cast(w) - 1 - right, top, static_cast(h) - 1 - bottom); + auto cropResult = img->image->crop(left, static_cast(w) - 1 - right, top, static_cast(h) - 1 - bottom, nullptr); if (cropResult.error) { return cropResult.error.error_struct(img->image.get()); } @@ -1805,11 +1806,9 @@ int heif_image_has_channel(const struct heif_image* img, enum heif_channel chann struct heif_error heif_image_add_plane(struct heif_image* image, heif_channel channel, int width, int height, int bit_depth) { - if (!image->image->add_plane(channel, width, height, bit_depth)) { - struct heif_error err = {heif_error_Memory_allocation_error, - heif_suberror_Unspecified, - "Cannot allocate memory for image plane"}; - return err; + // Note: no security limit, because this is explicitly requested by the user. + if (auto err = image->image->add_plane(channel, width, height, bit_depth, nullptr)) { + return err.error_struct(image->image.get()); } else { return heif_error_success; @@ -1822,7 +1821,7 @@ struct heif_error heif_image_add_channel(struct heif_image* image, int width, int height, heif_channel_datatype datatype, int bit_depth) { - if (!image->image->add_channel(channel, width, height, datatype, bit_depth)) { + if (!image->image->add_channel(channel, width, height, datatype, bit_depth, nullptr)) { struct heif_error err = {heif_error_Memory_allocation_error, heif_suberror_Unspecified, "Cannot allocate memory for image plane"}; @@ -1996,11 +1995,9 @@ int heif_image_is_premultiplied_alpha(struct heif_image* image) struct heif_error heif_image_extend_padding_to_size(struct heif_image* image, int min_physical_width, int min_physical_height) { - bool mem_alloc_success = image->image->extend_padding_to_size(min_physical_width, min_physical_height); - if (!mem_alloc_success) { - return heif_error{heif_error_Memory_allocation_error, - heif_suberror_Unspecified, - "Cannot allocate image memory."}; + Error err = image->image->extend_padding_to_size(min_physical_width, min_physical_height, false, nullptr); + if (err) { + return err.error_struct(image->image.get()); } else { return heif_error_success; @@ -2015,7 +2012,7 @@ struct heif_error heif_image_scale_image(const struct heif_image* input, { std::shared_ptr out_img; - Error err = input->image->scale_nearest_neighbor(out_img, width, height); + Error err = input->image->scale_nearest_neighbor(out_img, width, height, nullptr); if (err) { return err.error_struct(input->image.get()); } @@ -2030,11 +2027,9 @@ struct heif_error heif_image_scale_image(const struct heif_image* input, struct heif_error heif_image_extend_to_size_fill_with_zero(struct heif_image* image, uint32_t width, uint32_t height) { - bool success = image->image->extend_to_size_with_zero(width, height); - if (!success) { - return heif_error{heif_error_Memory_allocation_error, - heif_suberror_Unspecified, - "Not enough memory to extend image size."}; + Error err = image->image->extend_to_size_with_zero(width, height, nullptr); + if (err) { + return err.error_struct(image->image.get()); } return heif_error_ok; @@ -3055,7 +3050,7 @@ heif_encoder_parameter_get_valid_integer_range(const struct heif_encoder_paramet return heif_error_success; } -LIBHEIF_API + struct heif_error heif_encoder_parameter_get_valid_integer_values(const struct heif_encoder_parameter* param, int* have_minimum, int* have_maximum, int* minimum, int* maximum, @@ -3650,7 +3645,7 @@ struct heif_error heif_context_add_image_tile(struct heif_context* ctx, } #endif else if (auto grid_item = std::dynamic_pointer_cast(tiled_image->image)) { - Error err = grid_item->add_image_tile(tiled_image->image->get_id(), tile_x, tile_y, image->image, encoder); + Error err = grid_item->add_image_tile(tile_x, tile_y, image->image, encoder); return err.error_struct(ctx->context.get()); } else { diff --git a/libheif/api/libheif/heif.h b/libheif/api/libheif/heif.h index 2b041b6936..ca2d59bd50 100644 --- a/libheif/api/libheif/heif.h +++ b/libheif/api/libheif/heif.h @@ -55,7 +55,7 @@ extern "C" { // 1.18 5 7 1 1 1 1 // 1.19 6 7 2 1 1 1 -#if defined(_MSC_VER) && !defined(LIBHEIF_STATIC_BUILD) +#if (defined(_WIN32) || defined __CYGWIN__) && !defined(LIBHEIF_STATIC_BUILD) #ifdef LIBHEIF_EXPORTS #define LIBHEIF_API __declspec(dllexport) #else @@ -1324,7 +1324,10 @@ struct heif_entity_group // Use 0 for `type_filter` or `item_filter` to disable the filter. // Returns an array of heif_entity_group structs with *out_num_groups entries. LIBHEIF_API -struct heif_entity_group* heif_context_get_entity_groups(const struct heif_context*, uint32_t type_filter, uint32_t item_filter, int* out_num_groups); +struct heif_entity_group* heif_context_get_entity_groups(const struct heif_context*, + uint32_t type_filter, + heif_item_id item_filter, + int* out_num_groups); // Release an array of entity groups returned by heif_context_get_entity_groups(). LIBHEIF_API @@ -2363,7 +2366,7 @@ struct heif_encoding_options // Set this to the NCLX parameters to be used in the output image or set to NULL // when the same parameters as in the input image should be used. - const struct heif_color_profile_nclx* output_nclx_profile; + struct heif_color_profile_nclx* output_nclx_profile; uint8_t macOS_compatibility_workaround_no_nclx_profile; diff --git a/libheif/api/libheif/heif_experimental.h b/libheif/api/libheif/heif_experimental.h index 359cc46ecd..7d6d1b8361 100644 --- a/libheif/api/libheif/heif_experimental.h +++ b/libheif/api/libheif/heif_experimental.h @@ -27,7 +27,7 @@ extern "C" { #endif -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES /* =================================================================================== * This file contains candidate APIs that did not make it into the public API yet. @@ -137,7 +137,7 @@ struct heif_tiled_image_parameters { uint8_t tiles_are_sequential; // TODO: can we derive this automatically }; -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES LIBHEIF_API struct heif_error heif_context_add_tiled_image(struct heif_context* ctx, const struct heif_tiled_image_parameters* parameters, @@ -177,7 +177,7 @@ struct heif_unci_image_parameters { // TODO: interleave type, padding }; -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES LIBHEIF_API struct heif_error heif_context_add_unci_image(struct heif_context* ctx, const struct heif_unci_image_parameters* parameters, @@ -195,7 +195,7 @@ struct heif_pyramid_layer_info { uint32_t tile_columns_in_layer; }; -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES // The input images are automatically sorted according to resolution. You can provide them in any order. LIBHEIF_API struct heif_error heif_context_add_pyramid_entity_group(struct heif_context* ctx, @@ -221,7 +221,7 @@ enum heif_channel_datatype heif_channel_datatype_complex_number = 4 }; -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES LIBHEIF_API struct heif_error heif_image_add_channel(struct heif_image* image, enum heif_channel channel, @@ -245,7 +245,7 @@ struct heif_complex64 { double real, imaginary; }; -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES LIBHEIF_API enum heif_channel_datatype heif_image_get_datatype(const struct heif_image* img, enum heif_channel channel); @@ -372,7 +372,7 @@ struct heif_tai_clock_info }; -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES int heif_is_tai_clock_info_drift_rate_undefined(int32_t drift_rate); @@ -404,7 +404,7 @@ struct heif_tai_timestamp_packet uint8_t timestamp_is_modified; // bool }; -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES // Creates a new TAI timestamp property if one doesn't already exist for itemId. // Creates a new clock info property if one doesn't already exist for itemId. diff --git a/libheif/api/libheif/heif_items.cc b/libheif/api/libheif/heif_items.cc index 90cae4887d..6b8fdd9ec1 100644 --- a/libheif/api/libheif/heif_items.cc +++ b/libheif/api/libheif/heif_items.cc @@ -256,7 +256,7 @@ struct heif_error heif_context_add_mime_item(struct heif_context* ctx, } } -LIBHEIF_API + struct heif_error heif_context_add_precompressed_mime_item(struct heif_context* ctx, const char* content_type, const char* content_encoding, diff --git a/libheif/api/libheif/heif_properties.cc b/libheif/api/libheif/heif_properties.cc index 5040f81b00..93d71c9304 100644 --- a/libheif/api/libheif/heif_properties.cc +++ b/libheif/api/libheif/heif_properties.cc @@ -182,7 +182,6 @@ struct heif_error heif_item_get_property_user_description(const struct heif_cont } -LIBHEIF_API struct heif_error heif_item_add_property_user_description(const struct heif_context* context, heif_item_id itemId, const struct heif_property_user_description* description, @@ -359,7 +358,7 @@ struct heif_error find_property(const struct heif_context* context, } -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES const uint64_t heif_tai_clock_info_unknown_time_uncertainty = 0xFFFFFFFFFFFFFFFF; const uint64_t heif_unknown_tai_timestamp = 0xFFFFFFFFFFFFFFFF; const int32_t heif_tai_clock_info_unknown_drift_rate = 0x7FFFFFFF; @@ -390,7 +389,7 @@ struct heif_error heif_property_set_clock_info(struct heif_context* ctx, } // Create new taic if one doesn't exist for the itemId. - auto taic = ctx->context->get_heif_file()->get_property(itemId); + auto taic = ctx->context->get_heif_file()->get_property_for_item(itemId); if (!taic) { taic = std::make_shared(); } @@ -428,7 +427,7 @@ struct heif_error heif_property_get_clock_info(const struct heif_context* ctx, } // Check if taic exists for itemId - auto taic = file->get_property(itemId); + auto taic = file->get_property_for_item(itemId); if (!taic) { out_clock = nullptr; return {heif_error_Usage_error, heif_suberror_Invalid_property, "TAI Clock property not found for itemId"}; @@ -463,7 +462,7 @@ struct heif_error heif_property_set_tai_timestamp(struct heif_context* ctx, } // Create new itai if one doesn't exist for the itemId. - auto itai = file->get_property(itemId); + auto itai = file->get_property_for_item(itemId); if (!itai) { itai = std::make_shared(); } @@ -476,7 +475,7 @@ struct heif_error heif_property_set_tai_timestamp(struct heif_context* ctx, heif_property_id id = ctx->context->add_property(itemId, itai, false); // Create new taic if one doesn't exist for the itemId. - auto taic = file->get_property(itemId); + auto taic = file->get_property_for_item(itemId); if (!taic) { taic = std::make_shared(); ctx->context->add_property(itemId, taic, false); @@ -506,7 +505,7 @@ struct heif_error heif_property_get_tai_timestamp(const struct heif_context* ctx } //Check if itai exists for itemId - auto itai = file->get_property(itemId); + auto itai = file->get_property_for_item(itemId); if (!itai) { out_timestamp = nullptr; return {heif_error_Usage_error, heif_suberror_Invalid_property, "Timestamp property not found for itemId"}; diff --git a/libheif/api/libheif/heif_properties.h b/libheif/api/libheif/heif_properties.h index 9acb88ed90..99acfed755 100644 --- a/libheif/api/libheif/heif_properties.h +++ b/libheif/api/libheif/heif_properties.h @@ -135,7 +135,7 @@ void heif_item_get_property_transform_crop_borders(const struct heif_context* co int* left, int* top, int* right, int* bottom); /** - * @param context + * @param context The heif_context for the file * @param itemId The image item id to which this property belongs. * @param fourcc_type The short four-cc type of the property to add. * @param uuid_type If fourcc_type=='uuid', this should point to a 16-byte UUID type. It is ignored otherwise and can be NULL. @@ -160,7 +160,7 @@ struct heif_error heif_item_get_property_raw_size(const struct heif_context* con size_t* out_size); /** - * @param data_out User-supplied array to write the property data to. The required size of the output array is given by heif_item_get_property_raw_size(). + * @param out_data User-supplied array to write the property data to. The required size of the output array is given by heif_item_get_property_raw_size(). */ LIBHEIF_API struct heif_error heif_item_get_property_raw_data(const struct heif_context* context, @@ -177,8 +177,8 @@ struct heif_error heif_item_get_property_raw_data(const struct heif_context* con * * @param context the heif_context containing the HEIF file * @param itemId the image item id to which this property belongs. - * @param propertyID the property index (1-based) to get the extended type for - * @param extended_type output of the call, must be a pointer to at least 16-bytes. + * @param propertyId the property index (1-based) to get the extended type for + * @param out_extended_type output of the call, must be a pointer to at least 16-bytes. * @return heif_error_success or an error indicating the failure */ LIBHEIF_API diff --git a/libheif/bitstream.cc b/libheif/bitstream.cc index 9063718179..4e26c39c39 100644 --- a/libheif/bitstream.cc +++ b/libheif/bitstream.cc @@ -23,7 +23,13 @@ #include #include #include + +#define GCC_COMPILER (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__PGI)) +#if (defined(GCC_COMPILER) && __GNUC__ < 9) || (defined(__clang__) && __clang_major__ < 10) +#include +#else #include +#endif #define MAX_UVLC_LEADING_ZEROS 20 diff --git a/libheif/box.cc b/libheif/box.cc index 6c52531933..bc18fb21ce 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -461,6 +461,11 @@ Error Box::read(BitstreamRange& range, std::shared_ptr* result, const heif_ box = std::make_shared(); break; + case fourcc("free"): + case fourcc("skip"): + box = std::make_shared(); + break; + case fourcc("meta"): box = std::make_shared(); break; @@ -668,7 +673,7 @@ Error Box::read(BitstreamRange& range, std::shared_ptr* result, const heif_ box = std::make_shared(); break; -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES // --- TAI timestamps case fourcc("itai"): @@ -692,7 +697,7 @@ Error Box::read(BitstreamRange& range, std::shared_ptr* result, const heif_ box = std::make_shared(); break; -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES case fourcc("tilC"): box = std::make_shared(); break; @@ -861,6 +866,19 @@ bool Box::operator==(const Box& other) const } +bool Box::remove_child_box(const std::shared_ptr& box) +{ + for (int i=0; i<(int)m_children.size(); i++) { + if (m_children[i].get() == box.get()) { + m_children.erase(m_children.begin() + i); + return true; + } + } + + return false; +} + + bool Box::equal(const std::shared_ptr& box1, const std::shared_ptr& box2) { if (!box1 || !box2) { @@ -888,22 +906,24 @@ Error Box::read_children(BitstreamRange& range, uint32_t max_number, const heif_ return error; } - uint32_t max_children; - if (get_short_type() == fourcc("iinf")) { - max_children = limits->max_items; - } - else { - max_children = limits->max_children_per_box; - } + if (max_number == READ_CHILDREN_ALL) { + uint32_t max_children; + if (get_short_type() == fourcc("iinf")) { + max_children = limits->max_items; + } + else { + max_children = limits->max_children_per_box; + } - if (max_children && m_children.size() > max_children) { - std::stringstream sstr; - sstr << "Maximum number of child boxes (" << max_children << ") in '" << get_type_string() << "' box exceeded."; + if (max_children && m_children.size() > max_children) { + std::stringstream sstr; + sstr << "Maximum number of child boxes (" << max_children << ") in '" << get_type_string() << "' box exceeded."; - // Sanity check. - return Error(heif_error_Memory_allocation_error, - heif_suberror_Security_limit_exceeded, - sstr.str()); + // Sanity check. + return Error(heif_error_Memory_allocation_error, + heif_suberror_Security_limit_exceeded, + sstr.str()); + } } m_children.push_back(std::move(box)); @@ -1154,6 +1174,29 @@ Error Box_ftyp::write(StreamWriter& writer) const } +Error Box_free::parse(BitstreamRange& range, const heif_security_limits* limits) +{ + range.skip_to_end_of_box(); + return range.get_error(); +} + + +std::string Box_free::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << BoxHeader::dump(indent); + return sstr.str(); +} + + +Error Box_free::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + prepend_header(writer, box_start); + return Error::Ok; +} + + Error Box_meta::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); @@ -4582,7 +4625,7 @@ Error Box_cmex::write(StreamWriter& writer) const } -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES std::string Box_taic::dump(Indent& indent) const { std::ostringstream sstr; sstr << Box::dump(indent); @@ -4596,7 +4639,7 @@ std::string Box_taic::dump(Indent& indent) const { sstr << m_clock_drift_rate << "\n"; } - sstr << indent << "clock_type: " << m_clock_type << "\n"; + sstr << indent << "clock_type: " << static_cast(m_clock_type) << "\n"; return sstr.str(); } diff --git a/libheif/box.h b/libheif/box.h index 84aef8dc03..71f4504105 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -208,7 +208,18 @@ class Box : public BoxHeader return nullptr; } + template bool replace_child_box(const std::shared_ptr& box) + { + for (auto & b : m_children) { + if (std::dynamic_pointer_cast(b) != nullptr) { + b = box; + return true; + } + } + append_child_box(box); + return false; + } template std::vector> get_child_boxes() const @@ -233,6 +244,8 @@ class Box : public BoxHeader bool has_child_boxes() const { return !m_children.empty(); } + bool remove_child_box(const std::shared_ptr& box); + virtual bool operator==(const Box& other) const; static bool equal(const std::shared_ptr& box1, const std::shared_ptr& box2); @@ -412,6 +425,23 @@ class Box_ftyp : public Box }; +class Box_free : public Box +{ +public: + Box_free() + { + set_short_type(fourcc("free")); + } + + std::string dump(Indent&) const override; + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range, const heif_security_limits*) override; +}; + + class Box_meta : public FullBox { public: @@ -1550,7 +1580,7 @@ class Box_udes : public FullBox }; -#if ENABLE_EXPERIMENTAL_FEATURES +#if HEIF_ENABLE_EXPERIMENTAL_FEATURES class Box_taic : public FullBox { public: diff --git a/libheif/codecs/avif_boxes.cc b/libheif/codecs/avif_boxes.cc index 584a5072b2..b03b7b950c 100644 --- a/libheif/codecs/avif_boxes.cc +++ b/libheif/codecs/avif_boxes.cc @@ -124,6 +124,7 @@ std::string Box_av1C::dump(Indent& indent) const << indent << "seq_level_idx_0: " << ((int) c.seq_level_idx_0) << "\n" << indent << "high_bitdepth: " << ((int) c.high_bitdepth) << "\n" << indent << "twelve_bit: " << ((int) c.twelve_bit) << "\n" + << indent << "monochrome: " << ((int) c.monochrome) << "\n" << indent << "chroma_subsampling_x: " << ((int) c.chroma_subsampling_x) << "\n" << indent << "chroma_subsampling_y: " << ((int) c.chroma_subsampling_y) << "\n" << indent << "chroma_sample_position: " << ((int) c.chroma_sample_position) << "\n" diff --git a/libheif/codecs/avif_boxes.h b/libheif/codecs/avif_boxes.h index 8b678e511a..2ac2152900 100644 --- a/libheif/codecs/avif_boxes.h +++ b/libheif/codecs/avif_boxes.h @@ -37,7 +37,7 @@ class Box_av1C : public Box { // allow access to protected parse() method -friend class HeifFile; +friend class Box_mini; public: Box_av1C() @@ -68,7 +68,10 @@ friend class HeifFile; //unsigned int (8)[] configOBUs; heif_chroma get_heif_chroma() const { - if (chroma_subsampling_x==1 && chroma_subsampling_y==1) { + if (monochrome) { + return heif_chroma_monochrome; + } + else if (chroma_subsampling_x==1 && chroma_subsampling_y==1) { return heif_chroma_420; } else if (chroma_subsampling_x==1 && chroma_subsampling_y==0) { diff --git a/libheif/codecs/decoder.cc b/libheif/codecs/decoder.cc index 766a5408aa..6e9ecbea76 100644 --- a/libheif/codecs/decoder.cc +++ b/libheif/codecs/decoder.cc @@ -99,37 +99,39 @@ Result> DataExtent::read_data(uint64_t offset, uint64_t siz } -std::shared_ptr Decoder::alloc_for_infe_type(const HeifContext* ctx, heif_item_id id, uint32_t format_4cc) +std::shared_ptr Decoder::alloc_for_infe_type(const ImageItem* item) { + uint32_t format_4cc = item->get_infe_type(); + switch (format_4cc) { case fourcc("hvc1"): { - auto hvcC = ctx->get_heif_file()->get_property(id); + auto hvcC = item->get_property(); return std::make_shared(hvcC); } case fourcc("av01"): { - auto av1C = ctx->get_heif_file()->get_property(id); + auto av1C = item->get_property(); return std::make_shared(av1C); } case fourcc("avc1"): { - auto avcC = ctx->get_heif_file()->get_property(id); + auto avcC = item->get_property(); return std::make_shared(avcC); } case fourcc("j2k1"): { - auto j2kH = ctx->get_heif_file()->get_property(id); + auto j2kH = item->get_property(); return std::make_shared(j2kH); } case fourcc("vvc1"): { - auto vvcC = ctx->get_heif_file()->get_property(id); + auto vvcC = item->get_property(); return std::make_shared(vvcC); } case fourcc("jpeg"): { - auto jpgC = ctx->get_heif_file()->get_property(id); + auto jpgC = item->get_property(); return std::make_shared(jpgC); } #if WITH_UNCOMPRESSED_CODEC case fourcc("unci"): { - auto uncC = ctx->get_heif_file()->get_property(id); - auto cmpd = ctx->get_heif_file()->get_property(id); + auto uncC = item->get_property(); + auto cmpd = item->get_property(); return std::make_shared(uncC,cmpd); } #endif @@ -183,6 +185,11 @@ Decoder::decode_single_frame_from_compressed_data(const struct heif_decoding_opt // --- decode image with the plugin + if (decoder_plugin->new_decoder == nullptr) { + return Error(heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed, + "Cannot decode with a dummy decoder plugins."); + } + void* decoder; struct heif_error err = decoder_plugin->new_decoder(&decoder); if (err.code != heif_error_Ok) { diff --git a/libheif/codecs/decoder.h b/libheif/codecs/decoder.h index f6d82b4a4b..e00091ec71 100644 --- a/libheif/codecs/decoder.h +++ b/libheif/codecs/decoder.h @@ -61,7 +61,7 @@ struct DataExtent class Decoder { public: - static std::shared_ptr alloc_for_infe_type(const HeifContext* ctx, heif_item_id, uint32_t format_4cc); + static std::shared_ptr alloc_for_infe_type(const ImageItem* item); virtual ~Decoder() = default; diff --git a/libheif/codecs/hevc_boxes.h b/libheif/codecs/hevc_boxes.h index 9b2a4971e2..575a1ff9ca 100644 --- a/libheif/codecs/hevc_boxes.h +++ b/libheif/codecs/hevc_boxes.h @@ -33,6 +33,10 @@ class Box_hvcC : public Box { + +// allow access to protected parse() method +friend class Box_mini; + public: Box_hvcC() { diff --git a/libheif/codecs/uncompressed/decoder_abstract.cc b/libheif/codecs/uncompressed/decoder_abstract.cc index 4ff8e7a469..94a8ce3a57 100644 --- a/libheif/codecs/uncompressed/decoder_abstract.cc +++ b/libheif/codecs/uncompressed/decoder_abstract.cc @@ -23,7 +23,13 @@ #include #include #include + +#define GCC_COMPILER (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !defined(__PGI)) +#if (defined(GCC_COMPILER) && __GNUC__ < 9) || (defined(__clang__) && __clang_major__ < 10) +#include +#else #include +#endif #include "common_utils.h" #include "context.h" @@ -166,10 +172,16 @@ const Error AbstractDecoder::get_compressed_image_data_uncompressed(const HeifCo uint32_t tile_idx, const Box_iloc::Item* item) const { + auto image = context->get_image(ID, false); + if (!image) { + return {heif_error_Invalid_input, + heif_suberror_Nonexisting_item_referenced}; + } + // --- get codec configuration - std::shared_ptr cmpC_box = context->get_heif_file()->get_property(ID); - std::shared_ptr icef_box = context->get_heif_file()->get_property(ID); + std::shared_ptr cmpC_box = image->get_property(); + std::shared_ptr icef_box = image->get_property(); if (!cmpC_box) { // assume no generic compression diff --git a/libheif/codecs/uncompressed/unc_codec.cc b/libheif/codecs/uncompressed/unc_codec.cc index 49adab1ee3..08aa7b537e 100644 --- a/libheif/codecs/uncompressed/unc_codec.cc +++ b/libheif/codecs/uncompressed/unc_codec.cc @@ -441,7 +441,8 @@ static AbstractDecoder* makeDecoder(uint32_t width, uint32_t height, const std:: Result> UncompressedImageCodec::create_image(const std::shared_ptr cmpd, const std::shared_ptr uncC, uint32_t width, - uint32_t height) + uint32_t height, + const heif_security_limits* limits) { auto img = std::make_shared(); heif_chroma chroma = heif_chroma_undefined; @@ -465,10 +466,15 @@ Result> UncompressedImageCodec::create_image(con } if ((channel == heif_channel_Cb) || (channel == heif_channel_Cr)) { - img->add_plane(channel, (width / chroma_h_subsampling(chroma)), (height / chroma_v_subsampling(chroma)), component.component_bit_depth); + if (auto err = img->add_plane(channel, (width / chroma_h_subsampling(chroma)), (height / chroma_v_subsampling(chroma)), component.component_bit_depth, + limits)) { + return err; + } } else { - img->add_plane(channel, width, height, component.component_bit_depth); + if (auto err = img->add_plane(channel, width, height, component.component_bit_depth, limits)) { + return err; + } } } } @@ -483,9 +489,15 @@ Error UncompressedImageCodec::decode_uncompressed_image_tile(const HeifContext* uint32_t tile_x0, uint32_t tile_y0) { auto file = context->get_heif_file(); - std::shared_ptr ispe = file->get_property(ID); - std::shared_ptr cmpd = file->get_property(ID); - std::shared_ptr uncC = file->get_property(ID); + auto image = context->get_image(ID, false); + if (!image) { + return {heif_error_Invalid_input, + heif_suberror_Nonexisting_item_referenced}; + } + + std::shared_ptr ispe = image->get_property(); + std::shared_ptr cmpd = image->get_property(); + std::shared_ptr uncC = image->get_property(); Error error = check_header_validity(ispe, cmpd, uncC); if (error) { @@ -495,7 +507,7 @@ Error UncompressedImageCodec::decode_uncompressed_image_tile(const HeifContext* uint32_t tile_width = ispe->get_width() / uncC->get_number_of_tile_columns(); uint32_t tile_height = ispe->get_height() / uncC->get_number_of_tile_rows(); - Result> createImgResult = create_image(cmpd, uncC, tile_width, tile_height); + Result> createImgResult = create_image(cmpd, uncC, tile_width, tile_height, context->get_security_limits()); if (createImgResult.error) { return createImgResult.error; } @@ -589,9 +601,15 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte return error; } - std::shared_ptr ispe = context->get_heif_file()->get_property(ID); - std::shared_ptr cmpd = context->get_heif_file()->get_property(ID); - std::shared_ptr uncC = context->get_heif_file()->get_property(ID); + auto image = context->get_image(ID, false); + if (!image) { + return {heif_error_Invalid_input, + heif_suberror_Nonexisting_item_referenced}; + } + + std::shared_ptr ispe = image->get_property(); + std::shared_ptr cmpd = image->get_property(); + std::shared_ptr uncC = image->get_property(); error = check_header_validity(ispe, cmpd, uncC); if (error) { @@ -613,7 +631,7 @@ Error UncompressedImageCodec::decode_uncompressed_image(const HeifContext* conte return error; } - Result> createImgResult = create_image(cmpd, uncC, width, height); + Result> createImgResult = create_image(cmpd, uncC, width, height, context->get_security_limits()); if (createImgResult.error) { return createImgResult.error; } diff --git a/libheif/codecs/uncompressed/unc_codec.h b/libheif/codecs/uncompressed/unc_codec.h index b244c90eba..24ca3241dc 100644 --- a/libheif/codecs/uncompressed/unc_codec.h +++ b/libheif/codecs/uncompressed/unc_codec.h @@ -68,7 +68,8 @@ class UncompressedImageCodec static Result> create_image(std::shared_ptr, std::shared_ptr, uint32_t width, - uint32_t height); + uint32_t height, + const heif_security_limits* limits); static Error check_header_validity(std::optional>, const std::shared_ptr&, diff --git a/libheif/color-conversion/alpha.cc b/libheif/color-conversion/alpha.cc index c2d913d375..9f743feb5d 100644 --- a/libheif/color-conversion/alpha.cc +++ b/libheif/color-conversion/alpha.cc @@ -52,11 +52,12 @@ Op_drop_alpha_plane::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_drop_alpha_plane::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { uint32_t width = input->get_width(); uint32_t height = input->get_height(); @@ -74,7 +75,7 @@ Op_drop_alpha_plane::convert_colorspace(const std::shared_ptrhas_channel(channel)) { - outimg->copy_new_plane_from(input, channel, channel); + outimg->copy_new_plane_from(input, channel, channel, limits); } } diff --git a/libheif/color-conversion/alpha.h b/libheif/color-conversion/alpha.h index 850c3c271d..1bf6c27a45 100644 --- a/libheif/color-conversion/alpha.h +++ b/libheif/color-conversion/alpha.h @@ -34,11 +34,12 @@ class Op_drop_alpha_plane : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; #endif //LIBHEIF_COLORCONVERSION_ALPHA_H diff --git a/libheif/color-conversion/chroma_sampling.cc b/libheif/color-conversion/chroma_sampling.cc index 16492a16ad..d0d5d3e69d 100644 --- a/libheif/color-conversion/chroma_sampling.cc +++ b/libheif/color-conversion/chroma_sampling.cc @@ -75,11 +75,12 @@ Op_YCbCr444_to_YCbCr420_average::state_after_conversion(const ColorState& template -std::shared_ptr +Result> Op_YCbCr444_to_YCbCr420_average::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { bool hdr = !std::is_same::value; @@ -98,14 +99,14 @@ Op_YCbCr444_to_YCbCr420_average::convert_colorspace(const std::shared_ptr if (bpp_y > 8 || bpp_cb > 8 || bpp_cr > 8) { - return nullptr; + return Error::InternalError; } } else { if (bpp_y <= 8 || bpp_cb <= 8 || bpp_cr <= 8) { - return nullptr; + return Error::InternalError; } } @@ -113,7 +114,7 @@ Op_YCbCr444_to_YCbCr420_average::convert_colorspace(const std::shared_ptr if (bpp_y != bpp_cb || bpp_y != bpp_cr) { // TODO: test with varying bit depths when we have a test image - return nullptr; + return Error::InternalError; } @@ -129,22 +130,22 @@ Op_YCbCr444_to_YCbCr420_average::convert_colorspace(const std::shared_ptr uint32_t cwidth = (width + 1) / 2; uint32_t cheight = (height + 1) / 2; - if (!outimg->add_plane(heif_channel_Y, width, height, bpp_y) || - !outimg->add_plane(heif_channel_Cb, cwidth, cheight, bpp_cb) || - !outimg->add_plane(heif_channel_Cr, cwidth, cheight, bpp_cr)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Y, width, height, bpp_y, limits) || + outimg->add_plane(heif_channel_Cb, cwidth, cheight, bpp_cb, limits) || + outimg->add_plane(heif_channel_Cr, cwidth, cheight, bpp_cr, limits)) { + return err; } if (has_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, bpp_a)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, bpp_a, limits)) { + return err; } } - const Pixel* in_y, * in_cb, * in_cr, * in_a; + const Pixel* in_y, * in_cb, * in_cr; uint32_t in_y_stride = 0, in_cb_stride = 0, in_cr_stride = 0, in_a_stride = 0; - Pixel* out_y, * out_cb, * out_cr, * out_a; + Pixel* out_y, * out_cb, * out_cr; uint32_t out_y_stride = 0, out_cb_stride = 0, out_cr_stride = 0, out_a_stride = 0; in_y = (const Pixel*) input->get_plane(heif_channel_Y, &in_y_stride); @@ -154,27 +155,30 @@ Op_YCbCr444_to_YCbCr420_average::convert_colorspace(const std::shared_ptr out_cb = (Pixel*) outimg->get_plane(heif_channel_Cb, &out_cb_stride); out_cr = (Pixel*) outimg->get_plane(heif_channel_Cr, &out_cr_stride); - if (has_alpha) { - in_a = (const Pixel*) input->get_plane(heif_channel_Alpha, &in_a_stride); - out_a = (Pixel*) outimg->get_plane(heif_channel_Alpha, &out_a_stride); - } - else { - in_a = nullptr; - out_a = nullptr; - } - - if (hdr) { in_y_stride /= 2; in_cb_stride /= 2; in_cr_stride /= 2; - in_a_stride /= 2; out_y_stride /= 2; out_cb_stride /= 2; out_cr_stride /= 2; - out_a_stride /= 2; } + + // We only copy the alpha, do not access it as 16 bit + const uint8_t* in_a; + uint8_t* out_a; + + if (has_alpha) { + in_a = input->get_plane(heif_channel_Alpha, &in_a_stride); + out_a = outimg->get_plane(heif_channel_Alpha, &out_a_stride); + } + else { + in_a = nullptr; + out_a = nullptr; + } + + // --- fill right and bottom borders if the image size is odd if (height & 1) { @@ -228,7 +232,8 @@ Op_YCbCr444_to_YCbCr420_average::convert_colorspace(const std::shared_ptr memcpy(&out_y[y * out_y_stride], &in_y[y * in_y_stride], copyWidth); if (has_alpha) { - memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], copyWidth); + uint32_t alphaCopyWidth = (bpp_a > 8 ? width * 2 : width); + memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], alphaCopyWidth); } } @@ -293,11 +298,12 @@ Op_YCbCr444_to_YCbCr422_average::state_after_conversion(const ColorState& template -std::shared_ptr +Result> Op_YCbCr444_to_YCbCr422_average::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { bool hdr = !std::is_same::value; @@ -316,14 +322,14 @@ Op_YCbCr444_to_YCbCr422_average::convert_colorspace(const std::shared_ptr if (bpp_y > 8 || bpp_cb > 8 || bpp_cr > 8) { - return nullptr; + return Error::InternalError; } } else { if (bpp_y <= 8 || bpp_cb <= 8 || bpp_cr <= 8) { - return nullptr; + return Error::InternalError; } } @@ -331,7 +337,7 @@ Op_YCbCr444_to_YCbCr422_average::convert_colorspace(const std::shared_ptr if (bpp_y != bpp_cb || bpp_y != bpp_cr) { // TODO: test with varying bit depths when we have a test image - return nullptr; + return Error::InternalError; } @@ -347,22 +353,22 @@ Op_YCbCr444_to_YCbCr422_average::convert_colorspace(const std::shared_ptr uint32_t cwidth = (width + 1) / 2; uint32_t cheight = height; - if (!outimg->add_plane(heif_channel_Y, width, height, bpp_y) || - !outimg->add_plane(heif_channel_Cb, cwidth, cheight, bpp_cb) || - !outimg->add_plane(heif_channel_Cr, cwidth, cheight, bpp_cr)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Y, width, height, bpp_y, limits) || + outimg->add_plane(heif_channel_Cb, cwidth, cheight, bpp_cb, limits) || + outimg->add_plane(heif_channel_Cr, cwidth, cheight, bpp_cr, limits)) { + return err; } if (has_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, bpp_a)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, bpp_a, limits)) { + return err; } } - const Pixel* in_y, * in_cb, * in_cr, * in_a; + const Pixel* in_y, * in_cb, * in_cr; uint32_t in_y_stride = 0, in_cb_stride = 0, in_cr_stride = 0, in_a_stride = 0; - Pixel* out_y, * out_cb, * out_cr, * out_a; + Pixel* out_y, * out_cb, * out_cr; uint32_t out_y_stride = 0, out_cb_stride = 0, out_cr_stride = 0, out_a_stride = 0; in_y = (const Pixel*) input->get_plane(heif_channel_Y, &in_y_stride); @@ -372,9 +378,12 @@ Op_YCbCr444_to_YCbCr422_average::convert_colorspace(const std::shared_ptr out_cb = (Pixel*) outimg->get_plane(heif_channel_Cb, &out_cb_stride); out_cr = (Pixel*) outimg->get_plane(heif_channel_Cr, &out_cr_stride); + const uint8_t* in_a; + uint8_t* out_a; + if (has_alpha) { - in_a = (const Pixel*) input->get_plane(heif_channel_Alpha, &in_a_stride); - out_a = (Pixel*) outimg->get_plane(heif_channel_Alpha, &out_a_stride); + in_a = input->get_plane(heif_channel_Alpha, &in_a_stride); + out_a = outimg->get_plane(heif_channel_Alpha, &out_a_stride); } else { in_a = nullptr; @@ -386,11 +395,9 @@ Op_YCbCr444_to_YCbCr422_average::convert_colorspace(const std::shared_ptr in_y_stride /= 2; in_cb_stride /= 2; in_cr_stride /= 2; - in_a_stride /= 2; out_y_stride /= 2; out_cb_stride /= 2; out_cr_stride /= 2; - out_a_stride /= 2; } // --- fill right border if the image size is odd @@ -426,7 +433,8 @@ Op_YCbCr444_to_YCbCr422_average::convert_colorspace(const std::shared_ptr memcpy(&out_y[y * out_y_stride], &in_y[y * in_y_stride], copyWidth); if (has_alpha) { - memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], copyWidth); + uint32_t alphaCopyWidth = (bpp_a>8 ? width * 2 : width); + memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], alphaCopyWidth); } } @@ -487,11 +495,12 @@ Op_YCbCr420_bilinear_to_YCbCr444::state_after_conversion(const ColorState template -std::shared_ptr +Result> Op_YCbCr420_bilinear_to_YCbCr444::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { bool hdr = !std::is_same::value; @@ -510,14 +519,14 @@ Op_YCbCr420_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt if (bpp_y > 8 || bpp_cb > 8 || bpp_cr > 8) { - return nullptr; + return Error::InternalError; } } else { if (bpp_y <= 8 || bpp_cb <= 8 || bpp_cr <= 8) { - return nullptr; + return Error::InternalError; } } @@ -525,7 +534,7 @@ Op_YCbCr420_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt if (bpp_y != bpp_cb || bpp_y != bpp_cr) { // TODO: test with varying bit depths when we have a test image - return nullptr; + return Error::InternalError; } @@ -538,22 +547,22 @@ Op_YCbCr420_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt outimg->create(width, height, heif_colorspace_YCbCr, heif_chroma_444); - if (!outimg->add_plane(heif_channel_Y, width, height, bpp_y) || - !outimg->add_plane(heif_channel_Cb, width, height, bpp_cb) || - !outimg->add_plane(heif_channel_Cr, width, height, bpp_cr)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Y, width, height, bpp_y, limits) || + outimg->add_plane(heif_channel_Cb, width, height, bpp_cb, limits) || + outimg->add_plane(heif_channel_Cr, width, height, bpp_cr, limits)) { + return err; } if (has_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, bpp_a)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, bpp_a, limits)) { + return err; } } - const Pixel* in_y, * in_cb, * in_cr, * in_a; + const Pixel* in_y, * in_cb, * in_cr; uint32_t in_y_stride = 0, in_cb_stride = 0, in_cr_stride = 0, in_a_stride = 0; - Pixel* out_y, * out_cb, * out_cr, * out_a; + Pixel* out_y, * out_cb, * out_cr; uint32_t out_y_stride = 0, out_cb_stride = 0, out_cr_stride = 0, out_a_stride = 0; in_y = (const Pixel*) input->get_plane(heif_channel_Y, &in_y_stride); @@ -563,9 +572,12 @@ Op_YCbCr420_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt out_cb = (Pixel*) outimg->get_plane(heif_channel_Cb, &out_cb_stride); out_cr = (Pixel*) outimg->get_plane(heif_channel_Cr, &out_cr_stride); + const uint8_t* in_a; + uint8_t* out_a; + if (has_alpha) { - in_a = (const Pixel*) input->get_plane(heif_channel_Alpha, &in_a_stride); - out_a = (Pixel*) outimg->get_plane(heif_channel_Alpha, &out_a_stride); + in_a = input->get_plane(heif_channel_Alpha, &in_a_stride); + out_a = outimg->get_plane(heif_channel_Alpha, &out_a_stride); } else { in_a = nullptr; @@ -577,11 +589,9 @@ Op_YCbCr420_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt in_y_stride /= 2; in_cb_stride /= 2; in_cr_stride /= 2; - in_a_stride /= 2; out_y_stride /= 2; out_cb_stride /= 2; out_cr_stride /= 2; - out_a_stride /= 2; } /* @@ -702,7 +712,8 @@ Op_YCbCr420_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt memcpy(&out_y[y * out_y_stride], &in_y[y * in_y_stride], copyWidth); if (has_alpha) { - memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], copyWidth); + uint32_t alphaCopyWidth = (bpp_a > 8 ? width * 2 : width); + memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], alphaCopyWidth); } } @@ -764,11 +775,12 @@ Op_YCbCr422_bilinear_to_YCbCr444::state_after_conversion(const ColorState template -std::shared_ptr +Result> Op_YCbCr422_bilinear_to_YCbCr444::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { bool hdr = !std::is_same::value; @@ -787,14 +799,14 @@ Op_YCbCr422_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt if (bpp_y > 8 || bpp_cb > 8 || bpp_cr > 8) { - return nullptr; + return Error::InternalError; } } else { if (bpp_y <= 8 || bpp_cb <= 8 || bpp_cr <= 8) { - return nullptr; + return Error::InternalError; } } @@ -802,7 +814,7 @@ Op_YCbCr422_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt if (bpp_y != bpp_cb || bpp_y != bpp_cr) { // TODO: test with varying bit depths when we have a test image - return nullptr; + return Error::InternalError; } @@ -815,22 +827,22 @@ Op_YCbCr422_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt outimg->create(width, height, heif_colorspace_YCbCr, heif_chroma_444); - if (!outimg->add_plane(heif_channel_Y, width, height, bpp_y) || - !outimg->add_plane(heif_channel_Cb, width, height, bpp_cb) || - !outimg->add_plane(heif_channel_Cr, width, height, bpp_cr)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Y, width, height, bpp_y, limits) || + outimg->add_plane(heif_channel_Cb, width, height, bpp_cb, limits) || + outimg->add_plane(heif_channel_Cr, width, height, bpp_cr, limits)) { + return err; } if (has_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, bpp_a)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, bpp_a, limits)) { + return err; } } - const Pixel* in_y, * in_cb, * in_cr, * in_a; + const Pixel* in_y, * in_cb, * in_cr; uint32_t in_y_stride = 0, in_cb_stride = 0, in_cr_stride = 0, in_a_stride = 0; - Pixel* out_y, * out_cb, * out_cr, * out_a; + Pixel* out_y, * out_cb, * out_cr; uint32_t out_y_stride = 0, out_cb_stride = 0, out_cr_stride = 0, out_a_stride = 0; in_y = (const Pixel*) input->get_plane(heif_channel_Y, &in_y_stride); @@ -840,9 +852,12 @@ Op_YCbCr422_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt out_cb = (Pixel*) outimg->get_plane(heif_channel_Cb, &out_cb_stride); out_cr = (Pixel*) outimg->get_plane(heif_channel_Cr, &out_cr_stride); + const uint8_t* in_a; + uint8_t* out_a; + if (has_alpha) { - in_a = (const Pixel*) input->get_plane(heif_channel_Alpha, &in_a_stride); - out_a = (Pixel*) outimg->get_plane(heif_channel_Alpha, &out_a_stride); + in_a = input->get_plane(heif_channel_Alpha, &in_a_stride); + out_a = outimg->get_plane(heif_channel_Alpha, &out_a_stride); } else { in_a = nullptr; @@ -854,11 +869,9 @@ Op_YCbCr422_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt in_y_stride /= 2; in_cb_stride /= 2; in_cr_stride /= 2; - in_a_stride /= 2; out_y_stride /= 2; out_cb_stride /= 2; out_cr_stride /= 2; - out_a_stride /= 2; } /* @@ -925,7 +938,8 @@ Op_YCbCr422_bilinear_to_YCbCr444::convert_colorspace(const std::shared_pt memcpy(&out_y[y * out_y_stride], &in_y[y * in_y_stride], copyWidth); if (has_alpha) { - memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], copyWidth); + uint32_t alphaCopyWidth = (bpp_a > 8 ? width * 2 : width); + memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], alphaCopyWidth); } } diff --git a/libheif/color-conversion/chroma_sampling.h b/libheif/color-conversion/chroma_sampling.h index 446bd952f8..548e067a91 100644 --- a/libheif/color-conversion/chroma_sampling.h +++ b/libheif/color-conversion/chroma_sampling.h @@ -37,11 +37,12 @@ class Op_YCbCr444_to_YCbCr420_average : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -54,11 +55,12 @@ class Op_YCbCr444_to_YCbCr422_average : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -73,11 +75,12 @@ class Op_YCbCr420_bilinear_to_YCbCr444 : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; template @@ -89,11 +92,12 @@ class Op_YCbCr422_bilinear_to_YCbCr444 : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; #endif //LIBHEIF_CHROMA_SAMPLING_H diff --git a/libheif/color-conversion/colorconversion.cc b/libheif/color-conversion/colorconversion.cc index a550bd5d79..8be750cd73 100644 --- a/libheif/color-conversion/colorconversion.cc +++ b/libheif/color-conversion/colorconversion.cc @@ -427,7 +427,8 @@ std::string ColorConversionPipeline::debug_dump_pipeline() const } -std::shared_ptr ColorConversionPipeline::convert_image(const std::shared_ptr& input) +Result> ColorConversionPipeline::convert_image(const std::shared_ptr& input, + const heif_security_limits* limits) { std::shared_ptr in = input; std::shared_ptr out = in; @@ -439,9 +440,12 @@ std::shared_ptr ColorConversionPipeline::convert_image(const std print_spec(std::cerr, in); #endif - out = step.operation->convert_colorspace(in, step.input_state, step.output_state, m_options); - if (!out) { - return nullptr; // TODO: we should return a proper error + auto outResult = step.operation->convert_colorspace(in, step.input_state, step.output_state, m_options, limits); + if (outResult.error) { + return outResult.error; + } + else { + out = *outResult; } // --- pass the color profiles to the new image @@ -479,12 +483,13 @@ std::shared_ptr ColorConversionPipeline::convert_image(const std } -std::shared_ptr convert_colorspace(const std::shared_ptr& input, - heif_colorspace target_colorspace, - heif_chroma target_chroma, - const std::shared_ptr& target_profile, - int output_bpp, - const heif_color_conversion_options& options) +Result> convert_colorspace(const std::shared_ptr& input, + heif_colorspace target_colorspace, + heif_chroma target_chroma, + const std::shared_ptr& target_profile, + int output_bpp, + const heif_color_conversion_options& options, + const heif_security_limits* limits) { // --- check that input image is valid @@ -496,7 +501,7 @@ std::shared_ptr convert_colorspace(const std::shared_ptrhas_channel(heif_channel_Alpha)) { if (input->get_width(heif_channel_Alpha) != width || input->get_height(heif_channel_Alpha) != height) { - return nullptr; + return Error::InternalError; } } @@ -506,7 +511,7 @@ std::shared_ptr convert_colorspace(const std::shared_ptr convert_colorspace(const std::shared_ptr convert_colorspace(const std::shared_ptr& input, - heif_colorspace colorspace, - heif_chroma chroma, - const std::shared_ptr& target_profile, - int output_bpp, - const heif_color_conversion_options& options) +Result> convert_colorspace(const std::shared_ptr& input, + heif_colorspace colorspace, + heif_chroma chroma, + const std::shared_ptr& target_profile, + int output_bpp, + const heif_color_conversion_options& options, + const heif_security_limits* limits) { std::shared_ptr non_const_input = std::const_pointer_cast(input); - return convert_colorspace(non_const_input, colorspace, chroma, target_profile, output_bpp, options); + auto result = convert_colorspace(non_const_input, colorspace, chroma, target_profile, output_bpp, options, limits); + if (result.error) { + return result.error; + } + else { + // TODO: can we simplify this? It's a bit awkward to do these assignments just to get a "const HeifPixelImage". + std::shared_ptr constImage = *result; + return constImage; + } } diff --git a/libheif/color-conversion/colorconversion.h b/libheif/color-conversion/colorconversion.h index d35e3ff09c..562f8b186d 100644 --- a/libheif/color-conversion/colorconversion.h +++ b/libheif/color-conversion/colorconversion.h @@ -85,11 +85,12 @@ class ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const = 0; - virtual std::shared_ptr + virtual Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const = 0; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const = 0; }; @@ -105,7 +106,8 @@ class ColorConversionPipeline const ColorState& target_state, const heif_color_conversion_options& options); - std::shared_ptr convert_image(const std::shared_ptr& input); + Result> convert_image(const std::shared_ptr& input, + const heif_security_limits* limits); std::string debug_dump_pipeline() const; @@ -126,18 +128,20 @@ class ColorConversionPipeline // If no conversion is required, the input is simply passed through without copy. // The input image is never modified by this function, but the input is still non-const because we may pass it through. -std::shared_ptr convert_colorspace(const std::shared_ptr& input, - heif_colorspace colorspace, - heif_chroma chroma, - const std::shared_ptr& target_profile, - int output_bpp, - const heif_color_conversion_options& options); - -std::shared_ptr convert_colorspace(const std::shared_ptr& input, - heif_colorspace colorspace, - heif_chroma chroma, - const std::shared_ptr& target_profile, - int output_bpp, - const heif_color_conversion_options& options); +Result> convert_colorspace(const std::shared_ptr& input, + heif_colorspace colorspace, + heif_chroma chroma, + const std::shared_ptr& target_profile, + int output_bpp, + const heif_color_conversion_options& options, + const heif_security_limits* limits); + +Result> convert_colorspace(const std::shared_ptr& input, + heif_colorspace colorspace, + heif_chroma chroma, + const std::shared_ptr& target_profile, + int output_bpp, + const heif_color_conversion_options& options, + const heif_security_limits* limits); #endif diff --git a/libheif/color-conversion/hdr_sdr.cc b/libheif/color-conversion/hdr_sdr.cc index 6bf07f4d91..30d64fafa1 100644 --- a/libheif/color-conversion/hdr_sdr.cc +++ b/libheif/color-conversion/hdr_sdr.cc @@ -50,11 +50,12 @@ Op_to_hdr_planes::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_to_hdr_planes::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { auto outimg = std::make_shared(); @@ -73,8 +74,8 @@ Op_to_hdr_planes::convert_colorspace(const std::shared_ptr if (input->has_channel(channel)) { uint32_t width = input->get_width(channel); uint32_t height = input->get_height(channel); - if (!outimg->add_plane(channel, width, height, target_state.bits_per_pixel)) { - return nullptr; + if (auto err = outimg->add_plane(channel, width, height, target_state.bits_per_pixel, limits)) { + return err; } int input_bits = input->get_bits_per_pixel(channel); @@ -137,11 +138,12 @@ Op_to_sdr_planes::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_to_sdr_planes::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { auto outimg = std::make_shared(); @@ -164,8 +166,8 @@ Op_to_sdr_planes::convert_colorspace(const std::shared_ptr if (input_bits > 8) { uint32_t width = input->get_width(channel); uint32_t height = input->get_height(channel); - if (!outimg->add_plane(channel, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(channel, width, height, 8, limits)) { + return err; } int shift = input_bits - 8; @@ -187,8 +189,8 @@ Op_to_sdr_planes::convert_colorspace(const std::shared_ptr } else if (input_bits < 8) { uint32_t width = input->get_width(channel); uint32_t height = input->get_height(channel); - if (!outimg->add_plane(channel, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(channel, width, height, 8, limits)) { + return err; } // We also want to support converting inputs with < 4 bits per pixel covering the whole output range. @@ -227,7 +229,7 @@ Op_to_sdr_planes::convert_colorspace(const std::shared_ptr p_out[y * stride_out + x] = (uint8_t) ((in * mulFactor) >> 8); } } else { - outimg->copy_new_plane_from(input, channel, channel); + outimg->copy_new_plane_from(input, channel, channel, limits); } } } diff --git a/libheif/color-conversion/hdr_sdr.h b/libheif/color-conversion/hdr_sdr.h index 4fd79d4fe1..a497135b00 100644 --- a/libheif/color-conversion/hdr_sdr.h +++ b/libheif/color-conversion/hdr_sdr.h @@ -34,11 +34,12 @@ class Op_to_hdr_planes : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -50,11 +51,12 @@ class Op_to_sdr_planes : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; #endif //LIBHEIF_COLORCONVERSION_HDR_SDR_H diff --git a/libheif/color-conversion/monochrome.cc b/libheif/color-conversion/monochrome.cc index 1c0b9c5c9b..73bed99814 100644 --- a/libheif/color-conversion/monochrome.cc +++ b/libheif/color-conversion/monochrome.cc @@ -49,11 +49,12 @@ Op_mono_to_YCbCr420::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_mono_to_YCbCr420::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { auto outimg = std::make_shared(); @@ -67,18 +68,18 @@ Op_mono_to_YCbCr420::convert_colorspace(const std::shared_ptradd_plane(heif_channel_Y, width, height, input_bpp) || - !outimg->add_plane(heif_channel_Cb, chroma_width, chroma_height, input_bpp) || - !outimg->add_plane(heif_channel_Cr, chroma_width, chroma_height, input_bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Y, width, height, input_bpp, limits) || + outimg->add_plane(heif_channel_Cb, chroma_width, chroma_height, input_bpp, limits) || + outimg->add_plane(heif_channel_Cr, chroma_width, chroma_height, input_bpp, limits)) { + return err; } int alpha_bpp = 0; bool has_alpha = input->has_channel(heif_channel_Alpha); if (has_alpha) { alpha_bpp = input->get_bits_per_pixel(heif_channel_Alpha); - if (!outimg->add_plane(heif_channel_Alpha, width, height, alpha_bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, alpha_bpp, limits)) { + return err; } } @@ -200,17 +201,18 @@ Op_mono_to_RGB24_32::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_mono_to_RGB24_32::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { uint32_t width = input->get_width(); uint32_t height = input->get_height(); if (input->get_bits_per_pixel(heif_channel_Y) != 8) { - return nullptr; + return Error::InternalError; } auto outimg = std::make_shared(); @@ -224,8 +226,8 @@ Op_mono_to_RGB24_32::convert_colorspace(const std::shared_ptrcreate(width, height, heif_colorspace_RGB, heif_chroma_interleaved_24bit); } - if (!outimg->add_plane(heif_channel_interleaved, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_interleaved, width, height, 8, limits)) { + return err; } const uint8_t* in_y, * in_a = nullptr; diff --git a/libheif/color-conversion/monochrome.h b/libheif/color-conversion/monochrome.h index 9b93f7eda2..d603b77e90 100644 --- a/libheif/color-conversion/monochrome.h +++ b/libheif/color-conversion/monochrome.h @@ -34,11 +34,12 @@ class Op_mono_to_YCbCr420 : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -50,11 +51,12 @@ class Op_mono_to_RGB24_32 : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; #endif //LIBHEIF_COLORCONVERSION_MONOCHROME_H diff --git a/libheif/color-conversion/rgb2rgb.cc b/libheif/color-conversion/rgb2rgb.cc index 89b5e12499..eb4d7ba659 100644 --- a/libheif/color-conversion/rgb2rgb.cc +++ b/libheif/color-conversion/rgb2rgb.cc @@ -63,11 +63,12 @@ Op_RGB_to_RGB24_32::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_RGB_to_RGB24_32::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { bool has_alpha = input->has_channel(heif_channel_Alpha); bool want_alpha = target_state.has_alpha; @@ -75,11 +76,11 @@ Op_RGB_to_RGB24_32::convert_colorspace(const std::shared_ptrget_bits_per_pixel(heif_channel_R) != 8 || input->get_bits_per_pixel(heif_channel_G) != 8 || input->get_bits_per_pixel(heif_channel_B) != 8) { - return nullptr; + return Error::InternalError; } if (has_alpha && input->get_bits_per_pixel(heif_channel_Alpha) != 8) { - return nullptr; + return Error::InternalError; } auto outimg = std::make_shared(); @@ -90,8 +91,8 @@ Op_RGB_to_RGB24_32::convert_colorspace(const std::shared_ptrcreate(width, height, heif_colorspace_RGB, want_alpha ? heif_chroma_interleaved_32bit : heif_chroma_interleaved_24bit); - if (!outimg->add_plane(heif_channel_interleaved, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_interleaved, width, height, 8, limits)) { + return err; } const uint8_t* in_r, * in_g, * in_b, * in_a = nullptr; @@ -186,16 +187,17 @@ Op_RGB_HDR_to_RRGGBBaa_BE::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_RGB_HDR_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { if (input->get_bits_per_pixel(heif_channel_R) <= 8 || input->get_bits_per_pixel(heif_channel_G) <= 8 || input->get_bits_per_pixel(heif_channel_B) <= 8) { - return nullptr; + return Error::InternalError; } bool input_has_alpha = input->has_channel(heif_channel_Alpha); @@ -203,16 +205,16 @@ Op_RGB_HDR_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptrget_bits_per_pixel(heif_channel_Alpha) <= 8) { - return nullptr; + return Error::InternalError; } if (input->get_width(heif_channel_Alpha) != input->get_width(heif_channel_G) || input->get_height(heif_channel_Alpha) != input->get_height(heif_channel_G)) { - return nullptr; + return Error::InternalError; } } int bpp = input->get_bits_per_pixel(heif_channel_R); - if (bpp <= 0) return nullptr; + if (bpp <= 0) return Error::InternalError; auto outimg = std::make_shared(); @@ -221,8 +223,8 @@ Op_RGB_HDR_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptrcreate(width, height, heif_colorspace_RGB, output_has_alpha ? heif_chroma_interleaved_RRGGBBAA_BE : heif_chroma_interleaved_RRGGBB_BE); - if (!outimg->add_plane(heif_channel_interleaved, width, height, bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_interleaved, width, height, bpp, limits)) { + return err; } const uint16_t* in_r, * in_g, * in_b, * in_a = nullptr; @@ -242,7 +244,7 @@ Op_RGB_HDR_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptr +Result> Op_RGB_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { if (input->get_bits_per_pixel(heif_channel_R) != 8 || input->get_bits_per_pixel(heif_channel_G) != 8 || input->get_bits_per_pixel(heif_channel_B) != 8) { - return nullptr; + return Error::InternalError; } //int bpp = input->get_bits_per_pixel(heif_channel_R); @@ -338,7 +341,7 @@ Op_RGB_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptrget_bits_per_pixel(heif_channel_Alpha) != 8) { - return nullptr; + return Error::InternalError; } auto outimg = std::make_shared(); @@ -349,8 +352,8 @@ Op_RGB_to_RRGGBBaa_BE::convert_colorspace(const std::shared_ptrcreate(width, height, heif_colorspace_RGB, output_has_alpha ? heif_chroma_interleaved_RRGGBBAA_BE : heif_chroma_interleaved_RRGGBB_BE); - if (!outimg->add_plane(heif_channel_interleaved, width, height, input->get_bits_per_pixel(heif_channel_R))) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_interleaved, width, height, input->get_bits_per_pixel(heif_channel_R), limits)) { + return err; } const uint8_t* in_r, * in_g, * in_b, * in_a = nullptr; @@ -437,11 +440,12 @@ Op_RRGGBBaa_BE_to_RGB_HDR::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_RRGGBBaa_BE_to_RGB_HDR::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { bool has_alpha = (input->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_LE || input->get_chroma_format() == heif_chroma_interleaved_RRGGBBAA_BE); @@ -455,15 +459,15 @@ Op_RRGGBBaa_BE_to_RGB_HDR::convert_colorspace(const std::shared_ptrcreate(width, height, heif_colorspace_RGB, heif_chroma_444); - if (!outimg->add_plane(heif_channel_R, width, height, bpp) || - !outimg->add_plane(heif_channel_G, width, height, bpp) || - !outimg->add_plane(heif_channel_B, width, height, bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_R, width, height, bpp, limits) || + outimg->add_plane(heif_channel_G, width, height, bpp, limits) || + outimg->add_plane(heif_channel_B, width, height, bpp, limits)) { + return err; } if (want_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, bpp, limits)) { + return err; } } @@ -550,11 +554,12 @@ Op_RGB24_32_to_RGB::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_RGB24_32_to_RGB::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, - const ColorState& target_state, - const heif_color_conversion_options& options) const + const ColorState& target_state, + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { bool has_alpha = input->get_chroma_format() == heif_chroma_interleaved_RGBA; bool want_alpha = target_state.has_alpha; @@ -566,15 +571,15 @@ Op_RGB24_32_to_RGB::convert_colorspace(const std::shared_ptrcreate(width, height, heif_colorspace_RGB, heif_chroma_444); - if (!outimg->add_plane(heif_channel_R, width, height, 8) || - !outimg->add_plane(heif_channel_G, width, height, 8) || - !outimg->add_plane(heif_channel_B, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_R, width, height, 8, limits) || + outimg->add_plane(heif_channel_G, width, height, 8, limits) || + outimg->add_plane(heif_channel_B, width, height, 8, limits)) { + return err; } if (want_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, 8, limits)) { + return err; } } @@ -676,11 +681,12 @@ Op_RRGGBBaa_swap_endianness::state_after_conversion(const ColorState& input_stat } -std::shared_ptr +Result> Op_RRGGBBaa_swap_endianness::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { auto outimg = std::make_shared(); @@ -701,12 +707,12 @@ Op_RRGGBBaa_swap_endianness::convert_colorspace(const std::shared_ptrcreate(width, height, heif_colorspace_RGB, heif_chroma_interleaved_RRGGBBAA_LE); break; default: - return nullptr; + return Error::InternalError; } - if (!outimg->add_plane(heif_channel_interleaved, width, height, - input->get_bits_per_pixel(heif_channel_interleaved))) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_interleaved, width, height, + input->get_bits_per_pixel(heif_channel_interleaved), limits)) { + return err; } const uint8_t* in_p = nullptr; diff --git a/libheif/color-conversion/rgb2rgb.h b/libheif/color-conversion/rgb2rgb.h index dee344bade..014ab1c5ef 100644 --- a/libheif/color-conversion/rgb2rgb.h +++ b/libheif/color-conversion/rgb2rgb.h @@ -34,11 +34,12 @@ class Op_RGB_to_RGB24_32 : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -50,11 +51,12 @@ class Op_RGB_HDR_to_RRGGBBaa_BE : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -66,11 +68,12 @@ class Op_RGB_to_RRGGBBaa_BE : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -82,11 +85,12 @@ class Op_RRGGBBaa_BE_to_RGB_HDR : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -98,11 +102,12 @@ class Op_RGB24_32_to_RGB : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -114,11 +119,12 @@ class Op_RRGGBBaa_swap_endianness : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; diff --git a/libheif/color-conversion/rgb2yuv.cc b/libheif/color-conversion/rgb2yuv.cc index b7436c10ad..591003fdf8 100644 --- a/libheif/color-conversion/rgb2yuv.cc +++ b/libheif/color-conversion/rgb2yuv.cc @@ -89,13 +89,13 @@ Op_RGB_to_YCbCr::state_after_conversion(const ColorState& input_state, return states; } - template -std::shared_ptr +Result> Op_RGB_to_YCbCr::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { bool hdr = !std::is_same::value; @@ -108,13 +108,13 @@ Op_RGB_to_YCbCr::convert_colorspace(const std::shared_ptrget_bits_per_pixel(heif_channel_R); if (bpp < 8 || (bpp > 8) != hdr) { - return nullptr; + return Error::InternalError; } bool has_alpha = input->has_channel(heif_channel_Alpha); if (has_alpha && input->get_bits_per_pixel(heif_channel_Alpha) != bpp) { - return nullptr; + return Error::InternalError; } auto outimg = std::make_shared(); @@ -124,22 +124,22 @@ Op_RGB_to_YCbCr::convert_colorspace(const std::shared_ptradd_plane(heif_channel_Y, width, height, bpp) || - !outimg->add_plane(heif_channel_Cb, cwidth, cheight, bpp) || - !outimg->add_plane(heif_channel_Cr, cwidth, cheight, bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Y, width, height, bpp, limits) || + outimg->add_plane(heif_channel_Cb, cwidth, cheight, bpp, limits) || + outimg->add_plane(heif_channel_Cr, cwidth, cheight, bpp, limits)) { + return err; } if (has_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, bpp, limits)) { + return err; } } - const Pixel* in_r, * in_g, * in_b, * in_a; + const Pixel* in_r, * in_g, * in_b; uint32_t in_r_stride = 0, in_g_stride = 0, in_b_stride = 0, in_a_stride = 0; - Pixel* out_y, * out_cb, * out_cr, * out_a; + Pixel* out_y, * out_cb, * out_cr; uint32_t out_y_stride = 0, out_cb_stride = 0, out_cr_stride = 0, out_a_stride = 0; in_r = (const Pixel*) input->get_plane(heif_channel_R, &in_r_stride); @@ -149,9 +149,12 @@ Op_RGB_to_YCbCr::convert_colorspace(const std::shared_ptrget_plane(heif_channel_Cb, &out_cb_stride); out_cr = (Pixel*) outimg->get_plane(heif_channel_Cr, &out_cr_stride); + const uint8_t* in_a; + uint8_t* out_a; + if (has_alpha) { - in_a = (const Pixel*) input->get_plane(heif_channel_Alpha, &in_a_stride); - out_a = (Pixel*) outimg->get_plane(heif_channel_Alpha, &out_a_stride); + in_a = input->get_plane(heif_channel_Alpha, &in_a_stride); + out_a = outimg->get_plane(heif_channel_Alpha, &out_a_stride); } else { in_a = nullptr; @@ -162,11 +165,9 @@ Op_RGB_to_YCbCr::convert_colorspace(const std::shared_ptr::convert_colorspace(const std::shared_ptrget_bits_per_pixel(heif_channel_Alpha); + int alphaCopyWidth = (bpp_a > 8 ? width * 2 : width); + for (y = 0; y < height; y++) { - memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], copyWidth); + memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], alphaCopyWidth); } } @@ -336,11 +339,12 @@ Op_RRGGBBxx_HDR_to_YCbCr420::state_after_conversion(const ColorState& input_stat } -std::shared_ptr +Result> Op_RRGGBBxx_HDR_to_YCbCr420::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { uint32_t width = input->get_width(); uint32_t height = input->get_height(); @@ -359,15 +363,15 @@ Op_RRGGBBxx_HDR_to_YCbCr420::convert_colorspace(const std::shared_ptradd_plane(heif_channel_Y, width, height, bpp) || - !outimg->add_plane(heif_channel_Cb, cwidth, cheight, bpp) || - !outimg->add_plane(heif_channel_Cr, cwidth, cheight, bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Y, width, height, bpp, limits) || + outimg->add_plane(heif_channel_Cb, cwidth, cheight, bpp, limits) || + outimg->add_plane(heif_channel_Cr, cwidth, cheight, bpp, limits)) { + return err; } if (has_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, bpp, limits)) { + return err; } } @@ -542,11 +546,12 @@ inline void set_chroma_pixels(uint8_t* out_cb, uint8_t* out_cr, } -std::shared_ptr +Result> Op_RGB24_32_to_YCbCr::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { uint32_t width = input->get_width(); uint32_t height = input->get_height(); @@ -565,15 +570,15 @@ Op_RGB24_32_to_YCbCr::convert_colorspace(const std::shared_ptrget_chroma_format() == heif_chroma_interleaved_32bit); const bool want_alpha = target_state.has_alpha; - if (!outimg->add_plane(heif_channel_Y, width, height, 8) || - !outimg->add_plane(heif_channel_Cb, chroma_width, chroma_height, 8) || - !outimg->add_plane(heif_channel_Cr, chroma_width, chroma_height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Y, width, height, 8, limits) || + outimg->add_plane(heif_channel_Cb, chroma_width, chroma_height, 8, limits) || + outimg->add_plane(heif_channel_Cr, chroma_width, chroma_height, 8, limits)) { + return err; } if (want_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, 8, limits)) { + return err; } } @@ -814,11 +819,12 @@ Op_RGB24_32_to_YCbCr444_GBR::state_after_conversion(const ColorState& input_stat } -std::shared_ptr +Result> Op_RGB24_32_to_YCbCr444_GBR::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { uint32_t width = input->get_width(); uint32_t height = input->get_height(); @@ -830,15 +836,15 @@ Op_RGB24_32_to_YCbCr444_GBR::convert_colorspace(const std::shared_ptrget_chroma_format() == heif_chroma_interleaved_32bit); const bool want_alpha = target_state.has_alpha; - if (!outimg->add_plane(heif_channel_Y, width, height, 8) || - !outimg->add_plane(heif_channel_Cb, width, height, 8) || - !outimg->add_plane(heif_channel_Cr, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Y, width, height, 8, limits) || + outimg->add_plane(heif_channel_Cb, width, height, 8, limits) || + outimg->add_plane(heif_channel_Cr, width, height, 8, limits)) { + return err; } if (want_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, 8, limits)) { + return err; } } diff --git a/libheif/color-conversion/rgb2yuv.h b/libheif/color-conversion/rgb2yuv.h index 74b929b0a0..6b4da84798 100644 --- a/libheif/color-conversion/rgb2yuv.h +++ b/libheif/color-conversion/rgb2yuv.h @@ -35,11 +35,12 @@ class Op_RGB_to_YCbCr : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -52,11 +53,12 @@ class Op_RRGGBBxx_HDR_to_YCbCr420 : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -68,11 +70,12 @@ class Op_RGB24_32_to_YCbCr : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -84,11 +87,12 @@ class Op_RGB24_32_to_YCbCr444_GBR : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; #endif //LIBHEIF_COLORCONVERSION_RGB2YUV_H diff --git a/libheif/color-conversion/rgb2yuv_sharp.cc b/libheif/color-conversion/rgb2yuv_sharp.cc index da63393d84..13ea950cb7 100644 --- a/libheif/color-conversion/rgb2yuv_sharp.cc +++ b/libheif/color-conversion/rgb2yuv_sharp.cc @@ -121,12 +121,13 @@ Op_Any_RGB_to_YCbCr_420_Sharp::state_after_conversion( #endif } -std::shared_ptr +Result> Op_Any_RGB_to_YCbCr_420_Sharp::convert_colorspace( const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { #ifdef HAVE_LIBSHARPYUV uint32_t width = input->get_width(); @@ -154,15 +155,15 @@ Op_Any_RGB_to_YCbCr_420_Sharp::convert_colorspace( bool want_alpha = target_state.has_alpha; int output_bits = target_state.bits_per_pixel; - if (!outimg->add_plane(heif_channel_Y, width, height, output_bits) || - !outimg->add_plane(heif_channel_Cb, chroma_width, chroma_height, output_bits) || - !outimg->add_plane(heif_channel_Cr, chroma_width, chroma_height, output_bits)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Y, width, height, output_bits, limits) || + outimg->add_plane(heif_channel_Cb, chroma_width, chroma_height, output_bits, limits) || + outimg->add_plane(heif_channel_Cr, chroma_width, chroma_height, output_bits, limits)) { + return err; } if (want_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, output_bits)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, output_bits, limits)) { + return err; } } @@ -186,14 +187,14 @@ Op_Any_RGB_to_YCbCr_420_Sharp::convert_colorspace( in_b = input->get_plane(heif_channel_B, &in_b_stride); // The stride must be the same for all channels. if (in_r_stride != in_g_stride || in_r_stride != in_b_stride) { - return nullptr; + return Error::InternalError; } in_stride = in_r_stride; // Bpp must also be the same. input_bits = input->get_bits_per_pixel(heif_channel_R); if (input_bits != input->get_bits_per_pixel(heif_channel_G) || input_bits != input->get_bits_per_pixel(heif_channel_B)) { - return nullptr; + return Error::InternalError; } if (has_alpha) { in_a = input->get_plane(heif_channel_Alpha, &in_a_stride); @@ -237,7 +238,9 @@ Op_Any_RGB_to_YCbCr_420_Sharp::convert_colorspace( out_cr, out_cr_stride, output_bits, input->get_width(), input->get_height(), &yuv_matrix); if (!sharpyuv_ok) { - return nullptr; + return Error{heif_error_Unsupported_feature, + heif_suberror_Unsupported_color_conversion, + "SharpYuv color convertion failed"}; } if (want_alpha) { @@ -271,6 +274,6 @@ Op_Any_RGB_to_YCbCr_420_Sharp::convert_colorspace( return outimg; #else - return nullptr; + return Error::InternalError; #endif } diff --git a/libheif/color-conversion/rgb2yuv_sharp.h b/libheif/color-conversion/rgb2yuv_sharp.h index 057529cdbc..449637bac0 100644 --- a/libheif/color-conversion/rgb2yuv_sharp.h +++ b/libheif/color-conversion/rgb2yuv_sharp.h @@ -34,11 +34,12 @@ class Op_Any_RGB_to_YCbCr_420_Sharp : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; diff --git a/libheif/color-conversion/yuv2rgb.cc b/libheif/color-conversion/yuv2rgb.cc index bc8c1b7b8b..586ae80583 100644 --- a/libheif/color-conversion/yuv2rgb.cc +++ b/libheif/color-conversion/yuv2rgb.cc @@ -82,11 +82,12 @@ Op_YCbCr_to_RGB::state_after_conversion(const ColorState& input_state, template -std::shared_ptr +Result> Op_YCbCr_to_RGB::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { bool hdr = !std::is_same::value; @@ -107,14 +108,14 @@ Op_YCbCr_to_RGB::convert_colorspace(const std::shared_ptr::convert_colorspace(const std::shared_ptr::convert_colorspace(const std::shared_ptrcreate(width, height, heif_colorspace_RGB, heif_chroma_444); - if (!outimg->add_plane(heif_channel_R, width, height, bpp_y) || - !outimg->add_plane(heif_channel_G, width, height, bpp_y) || - !outimg->add_plane(heif_channel_B, width, height, bpp_y)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_R, width, height, bpp_y, limits) || + outimg->add_plane(heif_channel_G, width, height, bpp_y, limits) || + outimg->add_plane(heif_channel_B, width, height, bpp_y, limits)) { + return err; } if (has_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, bpp_a)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, bpp_a, limits)) { + return err; } } - const Pixel* in_y, * in_cb, * in_cr, * in_a; + const Pixel* in_y, * in_cb, * in_cr; uint32_t in_y_stride = 0, in_cb_stride = 0, in_cr_stride = 0, in_a_stride = 0; - Pixel* out_r, * out_g, * out_b, * out_a; + Pixel* out_r, * out_g, * out_b; uint32_t out_r_stride = 0, out_g_stride = 0, out_b_stride = 0, out_a_stride = 0; in_y = (const Pixel*) input->get_plane(heif_channel_Y, &in_y_stride); @@ -160,9 +161,14 @@ Op_YCbCr_to_RGB::convert_colorspace(const std::shared_ptrget_plane(heif_channel_G, &out_g_stride); out_b = (Pixel*) outimg->get_plane(heif_channel_B, &out_b_stride); + + // We only copy the alpha, do not access it as 16 bit + const uint8_t* in_a; + uint8_t* out_a; + if (has_alpha) { - in_a = (const Pixel*) input->get_plane(heif_channel_Alpha, &in_a_stride); - out_a = (Pixel*) outimg->get_plane(heif_channel_Alpha, &out_a_stride); + in_a = input->get_plane(heif_channel_Alpha, &in_a_stride); + out_a = outimg->get_plane(heif_channel_Alpha, &out_a_stride); } else { in_a = nullptr; @@ -182,11 +188,9 @@ Op_YCbCr_to_RGB::convert_colorspace(const std::shared_ptr::convert_colorspace(const std::shared_ptr8 ? width * 2 : width); + memcpy(&out_a[y * out_a_stride], &in_a[y * in_a_stride], alphaCopyWidth); } } @@ -308,16 +312,17 @@ Op_YCbCr420_to_RGB24::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_YCbCr420_to_RGB24::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { if (input->get_bits_per_pixel(heif_channel_Y) != 8 || input->get_bits_per_pixel(heif_channel_Cb) != 8 || input->get_bits_per_pixel(heif_channel_Cr) != 8) { - return nullptr; + return Error::InternalError; } auto outimg = std::make_shared(); @@ -327,8 +332,8 @@ Op_YCbCr420_to_RGB24::convert_colorspace(const std::shared_ptrcreate(width, height, heif_colorspace_RGB, heif_chroma_interleaved_24bit); - if (!outimg->add_plane(heif_channel_interleaved, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_interleaved, width, height, 8, limits)) { + return err; } auto colorProfile = input->get_color_profile_nclx(); @@ -418,16 +423,17 @@ Op_YCbCr420_to_RGB32::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_YCbCr420_to_RGB32::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { if (input->get_bits_per_pixel(heif_channel_Y) != 8 || input->get_bits_per_pixel(heif_channel_Cb) != 8 || input->get_bits_per_pixel(heif_channel_Cr) != 8) { - return nullptr; + return Error::InternalError; } auto outimg = std::make_shared(); @@ -437,8 +443,8 @@ Op_YCbCr420_to_RGB32::convert_colorspace(const std::shared_ptrcreate(width, height, heif_colorspace_RGB, heif_chroma_interleaved_32bit); - if (!outimg->add_plane(heif_channel_interleaved, width, height, 8)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_interleaved, width, height, 8, limits)) { + return err; } @@ -552,11 +558,12 @@ Op_YCbCr420_to_RRGGBBaa::state_after_conversion(const ColorState& input_state, } -std::shared_ptr +Result> Op_YCbCr420_to_RRGGBBaa::convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const + const heif_color_conversion_options& options, + const heif_security_limits* limits) const { uint32_t width = input->get_width(); uint32_t height = input->get_height(); @@ -572,13 +579,13 @@ Op_YCbCr420_to_RRGGBBaa::convert_colorspace(const std::shared_ptradd_plane(heif_channel_interleaved, width, height, bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_interleaved, width, height, bpp, limits)) { + return err; } if (has_alpha) { - if (!outimg->add_plane(heif_channel_Alpha, width, height, bpp)) { - return nullptr; + if (auto err = outimg->add_plane(heif_channel_Alpha, width, height, bpp, limits)) { + return err; } } diff --git a/libheif/color-conversion/yuv2rgb.h b/libheif/color-conversion/yuv2rgb.h index 40228ebdc5..d34d44737b 100644 --- a/libheif/color-conversion/yuv2rgb.h +++ b/libheif/color-conversion/yuv2rgb.h @@ -36,11 +36,12 @@ class Op_YCbCr_to_RGB : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -52,11 +53,12 @@ class Op_YCbCr420_to_RGB24 : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -68,11 +70,12 @@ class Op_YCbCr420_to_RGB32 : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; @@ -84,11 +87,12 @@ class Op_YCbCr420_to_RRGGBBaa : public ColorConversionOperation const ColorState& target_state, const heif_color_conversion_options& options) const override; - std::shared_ptr + Result> convert_colorspace(const std::shared_ptr& input, const ColorState& input_state, const ColorState& target_state, - const heif_color_conversion_options& options) const override; + const heif_color_conversion_options& options, + const heif_security_limits* limits) const override; }; #endif //LIBHEIF_COLORCONVERSION_YUV2RGB_H diff --git a/libheif/context.cc b/libheif/context.cc index f3fa925f7f..5abddeea5f 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -354,7 +354,15 @@ Error HeifContext::interpret_heif_file() m_top_level_images.push_back(image); } - Error err = image->on_load_file(); + std::vector> properties; + Error err = m_heif_file->get_properties(id, properties); + if (err) { + return err; + } + + image->set_properties(properties); + + err = image->on_load_file(); if (err) { return err; } @@ -555,7 +563,7 @@ Error HeifContext::interpret_heif_file() // --- this is an auxiliary image // check whether it is an alpha channel and attach to the main image if yes - std::shared_ptr auxC_property = m_heif_file->get_property(image->get_id()); + std::shared_ptr auxC_property = image->get_property(); if (!auxC_property) { std::stringstream sstr; sstr << "No auxC property for image " << image->get_id(); @@ -795,37 +803,40 @@ Error HeifContext::interpret_heif_file() // --- assign metadata to the image if (iref_box) { - std::vector references = iref_box->get_references_from(id); - for (const auto& ref : references) { - if (ref.header.get_short_type() == fourcc("cdsc")) { - std::vector refs = ref.to_item_ID; - - for(uint32_t ref: refs) { - uint32_t exif_image_id = ref; - auto img_iter = m_all_images.find(exif_image_id); - if (img_iter == m_all_images.end()) { - if (!m_heif_file->has_item_with_id(exif_image_id)) { - return Error(heif_error_Invalid_input, - heif_suberror_Nonexisting_item_referenced, - "Metadata assigned to non-existing image"); - } - - continue; - } - img_iter->second->add_metadata(metadata); - } - } - else if (ref.header.get_short_type() == fourcc("prem")) { - uint32_t color_image_id = ref.from_item_ID; - auto img_iter = m_all_images.find(color_image_id); - if (img_iter == m_all_images.end()) { + std::vector references = iref_box->get_references(id, fourcc("cdsc")); + for (heif_item_id exif_image_id : references) { + auto img_iter = m_all_images.find(exif_image_id); + if (img_iter == m_all_images.end()) { + if (!m_heif_file->has_item_with_id(exif_image_id)) { return Error(heif_error_Invalid_input, heif_suberror_Nonexisting_item_referenced, - "`prem` link assigned to non-existing image"); + "Metadata assigned to non-existing image"); } - img_iter->second->set_is_premultiplied_alpha(true);; + continue; + } + img_iter->second->add_metadata(metadata); + } + } + } + + // --- set premultiplied alpha flag + + for (heif_item_id id : image_IDs) { + if (iref_box) { + std::vector references = iref_box->get_references(id, fourcc("prem")); + for (heif_item_id ref : references) { + (void)ref; + + heif_item_id color_image_id = id; + auto img_iter = m_all_images.find(color_image_id); + if (img_iter == m_all_images.end()) { + return Error(heif_error_Invalid_input, + heif_suberror_Nonexisting_item_referenced, + "`prem` link assigned to non-existing image"); } + + img_iter->second->set_is_premultiplied_alpha(true); } } } @@ -1079,9 +1090,12 @@ Result> HeifContext::decode_image(heif_item_id I // TODO: check BPP changed if (different_chroma || different_colorspace) { - img = convert_colorspace(img, target_colorspace, target_chroma, nullptr, bpp, options.color_conversion_options); - if (!img) { - return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_color_conversion); + auto img_result = convert_colorspace(img, target_colorspace, target_chroma, nullptr, bpp, options.color_conversion_options, get_security_limits()); + if (img_result.error) { + return img_result.error; + } + else { + img = *img_result; } } @@ -1091,8 +1105,9 @@ Result> HeifContext::decode_image(heif_item_id I } -static std::shared_ptr -create_alpha_image_from_image_alpha_channel(const std::shared_ptr& image) +static Result> +create_alpha_image_from_image_alpha_channel(const std::shared_ptr& image, + const heif_security_limits* limits) { // --- generate alpha image @@ -1101,10 +1116,12 @@ create_alpha_image_from_image_alpha_channel(const std::shared_ptrhas_channel(heif_channel_Alpha)) { - alpha_image->copy_new_plane_from(image, heif_channel_Alpha, heif_channel_Y); + alpha_image->copy_new_plane_from(image, heif_channel_Alpha, heif_channel_Y, limits); } else if (image->get_chroma_format() == heif_chroma_interleaved_RGBA) { - alpha_image->extract_alpha_from_RGBA(image); + if (auto err = alpha_image->extract_alpha_from_RGBA(image, limits)) { + return err; + } } // TODO: 16 bit @@ -1154,7 +1171,7 @@ Result> HeifContext::encode_image(const std::shared_p heif_encoding_options options = in_options; if (const auto* nclx = output_image_item->get_forced_output_nclx()) { - options.output_nclx_profile = nclx; + options.output_nclx_profile = const_cast(nclx); } Result> srcImageResult = output_image_item->convert_colorspace_for_encoding(pixel_image, @@ -1187,7 +1204,12 @@ Result> HeifContext::encode_image(const std::shared_p // TODO: can we directly code a monochrome image instead of the dummy color channels? std::shared_ptr alpha_image; - alpha_image = create_alpha_image_from_image_alpha_channel(colorConvertedImage); + auto alpha_image_result = create_alpha_image_from_image_alpha_channel(colorConvertedImage, get_security_limits()); + if (!alpha_image_result) { + return alpha_image_result.error; + } + + alpha_image = *alpha_image_result; // --- encode the alpha image @@ -1208,6 +1230,12 @@ Result> HeifContext::encode_image(const std::shared_p } } + std::vector> properties; + err = m_heif_file->get_properties(output_image_item->get_id(), properties); + if (err) { + return err; + } + output_image_item->set_properties(properties); m_heif_file->set_brand(encoder->plugin->compression_format, output_image_item->is_miaf_compatible()); @@ -1276,7 +1304,7 @@ Result> HeifContext::encode_thumbnail(const std::shar std::shared_ptr thumbnail_image; - Error error = image->scale_nearest_neighbor(thumbnail_image, thumb_width, thumb_height); + Error error = image->scale_nearest_neighbor(thumbnail_image, thumb_width, thumb_height, get_security_limits()); if (error) { return error; } diff --git a/libheif/error.cc b/libheif/error.cc index 8fdf4c8952..722334d4a4 100644 --- a/libheif/error.cc +++ b/libheif/error.cc @@ -29,6 +29,10 @@ const char* cUnknownError = "Unknown error"; const Error Error::Ok(heif_error_Ok); +const Error Error::InternalError{heif_error_Unsupported_feature, // TODO: use better value + heif_suberror_Unspecified, + "Internal error"}; + Error::Error() = default; diff --git a/libheif/error.h b/libheif/error.h index 3eb5a7aa48..8be1002282 100644 --- a/libheif/error.h +++ b/libheif/error.h @@ -79,12 +79,23 @@ class Error static const Error Ok; + static const Error InternalError; + static const char kSuccess[]; bool operator==(const Error& other) const { return error_code == other.error_code; } bool operator!=(const Error& other) const { return !(*this == other); } + Error operator||(const Error& other) const { + if (error_code != heif_error_Ok) { + return *this; + } + else { + return other; + } + } + operator bool() const { return error_code != heif_error_Ok; } static const char* get_error_string(heif_error_code err); diff --git a/libheif/file.cc b/libheif/file.cc index 2904728bb9..d16df78b0d 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -27,6 +27,7 @@ #include "image-items/jpeg.h" #include "image-items/vvc.h" #include "codecs/avif_boxes.h" +#include "codecs/hevc_boxes.h" #include "codecs/uncompressed/unc_boxes.h" #include @@ -293,18 +294,6 @@ std::string HeifFile::debug_dump_boxes() const return sstr.str(); } -#if ENABLE_EXPERIMENTAL_MINI_FORMAT -static uint32_t get_item_type_for_brand(const heif_brand2 brand) -{ - switch(brand) { - case heif_brand2_avif: - return fourcc("av01"); - // TODO: more - default: - return 0; - } -} -#endif Error HeifFile::parse_heif_file() { @@ -346,16 +335,7 @@ Error HeifFile::parse_heif_file() #endif m_ftyp_box = m_file_layout->get_ftyp_box(); - m_meta_box = m_file_layout->get_meta_box(); - m_top_level_boxes.push_back(m_ftyp_box); - m_top_level_boxes.push_back(m_meta_box); - // TODO: we are missing 'mdat' top level boxes - -#if ENABLE_EXPERIMENTAL_MINI_FORMAT - m_mini_box = m_file_layout->get_mini_box(); - m_top_level_boxes.push_back(m_mini_box); -#endif // --- check whether this is a HEIF file and its structural format @@ -382,207 +362,24 @@ Error HeifFile::parse_heif_file() } #if ENABLE_EXPERIMENTAL_MINI_FORMAT - if (m_mini_box) { - m_hdlr_box = std::make_shared(); - m_hdlr_box->set_handler_type(fourcc("pict")); - - m_pitm_box = std::make_shared(); - m_pitm_box->set_item_ID(1); - - std::shared_ptr primary_infe_box = std::make_shared(); - primary_infe_box->set_version(2); - primary_infe_box->set_item_ID(1); - // TODO: check explicit codec flag - uint32_t minor_version = m_ftyp_box->get_minor_version(); - heif_brand2 mini_brand = minor_version; - uint32_t infe_type = get_item_type_for_brand(mini_brand); - if (infe_type == 0) { - // not found - std::stringstream sstr; - sstr << "Minimised file requires brand " << fourcc_to_string(mini_brand) << " but this is not yet supported."; - return Error(heif_error_Unsupported_filetype, - heif_suberror_Unspecified, - sstr.str()); - } - primary_infe_box->set_item_type_4cc(infe_type); - m_infe_boxes.insert(std::make_pair(1, primary_infe_box)); - - if (m_mini_box->get_alpha_item_data_size() != 0) { - std::shared_ptr alpha_infe_box = std::make_shared(); - alpha_infe_box->set_version(2); - alpha_infe_box->set_flags(1); - alpha_infe_box->set_item_ID(2); - alpha_infe_box->set_item_type_4cc(infe_type); - m_infe_boxes.insert(std::make_pair(2, alpha_infe_box)); - } - - if (m_mini_box->get_exif_flag()) { - std::shared_ptr exif_infe_box = std::make_shared(); - exif_infe_box->set_version(2); - exif_infe_box->set_flags(1); - exif_infe_box->set_item_ID(6); - exif_infe_box->set_item_type_4cc(fourcc("Exif")); - m_infe_boxes.insert(std::make_pair(6, exif_infe_box)); - } - - if (m_mini_box->get_xmp_flag()) { - std::shared_ptr xmp_infe_box = std::make_shared(); - xmp_infe_box->set_version(2); - xmp_infe_box->set_flags(1); - xmp_infe_box->set_item_ID(7); - xmp_infe_box->set_item_type_4cc(fourcc("mime")); - xmp_infe_box->set_content_type("application/rdf+xml"); - m_infe_boxes.insert(std::make_pair(7, xmp_infe_box)); - } - - m_ipco_box = std::make_shared(); - - // TODO: we should look this up based on the infe prop, not assume Box_av1C. - std::shared_ptr main_item_codec_prop = std::make_shared(); - std::shared_ptr istr = std::make_shared( - m_mini_box->get_main_item_codec_config().data(), - m_mini_box->get_main_item_codec_config().size(), - false - ); - BitstreamRange codec_range(istr, m_mini_box->get_main_item_codec_config().size(), nullptr); - main_item_codec_prop->parse(codec_range, heif_get_global_security_limits()); - m_ipco_box->append_child_box(main_item_codec_prop); // entry 1 - - std::shared_ptr ispe = std::make_shared(); - ispe->set_size(m_mini_box->get_width(), m_mini_box->get_height()); - m_ipco_box->append_child_box(ispe); // entry 2 - - std::shared_ptr pixi = std::make_shared(); - pixi->set_version(0); - // pixi->set_version(1); // TODO: when we support version 1 - // TODO: there is more when we do version 1, and anything other than RGB - pixi->add_channel_bits(m_mini_box->get_bit_depth()); - pixi->add_channel_bits(m_mini_box->get_bit_depth()); - pixi->add_channel_bits(m_mini_box->get_bit_depth()); - m_ipco_box->append_child_box(pixi); // entry 3 - - std::shared_ptr colr = std::make_shared(); - std::shared_ptr nclx = std::make_shared(); - nclx->set_colour_primaries(m_mini_box->get_colour_primaries()); - nclx->set_transfer_characteristics(m_mini_box->get_transfer_characteristics()); - nclx->set_matrix_coefficients(m_mini_box->get_matrix_coefficients()); - nclx->set_full_range_flag(m_mini_box->get_full_range_flag()); - colr->set_color_profile(nclx); - m_ipco_box->append_child_box(colr); // entry 4 - - std::shared_ptr colr_icc = std::make_shared(); - std::shared_ptr icc = std::make_shared(fourcc("prof"), m_mini_box->get_icc_data()); - colr_icc->set_color_profile(icc); - m_ipco_box->append_child_box(colr_icc); // entry 5 - - if (m_mini_box->get_alpha_item_codec_config().size() != 0) { - std::shared_ptr alpha_item_codec_prop = std::make_shared(); - std::shared_ptr istr = std::make_shared( - m_mini_box->get_alpha_item_codec_config().data(), - m_mini_box->get_alpha_item_codec_config().size(), - false - ); - BitstreamRange alpha_codec_range(istr, m_mini_box->get_alpha_item_codec_config().size(), nullptr); - alpha_item_codec_prop->parse(alpha_codec_range, heif_get_global_security_limits()); - m_ipco_box->append_child_box(alpha_item_codec_prop); // entry 6 - } - - if (m_mini_box->get_alpha_item_data_size() != 0) { - std::shared_ptr aux_type = std::make_shared(); - aux_type->set_aux_type("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"); - m_ipco_box->append_child_box(aux_type); // entry 7 - } - - // 8 - - // 9 - - // 10 - m_ipma_box = std::make_shared(); - m_ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(1)}); - m_ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{false, uint16_t(2)}); - m_ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{false, uint16_t(3)}); - m_ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(4)}); - if (m_mini_box->get_icc_flag()) { - // m_ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(5)}); - } - if (m_mini_box->get_alpha_item_data_size() > 0) { - m_ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(6)}); - m_ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{false, uint16_t(2)}); - m_ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(7)}); - // m_ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{false, uint16_t(8)}); - // m_ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(9)}); - // m_ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(10)}); - } - // TODO: will need more - - - - m_iloc_box = std::make_shared(); - Box_iloc::Item main_item; - main_item.item_ID = 1; - main_item.construction_method = 0; - main_item.base_offset = 0; - main_item.data_reference_index = 0; - Box_iloc::Extent main_item_extent; - main_item_extent.offset = m_mini_box->get_main_item_data_offset(); - main_item_extent.length = m_mini_box->get_main_item_data_size(); - main_item.extents.push_back(main_item_extent); - m_iloc_box->append_item(main_item); - - if (m_mini_box->get_alpha_item_data_size() != 0) { - Box_iloc::Item alpha_item; - alpha_item.item_ID = 2; - alpha_item.base_offset = 0; - alpha_item.data_reference_index = 0; - Box_iloc::Extent alpha_item_extent; - alpha_item_extent.offset = m_mini_box->get_alpha_item_data_offset(); - alpha_item_extent.length = m_mini_box->get_alpha_item_data_size(); - alpha_item.extents.push_back(alpha_item_extent); - m_iloc_box->append_item(alpha_item); - } - if (m_mini_box->get_exif_flag()) { - Box_iloc::Item exif_item; - exif_item.item_ID = 6; - exif_item.base_offset = 0; - exif_item.data_reference_index = 0; - Box_iloc::Extent exif_item_extent; - exif_item_extent.offset = m_mini_box->get_exif_item_data_offset(); - exif_item_extent.length = m_mini_box->get_exif_item_data_size(); - exif_item.extents.push_back(exif_item_extent); - m_iloc_box->append_item(exif_item); - } - if (m_mini_box->get_xmp_flag()) { - Box_iloc::Item xmp_item; - xmp_item.item_ID = 7; - xmp_item.base_offset = 0; - xmp_item.data_reference_index = 0; - Box_iloc::Extent xmp_item_extent; - xmp_item_extent.offset = m_mini_box->get_xmp_item_data_offset(); - xmp_item_extent.length = m_mini_box->get_xmp_item_data_size(); - xmp_item.extents.push_back(xmp_item_extent); - m_iloc_box->append_item(xmp_item); - } + m_mini_box = m_file_layout->get_mini_box(); + m_top_level_boxes.push_back(m_mini_box); - m_iref_box = std::make_shared(); - std::vector to_items = {1}; - if (m_mini_box->get_alpha_item_data_size() != 0) { - m_iref_box->add_references(2, fourcc("auxl"), to_items); - } - // TODO: if alpha prem - // TODO: if gainmap flag && item 4 - // TODO: if gainmap flag && !item 4 - if (m_mini_box->get_exif_flag()) { - m_iref_box->add_references(6, fourcc("cdsc"), to_items); - } - if (m_mini_box->get_xmp_flag()) { - m_iref_box->add_references(7, fourcc("cdsc"), to_items); + if (m_mini_box) { + Error err = m_mini_box->create_expanded_boxes(this); + if (err) { + return err; } return Error::Ok; } +#endif + + m_meta_box = m_file_layout->get_meta_box(); + m_top_level_boxes.push_back(m_meta_box); + // TODO: we are missing 'mdat' top level boxes // if we didn't find the mini box, meta is required -#endif + if (!m_meta_box) { return Error(heif_error_Invalid_input, heif_suberror_No_meta_box); @@ -1125,6 +922,12 @@ Result HeifFile::add_infe(uint32_t item_type, const uint8_t* data, } +void HeifFile::add_infe_box(heif_item_id id, std::shared_ptr infe) +{ + m_infe_boxes.insert(std::make_pair(id, std::move(infe))); +} + + Result HeifFile::add_infe_mime(const char* content_type, heif_metadata_compression content_encoding, const uint8_t* data, size_t size) { Result result; @@ -1269,9 +1072,43 @@ void HeifFile::replace_iloc_data(heif_item_id id, uint64_t offset, const std::ve void HeifFile::set_primary_item_id(heif_item_id id) { + if (!m_pitm_box) { + m_pitm_box = std::make_shared(); + m_meta_box->replace_child_box(m_pitm_box); + } + m_pitm_box->set_item_ID(id); } + +void HeifFile::set_ipco_box(std::shared_ptr ipco) +{ + m_ipco_box = ipco; + m_meta_box->replace_child_box(ipco); +} + + +void HeifFile::set_ipma_box(std::shared_ptr ipma) +{ + m_ipma_box = ipma; + m_meta_box->replace_child_box(ipma); +} + + +void HeifFile::set_iloc_box(std::shared_ptr iloc) +{ + m_iloc_box = iloc; + m_meta_box->replace_child_box(iloc); +} + + +void HeifFile::set_iref_box(std::shared_ptr iref) +{ + m_iref_box = iref; + m_meta_box->replace_child_box(iref); +} + + void HeifFile::add_iref_reference(heif_item_id from, uint32_t type, const std::vector& to) { diff --git a/libheif/file.h b/libheif/file.h index fbe2ef38c8..1a1585a403 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -36,6 +36,7 @@ #include #include #include +#include #if ENABLE_PARALLEL_TILE_DECODING @@ -72,6 +73,8 @@ class HeifFile void set_brand(heif_compression_format format, bool miaf_compatible); + void set_hdlr_box(std::shared_ptr box) { m_hdlr_box = std::move(box); } + void write(StreamWriter& writer); int get_num_images() const { return static_cast(m_infe_boxes.size()); } @@ -104,18 +107,26 @@ class HeifFile std::shared_ptr get_ftyp_box() { return m_ftyp_box; } + void init_meta_box() { m_meta_box = std::make_shared(); } + std::shared_ptr get_infe_box(heif_item_id imageID) const; std::shared_ptr get_infe_box(heif_item_id imageID); + void set_iref_box(std::shared_ptr); + std::shared_ptr get_iref_box() { return m_iref_box; } std::shared_ptr get_iref_box() const { return m_iref_box; } std::shared_ptr get_ipco_box() { return m_ipco_box; } + void set_ipco_box(std::shared_ptr); + std::shared_ptr get_ipco_box() const { return m_ipco_box; } + void set_ipma_box(std::shared_ptr); + std::shared_ptr get_ipma_box() { return m_ipma_box; } std::shared_ptr get_ipma_box() const { return m_ipma_box; } @@ -128,7 +139,7 @@ class HeifFile std::vector>& properties) const; template - std::shared_ptr get_property(heif_item_id imageID) const + std::shared_ptr get_property_for_item(heif_item_id imageID) const { std::vector> properties; Error err = get_properties(imageID, properties); @@ -168,6 +179,8 @@ class HeifFile Result add_infe(uint32_t item_type, const uint8_t* data, size_t size); + void add_infe_box(heif_item_id, std::shared_ptr infe); + Result add_infe_mime(const char* content_type, heif_metadata_compression content_encoding, const uint8_t* data, size_t size); Result add_precompressed_infe_mime(const char* content_type, std::string content_encoding, const uint8_t* data, size_t size); @@ -182,6 +195,10 @@ class HeifFile void replace_iloc_data(heif_item_id id, uint64_t offset, const std::vector& data, uint8_t construction_method = 0); + void set_iloc_box(std::shared_ptr); + + std::shared_ptr get_iloc_box() { return m_iloc_box; } + void set_primary_item_id(heif_item_id id); void add_iref_reference(heif_item_id from, uint32_t type, diff --git a/libheif/image-items/avc.cc b/libheif/image-items/avc.cc index 33f2f1d1ba..d10abb29ec 100644 --- a/libheif/image-items/avc.cc +++ b/libheif/image-items/avc.cc @@ -128,7 +128,7 @@ std::shared_ptr ImageItem_AVC::get_decoder() const Error ImageItem_AVC::on_load_file() { - auto avcC_box = get_file()->get_property(get_id()); + auto avcC_box = get_property(); if (!avcC_box) { return Error{heif_error_Invalid_input, heif_suberror_No_av1C_box}; diff --git a/libheif/image-items/avif.cc b/libheif/image-items/avif.cc index c6d343803f..0ad212c274 100644 --- a/libheif/image-items/avif.cc +++ b/libheif/image-items/avif.cc @@ -38,7 +38,7 @@ Error ImageItem_AVIF::on_load_file() { - auto av1C_box = get_file()->get_property(get_id()); + auto av1C_box = get_property(); if (!av1C_box) { return Error{heif_error_Invalid_input, heif_suberror_No_av1C_box}; @@ -102,7 +102,7 @@ Result ImageItem_AVIF::encode(const std::shared_ptr> ImageItem_AVIF::read_bitstream_configuration_data(heif_item_id itemId) const +Result> ImageItem_AVIF::read_bitstream_configuration_data() const { return m_decoder->read_bitstream_configuration_data(); } diff --git a/libheif/image-items/avif.h b/libheif/image-items/avif.h index 65b98581b1..182df6f148 100644 --- a/libheif/image-items/avif.h +++ b/libheif/image-items/avif.h @@ -57,7 +57,7 @@ class ImageItem_AVIF : public ImageItem enum heif_image_input_class input_class) override; protected: - Result> read_bitstream_configuration_data(heif_item_id itemId) const override; + Result> read_bitstream_configuration_data() const override; std::shared_ptr get_decoder() const override; diff --git a/libheif/image-items/grid.cc b/libheif/image-items/grid.cc index 883b9f4b7c..3621dd0a77 100644 --- a/libheif/image-items/grid.cc +++ b/libheif/image-items/grid.cc @@ -445,23 +445,30 @@ Error ImageItem_Grid::decode_and_paste_tile_image(heif_item_id tileID, uint32_t // --- generate the image canvas for combining all the tiles - if (!inout_image) { // this if avoids that we normally have to lock a mutex + if (!inout_image) { // this avoids that we normally have to lock a mutex +#if ENABLE_PARALLEL_TILE_DECODING static std::mutex createImageMutex; std::lock_guard lock(createImageMutex); +#endif if (!inout_image) { - inout_image = std::make_shared(); - inout_image->create_clone_image_at_new_size(tile_img, w, h); + auto grid_image = std::make_shared(); + auto err = grid_image->create_clone_image_at_new_size(tile_img, w, h, get_context()->get_security_limits()); + if (err) { + return err; + } // Fill alpha plane with opaque in case not all tiles have alpha planes - if (inout_image->has_channel(heif_channel_Alpha)) { - uint16_t alpha_bpp = inout_image->get_bits_per_pixel(heif_channel_Alpha); + if (grid_image->has_channel(heif_channel_Alpha)) { + uint16_t alpha_bpp = grid_image->get_bits_per_pixel(heif_channel_Alpha); assert(alpha_bpp <= 16); auto alpha_default_value = static_cast((1UL << alpha_bpp) - 1UL); - inout_image->fill_plane(heif_channel_Alpha, alpha_default_value); + grid_image->fill_plane(heif_channel_Alpha, alpha_default_value); } + + inout_image = grid_image; // We have to set this at the very end because of the unlocked check to `inout_image` above. } } @@ -479,8 +486,10 @@ Error ImageItem_Grid::decode_and_paste_tile_image(heif_item_id tileID, uint32_t inout_image->copy_image_to(tile_img, x0, y0); if (options.on_progress) { +#if ENABLE_PARALLEL_TILE_DECODING static std::mutex progressMutex; std::lock_guard lock(progressMutex); +#endif options.on_progress(heif_progress_step_total, ++progress_counter, options.progress_user_data); } @@ -658,7 +667,7 @@ Result> ImageItem_Grid::add_new_grid_item(HeifCo } -Error ImageItem_Grid::add_image_tile(heif_item_id grid_id, uint32_t tile_x, uint32_t tile_y, +Error ImageItem_Grid::add_image_tile(uint32_t tile_x, uint32_t tile_y, const std::shared_ptr& image, struct heif_encoder* encoder) { @@ -679,13 +688,13 @@ Error ImageItem_Grid::add_image_tile(heif_item_id grid_id, uint32_t tile_x, uint // Assign tile to grid heif_image_tiling tiling = get_heif_image_tiling(); - file->set_iref_reference(grid_id, fourcc("dimg"), tile_y * tiling.num_columns + tile_x, encoded_image->get_id()); + file->set_iref_reference(get_id(), fourcc("dimg"), tile_y * tiling.num_columns + tile_x, encoded_image->get_id()); set_grid_tile_id(tile_x, tile_y, encoded_image->get_id()); // Add PIXI property (copy from first tile) - auto pixi = file->get_property(encoded_image->get_id()); - file->add_property(grid_id, pixi, true); + auto pixi = encoded_image->get_property(); + add_property(pixi, true); return Error::Ok; } @@ -715,6 +724,8 @@ Result> ImageItem_Grid::add_and_encode_full_grid std::vector tile_ids; + std::shared_ptr pixi_property; + for (int i=0; i out_tile; auto encodingResult = ctx->encode_image(tiles[i], @@ -724,10 +735,17 @@ Result> ImageItem_Grid::add_and_encode_full_grid if (encodingResult.error) { return encodingResult.error; } + else { + out_tile = *encodingResult; + } heif_item_id tile_id = out_tile->get_id(); file->get_infe_box(tile_id)->set_hidden_item(true); // only show the full grid tile_ids.push_back(out_tile->get_id()); + + if (!pixi_property) { + pixi_property = out_tile->get_property(); + } } // Create Grid Item @@ -746,12 +764,14 @@ Result> ImageItem_Grid::add_and_encode_full_grid uint32_t image_width = tile_width * columns; uint32_t image_height = tile_height * rows; - file->add_ispe_property(grid_id, image_width, image_height, false); + + auto ispe = std::make_shared(); + ispe->set_size(image_width, image_height); + griditem->add_property(ispe, false); // Add PIXI property (copy from first tile) - auto pixi = file->get_property(tile_ids[0]); - file->add_property(grid_id, pixi, true); + griditem->add_property(pixi_property, true); // Set Brands diff --git a/libheif/image-items/grid.h b/libheif/image-items/grid.h index a2811020b8..c269937654 100644 --- a/libheif/image-items/grid.h +++ b/libheif/image-items/grid.h @@ -89,7 +89,7 @@ class ImageItem_Grid : public ImageItem uint16_t tile_columns, const struct heif_encoding_options* encoding_options); - Error add_image_tile(heif_item_id grid_id, uint32_t tile_x, uint32_t tile_y, + Error add_image_tile(uint32_t tile_x, uint32_t tile_y, const std::shared_ptr& image, struct heif_encoder* encoder); diff --git a/libheif/image-items/hevc.cc b/libheif/image-items/hevc.cc index bba8f71c5c..ac1a73e0fd 100644 --- a/libheif/image-items/hevc.cc +++ b/libheif/image-items/hevc.cc @@ -36,7 +36,7 @@ Error ImageItem_HEVC::on_load_file() { - auto hvcC_box = get_file()->get_property(get_id()); + auto hvcC_box = get_property(); if (!hvcC_box) { return Error{heif_error_Invalid_input, heif_suberror_No_hvcC_box}; @@ -139,7 +139,7 @@ Result ImageItem_HEVC::encode(const std::shared_ptr> ImageItem_HEVC::read_bitstream_configuration_data(heif_item_id itemId) const +Result> ImageItem_HEVC::read_bitstream_configuration_data() const { return m_decoder->read_bitstream_configuration_data(); } diff --git a/libheif/image-items/hevc.h b/libheif/image-items/hevc.h index 50bedfeea8..3f077eb478 100644 --- a/libheif/image-items/hevc.h +++ b/libheif/image-items/hevc.h @@ -58,7 +58,7 @@ class ImageItem_HEVC : public ImageItem void set_preencoded_hevc_image(const std::vector& data); protected: - Result> read_bitstream_configuration_data(heif_item_id itemId) const override; + Result> read_bitstream_configuration_data() const override; std::shared_ptr get_decoder() const override; diff --git a/libheif/image-items/image_item.cc b/libheif/image-items/image_item.cc index db24c7e6bb..de3d4ce014 100644 --- a/libheif/image-items/image_item.cc +++ b/libheif/image-items/image_item.cc @@ -67,6 +67,21 @@ std::shared_ptr ImageItem::get_file() const } +heif_property_id ImageItem::add_property(std::shared_ptr property, bool essential) +{ + // TODO: is this correct? What happens when add_property does deduplicate the property? + m_properties.push_back(property); + return get_file()->add_property(get_id(), property, essential); +} + + +heif_property_id ImageItem::add_property_without_deduplication(std::shared_ptr property, bool essential) +{ + m_properties.push_back(property); + return get_file()->add_property_without_deduplication(get_id(), property, essential); +} + + Error ImageItem::init_decoder_from_item(heif_item_id id) { m_id = id; @@ -407,12 +422,12 @@ Error ImageItem::encode_to_item(HeifContext* ctx, bool ImageItem::has_ispe_resolution() const { - return m_heif_context->get_heif_file()->get_property(m_id) != nullptr; + return get_property() != nullptr; } uint32_t ImageItem::get_ispe_width() const { - auto ispe = m_heif_context->get_heif_file()->get_property(m_id); + auto ispe = get_property(); if (!ispe) { return 0; } @@ -424,7 +439,7 @@ uint32_t ImageItem::get_ispe_width() const uint32_t ImageItem::get_ispe_height() const { - auto ispe = m_heif_context->get_heif_file()->get_property(m_id); + auto ispe = get_property(); if (!ispe) { return 0; } @@ -589,26 +604,27 @@ Result> ImageItem::convert_colorspace_for_encodi std::shared_ptr output_image; - if (colorspace != image->get_colorspace() || - chroma != image->get_chroma_format() || - !nclx_profile_matches_spec(colorspace, image->get_color_profile_nclx(), output_nclx_profile)) { - // @TODO: use color profile when converting - int output_bpp = 0; // same as input + if (colorspace == image->get_colorspace() && + chroma == image->get_chroma_format() && + nclx_profile_matches_spec(colorspace, image->get_color_profile_nclx(), output_nclx_profile)) { + return image; + } + - //auto target_nclx = std::make_shared(); - //target_nclx->set_from_heif_color_profile_nclx(target_heif_nclx); + // @TODO: use color profile when converting + int output_bpp = 0; // same as input - output_image = convert_colorspace(image, colorspace, chroma, target_nclx_profile, - output_bpp, options.color_conversion_options); - if (!output_image) { - return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_color_conversion); - } - } - else { - output_image = image; + //auto target_nclx = std::make_shared(); + //target_nclx->set_from_heif_color_profile_nclx(target_heif_nclx); + + auto output_image_result = convert_colorspace(image, colorspace, chroma, target_nclx_profile, + output_bpp, options.color_conversion_options, + get_context()->get_security_limits()); + if (output_image_result.error) { + return output_image_result.error; } - return output_image; + return *output_image_result; } @@ -740,7 +756,7 @@ Result> ImageItem::decode_image(const struct hei // --- check whether image size (according to 'ispe') exceeds maximum if (!decode_tile_only) { - auto ispe = m_heif_context->get_heif_file()->get_property(m_id); + auto ispe = get_property(); if (ispe) { Error err = check_for_valid_image_size(get_context()->get_security_limits(), ispe->get_width(), ispe->get_height()); if (err) { @@ -784,7 +800,7 @@ Result> ImageItem::decode_image(const struct hei for (const auto& property : properties) { if (auto rot = std::dynamic_pointer_cast(property)) { - auto rotateResult = img->rotate_ccw(rot->get_rotation_ccw()); + auto rotateResult = img->rotate_ccw(rot->get_rotation_ccw(), m_heif_context->get_security_limits()); if (rotateResult.error) { return error; } @@ -794,7 +810,8 @@ Result> ImageItem::decode_image(const struct hei if (auto mirror = std::dynamic_pointer_cast(property)) { - auto mirrorResult = img->mirror_inplace(mirror->get_mirror_direction()); + auto mirrorResult = img->mirror_inplace(mirror->get_mirror_direction(), + get_context()->get_security_limits()); if (mirrorResult.error) { return error; } @@ -828,7 +845,7 @@ Result> ImageItem::decode_image(const struct hei heif_suberror_Invalid_clean_aperture); } - auto cropResult = img->crop(left, right, top, bottom); + auto cropResult = img->crop(left, right, top, bottom, m_heif_context->get_security_limits()); if (error) { return error; } @@ -883,7 +900,7 @@ Result> ImageItem::decode_image(const struct hei if ((alpha_image->get_width() != img->get_width()) || (alpha_image->get_height() != img->get_height())) { std::shared_ptr scaled_alpha; - Error err = alpha->scale_nearest_neighbor(scaled_alpha, img->get_width(), img->get_height()); + Error err = alpha->scale_nearest_neighbor(scaled_alpha, img->get_width(), img->get_height(), m_heif_context->get_security_limits()); if (err) { return err; } @@ -921,21 +938,21 @@ Result> ImageItem::decode_image(const struct hei // CLLI - auto clli = get_file()->get_property(m_id); + auto clli = get_property(); if (clli) { img->set_clli(clli->clli); } // MDCV - auto mdcv = get_file()->get_property(m_id); + auto mdcv = get_property(); if (mdcv) { img->set_mdcv(mdcv->mdcv); } // PASP - auto pasp = get_file()->get_property(m_id); + auto pasp = get_property(); if (pasp) { img->set_pixel_ratio(pasp->hSpacing, pasp->vSpacing); } @@ -944,7 +961,7 @@ Result> ImageItem::decode_image(const struct hei return img; } - +#if 0 Result> ImageItem::read_bitstream_configuration_data_override(heif_item_id itemId, heif_compression_format format) const { auto item_codec = ImageItem::alloc_for_compression_format(const_cast(get_context()), format); @@ -957,7 +974,7 @@ Result> ImageItem::read_bitstream_configuration_data_overri return item_codec->read_bitstream_configuration_data(itemId); } - +#endif Result> ImageItem::get_compressed_image_data() const { @@ -967,7 +984,7 @@ Result> ImageItem::get_compressed_image_data() const // data from configuration blocks - Result> confData = read_bitstream_configuration_data(get_id()); + Result> confData = read_bitstream_configuration_data(); if (confData.error) { return confData.error; } diff --git a/libheif/image-items/image_item.h b/libheif/image-items/image_item.h index 2d6a94eceb..2d222a4fdb 100644 --- a/libheif/image-items/image_item.h +++ b/libheif/image-items/image_item.h @@ -80,7 +80,7 @@ class ImageItem : public ErrorBuffer virtual heif_compression_format get_compression_format() const { return heif_compression_undefined; } - virtual Result> read_bitstream_configuration_data(heif_item_id itemId) const { return std::vector{}; } + virtual Result> read_bitstream_configuration_data() const { return std::vector{}; } void clear() { @@ -96,6 +96,26 @@ class ImageItem : public ErrorBuffer std::shared_ptr get_file() const; + void set_properties(std::vector> properties) { + m_properties = std::move(properties); + } + + template + std::shared_ptr get_property() const + { + for (auto& property : m_properties) { + if (auto box = std::dynamic_pointer_cast(property)) { + return box; + } + } + + return nullptr; + } + + heif_property_id add_property(std::shared_ptr property, bool essential); + + heif_property_id add_property_without_deduplication(std::shared_ptr property, bool essential); + void set_resolution(uint32_t w, uint32_t h) { m_width = w; @@ -363,6 +383,7 @@ class ImageItem : public ErrorBuffer private: HeifContext* m_heif_context; + std::vector> m_properties; heif_item_id m_id = 0; uint32_t m_width = 0, m_height = 0; // after all transformations have been applied @@ -404,7 +425,7 @@ class ImageItem : public ErrorBuffer std::vector m_decoding_warnings; protected: - Result> read_bitstream_configuration_data_override(heif_item_id itemId, heif_compression_format format) const; + // Result> read_bitstream_configuration_data_override(heif_item_id itemId, heif_compression_format format) const; virtual Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, diff --git a/libheif/image-items/jpeg.cc b/libheif/image-items/jpeg.cc index fe4b6d3607..d8ff590cc1 100644 --- a/libheif/image-items/jpeg.cc +++ b/libheif/image-items/jpeg.cc @@ -112,7 +112,7 @@ Result ImageItem_JPEG::encode(const std::shared_ptr> ImageItem_JPEG::read_bitstream_configuration_data(heif_item_id itemId) const +Result> ImageItem_JPEG::read_bitstream_configuration_data() const { return m_decoder->read_bitstream_configuration_data(); } @@ -126,7 +126,7 @@ std::shared_ptr ImageItem_JPEG::get_decoder() const Error ImageItem_JPEG::on_load_file() { // Note: jpgC box is optional. NULL is a valid value. - auto jpgC_box = get_file()->get_property(get_id()); + auto jpgC_box = get_property(); m_decoder = std::make_shared(jpgC_box); diff --git a/libheif/image-items/jpeg.h b/libheif/image-items/jpeg.h index ecc9bd7533..45abd574ef 100644 --- a/libheif/image-items/jpeg.h +++ b/libheif/image-items/jpeg.h @@ -54,7 +54,7 @@ class ImageItem_JPEG : public ImageItem protected: std::shared_ptr get_decoder() const override; - Result> read_bitstream_configuration_data(heif_item_id itemId) const override; + Result> read_bitstream_configuration_data() const override; private: std::shared_ptr m_decoder; diff --git a/libheif/image-items/jpeg2000.cc b/libheif/image-items/jpeg2000.cc index 30f2e17577..9d9a48719d 100644 --- a/libheif/image-items/jpeg2000.cc +++ b/libheif/image-items/jpeg2000.cc @@ -69,11 +69,11 @@ Result ImageItem_JPEG2000::encode(const std::shared_p } -Result> ImageItem_JPEG2000::read_bitstream_configuration_data(heif_item_id itemId) const +Result> ImageItem_JPEG2000::read_bitstream_configuration_data() const { // --- get codec configuration - std::shared_ptr j2kH_box = get_file()->get_property(itemId); + std::shared_ptr j2kH_box = get_property(); if (!j2kH_box) { // TODO - Correctly Find the j2kH box @@ -95,7 +95,7 @@ std::shared_ptr ImageItem_JPEG2000::get_decoder() const Error ImageItem_JPEG2000::on_load_file() { - auto j2kH = get_file()->get_property(get_id()); + auto j2kH = get_property(); if (!j2kH) { return Error{heif_error_Invalid_input, heif_suberror_Unspecified, diff --git a/libheif/image-items/jpeg2000.h b/libheif/image-items/jpeg2000.h index be6471dfc1..da1f834a58 100644 --- a/libheif/image-items/jpeg2000.h +++ b/libheif/image-items/jpeg2000.h @@ -47,7 +47,7 @@ class ImageItem_JPEG2000 : public ImageItem enum heif_image_input_class input_class) override; protected: - Result> read_bitstream_configuration_data(heif_item_id itemId) const override; + Result> read_bitstream_configuration_data() const override; std::shared_ptr get_decoder() const override; diff --git a/libheif/image-items/mask_image.cc b/libheif/image-items/mask_image.cc index 98ddb06501..9c2c4e6a40 100644 --- a/libheif/image-items/mask_image.cc +++ b/libheif/image-items/mask_image.cc @@ -62,8 +62,14 @@ Error MaskImageCodec::decode_mask_image(const HeifContext* context, std::shared_ptr& img, const std::vector& data) { - std::shared_ptr ispe = context->get_heif_file()->get_property(ID); - std::shared_ptr mskC = context->get_heif_file()->get_property(ID); + auto image = context->get_image(ID, false); + if (!image) { + return {heif_error_Invalid_input, + heif_suberror_Nonexisting_item_referenced}; + } + + std::shared_ptr ispe = image->get_property(); + std::shared_ptr mskC = image->get_property(); uint32_t width = 0; uint32_t height = 0; @@ -99,7 +105,12 @@ Error MaskImageCodec::decode_mask_image(const HeifContext* context, img = std::make_shared(); img->create(width, height, heif_colorspace_monochrome, heif_chroma_monochrome); - img->add_plane(heif_channel_Y, width, height, mskC->get_bits_per_pixel()); + auto err = img->add_plane(heif_channel_Y, width, height, mskC->get_bits_per_pixel(), + context->get_security_limits()); + if (err) { + return err; + } + uint32_t stride; uint8_t* dst = img->get_plane(heif_channel_Y, &stride); if (((uint32_t)stride) == width) { @@ -193,7 +204,7 @@ Result ImageItem_mask::encode(const std::shared_ptrget_property(get_id()); + auto mskC = get_property(); if (!mskC) { return -1; } diff --git a/libheif/image-items/overlay.cc b/libheif/image-items/overlay.cc index df91f83e81..c010fee8e5 100644 --- a/libheif/image-items/overlay.cc +++ b/libheif/image-items/overlay.cc @@ -47,13 +47,12 @@ static int32_t readvec_signed(const std::vector& data, int& ptr, int le } bool negative = (val & high_bit) != 0; - val &= ~high_bit; if (negative) { - return -(high_bit - val); + return -static_cast((~val) & 0x7fffffff) -1; } else { - return val; + return static_cast(val); } return val; @@ -298,9 +297,15 @@ Result> ImageItem_Overlay::decode_overlay_image( img->create(w, h, heif_colorspace_RGB, heif_chroma_444); - img->add_plane(heif_channel_R, w, h, 8); // TODO: other bit depths - img->add_plane(heif_channel_G, w, h, 8); // TODO: other bit depths - img->add_plane(heif_channel_B, w, h, 8); // TODO: other bit depths + if (auto error = img->add_plane(heif_channel_R, w, h, 8, get_context()->get_security_limits())) { // TODO: other bit depths + return error; + } + if (auto error = img->add_plane(heif_channel_G, w, h, 8, get_context()->get_security_limits())) { // TODO: other bit depths + return error; + } + if (auto error = img->add_plane(heif_channel_B, w, h, 8, get_context()->get_security_limits())) { // TODO: other bit depths + return error; + } uint16_t bkg_color[4]; m_overlay_spec.get_background_color(bkg_color); @@ -340,9 +345,13 @@ Result> ImageItem_Overlay::decode_overlay_image( if (overlay_img->get_colorspace() != heif_colorspace_RGB || overlay_img->get_chroma_format() != heif_chroma_444) { - overlay_img = convert_colorspace(overlay_img, heif_colorspace_RGB, heif_chroma_444, nullptr, 0, options.color_conversion_options); - if (!overlay_img) { - return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_color_conversion); + auto overlay_img_result = convert_colorspace(overlay_img, heif_colorspace_RGB, heif_chroma_444, nullptr, 0, options.color_conversion_options, + get_context()->get_security_limits()); + if (overlay_img_result.error) { + return overlay_img_result.error; + } + else { + overlay_img = *overlay_img_result; } } @@ -391,6 +400,15 @@ int ImageItem_Overlay::get_chroma_bits_per_pixel() const } +Error ImageItem_Overlay::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const +{ + *out_colorspace = heif_colorspace_RGB; + *out_chroma = heif_chroma_444; + + return Error::Ok; +} + + Result> ImageItem_Overlay::add_new_overlay_item(HeifContext* ctx, const ImageOverlay& overlayspec) { if (overlayspec.get_num_offsets() > 0xFFFF) { @@ -425,11 +443,13 @@ Result> ImageItem_Overlay::add_new_overlay_it file->add_iref_reference(iovl_id, fourcc("dimg"), ref_ids); // Add ISPE property - file->add_ispe_property(iovl_id, overlayspec.get_canvas_width(), overlayspec.get_canvas_height(), false); + auto ispe = std::make_shared(); + ispe->set_size(overlayspec.get_canvas_width(), overlayspec.get_canvas_height()); + iovl_image->add_property(ispe, false); // Add PIXI property (copy from first image) - According to MIAF, all images shall have the same color information. - auto pixi = file->get_property(ref_ids[0]); - file->add_property(iovl_id, pixi, true); + auto pixi = file->get_property_for_item(ref_ids[0]); + iovl_image->add_property(pixi, true); // Set Brands //m_heif_file->set_brand(encoder->plugin->compression_format, diff --git a/libheif/image-items/overlay.h b/libheif/image-items/overlay.h index e3d42c2817..2d00501f33 100644 --- a/libheif/image-items/overlay.h +++ b/libheif/image-items/overlay.h @@ -103,6 +103,8 @@ class ImageItem_Overlay : public ImageItem int get_chroma_bits_per_pixel() const override; + Error get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const override; + Result encode(const std::shared_ptr& image, struct heif_encoder* encoder, const struct heif_encoding_options& options, diff --git a/libheif/image-items/tiled.cc b/libheif/image-items/tiled.cc index a219d08f1f..12975257da 100644 --- a/libheif/image-items/tiled.cc +++ b/libheif/image-items/tiled.cc @@ -91,7 +91,7 @@ void Box_tilC::init_heif_tiled_image_parameters(heif_tiled_image_parameters& par void Box_tilC::derive_box_version() { - set_version(1); + set_version(0); uint8_t flags = 0; @@ -157,6 +157,18 @@ Error Box_tilC::write(StreamWriter& writer) const writer.write32(m_parameters.extra_dimensions[i]); } + auto& tile_properties = m_children; + if (tile_properties.size() > 255) { + return {heif_error_Encoding_error, + heif_suberror_Unspecified, + "Cannot write more than 255 tile properties in tilC header"}; + } + + writer.write8(static_cast(tile_properties.size())); + for (const auto& property : tile_properties) { + property->write(writer); + } + prepend_header(writer, box_start); return Error::Ok; @@ -178,6 +190,9 @@ std::string Box_tilC::dump(Indent& indent) const << indent << "size field length: " << ((int) m_parameters.size_field_length) << " bits\n" << indent << "number of extra dimensions: " << ((int) m_parameters.number_of_extra_dimensions) << "\n"; + sstr << indent << "tile properties:\n" + << dump_children(indent, true); + return sstr.str(); } @@ -187,9 +202,10 @@ Error Box_tilC::parse(BitstreamRange& range, const heif_security_limits* limits) { parse_full_box_header(range); - if (get_version() != 1) { + // Note: actually, we should allow 0 only, but there are a few images around that use version 1. + if (get_version() > 1) { std::stringstream sstr; - sstr << "'tild' image version " << ((int) get_version()) << " is not implemented yet"; + sstr << "'tili' image version " << ((int) get_version()) << " is not implemented yet"; return {heif_error_Unsupported_feature, heif_suberror_Unsupported_data_version, @@ -254,7 +270,7 @@ Error Box_tilC::parse(BitstreamRange& range, const heif_security_limits* limits) if (size == 0) { return {heif_error_Invalid_input, heif_suberror_Unspecified, - "'tild' extra dimension may not be zero."}; + "'tili' extra dimension may not be zero."}; } if (i < 8) { @@ -265,6 +281,18 @@ Error Box_tilC::parse(BitstreamRange& range, const heif_security_limits* limits) } } + // --- read tile properties + + // Check version for backwards compatibility with old format. + // TODO: remove when spec is final and old test images have been converted + if (get_version() == 0) { + uint8_t num_properties = range.read8(); + + Error error = read_children(range, num_properties, limits); + if (error) { + return error; + } + } return range.get_error(); } @@ -312,7 +340,7 @@ Error TiledHeader::read_offset_table_range(const std::shared_ptr& file { const Error eofError(heif_error_Invalid_input, heif_suberror_Unspecified, - "Tild header data incomplete"); + "Tili header data incomplete"); std::vector data; @@ -475,14 +503,14 @@ Error ImageItem_Tiled::on_load_file() { auto heif_file = get_context()->get_heif_file(); - auto tilC_box = heif_file->get_property(get_id()); + auto tilC_box = get_property(); if (!tilC_box) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "Tiled image without 'tilC' property box."}; } - auto ispe_box = heif_file->get_property(get_id()); + auto ispe_box = get_property(); if (!ispe_box) { return {heif_error_Invalid_input, heif_suberror_Unspecified, @@ -496,18 +524,58 @@ Error ImageItem_Tiled::on_load_file() if (parameters.image_width == 0 || parameters.image_height == 0) { return {heif_error_Invalid_input, heif_suberror_Unspecified, - "'tild' image with zero width or height."}; + "'tili' image with zero width or height."}; } if (Error err = m_tild_header.set_parameters(parameters)) { return err; } - m_tile_decoder = Decoder::alloc_for_infe_type(get_context(), get_id(), parameters.compression_format_fourcc); + + // --- create a dummy image item for decoding tiles + + heif_compression_format format = compression_format_from_fourcc_infe_type(m_tild_header.get_parameters().compression_format_fourcc); + m_tile_item = ImageItem::alloc_for_compression_format(get_context(), format); + + // For backwards compatibility: copy over properties from `tili` item. + // TODO: remove when spec is final and old test images have been converted + if (tilC_box->get_version() == 1) { + auto propertiesResult = get_properties(); + if (propertiesResult.error) { + return propertiesResult.error; + } + + m_tile_item->set_properties(*propertiesResult); + } + else { + // --- This is the new method + + // Synthesize an ispe box if there was none in the file + + auto tile_properties = tilC_box->get_all_child_boxes(); + + bool have_ispe = false; + for (const auto& property : tile_properties) { + if (property->get_short_type() == fourcc("ispe")) { + have_ispe = true; + break; + } + } + + if (!have_ispe) { + auto ispe = std::make_shared(); + ispe->set_size(parameters.tile_width, parameters.tile_height); + tile_properties.emplace_back(std::move(ispe)); + } + + m_tile_item->set_properties(tile_properties); + } + + m_tile_decoder = Decoder::alloc_for_infe_type(m_tile_item.get()); if (!m_tile_decoder) { return {heif_error_Unsupported_feature, heif_suberror_Unsupported_codec, - "'tild' image with unsupported compression format."}; + "'tili' image with unsupported compression format."}; } if (m_preload_offset_table) { @@ -516,6 +584,7 @@ Error ImageItem_Tiled::on_load_file() } } + return Error::Ok; } @@ -532,7 +601,7 @@ ImageItem_Tiled::add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_par } - // Create 'tild' Item + // Create 'tili' Item auto file = ctx->get_heif_file(); @@ -546,7 +615,7 @@ ImageItem_Tiled::add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_par auto tilC_box = std::make_shared(); tilC_box->set_parameters(*parameters); tilC_box->set_compression_format(encoder->plugin->compression_format); - ctx->get_heif_file()->add_property(tild_id, tilC_box, true); + tild_image->add_property(tilC_box, true); // Create header + offset table @@ -566,10 +635,10 @@ ImageItem_Tiled::add_new_tiled_item(HeifContext* ctx, const heif_tiled_image_par } // Add ISPE property - file->add_ispe_property(tild_id, - static_cast(parameters->image_width), - static_cast(parameters->image_height), - true); + auto ispe = std::make_shared(); + ispe->set_size(static_cast(parameters->image_width), + static_cast(parameters->image_height)); + tild_image->add_property(ispe, true); #if 0 // TODO @@ -638,27 +707,37 @@ Error ImageItem_Tiled::add_image_tile(uint32_t tile_x, uint32_t tile_y, header.set_tild_tile_range(tile_x, tile_y, offset, static_cast(dataSize)); set_next_tild_position(offset + encodeResult.value.bitstream.size()); - std::vector> existing_properties; - Error err = get_file()->get_properties(get_id(), existing_properties); - if (err) { - return err; - } + auto tilC = get_property(); + assert(tilC); + + std::vector>& tile_properties = tilC->get_tile_properties(); for (auto& propertyBox : encodeResult.value.properties) { + + // we do not have to save ispe boxes in the tile properties as this is automatically synthesized + if (propertyBox->get_short_type() == fourcc("ispe")) { continue; } // skip properties that exist already - bool exists = std::any_of(existing_properties.begin(), - existing_properties.end(), + bool exists = std::any_of(tile_properties.begin(), + tile_properties.end(), [&propertyBox](const std::shared_ptr& p) { return p->get_short_type() == propertyBox->get_short_type();}); if (exists) { continue; } - get_file()->add_property(get_id(), propertyBox, propertyBox->is_essential()); + tile_properties.emplace_back(propertyBox); + + // some tile properties are also added to the tili image + + switch (propertyBox->get_short_type()) { + case fourcc("pixi"): + get_file()->add_property(get_id(), propertyBox, propertyBox->is_essential()); + break; + } } get_file()->set_brand(encoder->plugin->compression_format, @@ -688,7 +767,7 @@ ImageItem_Tiled::decode_compressed_image(const struct heif_decoding_options& opt } else { return Error{heif_error_Unsupported_feature, heif_suberror_Unspecified, - "'tild' images can only be access per tile"}; + "'tili' images can only be access per tile"}; } } @@ -716,21 +795,23 @@ Error ImageItem_Tiled::append_compressed_tile_data(std::vector& data, u } -Result> -ImageItem_Tiled::decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty) const +Result +ImageItem_Tiled::get_compressed_data_for_tile(uint32_t tx, uint32_t ty) const { - heif_compression_format format = compression_format_from_fourcc_infe_type( - m_tild_header.get_parameters().compression_format_fourcc); - // --- get compressed data - Result> dataResult = read_bitstream_configuration_data_override(get_id(), format); + Error err = m_tile_item->init_decoder_from_item(0); + if (err) { + return err; + } + + Result> dataResult = m_tile_item->read_bitstream_configuration_data(); if (dataResult.error) { return dataResult.error; } std::vector& data = dataResult.value; - Error err = append_compressed_tile_data(data, tx, ty); + err = append_compressed_tile_data(data, tx, ty); if (err) { return err; } @@ -740,7 +821,19 @@ ImageItem_Tiled::decode_grid_tile(const heif_decoding_options& options, uint32_t DataExtent extent; extent.m_raw = data; - m_tile_decoder->set_data_extent(std::move(extent)); + return extent; +} + + +Result> +ImageItem_Tiled::decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty) const +{ + Result extentResult = get_compressed_data_for_tile(tx, ty); + if (extentResult.error) { + return extentResult.error; + } + + m_tile_decoder->set_data_extent(std::move(*extentResult)); return m_tile_decoder->decode_single_frame_from_compressed_data(options); } @@ -785,6 +878,15 @@ void ImageItem_Tiled::get_tile_size(uint32_t& w, uint32_t& h) const Error ImageItem_Tiled::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const { + uint32_t tx=0, ty=0; // TODO: find a tile that is defined. + + Result extentResult = get_compressed_data_for_tile(tx, ty); + if (extentResult.error) { + return extentResult.error; + } + + m_tile_decoder->set_data_extent(std::move(*extentResult)); + Error err = m_tile_decoder->get_coded_image_colorspace(out_colorspace, out_chroma); if (err) { return err; diff --git a/libheif/image-items/tiled.h b/libheif/image-items/tiled.h index 0909305e29..4ff32afa89 100644 --- a/libheif/image-items/tiled.h +++ b/libheif/image-items/tiled.h @@ -23,6 +23,7 @@ #include "image_item.h" +#include "codecs/decoder.h" #include "box.h" #include #include @@ -69,6 +70,10 @@ class Box_tilC : public FullBox std::string dump(Indent&) const override; + std::vector>& get_tile_properties() { return m_children; } + + const std::vector>& get_tile_properties() const { return m_children; } + protected: Error parse(BitstreamRange& range, const heif_security_limits* limits) override; @@ -198,8 +203,12 @@ class ImageItem_Tiled : public ImageItem uint32_t mReadChunkSize_bytes = 64*1024; // 64 kiB bool m_preload_offset_table = false; + std::shared_ptr m_tile_item; std::shared_ptr m_tile_decoder; + Result + get_compressed_data_for_tile(uint32_t tx, uint32_t ty) const; + Result> decode_grid_tile(const heif_decoding_options& options, uint32_t tx, uint32_t ty) const; Error load_tile_offset_entry(uint32_t idx); diff --git a/libheif/image-items/unc_image.cc b/libheif/image-items/unc_image.cc index 2bba6342bc..98adbd5a11 100644 --- a/libheif/image-items/unc_image.cc +++ b/libheif/image-items/unc_image.cc @@ -336,19 +336,19 @@ Result> ImageItem_uncompressed::add_unci assert(headers.uncC); if (headers.uncC) { - file->add_property(unci_id, headers.uncC, true); + unci_image->add_property(headers.uncC, true); } if (headers.cmpd) { - file->add_property(unci_id, headers.cmpd, true); + unci_image->add_property(headers.cmpd, true); } // Add `ispe` property - file->add_ispe_property(unci_id, - static_cast(parameters->image_width), - static_cast(parameters->image_height), - true); + auto ispe = std::make_shared(); + ispe->set_size(static_cast(parameters->image_width), + static_cast(parameters->image_height)); + unci_image->add_property(ispe, true); if (parameters->compression != heif_unci_compression_off) { auto icef = std::make_shared(); @@ -374,8 +374,8 @@ Result> ImageItem_uncompressed::add_unci assert(false); } - file->add_property(unci_id, cmpC, true); - file->add_property_without_deduplication(unci_id, icef, true); // icef is empty. A normal add_property() would lead to a wrong deduplication. + unci_image->add_property(cmpC, true); + unci_image->add_property_without_deduplication(icef, true); // icef is empty. A normal add_property() would lead to a wrong deduplication. } // Create empty image. If we use compression, we append the data piece by piece. @@ -404,7 +404,7 @@ Result> ImageItem_uncompressed::add_unci Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, const std::shared_ptr& image) { - std::shared_ptr uncC = get_file()->get_property(get_id()); + std::shared_ptr uncC = get_property(); assert(uncC); uint32_t tile_width = image->get_width(); @@ -417,8 +417,8 @@ Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, c return codedBitstreamResult.error; } - std::shared_ptr cmpC = get_file()->get_property(get_id()); - std::shared_ptr icef = get_file()->get_property(get_id()); + std::shared_ptr cmpC = get_property(); + std::shared_ptr icef = get_property(); if (!icef || !cmpC) { assert(!icef); @@ -471,8 +471,8 @@ Error ImageItem_uncompressed::add_image_tile(uint32_t tile_x, uint32_t tile_y, c void ImageItem_uncompressed::get_tile_size(uint32_t& w, uint32_t& h) const { - auto ispe = get_file()->get_property(get_id()); - auto uncC = get_file()->get_property(get_id()); + auto ispe = get_property(); + auto uncC = get_property(); if (!ispe || !uncC) { w = h = 0; @@ -488,8 +488,8 @@ heif_image_tiling ImageItem_uncompressed::get_heif_image_tiling() const { heif_image_tiling tiling{}; - auto ispe = get_file()->get_property(get_id()); - auto uncC = get_file()->get_property(get_id()); + auto ispe = get_property(); + auto uncC = get_property(); assert(ispe && uncC); tiling.num_columns = uncC->get_number_of_tile_columns(); @@ -512,8 +512,8 @@ std::shared_ptr ImageItem_uncompressed::get_decoder() const Error ImageItem_uncompressed::on_load_file() { - std::shared_ptr cmpd = get_file()->get_property(get_id()); - std::shared_ptr uncC = get_file()->get_property(get_id()); + std::shared_ptr cmpd = get_property(); + std::shared_ptr uncC = get_property(); if (!uncC) { return Error{heif_error_Invalid_input, diff --git a/libheif/image-items/vvc.cc b/libheif/image-items/vvc.cc index 81be35d674..6377c26700 100644 --- a/libheif/image-items/vvc.cc +++ b/libheif/image-items/vvc.cc @@ -94,11 +94,11 @@ Result ImageItem_VVC::encode(const std::shared_ptr> ImageItem_VVC::read_bitstream_configuration_data(heif_item_id itemId) const +Result> ImageItem_VVC::read_bitstream_configuration_data() const { // --- get codec configuration - std::shared_ptr vvcC_box = get_file()->get_property(itemId); + std::shared_ptr vvcC_box = get_property(); if (!vvcC_box) { assert(false); @@ -124,7 +124,7 @@ std::shared_ptr ImageItem_VVC::get_decoder() const Error ImageItem_VVC::on_load_file() { - auto vvcC_box = get_file()->get_property(get_id()); + auto vvcC_box = get_property(); if (!vvcC_box) { return Error{heif_error_Invalid_input, heif_suberror_No_av1C_box}; diff --git a/libheif/image-items/vvc.h b/libheif/image-items/vvc.h index 384e7e9507..9a9a052021 100644 --- a/libheif/image-items/vvc.h +++ b/libheif/image-items/vvc.h @@ -53,7 +53,7 @@ class ImageItem_VVC : public ImageItem protected: std::shared_ptr get_decoder() const override; - Result> read_bitstream_configuration_data(heif_item_id itemId) const override; + Result> read_bitstream_configuration_data() const override; private: std::shared_ptr m_decoder; diff --git a/libheif/mini.cc b/libheif/mini.cc index cf49843503..8e53c363f2 100644 --- a/libheif/mini.cc +++ b/libheif/mini.cc @@ -19,12 +19,17 @@ */ #include "mini.h" +#include "file.h" +#include "codecs/avif_boxes.h" +#include "codecs/hevc_boxes.h" #include #include #include #include #include +#include + Error Box_mini::parse(BitstreamRange &range, const heif_security_limits *limits) { @@ -736,3 +741,292 @@ std::string Box_mini::dump(Indent &indent) const } return sstr.str(); } + + +static uint32_t get_item_type_for_brand(const heif_brand2 brand) +{ + switch(brand) { + case heif_brand2_avif: + return fourcc("av01"); + case heif_brand2_heic: + return fourcc("hvc1"); + default: + return 0; + } +} + + +Error Box_mini::create_expanded_boxes(class HeifFile* file) +{ + file->init_meta_box(); + + auto hdlr_box = std::make_shared(); + hdlr_box->set_handler_type(fourcc("pict")); + file->set_hdlr_box(hdlr_box); + + file->set_primary_item_id(1); + + std::shared_ptr primary_infe_box = std::make_shared(); + primary_infe_box->set_version(2); + primary_infe_box->set_item_ID(1); + + // TODO: check explicit codec flag + uint32_t minor_version = file->get_ftyp_box()->get_minor_version(); + heif_brand2 mini_brand = minor_version; + uint32_t infe_type = get_item_type_for_brand(mini_brand); + if (infe_type == 0) { + // not found + std::stringstream sstr; + sstr << "Minimised file requires brand " << fourcc_to_string(mini_brand) << " but this is not yet supported."; + return Error(heif_error_Unsupported_filetype, + heif_suberror_Unspecified, + sstr.str()); + } + primary_infe_box->set_item_type_4cc(infe_type); + file->add_infe_box(1, primary_infe_box); + + if (get_alpha_item_data_size() != 0) { + std::shared_ptr alpha_infe_box = std::make_shared(); + alpha_infe_box->set_version(2); + alpha_infe_box->set_flags(1); + alpha_infe_box->set_item_ID(2); + alpha_infe_box->set_item_type_4cc(infe_type); + file->add_infe_box(2, alpha_infe_box); + } + + if (get_exif_flag()) { + std::shared_ptr exif_infe_box = std::make_shared(); + exif_infe_box->set_version(2); + exif_infe_box->set_flags(1); + exif_infe_box->set_item_ID(6); + exif_infe_box->set_item_type_4cc(fourcc("Exif")); + file->add_infe_box(6, exif_infe_box); + } + + if (get_xmp_flag()) { + std::shared_ptr xmp_infe_box = std::make_shared(); + xmp_infe_box->set_version(2); + xmp_infe_box->set_flags(1); + xmp_infe_box->set_item_ID(7); + xmp_infe_box->set_item_type_4cc(fourcc("mime")); + xmp_infe_box->set_content_type("application/rdf+xml"); + file->add_infe_box(7, xmp_infe_box); + } + + auto ipco_box = std::make_shared(); + file->set_ipco_box(ipco_box); + + if (get_main_item_codec_config().size() != 0) { + std::shared_ptr istr = std::make_shared( + get_main_item_codec_config().data(), + get_main_item_codec_config().size(), + false + ); + BitstreamRange codec_range(istr, get_main_item_codec_config().size(), nullptr); + + std::shared_ptr main_item_codec_prop; + if (infe_type == fourcc("av01")) { + std::shared_ptr codec_prop = std::make_shared(); + codec_prop->parse(codec_range, heif_get_global_security_limits()); + main_item_codec_prop = std::move(codec_prop); + } else if (infe_type == fourcc("hvc1")) { + std::shared_ptr codec_prop = std::make_shared(); + codec_prop->parse(codec_range, heif_get_global_security_limits()); + main_item_codec_prop = std::move(codec_prop); + } else { + // not found + std::stringstream sstr; + sstr << "Minimised file requires infe support for " << fourcc_to_string(infe_type) << " but this is not yet supported."; + return Error(heif_error_Unsupported_filetype, + heif_suberror_Unspecified, + sstr.str()); + } + ipco_box->append_child_box(main_item_codec_prop); // entry 1 + } else { + ipco_box->append_child_box(std::make_shared()); // placeholder for entry 1 + } + + std::shared_ptr ispe = std::make_shared(); + ispe->set_size(get_width(), get_height()); + ipco_box->append_child_box(ispe); // entry 2 + + std::shared_ptr pixi = std::make_shared(); + pixi->set_version(0); + // pixi->set_version(1); // TODO: when we support version 1 + // TODO: there is more when we do version 1, and anything other than RGB + pixi->add_channel_bits(get_bit_depth()); + pixi->add_channel_bits(get_bit_depth()); + pixi->add_channel_bits(get_bit_depth()); + ipco_box->append_child_box(pixi); // entry 3 + + std::shared_ptr colr = std::make_shared(); + std::shared_ptr nclx = std::make_shared(); + nclx->set_colour_primaries(get_colour_primaries()); + nclx->set_transfer_characteristics(get_transfer_characteristics()); + nclx->set_matrix_coefficients(get_matrix_coefficients()); + nclx->set_full_range_flag(get_full_range_flag()); + colr->set_color_profile(nclx); + ipco_box->append_child_box(colr); // entry 4 + + if (get_icc_flag()) { + std::shared_ptr colr_icc = std::make_shared(); + std::shared_ptr icc = std::make_shared(fourcc("prof"), get_icc_data()); + colr_icc->set_color_profile(icc); + ipco_box->append_child_box(colr_icc); // entry 5 + } else { + ipco_box->append_child_box(std::make_shared()); // placeholder for entry 5 + } + + if (get_alpha_item_codec_config().size() != 0) { + std::shared_ptr istr = std::make_shared( + get_alpha_item_codec_config().data(), + get_alpha_item_codec_config().size(), + false + ); + BitstreamRange alpha_codec_range(istr, get_alpha_item_codec_config().size(), nullptr); + std::shared_ptr alpha_item_codec_prop; + if (infe_type == fourcc("av01")) { + std::shared_ptr codec_prop = std::make_shared(); + codec_prop->parse(alpha_codec_range, heif_get_global_security_limits()); + alpha_item_codec_prop = std::move(codec_prop); + } else if (infe_type == fourcc("hvc1")) { + std::shared_ptr codec_prop = std::make_shared(); + codec_prop->parse(alpha_codec_range, heif_get_global_security_limits()); + alpha_item_codec_prop = std::move(codec_prop); + } else { + // not found + std::stringstream sstr; + sstr << "Minimised file requires infe support for " << fourcc_to_string(infe_type) << " but this is not yet supported."; + return Error(heif_error_Unsupported_filetype, + heif_suberror_Unspecified, + sstr.str()); + } + ipco_box->append_child_box(alpha_item_codec_prop); // entry 6 + } else { + ipco_box->append_child_box(std::make_shared()); // placeholder for entry 6 + } + + if (get_alpha_item_data_size() != 0) { + std::shared_ptr aux_type = std::make_shared(); + aux_type->set_aux_type("urn:mpeg:mpegB:cicp:systems:auxiliary:alpha"); + ipco_box->append_child_box(aux_type); // entry 7 + } else { + ipco_box->append_child_box(std::make_shared()); // placeholder for entry 7 + } + + // TODO: replace this placeholder with pixi box version 1 once that is supported + ipco_box->append_child_box(std::make_shared()); // placeholder for entry 8 + + if (get_orientation() == 2) { + std::shared_ptr irot = std::make_shared(); + irot->set_rotation_ccw(2 * 90); + ipco_box->append_child_box(irot); // entry 9 + } else if ((get_orientation() == 4) || (get_orientation() == 6) || (get_orientation() == 7)) { + std::shared_ptr irot = std::make_shared(); + irot->set_rotation_ccw(1 * 90); + ipco_box->append_child_box(irot); // entry 9 + } else if (get_orientation() == 5) { + std::shared_ptr irot = std::make_shared(); + irot->set_rotation_ccw(3 * 90); + ipco_box->append_child_box(irot); // entry 9 + } else { + ipco_box->append_child_box(std::make_shared()); // placeholder for entry 9 + } + + if ((get_orientation() == 1) || (get_orientation() == 6)) { + std::shared_ptr imir = std::make_shared(); + imir->set_mirror_direction(heif_transform_mirror_direction_horizontal); + ipco_box->append_child_box(imir); // entry 10 + } else if ((get_orientation() == 3) || (get_orientation() == 4)) { + std::shared_ptr imir = std::make_shared(); + imir->set_mirror_direction(heif_transform_mirror_direction_vertical); + ipco_box->append_child_box(imir); // entry 10 + } else { + ipco_box->append_child_box(std::make_shared()); // placeholder for entry 10 + } + + auto ipma_box = std::make_shared(); + file->set_ipma_box(ipma_box); + ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(1)}); + ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{false, uint16_t(2)}); + ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{false, uint16_t(3)}); + ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(4)}); + ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(5)}); + ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(9)}); + ipma_box->add_property_for_item_ID(1, Box_ipma::PropertyAssociation{true, uint16_t(10)}); + + if (get_alpha_item_data_size() != 0) { + ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(6)}); + ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{false, uint16_t(2)}); + ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(7)}); + ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{false, uint16_t(8)}); + ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(9)}); + ipma_box->add_property_for_item_ID(2, Box_ipma::PropertyAssociation{true, uint16_t(10)}); + } + // TODO: will need more once we support HDR / gainmap representation + + auto iloc_box = std::make_shared(); + file->set_iloc_box(iloc_box); + Box_iloc::Item main_item; + main_item.item_ID = 1; + main_item.construction_method = 0; + main_item.base_offset = 0; + main_item.data_reference_index = 0; + Box_iloc::Extent main_item_extent; + main_item_extent.offset = get_main_item_data_offset(); + main_item_extent.length = get_main_item_data_size(); + main_item.extents.push_back(main_item_extent); + iloc_box->append_item(main_item); + + if (get_alpha_item_data_size() != 0) { + Box_iloc::Item alpha_item; + alpha_item.item_ID = 2; + alpha_item.base_offset = 0; + alpha_item.data_reference_index = 0; + Box_iloc::Extent alpha_item_extent; + alpha_item_extent.offset = get_alpha_item_data_offset(); + alpha_item_extent.length = get_alpha_item_data_size(); + alpha_item.extents.push_back(alpha_item_extent); + iloc_box->append_item(alpha_item); + } + if (get_exif_flag()) { + Box_iloc::Item exif_item; + exif_item.item_ID = 6; + exif_item.base_offset = 0; + exif_item.data_reference_index = 0; + Box_iloc::Extent exif_item_extent; + exif_item_extent.offset = get_exif_item_data_offset(); + exif_item_extent.length = get_exif_item_data_size(); + exif_item.extents.push_back(exif_item_extent); + iloc_box->append_item(exif_item); + } + if (get_xmp_flag()) { + Box_iloc::Item xmp_item; + xmp_item.item_ID = 7; + xmp_item.base_offset = 0; + xmp_item.data_reference_index = 0; + Box_iloc::Extent xmp_item_extent; + xmp_item_extent.offset = get_xmp_item_data_offset(); + xmp_item_extent.length = get_xmp_item_data_size(); + xmp_item.extents.push_back(xmp_item_extent); + iloc_box->append_item(xmp_item); + } + + auto iref_box = std::make_shared(); + file->set_iref_box(iref_box); + std::vector to_items = {1}; + if (get_alpha_item_data_size() != 0) { + iref_box->add_references(2, fourcc("auxl"), to_items); + } + // TODO: if alpha prem + // TODO: if gainmap flag && item 4 + // TODO: if gainmap flag && !item 4 + if (get_exif_flag()) { + iref_box->add_references(6, fourcc("cdsc"), to_items); + } + if (get_xmp_flag()) { + iref_box->add_references(7, fourcc("cdsc"), to_items); + } + + return Error::Ok; +} diff --git a/libheif/mini.h b/libheif/mini.h index 49872b9c7a..2358246a23 100644 --- a/libheif/mini.h +++ b/libheif/mini.h @@ -36,6 +36,8 @@ class Box_mini : public Box set_short_type(fourcc("mini")); } + Error create_expanded_boxes(class HeifFile* file); + bool get_icc_flag() const { return m_icc_flag; } bool get_exif_flag() const { return m_exif_flag; } bool get_xmp_flag() const { return m_xmp_flag; } @@ -45,6 +47,8 @@ class Box_mini : public Box uint8_t get_bit_depth() const { return m_bit_depth; } + uint8_t get_orientation() const { return m_orientation; } + std::vector get_main_item_codec_config() const { return m_main_item_codec_config; } std::vector get_alpha_item_codec_config() const { return m_alpha_item_codec_config; } std::vector get_icc_data() const { return m_icc_data; } diff --git a/libheif/pixelimage.cc b/libheif/pixelimage.cc index ee14ff0191..eac54113d7 100644 --- a/libheif/pixelimage.cc +++ b/libheif/pixelimage.cc @@ -189,7 +189,8 @@ static uint32_t rounded_size(uint32_t s) return s; } -bool HeifPixelImage::add_plane(heif_channel channel, uint32_t width, uint32_t height, int bit_depth) +Error HeifPixelImage::add_plane(heif_channel channel, uint32_t width, uint32_t height, int bit_depth, + const heif_security_limits* limits) { assert(!has_channel(channel)); @@ -206,31 +207,33 @@ bool HeifPixelImage::add_plane(heif_channel channel, uint32_t width, uint32_t he bit_depth = 8; } - if (plane.alloc(width, height, heif_channel_datatype_unsigned_integer, bit_depth, num_interleaved_pixels)) { - m_planes.insert(std::make_pair(channel, plane)); - return true; + if (auto err = plane.alloc(width, height, heif_channel_datatype_unsigned_integer, bit_depth, num_interleaved_pixels, limits)) { + return err; } else { - return false; + m_planes.insert(std::make_pair(channel, plane)); + return Error::Ok; } } -bool HeifPixelImage::add_channel(heif_channel channel, uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth) +Error HeifPixelImage::add_channel(heif_channel channel, uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth, + const heif_security_limits* limits) { ImagePlane plane; - if (plane.alloc(width, height, datatype, bit_depth, 1)) { - m_planes.insert(std::make_pair(channel, plane)); - return true; + if (Error err = plane.alloc(width, height, datatype, bit_depth, 1, limits)) { + return err; } else { - return false; + m_planes.insert(std::make_pair(channel, plane)); + return Error::Ok; } } -bool HeifPixelImage::ImagePlane::alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth, - int num_interleaved_components) // heif_chroma chroma) +Error HeifPixelImage::ImagePlane::alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth, + int num_interleaved_components, + const heif_security_limits* limits) { assert(bit_depth >= 1); assert(bit_depth <= 128); @@ -257,8 +260,19 @@ bool HeifPixelImage::ImagePlane::alloc(uint32_t width, uint32_t height, heif_cha stride = m_mem_width * bytes_per_pixel; stride = (stride + alignment - 1U) & ~(alignment - 1U); - if ((heif_get_global_security_limits()->max_memory_block_size - (alignment + 1)) / stride < m_mem_height) { - return false; + assert(alignment>=1); + + if (limits && + limits->max_memory_block_size && + (limits->max_memory_block_size < alignment - 1U || + (limits->max_memory_block_size - (alignment - 1U)) / stride < m_mem_height)) { + std::stringstream sstr; + sstr << "Allocating " << static_cast(m_mem_height) * stride + alignment - 1 << " exceeds the security limit of " + << limits->max_memory_block_size << " bytes"; + + return {heif_error_Memory_allocation_error, + heif_suberror_Security_limit_exceeded, + sstr.str()}; } try { @@ -275,15 +289,21 @@ bool HeifPixelImage::ImagePlane::alloc(uint32_t width, uint32_t height, heif_cha mem = mem_8; - return true; + return Error::Ok; } catch (const std::bad_alloc& excpt) { - return false; + std::stringstream sstr; + sstr << "Allocating " << static_cast(m_mem_height) * stride + alignment - 1 << " bytes failed"; + + return {heif_error_Memory_allocation_error, + heif_suberror_Unspecified, + sstr.str()}; } } -bool HeifPixelImage::extend_padding_to_size(uint32_t width, uint32_t height, bool adjust_size) +Error HeifPixelImage::extend_padding_to_size(uint32_t width, uint32_t height, bool adjust_size, + const heif_security_limits* limits) { for (auto& planeIter : m_planes) { auto* plane = &planeIter.second; @@ -301,8 +321,16 @@ bool HeifPixelImage::extend_padding_to_size(uint32_t width, uint32_t height, boo plane->m_mem_height < subsampled_height) { ImagePlane newPlane; - if (!newPlane.alloc(subsampled_width, subsampled_height, plane->m_datatype, plane->m_bit_depth, num_interleaved_pixels_per_plane(m_chroma))) { - return false; + if (auto err = newPlane.alloc(subsampled_width, subsampled_height, plane->m_datatype, plane->m_bit_depth, + num_interleaved_pixels_per_plane(m_chroma), + limits)) + { + return err; + } + + // This is not needed, but we have to silence the clang-tidy false positive. + if (!newPlane.mem) { + return Error::InternalError; } // copy the visible part of the old plane into the new plane @@ -349,11 +377,11 @@ bool HeifPixelImage::extend_padding_to_size(uint32_t width, uint32_t height, boo m_height = height; } - return true; + return Error::Ok; } -bool HeifPixelImage::extend_to_size_with_zero(uint32_t width, uint32_t height) +Error HeifPixelImage::extend_to_size_with_zero(uint32_t width, uint32_t height, const heif_security_limits* limits) { for (auto& planeIter : m_planes) { auto* plane = &planeIter.second; @@ -371,8 +399,13 @@ bool HeifPixelImage::extend_to_size_with_zero(uint32_t width, uint32_t height) plane->m_mem_height < subsampled_height) { ImagePlane newPlane; - if (!newPlane.alloc(subsampled_width, subsampled_height, plane->m_datatype, plane->m_bit_depth, num_interleaved_pixels_per_plane(m_chroma))) { - return false; + if (auto err = newPlane.alloc(subsampled_width, subsampled_height, plane->m_datatype, plane->m_bit_depth, num_interleaved_pixels_per_plane(m_chroma), limits)) { + return err; + } + + // This is not needed, but we have to silence the clang-tidy false positive. + if (!newPlane.mem) { + return Error::InternalError; } // copy the visible part of the old plane into the new plane @@ -413,7 +446,7 @@ bool HeifPixelImage::extend_to_size_with_zero(uint32_t width, uint32_t height) m_width = width; m_height = height; - return true; + return Error::Ok; } bool HeifPixelImage::has_channel(heif_channel channel) const @@ -525,9 +558,10 @@ int HeifPixelImage::get_number_of_interleaved_components(heif_channel channel) c } -void HeifPixelImage::copy_new_plane_from(const std::shared_ptr& src_image, - heif_channel src_channel, - heif_channel dst_channel) +Error HeifPixelImage::copy_new_plane_from(const std::shared_ptr& src_image, + heif_channel src_channel, + heif_channel dst_channel, + const heif_security_limits* limits) { assert(src_image->has_channel(src_channel)); assert(!has_channel(dst_channel)); @@ -539,9 +573,12 @@ void HeifPixelImage::copy_new_plane_from(const std::shared_ptrm_planes.end()); const auto& src_plane = src_plane_iter->second; - add_channel(dst_channel, width, height, - src_plane.m_datatype, - src_image->get_bits_per_pixel(src_channel)); + auto err = add_channel(dst_channel, width, height, + src_plane.m_datatype, + src_image->get_bits_per_pixel(src_channel), limits); + if (err) { + return err; + } uint8_t* dst; uint32_t dst_stride = 0; @@ -557,15 +594,20 @@ void HeifPixelImage::copy_new_plane_from(const std::shared_ptr& src_image) +Error HeifPixelImage::extract_alpha_from_RGBA(const std::shared_ptr& src_image, + const heif_security_limits* limits) { uint32_t width = src_image->get_width(); uint32_t height = src_image->get_height(); - add_plane(heif_channel_Y, width, height, src_image->get_bits_per_pixel(heif_channel_interleaved)); + if (Error err = add_plane(heif_channel_Y, width, height, src_image->get_bits_per_pixel(heif_channel_interleaved), limits)) { + return err; + } uint8_t* dst; uint32_t dst_stride = 0; @@ -583,13 +625,21 @@ void HeifPixelImage::extract_alpha_from_RGBA(const std::shared_ptr& } -Result> HeifPixelImage::rotate_ccw(int angle_degrees) +Result> HeifPixelImage::rotate_ccw(int angle_degrees, const heif_security_limits* limits) { // --- for some subsampled chroma colorspaces, we have to transform to 4:4:4 before rotation @@ -744,8 +794,12 @@ Result> HeifPixelImage::rotate_ccw(int angle_deg heif_color_conversion_options options{}; heif_color_conversion_options_set_defaults(&options); - auto converted_image = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options); - return converted_image->rotate_ccw(angle_degrees); + auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options, limits); + if (converted_image_result.error) { + return converted_image_result.error; + } + + return (*converted_image_result)->rotate_ccw(angle_degrees, limits); } @@ -787,7 +841,10 @@ Result> HeifPixelImage::rotate_ccw(int angle_deg std::swap(out_plane_width, out_plane_height); } - out_img->add_channel(channel, out_plane_width, out_plane_height, plane.m_datatype, plane.m_bit_depth); + Error err = out_img->add_channel(channel, out_plane_width, out_plane_height, plane.m_datatype, plane.m_bit_depth, limits); + if (err) { + return err; + } auto out_plane_iter = out_img->m_planes.find(channel); assert(out_plane_iter != out_img->m_planes.end()); @@ -871,7 +928,8 @@ void HeifPixelImage::ImagePlane::mirror_inplace(heif_transform_mirror_direction } -Result> HeifPixelImage::mirror_inplace(heif_transform_mirror_direction direction) +Result> HeifPixelImage::mirror_inplace(heif_transform_mirror_direction direction, + const heif_security_limits* limits) { // --- for some subsampled chroma colorspaces, we have to transform to 4:4:4 before rotation @@ -892,8 +950,12 @@ Result> HeifPixelImage::mirror_inplace(heif_tran heif_color_conversion_options options{}; heif_color_conversion_options_set_defaults(&options); - auto converted_image = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options); - return converted_image->mirror_inplace(direction); + auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options, limits); + if (converted_image_result.error) { + return converted_image_result.error; + } + + return (*converted_image_result)->mirror_inplace(direction, limits); } @@ -949,7 +1011,8 @@ int HeifPixelImage::ImagePlane::get_bytes_per_pixel() const } -Result> HeifPixelImage::crop(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) const +Result> HeifPixelImage::crop(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom, + const heif_security_limits* limits) const { // --- for some subsampled chroma colorspaces, we have to transform to 4:4:4 before rotation @@ -967,8 +1030,12 @@ Result> HeifPixelImage::crop(uint32_t left, uint heif_color_conversion_options options{}; heif_color_conversion_options_set_defaults(&options); - auto converted_image = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options); - return converted_image->crop(left, right, top, bottom); + auto converted_image_result = convert_colorspace(shared_from_this(), heif_colorspace_YCbCr, heif_chroma_444, nullptr, get_bits_per_pixel(heif_channel_Y), options, limits); + if (converted_image_result.error) { + return converted_image_result.error; + } + + return (*converted_image_result)->crop(left, right, top, bottom, limits); } @@ -991,11 +1058,15 @@ Result> HeifPixelImage::crop(uint32_t left, uint uint32_t plane_top = top * h / m_height; uint32_t plane_bottom = bottom * h / m_height; - out_img->add_channel(channel, - plane_right - plane_left + 1, - plane_bottom - plane_top + 1, - plane.m_datatype, - plane.m_bit_depth); + auto err = out_img->add_channel(channel, + plane_right - plane_left + 1, + plane_bottom - plane_top + 1, + plane.m_datatype, + plane.m_bit_depth, + limits); + if (err) { + return err; + } auto out_plane_iter = out_img->m_planes.find(channel); assert(out_plane_iter != out_img->m_planes.end()); @@ -1241,7 +1312,8 @@ Error HeifPixelImage::overlay(std::shared_ptr& overlay, int32_t Error HeifPixelImage::scale_nearest_neighbor(std::shared_ptr& out_img, - uint32_t width, uint32_t height) const + uint32_t width, uint32_t height, + const heif_security_limits* limits) const { out_img = std::make_shared(); out_img->create(width, height, m_colorspace, m_chroma); @@ -1250,7 +1322,9 @@ Error HeifPixelImage::scale_nearest_neighbor(std::shared_ptr& ou // --- create output image with scaled planes if (has_channel(heif_channel_interleaved)) { - out_img->add_plane(heif_channel_interleaved, width, height, get_bits_per_pixel(heif_channel_interleaved)); + if (auto err = out_img->add_plane(heif_channel_interleaved, width, height, get_bits_per_pixel(heif_channel_interleaved), limits)) { + return err; + } } else { if (get_colorspace() == heif_colorspace_RGB) { @@ -1260,16 +1334,24 @@ Error HeifPixelImage::scale_nearest_neighbor(std::shared_ptr& ou return {heif_error_Invalid_input, heif_suberror_Unspecified, "RGB input without R,G,B, planes"}; } - out_img->add_plane(heif_channel_R, width, height, get_bits_per_pixel(heif_channel_R)); - out_img->add_plane(heif_channel_G, width, height, get_bits_per_pixel(heif_channel_G)); - out_img->add_plane(heif_channel_B, width, height, get_bits_per_pixel(heif_channel_B)); + if (auto err = out_img->add_plane(heif_channel_R, width, height, get_bits_per_pixel(heif_channel_R), limits)) { + return err; + } + if (auto err = out_img->add_plane(heif_channel_G, width, height, get_bits_per_pixel(heif_channel_G), limits)) { + return err; + } + if (auto err = out_img->add_plane(heif_channel_B, width, height, get_bits_per_pixel(heif_channel_B), limits)) { + return err; + } } else if (get_colorspace() == heif_colorspace_monochrome) { if (!has_channel(heif_channel_Y)) { return {heif_error_Invalid_input, heif_suberror_Unspecified, "monochrome input with no Y plane"}; } - out_img->add_plane(heif_channel_Y, width, height, get_bits_per_pixel(heif_channel_Y)); + if (auto err = out_img->add_plane(heif_channel_Y, width, height, get_bits_per_pixel(heif_channel_Y), limits)) { + return err; + } } else if (get_colorspace() == heif_colorspace_YCbCr) { if (!has_channel(heif_channel_Y) || @@ -1280,16 +1362,24 @@ Error HeifPixelImage::scale_nearest_neighbor(std::shared_ptr& ou uint32_t cw, ch; get_subsampled_size(width, height, heif_channel_Cb, get_chroma_format(), &cw, &ch); - out_img->add_plane(heif_channel_Y, width, height, get_bits_per_pixel(heif_channel_Y)); - out_img->add_plane(heif_channel_Cb, cw, ch, get_bits_per_pixel(heif_channel_Cb)); - out_img->add_plane(heif_channel_Cr, cw, ch, get_bits_per_pixel(heif_channel_Cr)); + if (auto err = out_img->add_plane(heif_channel_Y, width, height, get_bits_per_pixel(heif_channel_Y), limits)) { + return err; + } + if (auto err = out_img->add_plane(heif_channel_Cb, cw, ch, get_bits_per_pixel(heif_channel_Cb), limits)) { + return err; + } + if (auto err = out_img->add_plane(heif_channel_Cr, cw, ch, get_bits_per_pixel(heif_channel_Cr), limits)) { + return err; + } } else { return {heif_error_Invalid_input, heif_suberror_Unspecified, "unknown color configuration"}; } if (has_channel(heif_channel_Alpha)) { - out_img->add_plane(heif_channel_Alpha, width, height, get_bits_per_pixel(heif_channel_Alpha)); + if (auto err = out_img->add_plane(heif_channel_Alpha, width, height, get_bits_per_pixel(heif_channel_Alpha), limits)) { + return err; + } } } @@ -1365,7 +1455,8 @@ void HeifPixelImage::debug_dump() const } } -void HeifPixelImage::create_clone_image_at_new_size(const std::shared_ptr& source, uint32_t w, uint32_t h) +Error HeifPixelImage::create_clone_image_at_new_size(const std::shared_ptr& source, uint32_t w, uint32_t h, + const heif_security_limits* limits) { heif_colorspace colorspace = source->get_colorspace(); heif_chroma chroma = source->get_chroma_format(); @@ -1374,17 +1465,31 @@ void HeifPixelImage::create_clone_image_at_new_size(const std::shared_ptrget_bits_per_pixel(heif_channel_Y)); + if (auto err = add_plane(heif_channel_Y, w, h, source->get_bits_per_pixel(heif_channel_Y), limits)) { + return err; + } break; case heif_colorspace_YCbCr: - add_plane(heif_channel_Y, w, h, source->get_bits_per_pixel(heif_channel_Y)); - add_plane(heif_channel_Cb, chroma_width(w, chroma), chroma_height(h, chroma), source->get_bits_per_pixel(heif_channel_Cb)); - add_plane(heif_channel_Cr, chroma_width(w, chroma), chroma_height(h, chroma), source->get_bits_per_pixel(heif_channel_Cr)); + if (auto err = add_plane(heif_channel_Y, w, h, source->get_bits_per_pixel(heif_channel_Y), limits)) { + return err; + } + if (auto err = add_plane(heif_channel_Cb, chroma_width(w, chroma), chroma_height(h, chroma), source->get_bits_per_pixel(heif_channel_Cb), limits)) { + return err; + } + if (auto err = add_plane(heif_channel_Cr, chroma_width(w, chroma), chroma_height(h, chroma), source->get_bits_per_pixel(heif_channel_Cr), limits)) { + return err; + } break; case heif_colorspace_RGB: - add_plane(heif_channel_R, w, h, source->get_bits_per_pixel(heif_channel_R)); - add_plane(heif_channel_G, w, h, source->get_bits_per_pixel(heif_channel_G)); - add_plane(heif_channel_B, w, h, source->get_bits_per_pixel(heif_channel_B)); + if (auto err = add_plane(heif_channel_R, w, h, source->get_bits_per_pixel(heif_channel_R), limits)) { + return err; + } + if (auto err = add_plane(heif_channel_G, w, h, source->get_bits_per_pixel(heif_channel_G), limits)) { + return err; + } + if (auto err = add_plane(heif_channel_B, w, h, source->get_bits_per_pixel(heif_channel_B), limits)) { + return err; + } break; default: assert(false); @@ -1392,6 +1497,10 @@ void HeifPixelImage::create_clone_image_at_new_size(const std::shared_ptrhas_alpha()) { - add_plane(heif_channel_Alpha, w, h, source->get_bits_per_pixel(heif_channel_Alpha)); + if (auto err = add_plane(heif_channel_Alpha, w, h, source->get_bits_per_pixel(heif_channel_Alpha), limits)) { + return err; + } } + + return Error::Ok; } diff --git a/libheif/pixelimage.h b/libheif/pixelimage.h index 0b24b852f9..f30729cb74 100644 --- a/libheif/pixelimage.h +++ b/libheif/pixelimage.h @@ -81,11 +81,13 @@ class HeifPixelImage : public std::enable_shared_from_this, void create(uint32_t width, uint32_t height, heif_colorspace colorspace, heif_chroma chroma); - void create_clone_image_at_new_size(const std::shared_ptr& source, uint32_t w, uint32_t h); + Error create_clone_image_at_new_size(const std::shared_ptr& source, uint32_t w, uint32_t h, + const heif_security_limits* limits); - bool add_plane(heif_channel channel, uint32_t width, uint32_t height, int bit_depth); + Error add_plane(heif_channel channel, uint32_t width, uint32_t height, int bit_depth, const heif_security_limits* limits); - bool add_channel(heif_channel channel, uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth); + Error add_channel(heif_channel channel, uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth, + const heif_security_limits* limits); bool has_channel(heif_channel channel) const; @@ -152,15 +154,16 @@ class HeifPixelImage : public std::enable_shared_from_this, return const_cast(this)->get_channel(channel, out_stride); } - void copy_new_plane_from(const std::shared_ptr& src_image, - heif_channel src_channel, - heif_channel dst_channel); + Error copy_new_plane_from(const std::shared_ptr& src_image, + heif_channel src_channel, + heif_channel dst_channel, + const heif_security_limits* limits); - void extract_alpha_from_RGBA(const std::shared_ptr& srcimage); + Error extract_alpha_from_RGBA(const std::shared_ptr& srcimage, const heif_security_limits* limits); void fill_plane(heif_channel dst_channel, uint16_t value); - void fill_new_plane(heif_channel dst_channel, uint16_t value, int width, int height, int bpp); + Error fill_new_plane(heif_channel dst_channel, uint16_t value, int width, int height, int bpp, const heif_security_limits* limits); void transfer_plane_from_image_as(const std::shared_ptr& source, heif_channel src_channel, @@ -168,17 +171,19 @@ class HeifPixelImage : public std::enable_shared_from_this, Error copy_image_to(const std::shared_ptr& source, uint32_t x0, uint32_t y0); - Result> rotate_ccw(int angle_degrees); + Result> rotate_ccw(int angle_degrees, const heif_security_limits* limits); - Result> mirror_inplace(heif_transform_mirror_direction); + Result> mirror_inplace(heif_transform_mirror_direction, const heif_security_limits* limits); - Result> crop(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) const; + Result> crop(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom, + const heif_security_limits* limits) const; Error fill_RGB_16bit(uint16_t r, uint16_t g, uint16_t b, uint16_t a); Error overlay(std::shared_ptr& overlay, int32_t dx, int32_t dy); - Error scale_nearest_neighbor(std::shared_ptr& output, uint32_t width, uint32_t height) const; + Error scale_nearest_neighbor(std::shared_ptr& output, uint32_t width, uint32_t height, + const heif_security_limits* limits) const; void set_color_profile_nclx(const std::shared_ptr& profile) { m_color_profile_nclx = profile; } @@ -190,9 +195,10 @@ class HeifPixelImage : public std::enable_shared_from_this, void debug_dump() const; - bool extend_padding_to_size(uint32_t width, uint32_t height, bool adjust_size = false); + Error extend_padding_to_size(uint32_t width, uint32_t height, bool adjust_size, + const heif_security_limits* limits); - bool extend_to_size_with_zero(uint32_t width, uint32_t height); + Error extend_to_size_with_zero(uint32_t width, uint32_t height, const heif_security_limits* limits); // --- pixel aspect ratio @@ -243,7 +249,9 @@ class HeifPixelImage : public std::enable_shared_from_this, private: struct ImagePlane { - bool alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth, int num_interleaved_components); + // limits=nullptr disables the limits + Error alloc(uint32_t width, uint32_t height, heif_channel_datatype datatype, int bit_depth, int num_interleaved_components, + const heif_security_limits* limits); heif_channel_datatype m_datatype = heif_channel_datatype_unsigned_integer; uint8_t m_bit_depth = 0; diff --git a/libheif/plugins/encoder_kvazaar.cc b/libheif/plugins/encoder_kvazaar.cc index 158f174b04..a32886b926 100644 --- a/libheif/plugins/encoder_kvazaar.cc +++ b/libheif/plugins/encoder_kvazaar.cc @@ -304,9 +304,8 @@ static void kvazaar_query_input_colorspace2(void* encoder_raw, heif_colorspace* } else { *colorspace = heif_colorspace_YCbCr; - if (*chroma != heif_chroma_420 && - *chroma != heif_chroma_422 && - *chroma != heif_chroma_444) { + if (*chroma != heif_chroma_420) { + // Encoding to 4:2:2 and 4:4:4 currently does not work with Kvazaar (https://github.com/ultravideo/kvazaar/issues/418). *chroma = heif_chroma_420; } } @@ -376,8 +375,11 @@ static struct heif_error kvazaar_encode_image(void* encoder_raw, const struct he bool isGreyscale = (heif_image_get_colorspace(image) == heif_colorspace_monochrome); heif_chroma chroma = heif_image_get_chroma_format(image); - const kvz_api* api = kvz_api_get(bit_depth); - if (api == nullptr) { + // Kvazaar uses a hard-coded bit depth (https://github.com/ultravideo/kvazaar/issues/399). + // Check whether this matches to the image bit depth. + // TODO: we should check the bit depth supported by the encoder (like query_input_colorspace2()) and output a warning + // if we have to encode at a different bit depth than requested. + if (bit_depth != KVZ_BIT_DEPTH) { struct heif_error err = { heif_error_Encoder_plugin_error, heif_suberror_Unsupported_bit_depth, @@ -386,6 +388,16 @@ static struct heif_error kvazaar_encode_image(void* encoder_raw, const struct he return err; } + const kvz_api* api = kvz_api_get(bit_depth); + if (api == nullptr) { + struct heif_error err = { + heif_error_Encoder_plugin_error, + heif_suberror_Unspecified, + "Could not initialize Kvazaar API" + }; + return err; + } + auto uconfig = make_guard(api->config_alloc(), [api](kvz_config* cfg) { api->config_destroy(cfg); }); kvz_config* config = uconfig.get(); api->config_init(config); // param, encoder->preset.c_str(), encoder->tune.c_str()); diff --git a/libheif/plugins/encoder_rav1e.cc b/libheif/plugins/encoder_rav1e.cc index 0047a87913..6a0ca759ab 100644 --- a/libheif/plugins/encoder_rav1e.cc +++ b/libheif/plugins/encoder_rav1e.cc @@ -81,7 +81,7 @@ static void rav1e_set_default_parameters(void* encoder); static const char* rav1e_plugin_name() { - strcpy(plugin_name, "Rav1e encoder"); + snprintf(plugin_name, MAX_PLUGIN_NAME_LENGTH, "Rav1e encoder v%s", rav1e_version_short()); return plugin_name; } diff --git a/libheif/plugins/encoder_x265.cc b/libheif/plugins/encoder_x265.cc index decf8acd7a..d4e1b01682 100644 --- a/libheif/plugins/encoder_x265.cc +++ b/libheif/plugins/encoder_x265.cc @@ -36,6 +36,7 @@ extern "C" { static const char* kError_unsupported_bit_depth = "Bit depth not supported by x265"; static const char* kError_unsupported_image_size = "Images smaller than 16 pixels are not supported"; +static const char* kError_unsupported_ctu_size = "Unsupported CTU size"; enum parameter_type @@ -738,7 +739,7 @@ static struct heif_error x265_encode_image(void* encoder_raw, const struct heif_ struct heif_error err = { heif_error_Encoder_plugin_error, heif_suberror_Invalid_parameter_value, - kError_unsupported_image_size + kError_unsupported_ctu_size }; return err; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 026ed786e0..bac2d31c77 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,19 +7,28 @@ set(TESTING_DATA_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data") set(LIBHEIFIO_TESTING_DATA_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/data/heifio") configure_file(test-config.cc.in ${CMAKE_BINARY_DIR}/generated/test-config.cc) +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set_source_files_properties(catch_amalgamated.cpp PROPERTIES COMPILE_FLAGS -Wno-conversion) +endif() + +add_library(testframework STATIC ${CMAKE_BINARY_DIR}/generated/test-config.cc test_utils.cc catch_amalgamated.cpp) +target_link_libraries(testframework PRIVATE heif) + macro(add_libheif_test TEST_FILE) set(TEST_NAME ${TEST_FILE}) - add_executable(${TEST_NAME} main.cc catch.hpp ${CMAKE_BINARY_DIR}/generated/test-config.cc test_utils.cc ${TEST_FILE}.cc) - target_link_libraries(${TEST_NAME} PRIVATE heif) + add_executable(${TEST_NAME} ${TEST_FILE}.cc) + target_link_libraries(${TEST_NAME} PRIVATE heif testframework) add_test(NAME ${TEST_NAME} COMMAND ./${TEST_NAME}) + set_tests_properties(${TEST_NAME} PROPERTIES SKIP_REGULAR_EXPRESSION "[1-9][0-9]* skipped") endmacro() macro(add_heifio_test TEST_FILE) set(TEST_NAME ${TEST_FILE}) - add_executable(${TEST_NAME} main.cc catch.hpp ${CMAKE_BINARY_DIR}/generated/test-config.cc test_utils.cc ${TEST_FILE}.cc) - target_link_libraries(${TEST_NAME} PRIVATE heif heifio) + add_executable(${TEST_NAME} main.cc ${TEST_FILE}.cc) + target_link_libraries(${TEST_NAME} PRIVATE heif heifio testframework) target_include_directories(${TEST_NAME} PRIVATE ${libheif_SOURCE_DIR}) add_test(NAME ${TEST_NAME} COMMAND ./${TEST_NAME}) + set_tests_properties(${TEST_NAME} PROPERTIES SKIP_REGULAR_EXPRESSION "[1-9][0-9]* skipped") endmacro() # --- tests that require access to internal symbols diff --git a/tests/avc_box.cc b/tests/avc_box.cc index 2866d3d5ad..e3f4b50a3c 100644 --- a/tests/avc_box.cc +++ b/tests/avc_box.cc @@ -24,7 +24,7 @@ SOFTWARE. */ -#include "catch.hpp" +#include "catch_amalgamated.hpp" #include "codecs/avc_boxes.h" #include "error.h" #include diff --git a/tests/bitstream_tests.cc b/tests/bitstream_tests.cc index 01b68c3e5a..aa0309e4d8 100644 --- a/tests/bitstream_tests.cc +++ b/tests/bitstream_tests.cc @@ -24,7 +24,7 @@ SOFTWARE. */ -#include "catch.hpp" +#include "catch_amalgamated.hpp" #include "error.h" #include #include diff --git a/tests/box_equals.cc b/tests/box_equals.cc index a9ee6c9c82..dca2a59349 100644 --- a/tests/box_equals.cc +++ b/tests/box_equals.cc @@ -24,7 +24,7 @@ SOFTWARE. */ -#include "catch.hpp" +#include "catch_amalgamated.hpp" #include "box.h" TEST_CASE("box equals") { diff --git a/tests/catch.hpp b/tests/catch.hpp deleted file mode 100644 index 14777b8997..0000000000 --- a/tests/catch.hpp +++ /dev/null @@ -1,17970 +0,0 @@ -/* - * Catch v2.13.9 - * Generated: 2022-04-12 22:37:23.260201 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -// start catch.hpp - - -#define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 13 -#define CATCH_VERSION_PATCH 9 - -#ifdef __clang__ -# pragma clang system_header -#elif defined __GNUC__ -# pragma GCC system_header -#endif - -// start catch_suppress_warnings.h - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif -#elif defined __GNUC__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" -#endif -// end catch_suppress_warnings.h -#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -# define CATCH_CONFIG_ALL_PARTS -#endif - -// In the impl file, we want to have access to all parts of the headers -// Can also be used to sanely support PCHs -#if defined(CATCH_CONFIG_ALL_PARTS) -# define CATCH_CONFIG_EXTERNAL_INTERFACES -# if defined(CATCH_CONFIG_DISABLE_MATCHERS) -# undef CATCH_CONFIG_DISABLE_MATCHERS -# endif -# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -# endif -#endif - -#if !defined(CATCH_CONFIG_IMPL_ONLY) -// start catch_platform.h - -// See e.g.: -// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html -#ifdef __APPLE__ -# include -# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ - (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) -# define CATCH_PLATFORM_MAC -# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) -# define CATCH_PLATFORM_IPHONE -# endif - -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX - -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS -#endif - -// end catch_platform.h - -#ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// start catch_user_interfaces.h - -namespace Catch { - unsigned int rngSeed(); -} - -// end catch_user_interfaces.h -// start catch_tag_alias_autoregistrar.h - -// start catch_common.h - -// start catch_compiler_capabilities.h - -// Detect a number of compiler features - by compiler -// The following features are defined: -// -// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? -// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? -// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? -// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? -// **************** -// Note to maintainers: if new toggles are added please document them -// in configuration.md, too -// **************** - -// In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -#ifdef __cplusplus - -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif - -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif - -#endif - -// Only GCC compiler should be used in this block, so other compilers trying to -// mask themselves as GCC should be ignored. -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) - -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) - -#endif - -#if defined(__clang__) - -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) - -// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug -// which results in calls to destructors being emitted for each temporary, -// without a matching initialization. In practice, this can result in something -// like `std::string::~string` being called on an uninitialized value. -// -// For example, this code will likely segfault under IBM XL: -// ``` -// REQUIRE(std::string("12") + "34" == "1234") -// ``` -// -// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) && !defined(__CUDACC__) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ -# endif - -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) - -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Assume that non-Windows platforms support posix signals by default -#if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS -#endif - -//////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -#endif - -#ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Android somehow still does not support std::to_string -#if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Not all Windows environments support SEH properly -#if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH -#endif - -//////////////////////////////////////////////////////////////////////////////// -// PS4 -#if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Cygwin -#ifdef __CYGWIN__ - -// Required for some versions of Cygwin to declare gettimeofday -// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE -// some versions of cygwin (most) do not support std::to_string. Use the libstd check. -// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) - -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING - -# endif -#endif // __CYGWIN__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#if defined(_MSC_VER) - -// Universal Windows platform does not support SEH -// Or console colours (or console at all...) -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -# define CATCH_CONFIG_COLOUR_NONE -# else -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif - -# if !defined(__clang__) // Handle Clang masquerading for msvc - -// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ -// _MSVC_TRADITIONAL == 0 means new conformant preprocessor -// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif // MSVC_TRADITIONAL - -// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -# endif // __clang__ - -#endif // _MSC_VER - -#if defined(_REENTRANT) || defined(_MSC_VER) -// Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// Check if we are compiled with -fno-exceptions or equivalent -#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED -#endif - -//////////////////////////////////////////////////////////////////////////////// -// DJGPP -#ifdef __DJGPP__ -# define CATCH_INTERNAL_CONFIG_NO_WCHAR -#endif // __DJGPP__ - -//////////////////////////////////////////////////////////////////////////////// -// Embarcadero C++Build -#if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// Use of __COUNTER__ is suppressed during code analysis in -// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly -// handled by it. -// Otherwise all supported compilers support COUNTER macro, -// but user still might want to turn it off -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// RTX is a special version of Windows that is real time. -// This means that it is detected as Windows, but does not provide -// the same set of capabilities as real Windows does. -#if defined(UNDER_RTSS) || defined(RTX64_BUILD) - #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH - #define CATCH_INTERNAL_CONFIG_NO_ASYNC - #define CATCH_CONFIG_COLOUR_NONE -#endif - -#if !defined(_GLIBCXX_USE_C99_MATH_TR1) -#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Various stdlib support checks that require __has_include -#if defined(__has_include) - // Check if string_view is available and usable - #if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW - #endif - - // Check if optional is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if byte is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # include - # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE - # endif - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if variant is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 - # include - # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # define CATCH_CONFIG_NO_CPP17_VARIANT - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__clang__) && (__clang_major__ < 8) - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // defined(__has_include) - -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER -#endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH -#endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS -#endif -// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. -#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) -# define CATCH_CONFIG_WCHAR -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) -# define CATCH_CONFIG_CPP17_BYTE -#endif - -#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS -#endif - -#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN -#endif - -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC -#endif - -#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) -# define CATCH_CONFIG_ANDROID_LOGWRITE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -# define CATCH_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Even if we do not think the compiler has that warning, we still have -// to provide a macro that can be used by the code. -#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS -#endif - -// The goal of this macro is to avoid evaluation of the arguments, but -// still have the compiler warn on problems inside... -#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) -#endif - -#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#elif defined(__clang__) && (__clang_major__ < 5) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -#define CATCH_TRY if ((true)) -#define CATCH_CATCH_ALL if ((false)) -#define CATCH_CATCH_ANON(type) if ((false)) -#else -#define CATCH_TRY try -#define CATCH_CATCH_ALL catch (...) -#define CATCH_CATCH_ANON(type) catch (type) -#endif - -#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) -#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#endif - -// end catch_compiler_capabilities.h -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#include -#include -#include - -// We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy {}; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); - -namespace Catch { - - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; - - protected: - NonCopyable(); - virtual ~NonCopyable(); - }; - - struct SourceLineInfo { - - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} - - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - - bool empty() const noexcept { return file[0] == '\0'; } - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; - - char const* file; - std::size_t line; - }; - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - - // Bring in operator<< from global namespace into Catch namespace - // This is necessary because the overload of operator<< above makes - // lookup stop at namespace Catch - using ::operator<<; - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() const; - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) - -// end catch_common.h -namespace Catch { - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - -// end catch_tag_alias_autoregistrar.h -// start catch_test_registry.h - -// start catch_interfaces_testcase.h - -#include - -namespace Catch { - - class TestSpec; - - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool isThrowSafe( TestCase const& testCase, IConfig const& config ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -// end catch_interfaces_testcase.h -// start catch_stringref.h - -#include -#include -#include -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; - - private: - static constexpr char const* const s_empty = ""; - - char const* m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != (StringRef const& other) const noexcept -> bool { - return !(*this == other); - } - - auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns the current start pointer. If the StringRef is not - // null-terminated, throws std::domain_exception - auto c_str() const -> char const*; - - public: // substrings and searches - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, size()). - // If start > size(), then the substring is empty. - auto substr( size_type start, size_type length ) const noexcept -> StringRef; - - // Returns the current start pointer. May not be null-terminated. - auto data() const noexcept -> char const*; - - constexpr auto isNullTerminated() const noexcept -> bool { - return m_start[m_size] == '\0'; - } - - public: // iterators - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - }; - - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - - constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch - -constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -// end catch_stringref.h -// start catch_preprocessor.hpp - - -#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ -#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) - -#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ -// MSVC needs more evaluations -#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) -#else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) -#endif - -#define CATCH_REC_END(...) -#define CATCH_REC_OUT - -#define CATCH_EMPTY() -#define CATCH_DEFER(id) id CATCH_EMPTY() - -#define CATCH_REC_GET_END2() 0, CATCH_REC_END -#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 -#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 -#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) - -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) - -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) - -// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, -// and passes userdata as the first parameter to each invocation, -// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) -#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) -#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ -#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ -#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) -#else -// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) -#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) -#endif - -#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ -#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) - -#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) - -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) -#else -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) -#endif - -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) - -#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) -#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) -#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) -#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) -#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) -#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) -#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) -#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) -#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) -#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) - -#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N - -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template