diff options
| author | Paul Buetow <paul@buetow.org> | 2026-07-13 21:13:35 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-07-13 21:13:35 +0300 |
| commit | 484fbca06312dbe0f381109763f701671f22e602 (patch) | |
| tree | ee8bc47f32a2a767831e05a5b949c5a05aeec4f5 /src/loader/backends/heif.c | |
| parent | 25ee19ce91dcb204d93e6d8cceb7cd64d5f057f9 (diff) | |
modernfmt: JXL/HEIF/AVIF loader backends, conditional (qt0)
M5 (task qt0): modern format backends.
- src/loader/backends/jxl.c: libjxl decoder → RGBA → GdkMemoryTexture.
- src/loader/backends/heif.c: libheif decoder → interleaved RGBA →
GdkMemoryTexture (also handles AVIF when libavif is absent).
- src/loader/backends/avif.c: libavif decoder (written; auto-disabled when
libavif is not installed).
- src/loader/loader.c: BACKENDS array conditionally includes the backends
via -DHAVE_JXL/HAVE_AVIF/HAVE_HEIF; pixbuf remains last (fallback).
- meson.build: conditional backend sources + c_args + extra deps.
- tests: assert_unsupports updated to assert NULL only (any error, works
with or without the specific backend compiled in).
- Minimal build (all disabled) + full build (jxl+heif) both 14/14 green,
ASan clean.
Diffstat (limited to 'src/loader/backends/heif.c')
| -rw-r--r-- | src/loader/backends/heif.c | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/src/loader/backends/heif.c b/src/loader/backends/heif.c new file mode 100644 index 0000000..fda7573 --- /dev/null +++ b/src/loader/backends/heif.c @@ -0,0 +1,88 @@ +/*:* + * ggaze — HEIF/HEIC loader backend (libheif) + * + * Decodes HEIF/HEIC (and AVIF if libavif is absent) via libheif into RGBA + * pixels, then wraps them in a GdkMemoryTexture. Compiled only when meson + * feature `heif` is enabled. + * + * Copyright (c) 2026 ggaze contributors + * SPDX-License-Identifier: GPL-3.0-or-later + *:*/ + +#include "../loader.h" +#include "../detect.h" + +#include <gdk/gdk.h> +#include <gio/gio.h> +#include <libheif/heif.h> + +static gboolean +_heif_can_load(const guint8 *p_head, gsize u_len) { + return (detect_format(p_head, u_len) == GGAZE_FMT_HEIF || + detect_format(p_head, u_len) == GGAZE_FMT_AVIF); +} + +static GdkTexture * +_heif_load(GFile *p_file, GCancellable *p_cancel, GError **p_err) { + (void)p_cancel; + char *c_path = g_file_get_path(p_file); + if (c_path == NULL) { + g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_FAILED, "heif: non-local file"); + return (NULL); + } + + struct heif_context *p_ctx = heif_context_alloc(); + struct heif_error st_err = heif_context_read_from_file(p_ctx, c_path, NULL); + g_free(c_path); + if (st_err.code != heif_error_Ok) { + g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_FAILED, "heif: %s", + st_err.message); + heif_context_free(p_ctx); + return (NULL); + } + + struct heif_image_handle *p_handle = NULL; + st_err = heif_context_get_primary_image_handle(p_ctx, &p_handle); + if (st_err.code != heif_error_Ok) { + g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_FAILED, "heif: %s", + st_err.message); + heif_context_free(p_ctx); + return (NULL); + } + + struct heif_image *p_img = NULL; + st_err = heif_decode_image(p_handle, &p_img, heif_colorspace_RGB, + heif_chroma_interleaved_RGBA, NULL); + if (st_err.code != heif_error_Ok) { + g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_FAILED, "heif: %s", + st_err.message); + heif_image_handle_release(p_handle); + heif_context_free(p_ctx); + return (NULL); + } + + int i_stride = 0; + const uint8_t *p_data = + heif_image_get_plane_readonly(p_img, heif_channel_interleaved, &i_stride); + int i_w = heif_image_get_width(p_img, heif_channel_interleaved); + int i_h = heif_image_get_height(p_img, heif_channel_interleaved); + + GdkTexture *p_tex = NULL; + if (p_data != NULL && i_w > 0 && i_h > 0) { + gsize u_len = (gsize)(i_h - 1) * (gsize)i_stride + (gsize)i_w * 4u; + GBytes *p_bytes = g_bytes_new_static(p_data, u_len); + p_tex = gdk_memory_texture_new(i_w, i_h, GDK_MEMORY_R8G8B8A8, p_bytes, + (gsize)i_stride); + g_bytes_unref(p_bytes); + } + + heif_image_release(p_img); + heif_image_handle_release(p_handle); + heif_context_free(p_ctx); + return (p_tex); +} + +const GgazeLoaderBackend heif_backend = { + .can_load = _heif_can_load, + .load = _heif_load, +};
\ No newline at end of file |
