diff --git a/libheif/heif.cc b/libheif/heif.cc index d410037f97..9115b82bb5 100644 --- a/libheif/heif.cc +++ b/libheif/heif.cc @@ -538,6 +538,12 @@ int heif_context_get_number_of_top_level_images(heif_context* ctx) } +int heif_context_get_number_of_images(heif_context* ctx) +{ + return (int) ctx->context->get_images().size(); +} + + int heif_context_get_list_of_top_level_image_IDs(struct heif_context* ctx, heif_item_id* ID_array, int count) @@ -559,6 +565,29 @@ int heif_context_get_list_of_top_level_image_IDs(struct heif_context* ctx, } +int heif_context_get_list_of_image_IDs(struct heif_context* ctx, + heif_item_id* ID_array, + int count) +{ + if (ID_array == nullptr || count == 0 || ctx == nullptr) { + return 0; + } + + // fill in ID values into output array + + const std::map> imgs = ctx->context->get_images(); + int n = (int) std::min(count, (int) imgs.size()); + + int i = 0; + for (auto itor = imgs.begin(); itor != imgs.end(); itor++) { + ID_array[i] = itor->second->get_id(); + i += 1; + } + + return n; +} + + struct heif_error heif_context_get_image_handle(struct heif_context* ctx, heif_item_id id, struct heif_image_handle** imgHdl) @@ -592,6 +621,103 @@ struct heif_error heif_context_get_image_handle(struct heif_context* ctx, } +struct heif_error heif_context_get_image_handle_from_all_images2(struct heif_context* ctx, + heif_item_id id, + struct heif_image_handle** imgHdl) +{ + if (!imgHdl) { + Error err(heif_error_Usage_error, + heif_suberror_Null_pointer_argument); + return err.error_struct(ctx->context.get()); + } + + std::map> images = ctx->context->get_images(); + + std::shared_ptr image; + for (auto& img : images) { + if (img.first == id) { + image = img.second; + break; + } + } + + if (!image) { + Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced); + return err.error_struct(ctx->context.get()); + } + + *imgHdl = new heif_image_handle(); + (*imgHdl)->image = image; + (*imgHdl)->context = ctx->context; + + return Error::Ok.error_struct(ctx->context.get()); +} + + +struct heif_error heif_context_get_image_handle_from_all_images(struct heif_context* ctx, + heif_item_id id, + struct heif_image_handle** imgHdl) +{ + if (!imgHdl) { + Error err(heif_error_Usage_error, + heif_suberror_Null_pointer_argument); + return err.error_struct(ctx->context.get()); + } + + std::map> images = ctx->context->get_images(); + + auto itor = images.find(id); + if (itor == images.end()) { + Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced); + return err.error_struct(ctx->context.get()); + } + + auto image = itor->second; + + *imgHdl = new heif_image_handle(); + (*imgHdl)->image = image; + (*imgHdl)->context = ctx->context; + + return Error::Ok.error_struct(ctx->context.get()); +} + + +struct heif_error heif_image_handle_get_image_width(struct heif_context* ctx, + heif_item_id id, + uint32_t* width) +{ + std::map> images = ctx->context->get_images(); + auto itor = images.find(id); + if (itor == images.end()) { + Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced); + return err.error_struct(ctx->context.get()); + } + + auto image = itor->second; + *width = image->get_width(); + + return Error::Ok.error_struct(ctx->context.get()); +} + + +struct heif_error heif_image_handle_get_image_height(struct heif_context* ctx, + heif_item_id id, + uint32_t* height) +{ + std::map> images = ctx->context->get_images(); + auto itor = images.find(id); + if (itor == images.end()) { + Error err(heif_error_Usage_error, heif_suberror_Nonexisting_item_referenced); + return err.error_struct(ctx->context.get()); + } + + auto image = itor->second; + *height = image->get_height(); + + return Error::Ok.error_struct(ctx->context.get()); +} + + int heif_image_handle_is_primary_image(const struct heif_image_handle* handle) { return handle->image->is_primary(); @@ -743,6 +869,97 @@ int heif_image_handle_get_width(const struct heif_image_handle* handle) } } +enum heif_image_type heif_image_handle_get_item_type(const struct heif_image_handle* handle, heif_item_id ID) { + heif_image_type image_type = heif_image_type_none; + std::string item_type; + + Error err = handle->context->get_image_type(ID, item_type); + if (err.error_code != heif_error_Ok) { + return heif_image_type_none; + } + + if (item_type == "hvc1") { + image_type = heif_image_type_hvc1; + } else if (item_type == "av01") { + image_type = heif_image_type_av01; + } else if (item_type == "grid") { + image_type = heif_image_type_grid; + } else if (item_type == "iden") { + image_type = heif_image_type_iden; + } else if (item_type == "iovl") { + image_type = heif_image_type_iovl; + } + + return image_type; +} + +int heif_image_handle_get_compressed_image_data(const struct heif_image_handle* handle, + heif_item_id ID, + int pad_start_pattern, + uint8_t **out_img, + size_t *out_size) +{ + heif_item_id id = handle->image->get_id(); + uint8_t *img = handle->context->get_compressed_image_data(id, pad_start_pattern, out_size); + if (NULL == img) { + return 0; + } + + *out_img = img; + return 1; +} + + +int heif_image_handle_fill_compressed_image_data(const struct heif_image_handle* handle, + heif_item_id ID, + int pad_start_pattern, + uint8_t *out_img, + size_t *out_size) +{ + heif_item_id id = handle->image->get_id(); + Error err = handle->context->fill_compressed_image_data(id, pad_start_pattern, out_img, out_size); + if (err.error_code != heif_error_Ok) { + return 0; + } + + return 1; +} + + +int heif_image_handle_get_overlay_info(const struct heif_image_handle* handle, + heif_item_id ID, + struct heif_overlay_info *overlay_info) +{ + if (NULL == overlay_info) { + return 0; + } + + Error err = handle->context->get_overlay_info(ID, *overlay_info); + if (err.error_code != heif_error_Ok) { + return 0; + } + + return 1; +} + + +int heif_image_handle_gcheck_not_compatible_but_moov_box(struct heif_context* ctx, const void* mem, size_t size) +{ + if (ctx->context->check_not_compatible_but_moov_box(mem, size, true) == true) { + return 1; + } + return 0; +} + + +int heif_image_handle_get_alpha_image_id(struct heif_context* ctx, const heif_item_id ID, heif_item_id *alpha_img_id) { + if (false == ctx->context->get_alpha_image_id(ID, *alpha_img_id)) { + return 0; + } + + return 1; +} + int heif_image_handle_get_height(const struct heif_image_handle* handle) { diff --git a/libheif/heif.h b/libheif/heif.h index 466139a945..68bd560a52 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -392,6 +392,39 @@ enum heif_brand heif_avis }; +enum heif_image_type +{ + heif_image_type_hvc1, + heif_image_type_av01, + heif_image_type_grid, + heif_image_type_iden, + heif_image_type_iovl, + heif_image_type_none_image, + heif_image_type_none, // guardian +}; + +struct heif_offset +{ + int32_t dx; + int32_t dy; +}; + +struct heif_overlay_info +{ + uint32_t canvas_width; + uint32_t canvas_height; + + uint16_t bg_color_r; + uint16_t bg_color_g; + uint16_t bg_color_b; + uint16_t bg_color_a; + + uint32_t num_items; + struct heif_offset *offsets; + heif_item_id *IDs; +}; + + // input data should be at least 12 bytes // DEPRECATED, use heif_read_main_brand() instead LIBHEIF_API @@ -535,6 +568,9 @@ struct heif_error heif_context_read_from_reader(struct heif_context*, LIBHEIF_API int heif_context_get_number_of_top_level_images(struct heif_context* ctx); +LIBHEIF_API +int heif_context_get_number_of_images(struct heif_context* ctx); + LIBHEIF_API int heif_context_is_top_level_image_ID(struct heif_context* ctx, heif_item_id id); @@ -545,6 +581,11 @@ int heif_context_get_list_of_top_level_image_IDs(struct heif_context* ctx, heif_item_id* ID_array, int count); +LIBHEIF_API +int heif_context_get_list_of_image_IDs(struct heif_context* ctx, + heif_item_id* ID_array, + int count); + LIBHEIF_API struct heif_error heif_context_get_primary_image_ID(struct heif_context* ctx, heif_item_id* id); @@ -560,6 +601,21 @@ struct heif_error heif_context_get_image_handle(struct heif_context* ctx, heif_item_id id, struct heif_image_handle**); +LIBHEIF_API +struct heif_error heif_context_get_image_handle_from_all_images(struct heif_context* ctx, + heif_item_id id, + struct heif_image_handle**); + +LIBHEIF_API +struct heif_error heif_image_handle_get_image_width(struct heif_context* ctx, + heif_item_id id, + uint32_t* width); + +LIBHEIF_API +struct heif_error heif_image_handle_get_image_height(struct heif_context* ctx, + heif_item_id id, + uint32_t* height); + // Print information about the boxes of a HEIF file to file descriptor. // This is for debugging and informational purposes only. You should not rely on // the output having a specific format. At best, you should not use this at all. @@ -601,6 +657,34 @@ int heif_image_handle_is_primary_image(const struct heif_image_handle* handle); LIBHEIF_API int heif_image_handle_get_width(const struct heif_image_handle* handle); +LIBHEIF_API +enum heif_image_type heif_image_handle_get_item_type(const struct heif_image_handle* handle, heif_item_id ID); + +LIBHEIF_API +int heif_image_handle_get_compressed_image_data(const struct heif_image_handle* handle, + heif_item_id ID, + int pad_start_pattern, + uint8_t **out_img, + size_t *out_size); + +LIBHEIF_API +int heif_image_handle_fill_compressed_image_data(const struct heif_image_handle* handle, + heif_item_id ID, + int pad_start_pattern, + uint8_t *out_img, + size_t *out_size); + +LIBHEIF_API +int heif_image_handle_get_overlay_info(const struct heif_image_handle* handle, + heif_item_id ID, + struct heif_overlay_info *overlay_info); + +LIBHEIF_API +int heif_image_handle_gcheck_not_compatible_but_moov_box(struct heif_context* ctx, const void* mem, size_t size); + +LIBHEIF_API +int heif_image_handle_get_alpha_image_id(struct heif_context* ctx, const heif_item_id ID, heif_item_id *alpha_img_id); + LIBHEIF_API int heif_image_handle_get_height(const struct heif_image_handle* handle); diff --git a/libheif/heif_context.cc b/libheif/heif_context.cc index bde845817f..8fd974cdf2 100644 --- a/libheif/heif_context.cc +++ b/libheif/heif_context.cc @@ -946,6 +946,16 @@ bool HeifContext::is_image(heif_item_id ID) const return false; } +bool HeifContext::get_alpha_image_id(heif_item_id ID, heif_item_id &alpha_img_id) const { + auto img = m_all_images.find(ID)->second; + auto alpha_img = img->get_alpha_channel(); + if (alpha_img == nullptr) { + return false; + } + + alpha_img_id = alpha_img->get_id(); + return true; +} bool HeifContext::has_alpha(heif_item_id ID) const { @@ -1115,6 +1125,125 @@ Error HeifContext::decode_image_user(heif_item_id ID, } + +Error HeifContext::get_image_type(heif_item_id ID, std::string &out_item_type) { + std::string image_type = m_heif_file->get_item_type(ID); + if ("" == image_type) { + return Error(heif_error_Input_does_not_exist, heif_suberror_Invalid_parameter_value); + } + + out_item_type = image_type; + return Error::Ok; +} + +uint8_t *HeifContext::get_compressed_image_data(heif_item_id ID, bool pad_start_pattern, size_t *out_size) const { + std::vector data; + Error error = m_heif_file->get_compressed_image_data(ID, &data); + if (error) { + return NULL; + } + + // TODO: + // add exception handling code for grid image + // it works only with hvc1 image type + + + size_t out_offset = 0; + size_t buff_size = data.size(); + uint8_t *out_buff = new uint8_t[buff_size + 4]; + *out_size = 0; + + if (true == pad_start_pattern) { + uint32_t nal_start_seq = 0x01000000; + const uint8_t* cdata = (const uint8_t*) data.data(); + size_t ptr = 0; + + while (ptr < buff_size) { + if (4 > buff_size - ptr) { + delete [] out_buff; + return NULL; + } + + uint32_t nal_size = (cdata[ptr] << 24) | (cdata[ptr + 1] << 16) | (cdata[ptr + 2] << 8) | (cdata[ptr + 3]); + ptr += 4; + + + if (nal_size > buff_size - ptr) { + delete [] out_buff; + return NULL; + } + + *((uint32_t *)(out_buff + out_offset)) = nal_start_seq; + out_offset += 4; + *out_size += 4; + + memcpy(out_buff + out_offset, cdata + ptr, nal_size); + out_offset += nal_size; + *out_size += nal_size; + + ptr += nal_size; + } + } else { + for (uint32_t i = 0; i < buff_size; ++i) { + out_buff[i + out_offset] = data[i]; + } + + *out_size = buff_size; + } + + return out_buff; +} + +Error HeifContext::fill_compressed_image_data(heif_item_id ID, bool pad_start_pattern, uint8_t *out_buff, size_t *out_size) const { + std::vector data; + Error error = m_heif_file->get_compressed_image_data(ID, &data); + if (error) { + return Error(heif_error_Input_does_not_exist, heif_suberror_No_item_data); + } + + size_t out_offset = 0; + size_t buff_size = data.size(); + + *out_size = 0; + + if (true == pad_start_pattern) { + uint32_t nal_start_seq = 0x01000000; + const uint8_t* cdata = (const uint8_t*) data.data(); + size_t ptr = 0; + + + while (ptr < buff_size) { + if (4 > buff_size - ptr) { + return Error(heif_error_Invalid_input, heif_suberror_Invalid_image_size); + } + + uint32_t nal_size = (cdata[ptr] << 24) | (cdata[ptr + 1] << 16) | (cdata[ptr + 2] << 8) | (cdata[ptr + 3]); + ptr += 4; + + if (nal_size > buff_size - ptr) { + return Error(heif_error_Invalid_input, heif_suberror_Invalid_image_size); + } + + *((uint32_t *)(out_buff + out_offset)) = nal_start_seq; + out_offset += 4; + *out_size += 4; + + memcpy(out_buff + out_offset, cdata + ptr, nal_size); + out_offset += nal_size; + *out_size += nal_size; + + ptr += nal_size; + } + } else { + for (uint32_t i = 0; i < buff_size; ++i) { + out_buff[i + out_offset] = data[i]; + } + + *out_size = buff_size; + } + + return Error::Ok; +} Error HeifContext::decode_image_planar(heif_item_id ID, std::shared_ptr& img, heif_colorspace out_colorspace, @@ -1863,6 +1992,86 @@ Error HeifContext::decode_overlay_image(heif_item_id ID, return err; } +Error HeifContext::get_overlay_info(heif_item_id ID, + struct heif_overlay_info &overlay_info) const +{ + std::string image_type = m_heif_file->get_item_type(ID); + if (image_type == "iovl") { + return Error(heif_error_Invalid_input, + heif_suberror_No_iref_box, + "No iref box available, but needed for iovl image"); + } + + std::vector overlay_data; + Error error = m_heif_file->get_compressed_image_data(ID, &overlay_data); + if (error) { + return error; + } + + // find the IDs this image is composed of + auto iref_box = m_heif_file->get_iref_box(); + if (!iref_box) { + return Error(heif_error_Invalid_input, + heif_suberror_No_iref_box, + "No iref box available, but needed for iovl image"); + } + + std::vector image_references = iref_box->get_references(ID, fourcc("dimg")); + + ImageOverlay overlay; + Error err = overlay.parse(image_references.size(), overlay_data); + if (err) { + return err; + } + + if (image_references.size() != overlay.get_num_offsets()) { + return Error(heif_error_Invalid_input, + heif_suberror_Invalid_overlay_data, + "Number of image offsets does not match the number of image references"); + } + + uint32_t w = overlay.get_canvas_width(); + uint32_t h = overlay.get_canvas_height(); + + overlay_info.canvas_height = w; + overlay_info.canvas_height = h; + + if (w >= m_maximum_image_width_limit || h >= m_maximum_image_height_limit) { + std::stringstream sstr; + sstr << "Image size " << w << "x" << h << " exceeds the maximum image size " + << m_maximum_image_width_limit << "x" << m_maximum_image_height_limit << "\n"; + + return Error(heif_error_Memory_allocation_error, + heif_suberror_Security_limit_exceeded, + sstr.str()); + } + + uint16_t bkg_color[4]; + overlay.get_background_color(bkg_color); + + overlay_info.bg_color_r = bkg_color[0]; + overlay_info.bg_color_g = bkg_color[1]; + overlay_info.bg_color_b = bkg_color[2]; + overlay_info.bg_color_a = bkg_color[3]; + + overlay_info.num_items = image_references.size(); + overlay_info.offsets = (struct heif_offset *)malloc(sizeof(struct heif_offset) * overlay_info.num_items); + overlay_info.IDs = (heif_item_id *)malloc(sizeof(heif_item_id) * overlay_info.num_items); + + for (size_t i = 0; i < image_references.size(); i++) { + overlay.get_offset(i, &overlay_info.offsets[i].dx, &overlay_info.offsets[i].dy); + overlay_info.IDs[i] = image_references[i]; + } + + return err; +} + +bool HeifContext::check_not_compatible_but_moov_box(const void* data, size_t size, bool copy) +{ + auto input_stream = std::make_shared((const uint8_t*) data, size, copy); + + return m_heif_file->check_not_compatible_but_moov_box(input_stream); +} static std::shared_ptr create_alpha_image_from_image_alpha_channel(const std::shared_ptr& image) diff --git a/libheif/heif_context.h b/libheif/heif_context.h index 71e8064970..36955e9de2 100644 --- a/libheif/heif_context.h +++ b/libheif/heif_context.h @@ -84,6 +84,19 @@ namespace heif { Error read_from_memory(const void* data, size_t size, bool copy); + Error get_image_type(heif_item_id ID, std::string &out_item_type); + + uint8_t *get_compressed_image_data(heif_item_id ID, bool pad_start_pattern, size_t *out_size) const; + + Error fill_compressed_image_data(heif_item_id ID, bool pad_start_pattern, uint8_t *out_buff, size_t *out_size) const; + + Error get_overlay_info(heif_item_id ID, + struct heif_overlay_info &overlay_info) const; + + bool check_not_compatible_but_moov_box(const void* data, size_t size, bool copy); + + bool get_alpha_image_id(heif_item_id ID, heif_item_id &alpha_img_id) const; + class Image : public ErrorBuffer { public: @@ -325,6 +338,8 @@ namespace heif { std::vector> get_top_level_images() { return m_top_level_images; } + std::map> get_images() { return m_all_images; } + std::shared_ptr get_top_level_image(heif_item_id id) { for (auto& img : m_top_level_images) { if (img->get_id() == id) { diff --git a/libheif/heif_file.cc b/libheif/heif_file.cc index efbc662b53..dfd412bbae 100644 --- a/libheif/heif_file.cc +++ b/libheif/heif_file.cc @@ -197,6 +197,50 @@ std::string HeifFile::debug_dump_boxes() const } +bool HeifFile::check_not_compatible_but_moov_box(const std::shared_ptr& reader) +{ + uint64_t maxSize = std::numeric_limits::max(); + heif::BitstreamRange range(reader, maxSize); + + std::shared_ptr ftyp_box; + bool has_moov_box = false; + + for (;;) { + std::shared_ptr box; + Error error = Box::read(range, &box); + + // When an EOF error is returned, this is not really a fatal exception, + // but simply the indication that we reached the end of the file. + if (error != Error::Ok || range.error() || range.eof()) { + break; + } + + if (fourcc("moov") == box->get_short_type()) { + has_moov_box = true; + } + + if (box->get_short_type() == fourcc("ftyp")) { + ftyp_box = std::dynamic_pointer_cast(box); + } + } + + if (!ftyp_box) { + return false; + } + + if (!ftyp_box->has_compatible_brand(fourcc("heic")) && + !ftyp_box->has_compatible_brand(fourcc("heix")) && + !ftyp_box->has_compatible_brand(fourcc("mif1")) && + !ftyp_box->has_compatible_brand(fourcc("avif"))) { + std::stringstream sstr; + sstr << "File does not include any supported brands.\n"; + + return has_moov_box; + } + + return false; +} + Error HeifFile::parse_heif_file(BitstreamRange& range) { // --- read all top-level boxes diff --git a/libheif/heif_file.h b/libheif/heif_file.h index 96ed1821ea..f79f3ef535 100644 --- a/libheif/heif_file.h +++ b/libheif/heif_file.h @@ -58,6 +58,8 @@ namespace heif { Error read_from_memory(const void* data, size_t size, bool copy); + bool check_not_compatible_but_moov_box(const std::shared_ptr& reader); + void new_empty_file(); void set_brand(heif_compression_format format, bool miaf_compatible);