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 --- .builds/archlinux.yml | 1 + .builds/debian.yml | 2 + .builds/fedora.yml | 2 + .builds/freebsd.yml | 1 + .builds/ubuntu.yml | 2 + Makefile | 6 ++ README.md | 1 + config.mk | 8 +- src/backend_libnsgif.c | 210 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.c | 5 ++ 10 files changed, 237 insertions(+), 1 deletion(-) create mode 100644 src/backend_libnsgif.c diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index c60bbf7..6186aba 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -5,6 +5,7 @@ packages: - freeimage - icu - libjpeg-turbo + - libnsgif - libpng - librsvg - libtiff diff --git a/.builds/debian.yml b/.builds/debian.yml index 81e2959..aed4546 100644 --- a/.builds/debian.yml +++ b/.builds/debian.yml @@ -23,6 +23,8 @@ tasks: - configure: | cd imv sed -i -e 's/BACKEND_\(.*\)=no/BACKEND_\1=yes/' config.mk + # libnsgif isn't packaged by debian + sed -i -e 's/BACKEND_LIBNSGIF=yes/BACKEND_LIBNSGIF=no/' config.mk cat config.mk - gcc: | cd imv diff --git a/.builds/fedora.yml b/.builds/fedora.yml index e641dde..0d07d5e 100644 --- a/.builds/fedora.yml +++ b/.builds/fedora.yml @@ -23,6 +23,8 @@ tasks: - configure: | cd imv sed -i -e 's/BACKEND_\(.*\)=no/BACKEND_\1=yes/' config.mk + # libnsgif isn't packaged by fedora + sed -i -e 's/BACKEND_LIBNSGIF=yes/BACKEND_LIBNSGIF=no/' config.mk cat config.mk - gcc: | cd imv diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 98b62e7..191ee87 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -6,6 +6,7 @@ packages: - graphics/freeimage - graphics/libGLU - graphics/libjpeg-turbo + - graphics/libnsgif - graphics/librsvg2 - graphics/png - graphics/tiff diff --git a/.builds/ubuntu.yml b/.builds/ubuntu.yml index 0477de9..be2b734 100644 --- a/.builds/ubuntu.yml +++ b/.builds/ubuntu.yml @@ -23,6 +23,8 @@ tasks: - configure: | cd imv sed -i -e 's/BACKEND_\(.*\)=no/BACKEND_\1=yes/' config.mk + # libnsgif isn't packaged by ubuntu + sed -i -e 's/BACKEND_LIBNSGIF=yes/BACKEND_LIBNSGIF=no/' config.mk cat config.mk - gcc: | cd imv diff --git a/Makefile b/Makefile index 2f0ff1d..79fd23a 100644 --- a/Makefile +++ b/Makefile @@ -89,6 +89,12 @@ ifeq ($(BACKEND_LIBRSVG),yes) override LIBS += $(shell pkg-config --libs librsvg-2.0) endif +ifeq ($(BACKEND_LIBNSGIF),yes) + SOURCES += src/backend_libnsgif.c + override CPPFLAGS += -DIMV_BACKEND_LIBNSGIF $(shell pkg-config --cflags libnsgif) + override LIBS += $(shell pkg-config --libs libnsgif) +endif + TEST_SOURCES := test/list.c test/navigator.c diff --git a/README.md b/README.md index 2410117..dacabcb 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,7 @@ Installation | libpng | | Optional. Provides PNG support. | | libjpeg-turbo | | Optional. Provides JPEG support. | | librsvg | >=v2.44 | Optional. Provides SVG support. | +| libnsgif | | Optional. Provides GIF support. | Dependencies are determined by which backends and window systems are enabled when building `imv`. You can find a summary of which backends are available and diff --git a/config.mk b/config.mk index 2889b63..a9c5941 100644 --- a/config.mk +++ b/config.mk @@ -16,7 +16,7 @@ BACKEND_FREEIMAGE=yes # libtiff # provides: tiff -# dependws: libjpeg zlib xz zstd +# depends: libjpeg zlib xz zstd # license: MIT BACKEND_LIBTIFF=no @@ -37,3 +37,9 @@ BACKEND_LIBJPEG=no # depends: gdk-pixbuf2 pango libcroco # license: LGPL BACKEND_LIBRSVG=yes + +# libnsgif https://www.netsurf-browser.org/projects/libnsgif/ +# provides: animated gif +# depends: none +# license: MIT +BACKEND_LIBNSGIF=no 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, +}; diff --git a/src/main.c b/src/main.c index ca2767e..cb0db83 100644 --- a/src/main.c +++ b/src/main.c @@ -7,6 +7,7 @@ extern const struct imv_backend imv_backend_libpng; extern const struct imv_backend imv_backend_librsvg; extern const struct imv_backend imv_backend_libtiff; extern const struct imv_backend imv_backend_libjpeg; +extern const struct imv_backend imv_backend_libnsgif; int main(int argc, char **argv) { @@ -32,6 +33,10 @@ int main(int argc, char **argv) imv_install_backend(imv, &imv_backend_librsvg); #endif +#ifdef IMV_BACKEND_LIBNSGIF + imv_install_backend(imv, &imv_backend_libnsgif); +#endif + #ifdef IMV_BACKEND_FREEIMAGE imv_install_backend(imv, &imv_backend_freeimage); #endif -- cgit v1.2.3