Subject: Re: bug#54242: Emacs can't display animated webp images

From: Lars Ingebrigtsen <[email-masked]>
Date: Thu, 07 Apr 2022 16:42:44 +0200

Lars Ingebrigtsen <[email-masked]> writes:

> Here's an example animated webp image, and indeed, Emacs fails at
> displaying it:
>
> https://mathiasbynens.be/demo/animated-webp-supported.webp

I've started poking at this just to see what's necessary.  As a first
attempt, I thought we could at least display the first frame in the
animate image, so I did the following according to the er documentation
from Google.

And indeed, I end up with something that looks like a mangled version of
the nyan cat in the test URL above -- but with wrong width and height or
something?  

The first image in the animation is very simple, though -- frame
width/height is the same as the canvas width height...  so I must be
doing something obviously wrong:

Data: 400 400 400 400 0 0

Anybody spot the obvious error?

There's also the "animation" functions from libwebp, but they are even
less clear as to how they're supposed to be used.

Amusingly, imagemagick also renders the animated webp image wrong, but
in a different way.

diff --git a/configure.ac b/configure.ac
index cda2a04be9..fc409ce8f1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2692,6 +2692,7 @@ AC_DEFUN
 
       EMACS_CHECK_MODULES([WEBP], [$WEBP_MODULE])
       AC_SUBST(WEBP_CFLAGS)
+      WEBP_LIBS="-lwebp -lwebpdemux"
       AC_SUBST(WEBP_LIBS)
    fi
    if test $HAVE_WEBP = yes; then
diff --git a/src/image.c b/src/image.c
index 519eafb904..f175be8923 100644
--- a/src/image.c
+++ b/src/image.c
@@ -9053,6 +9053,7 @@ gif_load (struct frame *f, struct image *img)
  ***********************************************************************/
 
 #include "webp/decode.h"
+#include "webp/demux.h"
 
 /* Indices of image specification fields in webp_format, below.  */
 
@@ -9224,19 +9225,61 @@ webp_load (struct frame *f, struct image *img)
       goto webp_error1;
     }
 
-  /* Decode WebP data.  */
-  uint8_t *decoded;
-  int width, height;
-  if (features.has_alpha)
-    /* Linear [r0, g0, b0, a0, r1, g1, b1, a1, ...] order.  */
-    decoded = WebPDecodeRGBA (contents, size, &width, &height);
+  uint8_t *decoded = NULL;
+  int width, height, x_offset = 0, y_offset = 0, fwidth, fheight;
+
+  if (features.has_animation)
+    {
+      /* Animated image.  */
+      WebPData webp_data;
+      webp_data.bytes = WebPMalloc (size);
+      webp_data.size = size;
+      memcpy ((void*) webp_data.bytes, contents, size);
+      
+      WebPDemuxer* demux = WebPDemux(&webp_data);
+      width = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH);
+      height = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT);
+
+      WebPIterator iter;
+      if (WebPDemuxGetFrame (demux, 1, &iter)) {
+	do {
+	  fprintf(stderr, "Complete is %d %d\n", iter.complete, iter.has_alpha);
+	  /* Decode WebP data.  */
+	  if (iter.has_alpha)
+	    /* Linear [r0, g0, b0, a0, r1, g1, b1, a1, ...] order.  */
+	    decoded = WebPDecodeRGBA (iter.fragment.bytes, iter.fragment.size,
+				      &fwidth, &fheight);
+	  else
+	    /* Linear [r0, g0, b0, r1, g1, b1, ...] order.  */
+	    decoded = WebPDecodeRGB (iter.fragment.bytes, iter.fragment.size,
+				     &fwidth, &fheight);
+	  x_offset = iter.x_offset;
+	  y_offset = iter.y_offset;
+	  fwidth = iter.width;
+	  fheight = iter.height;
+	  break;
+	} while (WebPDemuxNextFrame (&iter));
+	WebPDemuxReleaseIterator (&iter);
+      }
+      WebPDataClear (&webp_data);
+      WebPDemuxDelete (demux);
+    }
   else
-    /* Linear [r0, g0, b0, r1, g1, b1, ...] order.  */
-    decoded = WebPDecodeRGB (contents, size, &width, &height);
+    {
+      /* Non-animated image.  */
+      if (features.has_alpha)
+	/* Linear [r0, g0, b0, a0, r1, g1, b1, a1, ...] order.  */
+	decoded = WebPDecodeRGBA (contents, size, &width, &height);
+      else
+	/* Linear [r0, g0, b0, r1, g1, b1, ...] order.  */
+	decoded = WebPDecodeRGB (contents, size, &width, &height);
+      fwidth = width;
+      fheight = height;
+    }
 
   if (!decoded)
     {
-      image_error ("Error when interpreting WebP image data");
+      image_error ("Error when decoding WebP image data");
       goto webp_error1;
     }
 
@@ -9266,9 +9309,11 @@ webp_load (struct frame *f, struct image *img)
   init_color_table ();
 
   uint8_t *p = decoded;
-  for (int y = 0; y < height; ++y)
+  fprintf(stderr, "Data: %d %d %d %d %d %d\n", width, height, fwidth, fheight,
+	  x_offset, y_offset);
+  for (int y = y_offset; y < fheight; ++y)
     {
-      for (int x = 0; x < width; ++x)
+      for (int x = x_offset; x < fwidth; ++x)
 	{
 	  int r = *p++ << 8;
 	  int g = *p++ << 8;