aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarry Jeffery <harry@exec64.co.uk>2019-09-02 22:09:14 +0100
committerHarry Jeffery <harry@exec64.co.uk>2019-09-02 22:25:48 +0100
commita6deca0ad390d29ada6d72584a1be835a3913139 (patch)
tree2d9a8dc2900d91528535a5b10a056f2d5e05177b
parentc04a71d4a1306d23bd8f4ae2f4eba24290c78aeb (diff)
downloadimv-a6deca0ad390d29ada6d72584a1be835a3913139.tar.gz
libnsgif: Add libnsgif backend
-rw-r--r--.builds/archlinux.yml1
-rw-r--r--.builds/debian.yml2
-rw-r--r--.builds/fedora.yml2
-rw-r--r--.builds/freebsd.yml1
-rw-r--r--.builds/ubuntu.yml2
-rw-r--r--Makefile6
-rw-r--r--README.md1
-rw-r--r--config.mk8
-rw-r--r--src/backend_libnsgif.c210
-rw-r--r--src/main.c5
10 files changed, 237 insertions, 1 deletions
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 <fcntl.h>
+#include <libnsgif.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+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