aboutsummaryrefslogtreecommitdiffstats
path: root/src/image.c
diff options
context:
space:
mode:
authorLars Ingebrigtsen2022-04-10 13:12:30 +0200
committerLars Ingebrigtsen2022-04-10 13:12:41 +0200
commitd82e1a873df381b2c35bc9036da5665468bdfd31 (patch)
tree0b3067244c3c513ac70a4415eca9a1c630040d9a /src/image.c
parent735b45191041778824460807ee5bf4d1cebd3421 (diff)
downloademacs-d82e1a873df381b2c35bc9036da5665468bdfd31.tar.gz
emacs-d82e1a873df381b2c35bc9036da5665468bdfd31.zip
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).
Diffstat (limited to 'src/image.c')
-rw-r--r--src/image.c218
1 files changed, 205 insertions, 13 deletions
diff --git a/src/image.c b/src/image.c
index 519eafb9047..64438ef9678 100644
--- a/src/image.c
+++ b/src/image.c
@@ -9053,6 +9053,7 @@ gif_load (struct frame *f, struct image *img)
9053 ***********************************************************************/ 9053 ***********************************************************************/
9054 9054
9055#include "webp/decode.h" 9055#include "webp/decode.h"
9056#include "webp/demux.h"
9056 9057
9057/* Indices of image specification fields in webp_format, below. */ 9058/* Indices of image specification fields in webp_format, below. */
9058 9059
@@ -9067,6 +9068,7 @@ enum webp_keyword_index
9067 WEBP_ALGORITHM, 9068 WEBP_ALGORITHM,
9068 WEBP_HEURISTIC_MASK, 9069 WEBP_HEURISTIC_MASK,
9069 WEBP_MASK, 9070 WEBP_MASK,
9071 WEBP_INDEX,
9070 WEBP_BACKGROUND, 9072 WEBP_BACKGROUND,
9071 WEBP_LAST 9073 WEBP_LAST
9072}; 9074};
@@ -9085,6 +9087,7 @@ static const struct image_keyword webp_format[WEBP_LAST] =
9085 {":conversion", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, 9087 {":conversion", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
9086 {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, 9088 {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
9087 {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}, 9089 {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
9090 {":index", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0},
9088 {":background", IMAGE_STRING_OR_NIL_VALUE, 0} 9091 {":background", IMAGE_STRING_OR_NIL_VALUE, 0}
9089}; 9092};
9090 9093
@@ -9117,6 +9120,17 @@ DEF_DLL_FN (VP8StatusCode, WebPGetFeaturesInternal,
9117DEF_DLL_FN (uint8_t *, WebPDecodeRGBA, (const uint8_t *, size_t, int *, int *)); 9120DEF_DLL_FN (uint8_t *, WebPDecodeRGBA, (const uint8_t *, size_t, int *, int *));
9118DEF_DLL_FN (uint8_t *, WebPDecodeRGB, (const uint8_t *, size_t, int *, int *)); 9121DEF_DLL_FN (uint8_t *, WebPDecodeRGB, (const uint8_t *, size_t, int *, int *));
9119DEF_DLL_FN (void, WebPFree, (void *)); 9122DEF_DLL_FN (void, WebPFree, (void *));
9123DEF_DLL_FN (uint32_t, WebPDemuxGetI, (const WebPDemuxer* dmux,
9124 WebPFormatFeature feature));
9125DEF_DLL_FN (WebPDemuxer*, WebPDemux, (const WebPData* data));
9126DEF_DLL_FN (void, WebPDemuxDelete, (WebPDemuxer* dmux));
9127DEF_DLL_FN (int, WebPAnimDecoderGetNext,
9128 (WebPAnimDecoder* dec, uint8_t** buf, int* timestamp));
9129DEF_DLL_FN (WebPAnimDecoder*, WebPAnimDecoderNew,
9130 (const WebPData* webp_data,
9131 const WebPAnimDecoderOptions* dec_options));
9132DEF_DLL_FN (int, WebPAnimDecoderHasMoreFrames, (const WebPAnimDecoder* dec));
9133DEF_DLL_FN (void, WebPAnimDecoderDelete, (WebPAnimDecoder* dec));
9120 9134
9121static bool 9135static bool
9122init_webp_functions (void) 9136init_webp_functions (void)
@@ -9131,6 +9145,13 @@ init_webp_functions (void)
9131 LOAD_DLL_FN (library, WebPDecodeRGBA); 9145 LOAD_DLL_FN (library, WebPDecodeRGBA);
9132 LOAD_DLL_FN (library, WebPDecodeRGB); 9146 LOAD_DLL_FN (library, WebPDecodeRGB);
9133 LOAD_DLL_FN (library, WebPFree); 9147 LOAD_DLL_FN (library, WebPFree);
9148 LOAD_DLL_FN (library, WebPDemuxGetI);
9149 LOAD_DLL_FN (library, WebPDemux);
9150 LOAD_DLL_FN (library, WebPDemuxDelete);
9151 LOAD_DLL_FN (library, WebPAnimDecoderGetNext);
9152 LOAD_DLL_FN (library, WebPAnimDecoderNew);
9153 LOAD_DLL_FN (library, WebPAnimDecoderHasMoreFrames);
9154 LOAD_DLL_FN (library, WebPAnimDecoderDelete);
9134 return true; 9155 return true;
9135} 9156}
9136 9157
@@ -9139,6 +9160,13 @@ init_webp_functions (void)
9139#undef WebPDecodeRGBA 9160#undef WebPDecodeRGBA
9140#undef WebPDecodeRGB 9161#undef WebPDecodeRGB
9141#undef WebPFree 9162#undef WebPFree
9163#undef WebPDemuxGetI
9164#undef WebPDemux
9165#undef WebPDemuxDelete
9166#undef WebPAnimDecoderGetNext
9167#undef WebPAnimDecoderNew
9168#undef WebPAnimDecoderHasMoreFrames
9169#undef WebPAnimDecoderDelete
9142 9170
9143#define WebPGetInfo fn_WebPGetInfo 9171#define WebPGetInfo fn_WebPGetInfo
9144#define WebPGetFeatures(d,s,f) \ 9172#define WebPGetFeatures(d,s,f) \
@@ -9146,9 +9174,92 @@ init_webp_functions (void)
9146#define WebPDecodeRGBA fn_WebPDecodeRGBA 9174#define WebPDecodeRGBA fn_WebPDecodeRGBA
9147#define WebPDecodeRGB fn_WebPDecodeRGB 9175#define WebPDecodeRGB fn_WebPDecodeRGB
9148#define WebPFree fn_WebPFree 9176#define WebPFree fn_WebPFree
9177#define WebPDemuxGetI fn_WebPDemuxGetI
9178#define WebPDemux fn_WebPDemux
9179#define WebPDemuxDelete fn_WebPDemuxDelete
9180#define WebPAnimDecoderGetNext fn_WebPAnimDecoderGetNext
9181#define WebPAnimDecoderNew fn_WebPAnimDecoderNew
9182#define WebPAnimDecoderHasMoreFrames fn_WebPAnimDecoderHasMoreFrames
9183#define WebPAnimDecoderDelete fn_WebPAnimDecoderDelete
9149 9184
9150#endif /* WINDOWSNT */ 9185#endif /* WINDOWSNT */
9151 9186
9187/* To speed webp animations up, we keep a cache (based on EQ-ness of
9188 the image spec/object) where we put the libwebp animator
9189 iterator. */
9190
9191struct webp_cache
9192{
9193 Lisp_Object spec;
9194 WebPAnimDecoder* anim;
9195 int index, width, height, frames;
9196 struct timespec update_time;
9197 struct webp_cache *next;
9198};
9199
9200static struct webp_cache *webp_cache = NULL;
9201
9202static struct webp_cache *
9203webp_create_cache (Lisp_Object spec)
9204{
9205 struct webp_cache *cache = xmalloc (sizeof (struct webp_cache));
9206 cache->anim = NULL;
9207
9208 cache->index = 0;
9209 cache->next = NULL;
9210 /* FIXME: Does this need gc protection? */
9211 cache->spec = spec;
9212 return cache;
9213}
9214
9215/* Discard cached images that haven't been used for a minute. */
9216static void
9217webp_prune_animation_cache (void)
9218{
9219 struct webp_cache **pcache = &webp_cache;
9220 struct timespec old = timespec_sub (current_timespec (),
9221 make_timespec (60, 0));
9222
9223 while (*pcache)
9224 {
9225 struct webp_cache *cache = *pcache;
9226 if (timespec_cmp (old, cache->update_time) <= 0)
9227 pcache = &cache->next;
9228 else
9229 {
9230 if (cache->anim)
9231 WebPAnimDecoderDelete (cache->anim);
9232 *pcache = cache->next;
9233 xfree (cache);
9234 }
9235 }
9236}
9237
9238static struct webp_cache *
9239webp_get_animation_cache (Lisp_Object spec)
9240{
9241 struct webp_cache *cache;
9242 struct webp_cache **pcache = &webp_cache;
9243
9244 webp_prune_animation_cache ();
9245
9246 while (1)
9247 {
9248 cache = *pcache;
9249 if (! cache)
9250 {
9251 *pcache = cache = webp_create_cache (spec);
9252 break;
9253 }
9254 if (EQ (spec, cache->spec))
9255 break;
9256 pcache = &cache->next;
9257 }
9258
9259 cache->update_time = current_timespec ();
9260 return cache;
9261}
9262
9152/* Load WebP image IMG for use on frame F. Value is true if 9263/* Load WebP image IMG for use on frame F. Value is true if
9153 successful. */ 9264 successful. */
9154 9265
@@ -9158,6 +9269,9 @@ webp_load (struct frame *f, struct image *img)
9158 ptrdiff_t size = 0; 9269 ptrdiff_t size = 0;
9159 uint8_t *contents; 9270 uint8_t *contents;
9160 Lisp_Object file = Qnil; 9271 Lisp_Object file = Qnil;
9272 int frames = 0;
9273 double delay = 0;
9274 WebPAnimDecoder* anim = NULL;
9161 9275
9162 /* Open the WebP file. */ 9276 /* Open the WebP file. */
9163 Lisp_Object specified_file = image_spec_value (img->spec, QCfile, NULL); 9277 Lisp_Object specified_file = image_spec_value (img->spec, QCfile, NULL);
@@ -9201,6 +9315,9 @@ webp_load (struct frame *f, struct image *img)
9201 goto webp_error1; 9315 goto webp_error1;
9202 } 9316 }
9203 9317
9318 Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL);
9319 ptrdiff_t idx = FIXNUMP (image_number) ? XFIXNAT (image_number) : 0;
9320
9204 /* Get WebP features. */ 9321 /* Get WebP features. */
9205 WebPBitstreamFeatures features; 9322 WebPBitstreamFeatures features;
9206 VP8StatusCode result = WebPGetFeatures (contents, size, &features); 9323 VP8StatusCode result = WebPGetFeatures (contents, size, &features);
@@ -9224,19 +9341,76 @@ webp_load (struct frame *f, struct image *img)
9224 goto webp_error1; 9341 goto webp_error1;
9225 } 9342 }
9226 9343
9227 /* Decode WebP data. */ 9344 uint8_t *decoded = NULL;
9228 uint8_t *decoded;
9229 int width, height; 9345 int width, height;
9230 if (features.has_alpha) 9346
9231 /* Linear [r0, g0, b0, a0, r1, g1, b1, a1, ...] order. */ 9347 if (features.has_animation)
9232 decoded = WebPDecodeRGBA (contents, size, &width, &height); 9348 {
9349 /* Animated image. */
9350 WebPData webp_data;
9351 webp_data.bytes = contents;
9352 webp_data.size = size;
9353 int timestamp;
9354
9355 struct webp_cache* cache = webp_get_animation_cache (img->spec);
9356 /* Get the next frame from the animation cache. */
9357 if (cache->anim && cache->index == idx - 1)
9358 {
9359 WebPAnimDecoderGetNext (cache->anim, &decoded, &timestamp);
9360 delay = timestamp;
9361 cache->index++;
9362 anim = cache->anim;
9363 width = cache->width;
9364 height = cache->height;
9365 frames = cache->frames;
9366 }
9367 else
9368 {
9369 /* Start a new cache entry. */
9370 if (cache->anim)
9371 WebPAnimDecoderDelete (cache->anim);
9372
9373 /* Get the width/height of the total image. */
9374 WebPDemuxer* demux = WebPDemux (&webp_data);
9375 cache->width = width = WebPDemuxGetI (demux, WEBP_FF_CANVAS_WIDTH);
9376 cache->height = height = WebPDemuxGetI (demux,
9377 WEBP_FF_CANVAS_HEIGHT);
9378 cache->frames = frames = WebPDemuxGetI (demux, WEBP_FF_FRAME_COUNT);
9379 WebPDemuxDelete (demux);
9380
9381 WebPAnimDecoderOptions dec_options;
9382 WebPAnimDecoderOptionsInit (&dec_options);
9383 anim = WebPAnimDecoderNew (&webp_data, &dec_options);
9384
9385 cache->anim = anim;
9386 cache->index = idx;
9387
9388 while (WebPAnimDecoderHasMoreFrames (anim)) {
9389 WebPAnimDecoderGetNext (anim, &decoded, &timestamp);
9390 /* Each frame has its own delay, but we don't really support
9391 that. So just use the delay from the first frame. */
9392 if (delay == 0)
9393 delay = timestamp;
9394 /* Stop when we get to the desired index. */
9395 if (idx-- == 0)
9396 break;
9397 }
9398 }
9399 }
9233 else 9400 else
9234 /* Linear [r0, g0, b0, r1, g1, b1, ...] order. */ 9401 {
9235 decoded = WebPDecodeRGB (contents, size, &width, &height); 9402 /* Non-animated image. */
9403 if (features.has_alpha)
9404 /* Linear [r0, g0, b0, a0, r1, g1, b1, a1, ...] order. */
9405 decoded = WebPDecodeRGBA (contents, size, &width, &height);
9406 else
9407 /* Linear [r0, g0, b0, r1, g1, b1, ...] order. */
9408 decoded = WebPDecodeRGB (contents, size, &width, &height);
9409 }
9236 9410
9237 if (!decoded) 9411 if (!decoded)
9238 { 9412 {
9239 image_error ("Error when interpreting WebP image data"); 9413 image_error ("Error when decoding WebP image data");
9240 goto webp_error1; 9414 goto webp_error1;
9241 } 9415 }
9242 9416
@@ -9255,7 +9429,8 @@ webp_load (struct frame *f, struct image *img)
9255 /* Create an image and pixmap serving as mask if the WebP image 9429 /* Create an image and pixmap serving as mask if the WebP image
9256 contains an alpha channel. */ 9430 contains an alpha channel. */
9257 if (features.has_alpha 9431 if (features.has_alpha
9258 && !image_create_x_image_and_pixmap (f, img, width, height, 1, &mask_img, true)) 9432 && !image_create_x_image_and_pixmap (f, img, width, height, 1,
9433 &mask_img, true))
9259 { 9434 {
9260 image_destroy_x_image (ximg); 9435 image_destroy_x_image (ximg);
9261 image_clear_image_1 (f, img, CLEAR_IMAGE_PIXMAP); 9436 image_clear_image_1 (f, img, CLEAR_IMAGE_PIXMAP);
@@ -9265,6 +9440,13 @@ webp_load (struct frame *f, struct image *img)
9265 /* Fill the X image and mask from WebP data. */ 9440 /* Fill the X image and mask from WebP data. */
9266 init_color_table (); 9441 init_color_table ();
9267 9442
9443 img->corners[TOP_CORNER] = 0;
9444 img->corners[LEFT_CORNER] = 0;
9445 img->corners[BOT_CORNER]
9446 = img->corners[TOP_CORNER] + height;
9447 img->corners[RIGHT_CORNER]
9448 = img->corners[LEFT_CORNER] + width;
9449
9268 uint8_t *p = decoded; 9450 uint8_t *p = decoded;
9269 for (int y = 0; y < height; ++y) 9451 for (int y = 0; y < height; ++y)
9270 { 9452 {
@@ -9279,7 +9461,7 @@ webp_load (struct frame *f, struct image *img)
9279 image. WebP allows up to 256 levels of partial transparency. 9461 image. WebP allows up to 256 levels of partial transparency.
9280 We handle this like with PNG (which see), using the frame's 9462 We handle this like with PNG (which see), using the frame's
9281 background color to combine the image with. */ 9463 background color to combine the image with. */
9282 if (features.has_alpha) 9464 if (features.has_alpha || anim)
9283 { 9465 {
9284 if (mask_img) 9466 if (mask_img)
9285 PUT_PIXEL (mask_img, x, y, *p > 0 ? PIX_MASK_DRAW : PIX_MASK_RETAIN); 9467 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)
9310 img->width = width; 9492 img->width = width;
9311 img->height = height; 9493 img->height = height;
9312 9494
9495 /* Return animation data. */
9496 img->lisp_data = Fcons (Qcount,
9497 Fcons (make_fixnum (frames),
9498 img->lisp_data));
9499 img->lisp_data = Fcons (Qdelay,
9500 Fcons (make_float (delay / 1000),
9501 img->lisp_data));
9502
9313 /* Clean up. */ 9503 /* Clean up. */
9314 WebPFree (decoded); 9504 if (!anim)
9505 WebPFree (decoded);
9315 if (NILP (specified_data)) 9506 if (NILP (specified_data))
9316 xfree (contents); 9507 xfree (contents);
9317 return true; 9508 return true;
9318 9509
9319 webp_error2: 9510 webp_error2:
9320 WebPFree (decoded); 9511 if (!anim)
9512 WebPFree (decoded);
9321 9513
9322 webp_error1: 9514 webp_error1:
9323 if (NILP (specified_data)) 9515 if (NILP (specified_data))
@@ -9484,7 +9676,7 @@ imagemagick_filename_hint (Lisp_Object spec, char hint_buffer[MaxTextExtent])
9484 (which is the first one, and then there's a number of images that 9676 (which is the first one, and then there's a number of images that
9485 follow. If following images have non-transparent colors, these are 9677 follow. If following images have non-transparent colors, these are
9486 composed "on top" of the master image. So, in general, one has to 9678 composed "on top" of the master image. So, in general, one has to
9487 compute ann the preceding images to be able to display a particular 9679 compute all the preceding images to be able to display a particular
9488 sub-image. 9680 sub-image.
9489 9681
9490 Computing all the preceding images is too slow, so we maintain a 9682 Computing all the preceding images is too slow, so we maintain a