Description: Use animated webp function from imlib2 1.7.1
 From patch on bugs.d.o:
 "nsxiv fails to even load animated webp images this is intended as v29
 depends on imlib2 1.7.5 for animated webp support, this reverts the webp
 function to the older version that works with 1.7.1, in the future the plan
 for nsxiv is to depend on 1.8.0 to handle multi frame images, altho the
 change was intended for v30 it is currenlty on hold as it causes performance
 regressions."
Author: Edgar Yllescas <ed.ylles1997@gmail.com>
Bug: https://github.com/nsxiv/nsxiv/issues/193
Forwarded: not-needed
Reviewed-by: Nick Morrott <nickm@debian.org>
Last-Update: 2022-08-11
---
--- a/README.md
+++ b/README.md
@@ -79,7 +79,6 @@
   * libexif : Used for auto-orientation and exif thumbnails.
     Disable via `HAVE_LIBEXIF=0`
   * libwebp : Used for animated webp playback.
-    (NOTE: animated webp also requires Imlib2 v1.7.5 or above)
     Disabled via `HAVE_LIBWEBP=0`.
 
 Please make sure to install the corresponding development packages in case that
--- a/image.c
+++ b/image.c
@@ -135,23 +135,6 @@
 }
 #endif
 
-#if HAVE_LIBGIF || HAVE_LIBWEBP
-static void img_multiframe_context_set(img_t *img)
-{
-	if (img->multi.cnt > 1) {
-		imlib_context_set_image(img->im);
-		imlib_free_image();
-		img->im = img->multi.frames[0].im;
-	} else if (img->multi.cnt == 1) {
-		imlib_context_set_image(img->multi.frames[0].im);
-		imlib_free_image();
-		img->multi.cnt = 0;
-	}
-
-	imlib_context_set_image(img->im);
-}
-#endif
-
 #if HAVE_LIBGIF
 static bool img_load_gif(img_t *img, const fileinfo_t *file)
 {
@@ -315,7 +298,17 @@
 	if (err && (file->flags & FF_WARN))
 		error(0, 0, "%s: Corrupted gif file", file->name);
 
-	img_multiframe_context_set(img);
+	if (img->multi.cnt > 1) {
+		imlib_context_set_image(img->im);
+		imlib_free_image();
+		img->im = img->multi.frames[0].im;
+	} else if (img->multi.cnt == 1) {
+		imlib_context_set_image(img->multi.frames[0].im);
+		imlib_free_image();
+		img->multi.cnt = 0;
+	}
+
+	imlib_context_set_image(img->im);
 
 	return !err;
 }
@@ -323,35 +316,60 @@
 
 
 #if HAVE_LIBWEBP
-static bool img_load_webp(img_t *img, const fileinfo_t *file)
+static bool is_webp(const char *path)
+{
+	/* The size (in bytes) of the largest amount of data required to verify a WebP image. */
+	enum { max = 30 };
+	const unsigned char fmt[max];
+	bool ret = false;
+	FILE *f;
+
+	if ((f = fopen(path, "rb")) != NULL) {
+		if (fread((unsigned char *) fmt, 1, max, f) == max)
+			ret = WebPGetInfo(fmt, max, NULL, NULL);
+		fclose(f);
+	}
+	return ret;
+}
+
+/* fframe   img
+ * NULL     NULL  = do nothing
+ * x        NULL  = load the first frame as an Imlib_Image
+ * NULL     x     = load all frames into img->multi.
+ */
+static bool img_load_webp(const fileinfo_t *file, Imlib_Image *fframe, img_t *img)
 {
 	FILE *webp_file;
 	WebPData data;
+
 	Imlib_Image im = NULL;
 	struct WebPAnimDecoderOptions opts;
 	WebPAnimDecoder *dec = NULL;
 	struct WebPAnimInfo info;
-	unsigned char *buf = NULL, *bytes = NULL;
+	unsigned char *buf = NULL;
 	int ts;
 	const WebPDemuxer *demux;
 	WebPIterator iter;
 	unsigned long flags;
 	unsigned int delay;
 	bool err = false;
+	data.bytes = NULL;
 
-	if ((webp_file = fopen(file->path, "rb")) == NULL) {
-		error(0, errno, "%s: Error opening webp image", file->name);
-		return false;
+	if ((err = fframe == NULL && img == NULL))
+		goto fail;
+
+	if ((err = (webp_file = fopen(file->path, "rb")) == NULL)) {
+		error(0, 0, "%s: Error opening webp image", file->name);
+		goto fail;
 	}
 	fseek(webp_file, 0L, SEEK_END);
 	data.size = ftell(webp_file);
 	rewind(webp_file);
-	bytes = emalloc(data.size);
-	if ((err = fread(bytes, 1, data.size, webp_file) != data.size)) {
+	data.bytes = emalloc(data.size);
+	if ((err = fread((unsigned char *)data.bytes, 1, data.size, webp_file) != data.size)) {
 		error(0, 0, "%s: Error reading webp image", file->name);
 		goto fail;
 	}
-	data.bytes = bytes;
 
 	/* Setup the WebP Animation Decoder */
 	if ((err = !WebPAnimDecoderOptionsInit(&opts))) {
@@ -368,42 +386,65 @@
 	}
 	demux = WebPAnimDecoderGetDemuxer(dec);
 
-	/* Get global information for the image */
-	flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
-	img->w = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
-	img->h = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
-
-	if (info.frame_count > img->multi.cap) {
-		img->multi.cap = info.frame_count;
-		img->multi.frames = erealloc(img->multi.frames,
-		                             img->multi.cap * sizeof(img_frame_t));
-	}
+	if (img == NULL) { /* Only get the first frame and put it into fframe. */
+		if ((err = !WebPAnimDecoderGetNext(dec, &buf, &ts))) {
+			error(0, 0, "%s: Error loading first frame", file->name);
+			goto fail;
+		}
+		*fframe = imlib_create_image_using_copied_data(
+		          info.canvas_width, info.canvas_height, (DATA32*)buf);
+		imlib_context_set_image(*fframe);
+		imlib_image_set_has_alpha(WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS) & ALPHA_FLAG);
+	} else {
+		/* Get global information for the image */
+		flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS);
+		img->w = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
+		img->h = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
+
+		if (img->multi.cap == 0) {
+			img->multi.cap = info.frame_count;
+			img->multi.frames = emalloc(img->multi.cap * sizeof(img_frame_t));
+		} else if (info.frame_count > img->multi.cap) {
+			img->multi.cap = info.frame_count;
+			img->multi.frames = erealloc(img->multi.frames,
+			                             img->multi.cap * sizeof(img_frame_t));
+		}
 
-	/* Load and decode frames (also works on images with only 1 frame) */
-	img->multi.cnt = img->multi.sel = 0;
-	while (WebPAnimDecoderGetNext(dec, &buf, &ts)) {
-		im = imlib_create_image_using_copied_data(
-		     info.canvas_width, info.canvas_height, (DATA32*)buf);
-		imlib_context_set_image(im);
-		imlib_image_set_format("webp");
-		/* Get an iterator of this frame - used for frame info (duration, etc.) */
-		WebPDemuxGetFrame(demux, img->multi.cnt+1, &iter);
-		imlib_image_set_has_alpha((flags & ALPHA_FLAG) == ALPHA_FLAG);
-		/* Store info for this frame */
-		img->multi.frames[img->multi.cnt].im = im;
-		delay = iter.duration > 0 ? iter.duration : DEF_WEBP_DELAY;
-		img->multi.frames[img->multi.cnt].delay = delay;
-		img->multi.length += img->multi.frames[img->multi.cnt].delay;
-		img->multi.cnt++;
-	}
-	WebPDemuxReleaseIterator(&iter);
+		/* Load and decode frames (also works on images with only 1 frame) */
+		img->multi.cnt = img->multi.sel = 0;
+		while (WebPAnimDecoderGetNext(dec, &buf, &ts)) {
+			im = imlib_create_image_using_copied_data(
+			     info.canvas_width, info.canvas_height, (DATA32*)buf);
+			imlib_context_set_image(im);
+			imlib_image_set_format("webp");
+			/* Get an iterator of this frame - used for frame info (duration, etc.) */
+			WebPDemuxGetFrame(demux, img->multi.cnt+1, &iter);
+			imlib_image_set_has_alpha((flags & ALPHA_FLAG) == ALPHA_FLAG);
+			/* Store info for this frame */
+			img->multi.frames[img->multi.cnt].im = im;
+			delay = iter.duration > 0 ? iter.duration : DEF_WEBP_DELAY;
+			img->multi.frames[img->multi.cnt].delay = delay;
+			img->multi.length += img->multi.frames[img->multi.cnt].delay;
+			img->multi.cnt++;
+		}
+		WebPDemuxReleaseIterator(&iter);
 
-	img_multiframe_context_set(img);
+		if (img->multi.cnt > 1) {
+			imlib_context_set_image(img->im);
+			imlib_free_image();
+			img->im = img->multi.frames[0].im;
+		} else if (img->multi.cnt == 1) {
+			imlib_context_set_image(img->multi.frames[0].im);
+			imlib_free_image();
+			img->multi.cnt = 0;
+		}
+		imlib_context_set_image(img->im);
+	}
+	imlib_image_set_format("webp");
 fail:
 	if (dec != NULL)
 		WebPAnimDecoderDelete(dec);
-	free(bytes);
-	fclose(webp_file);
+	free((unsigned char *)data.bytes);
 	return !err;
 }
 #endif /* HAVE_LIBWEBP */
@@ -416,7 +457,12 @@
 	if (access(file->path, R_OK) == 0 &&
 	    stat(file->path, &st) == 0 && S_ISREG(st.st_mode))
 	{
-		im = imlib_load_image(file->path);
+#if HAVE_LIBWEBP
+		if (is_webp(file->path))
+			img_load_webp(file, &im, NULL);
+		else
+#endif
+			im = imlib_load_image(file->path);
 		if (im != NULL) {
 			imlib_context_set_image(im);
 			if (imlib_image_get_data_for_reading_only() == NULL) {
@@ -454,7 +500,7 @@
 #endif
 #if HAVE_LIBWEBP
 		if (STREQ(fmt, "webp"))
-			img_load_webp(img, file);
+			img_load_webp(file, NULL, img);
 #endif
 #if HAVE_LIBEXIF && defined(IMLIB2_VERSION)
 		if (!STREQ(fmt, "jpeg") && !STREQ(fmt, "jpg"))
