diff options
Diffstat (limited to 'src/loader/backends/avif.c')
| -rw-r--r-- | src/loader/backends/avif.c | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/src/loader/backends/avif.c b/src/loader/backends/avif.c new file mode 100644 index 0000000..112d52d --- /dev/null +++ b/src/loader/backends/avif.c @@ -0,0 +1,82 @@ +/*:* + * ggaze — AVIF loader backend (libavif) + * + * Decodes AVIF via libavif. Compiled only when meson feature `avif` is enabled. + * If libavif is absent, the heif backend (libheif) also handles AVIF (its + * can_load accepts GGAZE_FMT_AVIF). This backend is preferred when present. + * + * Copyright (c) 2026 ggaze contributors + * SPDX-License-Identifier: GPL-3.0-or-later + *:*/ + +#include "../loader.h" +#include "../detect.h" + +#include <avif/avif.h> +#include <gdk/gdk.h> +#include <gio/gio.h> + +static gboolean +_avif_can_load(const guint8 *p_head, gsize u_len) { + return (detect_format(p_head, u_len) == GGAZE_FMT_AVIF); +} + +static GdkTexture * +_avif_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, "avif: non-local file"); + return (NULL); + } + + avifDecoder *p_dec = avifDecoderCreate(); + avifResult e_r = avifDecoderSetIOFile(p_dec, c_path); + g_free(c_path); + if (e_r != AVIF_RESULT_OK) { + g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_FAILED, "avif: %s", + avifResultToString(e_r)); + avifDecoderDestroy(p_dec); + return (NULL); + } + e_r = avifDecoderParse(p_dec); + if (e_r != AVIF_RESULT_OK) { + g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_FAILED, "avif: %s", + avifResultToString(e_r)); + avifDecoderDestroy(p_dec); + return (NULL); + } + e_r = avifDecoderNextImage(p_dec); + if (e_r != AVIF_RESULT_OK) { + g_set_error(p_err, G_IO_ERROR, G_IO_ERROR_FAILED, "avif: %s", + avifResultToString(e_r)); + avifDecoderDestroy(p_dec); + return (NULL); + } + + avifRGBImage st_rgb; + avifRGBImageSetDefaults(&st_rgb, &p_dec->image); + st_rgb.format = AVIF_RGB_FORMAT_RGBA; + st_rgb.depth = 8; + avifRGBImageAllocatePixels(&st_rgb); + avifImageYCToRGB(&p_dec->image, &st_rgb); + + GdkTexture *p_tex = NULL; + if (st_rgb.pixels != NULL) { + GBytes *p_bytes = g_bytes_new_static( + st_rgb.pixels, (gsize)st_rgb.rowBytes * (gsize)st_rgb.height); + p_tex = gdk_memory_texture_new((int)st_rgb.width, (int)st_rgb.height, + GDK_MEMORY_R8G8B8A8, p_bytes, + (gsize)st_rgb.rowBytes); + g_bytes_unref(p_bytes); + } + + avifRGBImageFreePixels(&st_rgb); + avifDecoderDestroy(p_dec); + return (p_tex); +} + +const GgazeLoaderBackend avif_backend = { + .can_load = _avif_can_load, + .load = _avif_load, +};
\ No newline at end of file |
