Blob Blame History Raw
From 927d3d3946341d2ec4a53a4390842b903decb3c1 Mon Sep 17 00:00:00 2001
From: Aleksei Bavshin <alebastr89@gmail.com>
Date: Thu, 1 Jun 2023 23:39:16 -0700
Subject: [PATCH imv 1/2] libheif: run library init/deinit routines.

A plugin interface[1] added in libheif 1.14.0 requires running
`heif_init` to discover and load the plugins and `heif_deinit` for a
proper cleanup.
To support that, we need to extend the imv_backend structure with
init/uninit callbacks and invoke them when necessary.

Fixes discovery of libheif codecs shipped as dynamic plugins.

[1]: https://github.com/strukturag/libheif#codec-plugins
---
 src/backend.h         |  8 ++++++++
 src/backend_libheif.c | 25 +++++++++++++++++++++++++
 src/imv.c             | 18 ++++++++++++++++--
 3 files changed, 49 insertions(+), 2 deletions(-)

diff --git a/src/backend.h b/src/backend.h
index 00fd092..b754a2b 100644
--- a/src/backend.h
+++ b/src/backend.h
@@ -46,6 +46,14 @@ struct imv_backend {
    * and src will point to an imv_source instance for the given data.
    */
   enum backend_result (*open_memory)(void *data, size_t len, struct imv_source **src);
+
+  /* Run initialization code before registering the the backend. If successful,
+   * BACKEND_SUCCESS is returned.
+   */
+  enum backend_result (*init)();
+
+  /* Uninitialize backend */
+  void (*uninit)();
 };
 
 #endif
diff --git a/src/backend_libheif.c b/src/backend_libheif.c
index 8264e87..caa6810 100644
--- a/src/backend_libheif.c
+++ b/src/backend_libheif.c
@@ -5,6 +5,7 @@
 #include "backend.h"
 #include "bitmap.h"
 #include "image.h"
+#include "log.h"
 #include "source_private.h"
 
 struct private {
@@ -54,10 +55,15 @@ struct heif_error get_primary_image(struct heif_context *ctx, struct heif_image
   struct heif_image_handle *handle;
   struct heif_error err = heif_context_get_primary_image_handle(ctx, &handle);
   if (err.code != heif_error_Ok) {
+    imv_log(IMV_ERROR, "libheif: failed to get image handle (%s)\n", err.message);
     return err;
   }
 
   err = heif_decode_image(handle, img, heif_colorspace_RGB, heif_chroma_interleaved_RGBA, NULL);
+  if (err.code != heif_error_Ok) {
+    imv_log(IMV_ERROR, "libheif: failed to decode image (%s)\n", err.message);
+  }
+
   heif_image_handle_release(handle);
   return err;
 }
@@ -109,6 +115,21 @@ static enum backend_result open_memory(void *data, size_t len, struct imv_source
   return BACKEND_SUCCESS;
 }
 
+#if LIBHEIF_HAVE_VERSION(1,13,0)
+
+static enum backend_result init()
+{
+    struct heif_error err = heif_init(NULL);
+    if (err.code != heif_error_Ok) {
+        imv_log(IMV_ERROR, "libheif: failed to initialize backend (%s)\n", err.message);
+        heif_deinit();
+        return BACKEND_UNSUPPORTED;
+    }
+    return BACKEND_SUCCESS;
+}
+
+#endif
+
 const struct imv_backend imv_backend_libheif = {
   .name = "libheif",
   .description = "ISO/IEC 23008-12:2017 HEIF file format decoder and encoder.",
@@ -116,4 +137,8 @@ const struct imv_backend imv_backend_libheif = {
   .license = "GNU Lesser General Public License",
   .open_path = &open_path,
   .open_memory = &open_memory,
+#if LIBHEIF_HAVE_VERSION(1,13,0)
+  .init = &init,
+  .uninit = &heif_deinit,
+#endif
 };
diff --git a/src/imv.c b/src/imv.c
index bca52fd..a7744b2 100644
--- a/src/imv.c
+++ b/src/imv.c
@@ -223,6 +223,7 @@ static void render_window(struct imv *imv);
 static void update_env_vars(struct imv *imv);
 static size_t generate_env_text(struct imv *imv, char *buf, size_t len, const char *format);
 static size_t read_from_stdin(void **buffer);
+static void imv_backends_free(struct list *backends);
 
 /* Finds the next split between commands in a string (';'). Provides a pointer
  * to the next character after the delimiter as out, or a pointer to '\0' if
@@ -661,7 +662,7 @@ void imv_free(struct imv *imv)
     imv_window_free(imv->window);
   }
 
-  list_free(imv->backends);
+  imv_backends_free(imv->backends);
 
   list_free(imv->startup_commands);
 
@@ -670,7 +671,20 @@ void imv_free(struct imv *imv)
 
 void imv_install_backend(struct imv *imv, const struct imv_backend *backend)
 {
-  list_append(imv->backends, (void*)backend);
+  if (!backend->init || backend->init() == BACKEND_SUCCESS) {
+    list_append(imv->backends, (void*)backend);
+  }
+}
+
+static void imv_backends_free(struct list *backends)
+{
+  for (size_t i = 0; i < backends->len; ++i) {
+    struct imv_backend *backend = backends->items[i];
+    if (backend->uninit) {
+      backend->uninit();
+    }
+  }
+  list_free(backends);
 }
 
 static bool parse_bg(struct imv *imv, const char *bg)
-- 
2.40.1


From 0ffb9f4f68cf37b30faa880e225d274f2a2cf8eb Mon Sep 17 00:00:00 2001
From: Aleksei Bavshin <alebastr89@gmail.com>
Date: Fri, 2 Jun 2023 00:25:53 -0700
Subject: [PATCH imv 2/2] libheif: fix handling of odd image sizes

HEIC requires image sizes to be a multiple of 2 and AVIF wants the sizes
to be a multiple of 8, 16 or 32. libheif addresses that by rounding up
the stride and reporting a cropped box with actual image size that does
not reflect the internal plane layout.
As a result, imv reads few extra pixels for each line and renders a
distorted image (see odd-width cases in the test set).

The naive approach implemented here copies the bitmap line by line,
using the image width and taking stride into account.

Tested with libheif 1.16.1 and <https://github.com/link-u/avif-sample-images>.
---
 src/backend_libheif.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/backend_libheif.c b/src/backend_libheif.c
index caa6810..26c4313 100644
--- a/src/backend_libheif.c
+++ b/src/backend_libheif.c
@@ -35,7 +35,16 @@ static void load_image(void *raw_private, struct imv_image **image, int *frameti
   int width = heif_image_get_width(private->img, heif_channel_interleaved);
   int height = heif_image_get_height(private->img, heif_channel_interleaved);
   unsigned char *bitmap = malloc(width * height * 4);
-  memcpy(bitmap, data, width * height * 4);
+  if (stride == width * 4) {
+    memcpy(bitmap, data, width * height * 4);
+  } else {
+    unsigned char *ptr = bitmap;
+
+    for (int i = 0; i < height; ++i) {
+      memcpy(ptr, data + stride * i, width * 4);
+      ptr += width * 4;
+    }
+  }
 
   struct imv_bitmap *bmp = malloc(sizeof *bmp);
   bmp->width = width,
-- 
2.40.1