mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-28 00:01:33 -08:00
Add support for animated webp images
* configure.ac (HAVE_RSVG): Also include the webpdemux library. It was new in version 0.4.4, and we require 0.6.0, so it should be safe. * src/image.c: Include demux.h. (enum webp_keyword_index, webp_format): Include :index for animations. (init_webp_functions): Add Windows LOAD_DLLs. (struct webp_cache, webp_create_cache) (webp_prune_animation_cache, webp_get_animation_cache): New functions. (webp_load): Support animated webp images (bug#54242).
This commit is contained in:
parent
735b451910
commit
d82e1a873d
2 changed files with 208 additions and 13 deletions
|
|
@ -2695,6 +2695,9 @@ if test "${with_webp}" != "no"; then
|
|||
WEBP_MODULE="libwebp >= $WEBP_REQUIRED"
|
||||
|
||||
EMACS_CHECK_MODULES([WEBP], [$WEBP_MODULE])
|
||||
if test "$HAVE_WEBP" = "yes"; then
|
||||
WEBP_LIBS="-lwebp -lwebpdemux"
|
||||
fi
|
||||
AC_SUBST(WEBP_CFLAGS)
|
||||
AC_SUBST(WEBP_LIBS)
|
||||
fi
|
||||
|
|
|
|||
218
src/image.c
218
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. */
|
||||
|
||||
|
|
@ -9067,6 +9068,7 @@ enum webp_keyword_index
|
|||
WEBP_ALGORITHM,
|
||||
WEBP_HEURISTIC_MASK,
|
||||
WEBP_MASK,
|
||||
WEBP_INDEX,
|
||||
WEBP_BACKGROUND,
|
||||
WEBP_LAST
|
||||
};
|
||||
|
|
@ -9085,6 +9087,7 @@ static const struct image_keyword webp_format[WEBP_LAST] =
|
|||
{":conversion", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
|
||||
{":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
|
||||
{":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
|
||||
{":index", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0},
|
||||
{":background", IMAGE_STRING_OR_NIL_VALUE, 0}
|
||||
};
|
||||
|
||||
|
|
@ -9117,6 +9120,17 @@ DEF_DLL_FN (VP8StatusCode, WebPGetFeaturesInternal,
|
|||
DEF_DLL_FN (uint8_t *, WebPDecodeRGBA, (const uint8_t *, size_t, int *, int *));
|
||||
DEF_DLL_FN (uint8_t *, WebPDecodeRGB, (const uint8_t *, size_t, int *, int *));
|
||||
DEF_DLL_FN (void, WebPFree, (void *));
|
||||
DEF_DLL_FN (uint32_t, WebPDemuxGetI, (const WebPDemuxer* dmux,
|
||||
WebPFormatFeature feature));
|
||||
DEF_DLL_FN (WebPDemuxer*, WebPDemux, (const WebPData* data));
|
||||
DEF_DLL_FN (void, WebPDemuxDelete, (WebPDemuxer* dmux));
|
||||
DEF_DLL_FN (int, WebPAnimDecoderGetNext,
|
||||
(WebPAnimDecoder* dec, uint8_t** buf, int* timestamp));
|
||||
DEF_DLL_FN (WebPAnimDecoder*, WebPAnimDecoderNew,
|
||||
(const WebPData* webp_data,
|
||||
const WebPAnimDecoderOptions* dec_options));
|
||||
DEF_DLL_FN (int, WebPAnimDecoderHasMoreFrames, (const WebPAnimDecoder* dec));
|
||||
DEF_DLL_FN (void, WebPAnimDecoderDelete, (WebPAnimDecoder* dec));
|
||||
|
||||
static bool
|
||||
init_webp_functions (void)
|
||||
|
|
@ -9131,6 +9145,13 @@ init_webp_functions (void)
|
|||
LOAD_DLL_FN (library, WebPDecodeRGBA);
|
||||
LOAD_DLL_FN (library, WebPDecodeRGB);
|
||||
LOAD_DLL_FN (library, WebPFree);
|
||||
LOAD_DLL_FN (library, WebPDemuxGetI);
|
||||
LOAD_DLL_FN (library, WebPDemux);
|
||||
LOAD_DLL_FN (library, WebPDemuxDelete);
|
||||
LOAD_DLL_FN (library, WebPAnimDecoderGetNext);
|
||||
LOAD_DLL_FN (library, WebPAnimDecoderNew);
|
||||
LOAD_DLL_FN (library, WebPAnimDecoderHasMoreFrames);
|
||||
LOAD_DLL_FN (library, WebPAnimDecoderDelete);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -9139,6 +9160,13 @@ init_webp_functions (void)
|
|||
#undef WebPDecodeRGBA
|
||||
#undef WebPDecodeRGB
|
||||
#undef WebPFree
|
||||
#undef WebPDemuxGetI
|
||||
#undef WebPDemux
|
||||
#undef WebPDemuxDelete
|
||||
#undef WebPAnimDecoderGetNext
|
||||
#undef WebPAnimDecoderNew
|
||||
#undef WebPAnimDecoderHasMoreFrames
|
||||
#undef WebPAnimDecoderDelete
|
||||
|
||||
#define WebPGetInfo fn_WebPGetInfo
|
||||
#define WebPGetFeatures(d,s,f) \
|
||||
|
|
@ -9146,9 +9174,92 @@ init_webp_functions (void)
|
|||
#define WebPDecodeRGBA fn_WebPDecodeRGBA
|
||||
#define WebPDecodeRGB fn_WebPDecodeRGB
|
||||
#define WebPFree fn_WebPFree
|
||||
#define WebPDemuxGetI fn_WebPDemuxGetI
|
||||
#define WebPDemux fn_WebPDemux
|
||||
#define WebPDemuxDelete fn_WebPDemuxDelete
|
||||
#define WebPAnimDecoderGetNext fn_WebPAnimDecoderGetNext
|
||||
#define WebPAnimDecoderNew fn_WebPAnimDecoderNew
|
||||
#define WebPAnimDecoderHasMoreFrames fn_WebPAnimDecoderHasMoreFrames
|
||||
#define WebPAnimDecoderDelete fn_WebPAnimDecoderDelete
|
||||
|
||||
#endif /* WINDOWSNT */
|
||||
|
||||
/* To speed webp animations up, we keep a cache (based on EQ-ness of
|
||||
the image spec/object) where we put the libwebp animator
|
||||
iterator. */
|
||||
|
||||
struct webp_cache
|
||||
{
|
||||
Lisp_Object spec;
|
||||
WebPAnimDecoder* anim;
|
||||
int index, width, height, frames;
|
||||
struct timespec update_time;
|
||||
struct webp_cache *next;
|
||||
};
|
||||
|
||||
static struct webp_cache *webp_cache = NULL;
|
||||
|
||||
static struct webp_cache *
|
||||
webp_create_cache (Lisp_Object spec)
|
||||
{
|
||||
struct webp_cache *cache = xmalloc (sizeof (struct webp_cache));
|
||||
cache->anim = NULL;
|
||||
|
||||
cache->index = 0;
|
||||
cache->next = NULL;
|
||||
/* FIXME: Does this need gc protection? */
|
||||
cache->spec = spec;
|
||||
return cache;
|
||||
}
|
||||
|
||||
/* Discard cached images that haven't been used for a minute. */
|
||||
static void
|
||||
webp_prune_animation_cache (void)
|
||||
{
|
||||
struct webp_cache **pcache = &webp_cache;
|
||||
struct timespec old = timespec_sub (current_timespec (),
|
||||
make_timespec (60, 0));
|
||||
|
||||
while (*pcache)
|
||||
{
|
||||
struct webp_cache *cache = *pcache;
|
||||
if (timespec_cmp (old, cache->update_time) <= 0)
|
||||
pcache = &cache->next;
|
||||
else
|
||||
{
|
||||
if (cache->anim)
|
||||
WebPAnimDecoderDelete (cache->anim);
|
||||
*pcache = cache->next;
|
||||
xfree (cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct webp_cache *
|
||||
webp_get_animation_cache (Lisp_Object spec)
|
||||
{
|
||||
struct webp_cache *cache;
|
||||
struct webp_cache **pcache = &webp_cache;
|
||||
|
||||
webp_prune_animation_cache ();
|
||||
|
||||
while (1)
|
||||
{
|
||||
cache = *pcache;
|
||||
if (! cache)
|
||||
{
|
||||
*pcache = cache = webp_create_cache (spec);
|
||||
break;
|
||||
}
|
||||
if (EQ (spec, cache->spec))
|
||||
break;
|
||||
pcache = &cache->next;
|
||||
}
|
||||
|
||||
cache->update_time = current_timespec ();
|
||||
return cache;
|
||||
}
|
||||
|
||||
/* Load WebP image IMG for use on frame F. Value is true if
|
||||
successful. */
|
||||
|
||||
|
|
@ -9158,6 +9269,9 @@ webp_load (struct frame *f, struct image *img)
|
|||
ptrdiff_t size = 0;
|
||||
uint8_t *contents;
|
||||
Lisp_Object file = Qnil;
|
||||
int frames = 0;
|
||||
double delay = 0;
|
||||
WebPAnimDecoder* anim = NULL;
|
||||
|
||||
/* Open the WebP file. */
|
||||
Lisp_Object specified_file = image_spec_value (img->spec, QCfile, NULL);
|
||||
|
|
@ -9201,6 +9315,9 @@ webp_load (struct frame *f, struct image *img)
|
|||
goto webp_error1;
|
||||
}
|
||||
|
||||
Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL);
|
||||
ptrdiff_t idx = FIXNUMP (image_number) ? XFIXNAT (image_number) : 0;
|
||||
|
||||
/* Get WebP features. */
|
||||
WebPBitstreamFeatures features;
|
||||
VP8StatusCode result = WebPGetFeatures (contents, size, &features);
|
||||
|
|
@ -9224,19 +9341,76 @@ webp_load (struct frame *f, struct image *img)
|
|||
goto webp_error1;
|
||||
}
|
||||
|
||||
/* Decode WebP data. */
|
||||
uint8_t *decoded;
|
||||
uint8_t *decoded = NULL;
|
||||
int width, height;
|
||||
if (features.has_alpha)
|
||||
/* Linear [r0, g0, b0, a0, r1, g1, b1, a1, ...] order. */
|
||||
decoded = WebPDecodeRGBA (contents, size, &width, &height);
|
||||
|
||||
if (features.has_animation)
|
||||
{
|
||||
/* Animated image. */
|
||||
WebPData webp_data;
|
||||
webp_data.bytes = contents;
|
||||
webp_data.size = size;
|
||||
int timestamp;
|
||||
|
||||
struct webp_cache* cache = webp_get_animation_cache (img->spec);
|
||||
/* Get the next frame from the animation cache. */
|
||||
if (cache->anim && cache->index == idx - 1)
|
||||
{
|
||||
WebPAnimDecoderGetNext (cache->anim, &decoded, ×tamp);
|
||||
delay = timestamp;
|
||||
cache->index++;
|
||||
anim = cache->anim;
|
||||
width = cache->width;
|
||||
height = cache->height;
|
||||
frames = cache->frames;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Start a new cache entry. */
|
||||
if (cache->anim)
|
||||
WebPAnimDecoderDelete (cache->anim);
|
||||
|
||||
/* Get the width/height of the total image. */
|
||||
WebPDemuxer* demux = WebPDemux (&webp_data);
|
||||
cache->width = width = WebPDemuxGetI (demux, WEBP_FF_CANVAS_WIDTH);
|
||||
cache->height = height = WebPDemuxGetI (demux,
|
||||
WEBP_FF_CANVAS_HEIGHT);
|
||||
cache->frames = frames = WebPDemuxGetI (demux, WEBP_FF_FRAME_COUNT);
|
||||
WebPDemuxDelete (demux);
|
||||
|
||||
WebPAnimDecoderOptions dec_options;
|
||||
WebPAnimDecoderOptionsInit (&dec_options);
|
||||
anim = WebPAnimDecoderNew (&webp_data, &dec_options);
|
||||
|
||||
cache->anim = anim;
|
||||
cache->index = idx;
|
||||
|
||||
while (WebPAnimDecoderHasMoreFrames (anim)) {
|
||||
WebPAnimDecoderGetNext (anim, &decoded, ×tamp);
|
||||
/* Each frame has its own delay, but we don't really support
|
||||
that. So just use the delay from the first frame. */
|
||||
if (delay == 0)
|
||||
delay = timestamp;
|
||||
/* Stop when we get to the desired index. */
|
||||
if (idx-- == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
if (!decoded)
|
||||
{
|
||||
image_error ("Error when interpreting WebP image data");
|
||||
image_error ("Error when decoding WebP image data");
|
||||
goto webp_error1;
|
||||
}
|
||||
|
||||
|
|
@ -9255,7 +9429,8 @@ webp_load (struct frame *f, struct image *img)
|
|||
/* Create an image and pixmap serving as mask if the WebP image
|
||||
contains an alpha channel. */
|
||||
if (features.has_alpha
|
||||
&& !image_create_x_image_and_pixmap (f, img, width, height, 1, &mask_img, true))
|
||||
&& !image_create_x_image_and_pixmap (f, img, width, height, 1,
|
||||
&mask_img, true))
|
||||
{
|
||||
image_destroy_x_image (ximg);
|
||||
image_clear_image_1 (f, img, CLEAR_IMAGE_PIXMAP);
|
||||
|
|
@ -9265,6 +9440,13 @@ webp_load (struct frame *f, struct image *img)
|
|||
/* Fill the X image and mask from WebP data. */
|
||||
init_color_table ();
|
||||
|
||||
img->corners[TOP_CORNER] = 0;
|
||||
img->corners[LEFT_CORNER] = 0;
|
||||
img->corners[BOT_CORNER]
|
||||
= img->corners[TOP_CORNER] + height;
|
||||
img->corners[RIGHT_CORNER]
|
||||
= img->corners[LEFT_CORNER] + width;
|
||||
|
||||
uint8_t *p = decoded;
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
|
|
@ -9279,7 +9461,7 @@ webp_load (struct frame *f, struct image *img)
|
|||
image. WebP allows up to 256 levels of partial transparency.
|
||||
We handle this like with PNG (which see), using the frame's
|
||||
background color to combine the image with. */
|
||||
if (features.has_alpha)
|
||||
if (features.has_alpha || anim)
|
||||
{
|
||||
if (mask_img)
|
||||
PUT_PIXEL (mask_img, x, y, *p > 0 ? PIX_MASK_DRAW : PIX_MASK_RETAIN);
|
||||
|
|
@ -9310,14 +9492,24 @@ webp_load (struct frame *f, struct image *img)
|
|||
img->width = width;
|
||||
img->height = height;
|
||||
|
||||
/* Return animation data. */
|
||||
img->lisp_data = Fcons (Qcount,
|
||||
Fcons (make_fixnum (frames),
|
||||
img->lisp_data));
|
||||
img->lisp_data = Fcons (Qdelay,
|
||||
Fcons (make_float (delay / 1000),
|
||||
img->lisp_data));
|
||||
|
||||
/* Clean up. */
|
||||
WebPFree (decoded);
|
||||
if (!anim)
|
||||
WebPFree (decoded);
|
||||
if (NILP (specified_data))
|
||||
xfree (contents);
|
||||
return true;
|
||||
|
||||
webp_error2:
|
||||
WebPFree (decoded);
|
||||
if (!anim)
|
||||
WebPFree (decoded);
|
||||
|
||||
webp_error1:
|
||||
if (NILP (specified_data))
|
||||
|
|
@ -9484,7 +9676,7 @@ imagemagick_filename_hint (Lisp_Object spec, char hint_buffer[MaxTextExtent])
|
|||
(which is the first one, and then there's a number of images that
|
||||
follow. If following images have non-transparent colors, these are
|
||||
composed "on top" of the master image. So, in general, one has to
|
||||
compute ann the preceding images to be able to display a particular
|
||||
compute all the preceding images to be able to display a particular
|
||||
sub-image.
|
||||
|
||||
Computing all the preceding images is too slow, so we maintain a
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue