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
---
meson.build | 2 +-
src/backend.h | 8 ++++++++
src/backend_libheif.c | 24 ++++++++++++++++++++++++
src/imv.c | 18 ++++++++++++++++--
4 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/meson.build b/meson.build
index 67ea830..8c59535 100644
--- a/meson.build
+++ b/meson.build
@@ -126,7 +126,7 @@ foreach backend : [
['libjpeg', 'dependency', 'libturbojpeg', []],
['librsvg', 'dependency', 'librsvg-2.0', '>= 2.44'],
['libnsgif', 'dependency', 'libnsgif', []],
- ['libheif', 'dependency', 'libheif', []],
+ ['libheif', 'dependency', 'libheif', '>= 1.13.0'],
]
_backend_name = backend[0]
_dep_type = backend[1]
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..a020933 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,22 @@ static enum backend_result open_memory(void *data, size_t len, struct imv_source
return BACKEND_SUCCESS;
}
+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;
+}
+
+static void uninit()
+{
+ heif_deinit();
+}
+
const struct imv_backend imv_backend_libheif = {
.name = "libheif",
.description = "ISO/IEC 23008-12:2017 HEIF file format decoder and encoder.",
@@ -116,4 +138,6 @@ const struct imv_backend imv_backend_libheif = {
.license = "GNU Lesser General Public License",
.open_path = &open_path,
.open_memory = &open_memory,
+ .init = &init,
+ .uninit = &uninit,
};
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
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.
A more correct solution would be storing stride for the imv_bitmap and
using it to calculate the value for `GL_UNPACK_ROW_LENGTH`, but it
requires more changes across the whole codebase than I'm willing to do.
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 a020933..254c7bc 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
On 6/2/23 00:58, builds.sr.ht wrote:
> imv/patches: FAILED in 4m50s
>
> [libheif: run library init/deinit routines.][0] from [Aleksei Bavshin][1]
>
> [0]: https://lists.sr.ht/~exec64/imv-devel/patches/41580
> [1]: alebastr89@gmail.com
>
> ✓ #1000071 SUCCESS imv/patches/debian.yml https://builds.sr.ht/~exec64/job/1000071
> ✓ #1000069 SUCCESS imv/patches/freebsd.yml https://builds.sr.ht/~exec64/job/1000069
> ✓ #1000072 SUCCESS imv/patches/archlinux.yml https://builds.sr.ht/~exec64/job/1000072
> ✗ #1000070 FAILED imv/patches/ubuntu.yml https://builds.sr.ht/~exec64/job/1000070
Hm, I haven't checked the release date for 1.13.0. Apparently, it's too
new to be in Ubuntu LTS :(
Fixup and cleanup of unneeded function:
diff --git a/meson.build b/meson.build
index 8c59535..67ea830 100644
--- a/meson.build
+++ b/meson.build
@@ -126,7 +126,7 @@ foreach backend : [
['libjpeg', 'dependency', 'libturbojpeg', []],
['librsvg', 'dependency', 'librsvg-2.0', '>= 2.44'],
['libnsgif', 'dependency', 'libnsgif', []],
- ['libheif', 'dependency', 'libheif', '>= 1.13.0'],
+ ['libheif', 'dependency', 'libheif', []],
]
_backend_name = backend[0]
_dep_type = backend[1]
diff --git a/src/backend_libheif.c b/src/backend_libheif.c
index 254c7bc..26c4313 100644
--- a/src/backend_libheif.c
+++ b/src/backend_libheif.c
@@ -124,6 +124,8 @@ 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);
@@ -135,10 +137,7 @@ static enum backend_result init()
return BACKEND_SUCCESS;
}
-static void uninit()
-{
- heif_deinit();
-}
+#endif
const struct imv_backend imv_backend_libheif = {
.name = "libheif",
@@ -147,6 +146,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 = &uninit,
+ .uninit = &heif_deinit,
+#endif
};
--
With best regards,
Aleksei Bavshin