aboutsummaryrefslogtreecommitdiff
path: root/src
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 /src
parentc04a71d4a1306d23bd8f4ae2f4eba24290c78aeb (diff)
downloadimv-a6deca0ad390d29ada6d72584a1be835a3913139.tar.gz
libnsgif: Add libnsgif backend
Diffstat (limited to 'src')
-rw-r--r--src/backend_libnsgif.c210
-rw-r--r--src/main.c5
2 files changed, 215 insertions, 0 deletions
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