From a6deca0ad390d29ada6d72584a1be835a3913139 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Mon, 2 Sep 2019 22:09:14 +0100 Subject: libnsgif: Add libnsgif backend --- src/backend_libnsgif.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 src/backend_libnsgif.c (limited to 'src/backend_libnsgif.c') diff --git a/src/backend_libnsgif.c b/src/backend_libnsgif.c new file mode 100644 index 0000000..f5fcd3a --- /dev/null +++ b/src/backend_libnsgif.c @@ -0,0 +1,210 @@ +#include "backend.h" +#include "bitmap.h" +#include "image.h" +#include "log.h" +#include "source.h" +#include "source_private.h" + +#include +#include +#include +#include +#include +#include + +struct private { + int current_frame; + gif_animation gif; + void *data; + size_t len; +}; + +static void* bitmap_create(int width, int height) +{ + const size_t bytes_per_pixel = 4; + return calloc(width * height, bytes_per_pixel); +} + +static void bitmap_destroy(void *bitmap) +{ + free(bitmap); +} + +static unsigned char* bitmap_get_buffer(void *bitmap) +{ + return bitmap; +} + +static void bitmap_set_opaque(void *bitmap, bool opaque) +{ + (void)bitmap; + (void)opaque; +} + +static bool bitmap_test_opaque(void *bitmap) +{ + (void)bitmap; + return false; +} + +static void bitmap_mark_modified(void *bitmap) +{ + (void)bitmap; +} + +static gif_bitmap_callback_vt bitmap_callbacks = { + bitmap_create, + bitmap_destroy, + bitmap_get_buffer, + bitmap_set_opaque, + bitmap_test_opaque, + bitmap_mark_modified +}; + + +static void free_private(void *raw_private) +{ + if (!raw_private) { + return; + } + + struct private *private = raw_private; + gif_finalise(&private->gif); + munmap(private->data, private->len); + free(private); +} + +static void push_current_image(struct private *private, + struct imv_image **image, int *frametime) +{ + struct imv_bitmap *bmp = malloc(sizeof *bmp); + bmp->width = private->gif.width; + bmp->height = private->gif.height; + bmp->format = IMV_ABGR; + size_t len = 4 * bmp->width * bmp->height; + bmp->data = malloc(len); + memcpy(bmp->data, private->gif.frame_image, len); + + *image = imv_image_create_from_bitmap(bmp); + *frametime = private->gif.frames[private->current_frame].frame_delay * 10.0; +} + +static void first_frame(void *raw_private, struct imv_image **image, int *frametime) +{ + *image = NULL; + *frametime = 0; + + struct private *private = raw_private; + private->current_frame = 0; + + gif_result code = gif_decode_frame(&private->gif, private->current_frame); + if (code != GIF_OK) { + imv_log(IMV_DEBUG, "libnsgif: failed to decode first frame\n"); + return; + } + + push_current_image(private, image, frametime); +} + +static void next_frame(void *raw_private, struct imv_image **image, int *frametime) +{ + *image = NULL; + *frametime = 0; + + struct private *private = raw_private; + + private->current_frame++; + private->current_frame %= private->gif.frame_count; + + gif_result code = gif_decode_frame(&private->gif, private->current_frame); + if (code != GIF_OK) { + imv_log(IMV_DEBUG, "libnsgif: failed to decode first frame\n"); + return; + } + + push_current_image(private, image, frametime); +} + +static const struct imv_source_vtable vtable = { + .load_first_frame = first_frame, + .load_next_frame = next_frame, + .free = free_private +}; + +static enum backend_result open_memory(void *data, size_t len, struct imv_source **src) +{ + struct private *private = calloc(1, sizeof *private); + gif_create(&private->gif, &bitmap_callbacks); + + gif_result code; + do { + code = gif_initialise(&private->gif, len, data); + } while (code == GIF_WORKING); + + if (code != GIF_OK) { + gif_finalise(&private->gif); + free(private); + imv_log(IMV_DEBUG, "libsngif: unsupported file\n"); + return BACKEND_UNSUPPORTED; + } + + *src = imv_source_create(&vtable, private); + return BACKEND_SUCCESS; +} + +static enum backend_result open_path(const char *path, struct imv_source **src) +{ + imv_log(IMV_DEBUG, "libnsgif: open_path(%s)\n", path); + + int fd = open(path, O_RDONLY); + if (fd < 0) { + return BACKEND_BAD_PATH; + } + + off_t len = lseek(fd, 0, SEEK_END); + if (len < 0) { + close(fd); + return BACKEND_BAD_PATH; + } + + void *data = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); + close(fd); + if (data == MAP_FAILED || !data) { + return BACKEND_BAD_PATH; + } + + struct private *private = calloc(1, sizeof *private); + private->data = data; + private->len = len; + gif_create(&private->gif, &bitmap_callbacks); + + gif_result code; + do { + code = gif_initialise(&private->gif, private->len, private->data); + } while (code == GIF_WORKING); + + if (code != GIF_OK) { + gif_finalise(&private->gif); + munmap(private->data, private->len); + free(private); + imv_log(IMV_DEBUG, "libsngif: unsupported file\n"); + return BACKEND_UNSUPPORTED; + } + + imv_log(IMV_DEBUG, "libnsgif: num_frames=%d\n", private->gif.frame_count); + imv_log(IMV_DEBUG, "libnsgif: width=%d\n", private->gif.width); + imv_log(IMV_DEBUG, "libnsgif: height=%d\n", private->gif.height); + + *src = imv_source_create(&vtable, private); + return BACKEND_SUCCESS; +} + + +const struct imv_backend imv_backend_libnsgif = { + .name = "libnsgif", + .description = "Tiny GIF decoding library from the NetSurf project", + .website = "https://www.netsurf-browser.org/projects/libnsgif/", + .license = "MIT", + .open_path = &open_path, + .open_memory = &open_memory, +}; -- cgit v1.2.3