diff options
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 |
