Commit 96c6d334 for libheif
commit 96c6d334402a4570610372e3ac3f038f0faf826c
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Sun May 17 14:22:15 2026 +0200
heif-enc: add HEIF input (e.g. for HEIC to AVIF convertion)
diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc
index c0f2b4a3..c5be8aa9 100644
--- a/examples/heif_enc.cc
+++ b/examples/heif_enc.cc
@@ -48,6 +48,7 @@
#include <libheif/heif_properties.h>
#include "libheif/heif_items.h"
+#include "heifio/decoder_heif.h"
#include "heifio/decoder_jpeg.h"
#include "heifio/decoder_png.h"
#include "heifio/decoder_tiff.h"
@@ -885,7 +886,7 @@ InputImage load_image(const std::string& input_filename, int output_bit_depth)
enum
{
- PNG, JPEG, Y4M, TIFF, RAW, WEBP
+ PNG, JPEG, Y4M, TIFF, RAW, WEBP, HEIF
} filetype = JPEG;
if (suffix == "png") {
filetype = PNG;
@@ -902,6 +903,10 @@ InputImage load_image(const std::string& input_filename, int output_bit_depth)
else if (suffix == "webp") {
filetype = WEBP;
}
+ else if (suffix == "heif" || suffix == "heic" || suffix == "hif" ||
+ suffix == "avif" || suffix == "avifs") {
+ filetype = HEIF;
+ }
if (force_raw_input) {
filetype = RAW;
@@ -946,6 +951,13 @@ InputImage load_image(const std::string& input_filename, int output_bit_depth)
}
}
#endif
+ else if (filetype == HEIF) {
+ heif_error err = loadHEIF(input_filename.c_str(), &input_image);
+ if (err.code != heif_error_Ok) {
+ std::cerr << "Can not load HEIF input image: " << err.message << '\n';
+ exit(1);
+ }
+ }
else {
heif_error err = loadJPEG(input_filename.c_str(), &input_image);
if (err.code != heif_error_Ok) {
diff --git a/heifio/CMakeLists.txt b/heifio/CMakeLists.txt
index a92fb361..ee0365d2 100644
--- a/heifio/CMakeLists.txt
+++ b/heifio/CMakeLists.txt
@@ -3,6 +3,8 @@ include_directories(${libheif_BINARY_DIR} ${libheif_SOURCE_DIR}/libheif/api ${li
add_library(heifio STATIC
decoder.h
+ decoder_heif.cc
+ decoder_heif.h
decoder_raw.cc
decoder_raw.h
decoder_y4m.cc
diff --git a/heifio/decoder_heif.cc b/heifio/decoder_heif.cc
new file mode 100644
index 00000000..3258c49d
--- /dev/null
+++ b/heifio/decoder_heif.cc
@@ -0,0 +1,139 @@
+/*
+ libheif example application "heif".
+
+ MIT License
+
+ Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "decoder_heif.h"
+
+static struct heif_error heif_error_ok = {heif_error_Ok, heif_suberror_Unspecified, "Success"};
+
+// libheif's heif_error.message is owned by the heif_context (or heif_image_handle)
+// and becomes invalid once the owning object is released. Copy the message into a
+// thread-local string so it stays valid for the caller after we have released the
+// context, and so concurrent callers on different threads do not race on the buffer.
+// The caller is expected to log the error promptly (heif-enc calls exit() immediately).
+static heif_error stable_error(const heif_error& err)
+{
+ static thread_local std::string msg;
+ msg = err.message ? err.message : "";
+ return {err.code, err.subcode, msg.c_str()};
+}
+
+
+heif_error loadHEIF(const char* filename, InputImage* input_image)
+{
+ heif_context* ctx = heif_context_alloc();
+ if (!ctx) {
+ return {heif_error_Memory_allocation_error, heif_suberror_Unspecified,
+ "Cannot allocate HEIF context"};
+ }
+
+ heif_error err = heif_context_read_from_file(ctx, filename, nullptr);
+ if (err.code != heif_error_Ok) {
+ heif_error stable = stable_error(err);
+ heif_context_free(ctx);
+ return stable;
+ }
+
+ heif_item_id primary_id;
+ err = heif_context_get_primary_image_ID(ctx, &primary_id);
+ if (err.code != heif_error_Ok) {
+ heif_error stable = stable_error(err);
+ heif_context_free(ctx);
+ return stable;
+ }
+
+ heif_image_handle* handle = nullptr;
+ err = heif_context_get_image_handle(ctx, primary_id, &handle);
+ if (err.code != heif_error_Ok) {
+ heif_error stable = stable_error(err);
+ heif_context_free(ctx);
+ return stable;
+ }
+
+ // Decode in the file's native colorspace/chroma so the encoder side does not have
+ // to perform any extra colorspace conversion. Transforms (irot/imir) are applied
+ // during decode (default ignore_transformations=0), so the resulting pixels are
+ // already in display orientation and input_image->orientation stays 'normal'.
+
+ heif_decoding_options* opts = heif_decoding_options_alloc();
+
+ heif_image* image = nullptr;
+ err = heif_decode_image(handle, &image,
+ heif_colorspace_undefined, heif_chroma_undefined,
+ opts);
+
+ heif_decoding_options_free(opts);
+
+ if (err.code != heif_error_Ok) {
+ heif_error stable = stable_error(err);
+ heif_image_handle_release(handle);
+ heif_context_free(ctx);
+ return stable;
+ }
+
+ input_image->image = std::shared_ptr<heif_image>(image,
+ [](heif_image* img) { heif_image_release(img); });
+
+ // Extract EXIF and XMP metadata. Pass the bytes through verbatim, including the
+ // 4-byte TIFF-offset prefix that libheif's EXIF item format carries — the encoder
+ // side (heif_context_add_exif_metadata) expects the same layout.
+
+ int numMetadata = heif_image_handle_get_number_of_metadata_blocks(handle, nullptr);
+ if (numMetadata > 0) {
+ std::vector<heif_item_id> ids(numMetadata);
+ heif_image_handle_get_list_of_metadata_block_IDs(handle, nullptr, ids.data(), numMetadata);
+
+ for (int n = 0; n < numMetadata; n++) {
+ std::string itemtype = heif_image_handle_get_metadata_type(handle, ids[n]);
+ std::string contenttype = heif_image_handle_get_metadata_content_type(handle, ids[n]);
+
+ if (itemtype == "Exif" && input_image->exif.empty()) {
+ size_t size = heif_image_handle_get_metadata_size(handle, ids[n]);
+ input_image->exif.resize(size);
+ heif_error e = heif_image_handle_get_metadata(handle, ids[n], input_image->exif.data());
+ if (e.code != heif_error_Ok) {
+ input_image->exif.clear();
+ }
+ }
+ else if (contenttype == "application/rdf+xml" && input_image->xmp.empty()) {
+ size_t size = heif_image_handle_get_metadata_size(handle, ids[n]);
+ input_image->xmp.resize(size);
+ heif_error e = heif_image_handle_get_metadata(handle, ids[n], input_image->xmp.data());
+ if (e.code != heif_error_Ok) {
+ input_image->xmp.clear();
+ }
+ }
+ }
+ }
+
+ heif_image_handle_release(handle);
+ heif_context_free(ctx);
+
+ return heif_error_ok;
+}
diff --git a/heifio/decoder_heif.h b/heifio/decoder_heif.h
new file mode 100644
index 00000000..2125a6de
--- /dev/null
+++ b/heifio/decoder_heif.h
@@ -0,0 +1,35 @@
+/*
+ libheif example application "heif".
+
+ MIT License
+
+ Copyright (c) 2026 Dirk Farin <dirk.farin@gmail.com>
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+#ifndef LIBHEIF_DECODER_HEIF_H
+#define LIBHEIF_DECODER_HEIF_H
+
+#include "decoder.h"
+
+LIBHEIF_API
+heif_error loadHEIF(const char* filename, InputImage* input_image);
+
+#endif //LIBHEIF_DECODER_HEIF_H