Krolyxon
Commit af98249

Branches

Refs →

nsxivaf98249

Add animated webp support (#20)

Author
Sam Whitehead
Date
2021-09-24 05:20 -0600
Committer
NRK · 2021-09-24 17:27 +0600
Parent
0e7ab7e
Commit
af98249b6886defcd9d456bd5a23082f9ec7f75f

+159 −2

3 files changed

  1. M Makefile +7 −1
  2. M README.md +1 −0
  3. M image.c +151 −1
M Makefile View
@@ -30,9 +30,15 @@ ifeq ($(HAVE_LIBGIF), 1)
3030 else
3131 HAVE_LIBGIF = 0
3232 endif
33+ifeq ($(HAVE_LIBWEBP), 1)
34+ OPTIONAL_LIBS += -lwebpdemux -lwebp
35+else
36+ HAVE_LIBWEBP = 0
37+endif
3338
3439 CPPFLAGS = -D_XOPEN_SOURCE=700 \
3540 -DHAVE_LIBGIF=$(HAVE_LIBGIF) -DHAVE_LIBEXIF=$(HAVE_LIBEXIF) \
41+ -DHAVE_LIBWEBP=$(HAVE_LIBWEBP) \
3642 -I/usr/include/freetype2 -I$(PREFIX)/include/freetype2
3743
3844 LDLIBS = -lImlib2 -lX11 -lXft -lfontconfig $(OPTIONAL_LIBS)
@@ -61,7 +67,7 @@ window.o: icon/data.h
6167 config.mk:
6268 @echo "GEN $@"
6369 @echo "# 0 = disable, 1 = enable" > config.mk
64− @for lib in exif gif; do \
70+ @for lib in exif gif webp; do \
6571 if echo "int main(){}" | $(CC) "-l$$lib" -o /dev/null -x c - 2>/dev/null ; then \
6672 echo "HAVE_LIB$$lib=1" | tr '[:lower:]' '[:upper:]' >> config.mk ; \
6773 fi \
M README.md View
@@ -53,6 +53,7 @@ The following libraries are optional. They are automatically enabled if installe
5353
5454 * giflib : Used for animated gif playback.
5555 * libexif : Used for auto-orientation and exif thumbnails.
56+ * libwebp : Used for animated webp playback.
5657
5758 Please make sure to install the corresponding development packages in case that
5859 you want to build nsxiv on a distribution with separate runtime and development
M image.c View
@@ -36,6 +36,12 @@
3636 enum { DEF_GIF_DELAY = 75 };
3737 #endif
3838
39+#if HAVE_LIBWEBP
40+#include <webp/decode.h>
41+#include <webp/demux.h>
42+enum { DEF_WEBP_DELAY = 75 };
43+#endif
44+
3945 float zoom_min;
4046 float zoom_max;
4147
@@ -297,6 +303,135 @@ bool img_load_gif(img_t *img, const fileinfo_t *file)
297303 }
298304 #endif /* HAVE_LIBGIF */
299305
306+
307+#if HAVE_LIBWEBP
308+bool is_webp(const char *path)
309+{
310+ /* The size (in bytes) of the largest amount of data required to verify a WebP image. */
311+ enum { max = 30 };
312+ const unsigned char fmt[max];
313+ bool ret = false;
314+ FILE *f;
315+
316+ if ((f = fopen(path, "rb")) != NULL) {
317+ if (fread((unsigned char *) fmt, 1, max, f) == max)
318+ ret = WebPGetInfo(fmt, max, NULL, NULL);
319+ fclose(f);
320+ }
321+ return ret;
322+}
323+
324+/* fframe img
325+ * NULL NULL = do nothing
326+ * x NULL = load the first frame as an Imlib_Image
327+ * NULL x = load all frames into img->multi.
328+ */
329+bool img_load_webp(const fileinfo_t *file, Imlib_Image *fframe, img_t *img)
330+{
331+ FILE *webp_file;
332+ WebPData data;
333+ data.bytes = NULL;
334+
335+ Imlib_Image im = NULL;
336+ struct WebPAnimDecoderOptions opts;
337+ WebPAnimDecoder *dec = NULL;
338+ struct WebPAnimInfo info;
339+ unsigned char *buf = NULL;
340+ int ts;
341+ const WebPDemuxer *demux;
342+ WebPIterator iter;
343+ unsigned long flags;
344+ unsigned int delay;
345+ bool err = false;
346+
347+ if ((err = fframe == NULL && img == NULL))
348+ goto fail;
349+
350+ if ((err = (webp_file = fopen(file->path, "rb")) == NULL)) {
351+ error(0, 0, "%s: Error opening webp image", file->name);
352+ goto fail;
353+ }
354+ fseek(webp_file, 0L, SEEK_END);
355+ data.size = ftell(webp_file);
356+ rewind(webp_file);
357+ data.bytes = emalloc(data.size);
358+ if ((err = fread((unsigned char *)data.bytes, 1, data.size, webp_file) != data.size)) {
359+ error(0, 0, "%s: Error reading webp image", file->name);
360+ goto fail;
361+ }
362+
363+ /* Setup the WebP Animation Decoder */
364+ if ((err = !WebPAnimDecoderOptionsInit(&opts))) {
365+ error(0, 0, "%s: WebP library version mismatch", file->name);
366+ goto fail;
367+ }
368+ opts.color_mode = MODE_BGRA;
369+ /* NOTE: Multi-threaded decoding may cause problems on some system */
370+ opts.use_threads = true;
371+ dec = WebPAnimDecoderNew(&data, &opts);
372+ if ((err = (dec == NULL) || !WebPAnimDecoderGetInfo(dec, &info))) {
373+ error(0, 0, "%s: WebP parsing or memory error (file is corrupt?)", file->name);
374+ goto fail;
375+ }
376+ demux = WebPAnimDecoderGetDemuxer(dec);
377+
378+ if (img == NULL) { /* Only get the first frame and put it into fframe. */
379+ if ((err = !WebPAnimDecoderGetNext(dec, &buf, &ts))) {
380+ error(0, 0, "%s: Error loading first frame", file->name);
381+ goto fail;
382+ }
383+ *fframe = imlib_create_image_using_copied_data(
384+ info.canvas_width, info.canvas_height, (DATA32*)buf);
385+ imlib_context_set_image(*fframe);
386+ imlib_image_set_has_alpha(WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS) & ALPHA_FLAG);
387+ } else {
388+ /* Get global information for the image */
389+ flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
390+ img->w = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
391+ img->h = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
392+ img->multi.cap = info.frame_count;
393+ img->multi.sel = 0;
394+ img->multi.frames = emalloc(info.frame_count * sizeof(img_frame_t));
395+
396+ /* Load and decode frames (also works on images with only 1 frame) */
397+ img->multi.cnt = 0;
398+ while (WebPAnimDecoderGetNext(dec, &buf, &ts)) {
399+ im = imlib_create_image_using_copied_data(
400+ info.canvas_width, info.canvas_height, (DATA32*)buf);
401+ imlib_context_set_image(im);
402+ imlib_image_set_format("webp");
403+ /* Get an iterator of this frame - used for frame info (duration, etc.) */
404+ WebPDemuxGetFrame(demux, img->multi.cnt+1, &iter);
405+ imlib_image_set_has_alpha((flags & ALPHA_FLAG) == ALPHA_FLAG);
406+ /* Store info for this frame */
407+ img->multi.frames[img->multi.cnt].im = im;
408+ delay = iter.duration > 0 ? iter.duration : DEF_WEBP_DELAY;
409+ img->multi.frames[img->multi.cnt].delay = delay;
410+ img->multi.length += img->multi.frames[img->multi.cnt].delay;
411+ img->multi.cnt++;
412+ }
413+ WebPDemuxReleaseIterator(&iter);
414+
415+ if (img->multi.cnt > 1) {
416+ imlib_context_set_image(img->im);
417+ imlib_free_image();
418+ img->im = img->multi.frames[0].im;
419+ } else if (img->multi.cnt == 1) {
420+ imlib_context_set_image(img->multi.frames[0].im);
421+ imlib_free_image();
422+ img->multi.cnt = 0;
423+ }
424+ imlib_context_set_image(img->im);
425+ }
426+ imlib_image_set_format("webp");
427+fail:
428+ if (dec != NULL)
429+ WebPAnimDecoderDelete(dec);
430+ free((unsigned char *)data.bytes);
431+ return !err;
432+}
433+#endif /* HAVE_LIBWEBP */
434+
300435 Imlib_Image img_open(const fileinfo_t *file)
301436 {
302437 struct stat st;
@@ -305,10 +440,21 @@ Imlib_Image img_open(const fileinfo_t *file)
305440 if (access(file->path, R_OK) == 0 &&
306441 stat(file->path, &st) == 0 && S_ISREG(st.st_mode))
307442 {
308− im = imlib_load_image(file->path);
443+#if HAVE_LIBWEBP
444+ if (is_webp(file->path))
445+ img_load_webp(file, &im, NULL);
446+ else
447+#endif
448+ im = imlib_load_image(file->path);
309449 if (im != NULL) {
310450 imlib_context_set_image(im);
451+#if HAVE_LIBWEBP
452+ const char *fmt;
453+ if ((fmt = imlib_image_format()) != NULL && !STREQ(fmt, "webp") &&
454+ imlib_image_get_data_for_reading_only() == NULL) {
455+#else
311456 if (imlib_image_get_data_for_reading_only() == NULL) {
457+#endif
312458 imlib_free_image();
313459 im = NULL;
314460 }
@@ -336,6 +482,10 @@ bool img_load(img_t *img, const fileinfo_t *file)
336482 #if HAVE_LIBGIF
337483 if (STREQ(fmt, "gif"))
338484 img_load_gif(img, file);
485+#endif
486+#if HAVE_LIBWEBP
487+ if (STREQ(fmt, "webp"))
488+ img_load_webp(file, NULL, img);
339489 #endif
340490 }
341491 img->w = imlib_image_get_width();