diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/image.c | 516 |
1 files changed, 305 insertions, 211 deletions
diff --git a/src/image.c b/src/image.c index f5db851cfbd..59be186a839 100644 --- a/src/image.c +++ b/src/image.c | |||
| @@ -1568,7 +1568,7 @@ parse_image_spec (Lisp_Object spec, struct image_keyword *keywords, | |||
| 1568 | /* Unlike the other integer-related cases, this one does not | 1568 | /* Unlike the other integer-related cases, this one does not |
| 1569 | verify that VALUE fits in 'int'. This is because callers | 1569 | verify that VALUE fits in 'int'. This is because callers |
| 1570 | want EMACS_INT. */ | 1570 | want EMACS_INT. */ |
| 1571 | if (!FIXNUMP (value) || XFIXNUM (value) < 0) | 1571 | if (!FIXNATP (value)) |
| 1572 | return false; | 1572 | return false; |
| 1573 | break; | 1573 | break; |
| 1574 | 1574 | ||
| @@ -2268,8 +2268,7 @@ filter_image_spec (Lisp_Object spec) | |||
| 2268 | breaks the image cache. Filter those out. */ | 2268 | breaks the image cache. Filter those out. */ |
| 2269 | if (!(EQ (key, QCanimate_buffer) | 2269 | if (!(EQ (key, QCanimate_buffer) |
| 2270 | || EQ (key, QCanimate_tardiness) | 2270 | || EQ (key, QCanimate_tardiness) |
| 2271 | || EQ (key, QCanimate_position) | 2271 | || EQ (key, QCanimate_position))) |
| 2272 | || EQ (key, QCanimate_multi_frame_data))) | ||
| 2273 | { | 2272 | { |
| 2274 | out = Fcons (value, out); | 2273 | out = Fcons (value, out); |
| 2275 | out = Fcons (key, out); | 2274 | out = Fcons (key, out); |
| @@ -3682,6 +3681,27 @@ cache_image (struct frame *f, struct image *img) | |||
| 3682 | 3681 | ||
| 3683 | #if defined (HAVE_WEBP) || defined (HAVE_GIF) | 3682 | #if defined (HAVE_WEBP) || defined (HAVE_GIF) |
| 3684 | 3683 | ||
| 3684 | # ifdef HAVE_GIF | ||
| 3685 | struct gif_anim_handle | ||
| 3686 | { | ||
| 3687 | struct GifFileType *gif; | ||
| 3688 | unsigned long *pixmap; | ||
| 3689 | }; | ||
| 3690 | # endif /* HAVE_GIF */ | ||
| 3691 | |||
| 3692 | # ifdef HAVE_WEBP | ||
| 3693 | struct webp_anim_handle | ||
| 3694 | { | ||
| 3695 | /* Decoder iterator+compositor. */ | ||
| 3696 | struct WebPAnimDecoder *dec; | ||
| 3697 | /* Owned copy of input WebP bitstream data consumed by decoder, | ||
| 3698 | which it must outlive unchanged. */ | ||
| 3699 | uint8_t *contents; | ||
| 3700 | /* Timestamp in milliseconds of last decoded frame. */ | ||
| 3701 | int timestamp; | ||
| 3702 | }; | ||
| 3703 | # endif /* HAVE_WEBP */ | ||
| 3704 | |||
| 3685 | /* To speed animations up, we keep a cache (based on EQ-ness of the | 3705 | /* To speed animations up, we keep a cache (based on EQ-ness of the |
| 3686 | image spec/object) where we put the animator iterator. */ | 3706 | image spec/object) where we put the animator iterator. */ |
| 3687 | 3707 | ||
| @@ -3690,12 +3710,20 @@ struct anim_cache | |||
| 3690 | /* 'Key' of this cache entry. | 3710 | /* 'Key' of this cache entry. |
| 3691 | Typically the cdr (plist) of an image spec. */ | 3711 | Typically the cdr (plist) of an image spec. */ |
| 3692 | Lisp_Object spec; | 3712 | Lisp_Object spec; |
| 3693 | /* For webp, this will be an iterator, and for libgif, a gif handle. */ | 3713 | /* Image type dependent animation handle (e.g., WebP iterator), freed |
| 3694 | void *handle; | 3714 | by 'destructor'. The union allows maintaining multiple fields per |
| 3695 | /* If we need to maintain temporary data of some sort. */ | 3715 | image type and image frame without further heap allocations. */ |
| 3696 | void *temp; | 3716 | union anim_handle |
| 3717 | { | ||
| 3718 | # ifdef HAVE_GIF | ||
| 3719 | struct gif_anim_handle gif; | ||
| 3720 | # endif /* HAVE_GIF */ | ||
| 3721 | # ifdef HAVE_WEBP | ||
| 3722 | struct webp_anim_handle webp; | ||
| 3723 | # endif /* HAVE_WEBP */ | ||
| 3724 | } handle; | ||
| 3697 | /* A function to call to free the handle. */ | 3725 | /* A function to call to free the handle. */ |
| 3698 | void (*destructor) (void *); | 3726 | void (*destructor) (union anim_handle *); |
| 3699 | /* Current frame index, and total number of frames. Note that | 3727 | /* Current frame index, and total number of frames. Note that |
| 3700 | different image formats may start at different indices. */ | 3728 | different image formats may start at different indices. */ |
| 3701 | int index, frames; | 3729 | int index, frames; |
| @@ -3716,17 +3744,15 @@ static struct anim_cache *anim_cache = NULL; | |||
| 3716 | /* Return a new animation cache entry for image SPEC (which need not be | 3744 | /* Return a new animation cache entry for image SPEC (which need not be |
| 3717 | an image specification, and is typically its cdr/plist). | 3745 | an image specification, and is typically its cdr/plist). |
| 3718 | Freed only by pruning the cache. */ | 3746 | Freed only by pruning the cache. */ |
| 3719 | static struct anim_cache * | 3747 | static ATTRIBUTE_MALLOC struct anim_cache * |
| 3720 | anim_create_cache (Lisp_Object spec) | 3748 | anim_create_cache (Lisp_Object spec) |
| 3721 | { | 3749 | { |
| 3722 | struct anim_cache *cache = xmalloc (sizeof (struct anim_cache)); | 3750 | struct anim_cache *cache = xzalloc (sizeof *cache); |
| 3723 | cache->handle = NULL; | ||
| 3724 | cache->temp = NULL; | ||
| 3725 | |||
| 3726 | cache->index = -1; | ||
| 3727 | cache->next = NULL; | ||
| 3728 | cache->spec = spec; | 3751 | cache->spec = spec; |
| 3729 | cache->byte_size = 0; | 3752 | cache->index = -1; |
| 3753 | cache->frames = -1; | ||
| 3754 | cache->width = -1; | ||
| 3755 | cache->height = -1; | ||
| 3730 | return cache; | 3756 | return cache; |
| 3731 | } | 3757 | } |
| 3732 | 3758 | ||
| @@ -3748,10 +3774,8 @@ anim_prune_animation_cache (Lisp_Object clear) | |||
| 3748 | || (NILP (clear) && timespec_cmp (old, cache->update_time) > 0) | 3774 | || (NILP (clear) && timespec_cmp (old, cache->update_time) > 0) |
| 3749 | || EQ (clear, cache->spec)) | 3775 | || EQ (clear, cache->spec)) |
| 3750 | { | 3776 | { |
| 3751 | if (cache->handle) | 3777 | if (cache->destructor) |
| 3752 | cache->destructor (cache); | 3778 | cache->destructor (&cache->handle); |
| 3753 | if (cache->temp) | ||
| 3754 | xfree (cache->temp); | ||
| 3755 | *pcache = cache->next; | 3779 | *pcache = cache->next; |
| 3756 | xfree (cache); | 3780 | xfree (cache); |
| 3757 | } | 3781 | } |
| @@ -9629,7 +9653,8 @@ static const struct image_keyword gif_format[GIF_LAST] = | |||
| 9629 | {":background", IMAGE_STRING_OR_NIL_VALUE, 0} | 9653 | {":background", IMAGE_STRING_OR_NIL_VALUE, 0} |
| 9630 | }; | 9654 | }; |
| 9631 | 9655 | ||
| 9632 | /* Free X resources of GIF image IMG which is used on frame F. */ | 9656 | /* Free X resources of GIF image IMG which is used on frame F. |
| 9657 | Also used by other image types. */ | ||
| 9633 | 9658 | ||
| 9634 | static void | 9659 | static void |
| 9635 | gif_clear_image (struct frame *f, struct image *img) | 9660 | gif_clear_image (struct frame *f, struct image *img) |
| @@ -9821,11 +9846,15 @@ static const int interlace_increment[] = {8, 8, 4, 2}; | |||
| 9821 | 9846 | ||
| 9822 | #define GIF_LOCAL_DESCRIPTOR_EXTENSION 249 | 9847 | #define GIF_LOCAL_DESCRIPTOR_EXTENSION 249 |
| 9823 | 9848 | ||
| 9849 | /* Release gif_anim_handle resources. */ | ||
| 9824 | static void | 9850 | static void |
| 9825 | gif_destroy (struct anim_cache* cache) | 9851 | gif_destroy (union anim_handle *handle) |
| 9826 | { | 9852 | { |
| 9827 | int gif_err; | 9853 | struct gif_anim_handle *h = &handle->gif; |
| 9828 | gif_close (cache->handle, &gif_err); | 9854 | gif_close (h->gif, NULL); |
| 9855 | h->gif = NULL; | ||
| 9856 | xfree (h->pixmap); | ||
| 9857 | h->pixmap = NULL; | ||
| 9829 | } | 9858 | } |
| 9830 | 9859 | ||
| 9831 | static bool | 9860 | static bool |
| @@ -9842,6 +9871,7 @@ gif_load (struct frame *f, struct image *img) | |||
| 9842 | EMACS_INT idx = -1; | 9871 | EMACS_INT idx = -1; |
| 9843 | int gif_err; | 9872 | int gif_err; |
| 9844 | struct anim_cache* cache = NULL; | 9873 | struct anim_cache* cache = NULL; |
| 9874 | struct gif_anim_handle *anim_handle = NULL; | ||
| 9845 | /* Which sub-image are we to display? */ | 9875 | /* Which sub-image are we to display? */ |
| 9846 | Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL); | 9876 | Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL); |
| 9847 | intmax_t byte_size = 0; | 9877 | intmax_t byte_size = 0; |
| @@ -9852,12 +9882,15 @@ gif_load (struct frame *f, struct image *img) | |||
| 9852 | { | 9882 | { |
| 9853 | /* If this is an animated image, create a cache for it. */ | 9883 | /* If this is an animated image, create a cache for it. */ |
| 9854 | cache = anim_get_animation_cache (XCDR (img->spec)); | 9884 | cache = anim_get_animation_cache (XCDR (img->spec)); |
| 9885 | anim_handle = &cache->handle.gif; | ||
| 9855 | /* We have an old cache entry, so use it. */ | 9886 | /* We have an old cache entry, so use it. */ |
| 9856 | if (cache->handle) | 9887 | if (anim_handle->gif) |
| 9857 | { | 9888 | { |
| 9858 | gif = cache->handle; | 9889 | gif = anim_handle->gif; |
| 9859 | pixmap = cache->temp; | 9890 | pixmap = anim_handle->pixmap; |
| 9860 | /* We're out of sync, so start from the beginning. */ | 9891 | /* We're out of sync, so start from the beginning. |
| 9892 | FIXME: Can't we fast-forward like webp_load does when | ||
| 9893 | idx > cache->index, instead of restarting? */ | ||
| 9861 | if (cache->index != idx - 1) | 9894 | if (cache->index != idx - 1) |
| 9862 | cache->index = -1; | 9895 | cache->index = -1; |
| 9863 | } | 9896 | } |
| @@ -10014,10 +10047,10 @@ gif_load (struct frame *f, struct image *img) | |||
| 10014 | } | 10047 | } |
| 10015 | 10048 | ||
| 10016 | /* It's an animated image, so initialize the cache. */ | 10049 | /* It's an animated image, so initialize the cache. */ |
| 10017 | if (cache && !cache->handle) | 10050 | if (cache && !anim_handle->gif) |
| 10018 | { | 10051 | { |
| 10019 | cache->handle = gif; | 10052 | anim_handle->gif = gif; |
| 10020 | cache->destructor = (void (*)(void *)) &gif_destroy; | 10053 | cache->destructor = gif_destroy; |
| 10021 | cache->width = width; | 10054 | cache->width = width; |
| 10022 | cache->height = height; | 10055 | cache->height = height; |
| 10023 | cache->byte_size = byte_size; | 10056 | cache->byte_size = byte_size; |
| @@ -10046,8 +10079,8 @@ gif_load (struct frame *f, struct image *img) | |||
| 10046 | if (!pixmap) | 10079 | if (!pixmap) |
| 10047 | { | 10080 | { |
| 10048 | pixmap = xmalloc (width * height * sizeof (unsigned long)); | 10081 | pixmap = xmalloc (width * height * sizeof (unsigned long)); |
| 10049 | if (cache) | 10082 | if (anim_handle) |
| 10050 | cache->temp = pixmap; | 10083 | anim_handle->pixmap = pixmap; |
| 10051 | } | 10084 | } |
| 10052 | 10085 | ||
| 10053 | /* Clear the part of the screen image not covered by the image. | 10086 | /* Clear the part of the screen image not covered by the image. |
| @@ -10100,7 +10133,7 @@ gif_load (struct frame *f, struct image *img) | |||
| 10100 | int start_frame = 0; | 10133 | int start_frame = 0; |
| 10101 | 10134 | ||
| 10102 | /* We have animation data in the cache. */ | 10135 | /* We have animation data in the cache. */ |
| 10103 | if (cache && cache->temp) | 10136 | if (cache && anim_handle->pixmap) |
| 10104 | { | 10137 | { |
| 10105 | start_frame = cache->index + 1; | 10138 | start_frame = cache->index + 1; |
| 10106 | if (start_frame > idx) | 10139 | if (start_frame > idx) |
| @@ -10260,11 +10293,16 @@ gif_load (struct frame *f, struct image *img) | |||
| 10260 | delay |= ext->Bytes[1]; | 10293 | delay |= ext->Bytes[1]; |
| 10261 | } | 10294 | } |
| 10262 | } | 10295 | } |
| 10296 | /* FIXME: Expose this via a nicer interface (bug#66221#122). */ | ||
| 10263 | img->lisp_data = list2 (Qextension_data, img->lisp_data); | 10297 | img->lisp_data = list2 (Qextension_data, img->lisp_data); |
| 10298 | /* We used to return a default delay of 1/15th of a second. | ||
| 10299 | Meanwhile browsers have settled on 1/10th of a second. | ||
| 10300 | For consistency across image types and to afford user | ||
| 10301 | configuration, we now return a non-nil nonnumeric value that | ||
| 10302 | image-multi-frame-p turns into image-default-frame-delay. */ | ||
| 10264 | img->lisp_data | 10303 | img->lisp_data |
| 10265 | = Fcons (Qdelay, | 10304 | = Fcons (Qdelay, |
| 10266 | /* Default GIF delay is 1/15th of a second. */ | 10305 | Fcons (delay ? make_float (delay / 100.0) : Qt, |
| 10267 | Fcons (make_float (delay? delay / 100.0: 1.0 / 15), | ||
| 10268 | img->lisp_data)); | 10306 | img->lisp_data)); |
| 10269 | } | 10307 | } |
| 10270 | 10308 | ||
| @@ -10275,8 +10313,7 @@ gif_load (struct frame *f, struct image *img) | |||
| 10275 | 10313 | ||
| 10276 | if (!cache) | 10314 | if (!cache) |
| 10277 | { | 10315 | { |
| 10278 | if (pixmap) | 10316 | xfree (pixmap); |
| 10279 | xfree (pixmap); | ||
| 10280 | if (gif_close (gif, &gif_err) == GIF_ERROR) | 10317 | if (gif_close (gif, &gif_err) == GIF_ERROR) |
| 10281 | { | 10318 | { |
| 10282 | #if HAVE_GIFERRORSTRING | 10319 | #if HAVE_GIFERRORSTRING |
| @@ -10302,13 +10339,12 @@ gif_load (struct frame *f, struct image *img) | |||
| 10302 | return true; | 10339 | return true; |
| 10303 | 10340 | ||
| 10304 | gif_error: | 10341 | gif_error: |
| 10305 | if (pixmap) | 10342 | xfree (pixmap); |
| 10306 | xfree (pixmap); | ||
| 10307 | gif_close (gif, NULL); | 10343 | gif_close (gif, NULL); |
| 10308 | if (cache) | 10344 | if (anim_handle) |
| 10309 | { | 10345 | { |
| 10310 | cache->handle = NULL; | 10346 | anim_handle->gif = NULL; |
| 10311 | cache->temp = NULL; | 10347 | anim_handle->pixmap = NULL; |
| 10312 | } | 10348 | } |
| 10313 | return false; | 10349 | return false; |
| 10314 | } | 10350 | } |
| @@ -10381,7 +10417,6 @@ webp_image_p (Lisp_Object object) | |||
| 10381 | 10417 | ||
| 10382 | /* WebP library details. */ | 10418 | /* WebP library details. */ |
| 10383 | 10419 | ||
| 10384 | DEF_DLL_FN (int, WebPGetInfo, (const uint8_t *, size_t, int *, int *)); | ||
| 10385 | /* WebPGetFeatures is a static inline function defined in WebP's | 10420 | /* WebPGetFeatures is a static inline function defined in WebP's |
| 10386 | decode.h. Since we cannot use that with dynamically-loaded libwebp | 10421 | decode.h. Since we cannot use that with dynamically-loaded libwebp |
| 10387 | DLL, we instead load the internal function it calls and redirect to | 10422 | DLL, we instead load the internal function it calls and redirect to |
| @@ -10392,16 +10427,16 @@ DEF_DLL_FN (uint8_t *, WebPDecodeRGBA, (const uint8_t *, size_t, int *, int *)); | |||
| 10392 | DEF_DLL_FN (uint8_t *, WebPDecodeRGB, (const uint8_t *, size_t, int *, int *)); | 10427 | DEF_DLL_FN (uint8_t *, WebPDecodeRGB, (const uint8_t *, size_t, int *, int *)); |
| 10393 | DEF_DLL_FN (void, WebPFree, (void *)); | 10428 | DEF_DLL_FN (void, WebPFree, (void *)); |
| 10394 | DEF_DLL_FN (uint32_t, WebPDemuxGetI, (const WebPDemuxer *, WebPFormatFeature)); | 10429 | DEF_DLL_FN (uint32_t, WebPDemuxGetI, (const WebPDemuxer *, WebPFormatFeature)); |
| 10395 | DEF_DLL_FN (WebPDemuxer *, WebPDemuxInternal, | 10430 | DEF_DLL_FN (int, WebPAnimDecoderGetInfo, |
| 10396 | (const WebPData *, int, WebPDemuxState *, int)); | 10431 | (const WebPAnimDecoder* dec, WebPAnimInfo* info)); |
| 10397 | DEF_DLL_FN (void, WebPDemuxDelete, (WebPDemuxer *)); | ||
| 10398 | DEF_DLL_FN (int, WebPAnimDecoderGetNext, | 10432 | DEF_DLL_FN (int, WebPAnimDecoderGetNext, |
| 10399 | (WebPAnimDecoder *, uint8_t **, int *)); | 10433 | (WebPAnimDecoder *, uint8_t **, int *)); |
| 10400 | DEF_DLL_FN (WebPAnimDecoder *, WebPAnimDecoderNewInternal, | 10434 | DEF_DLL_FN (WebPAnimDecoder *, WebPAnimDecoderNewInternal, |
| 10401 | (const WebPData *, const WebPAnimDecoderOptions *, int)); | 10435 | (const WebPData *, const WebPAnimDecoderOptions *, int)); |
| 10402 | DEF_DLL_FN (int, WebPAnimDecoderOptionsInitInternal, | ||
| 10403 | (WebPAnimDecoderOptions *, int)); | ||
| 10404 | DEF_DLL_FN (int, WebPAnimDecoderHasMoreFrames, (const WebPAnimDecoder *)); | 10436 | DEF_DLL_FN (int, WebPAnimDecoderHasMoreFrames, (const WebPAnimDecoder *)); |
| 10437 | DEF_DLL_FN (void, WebPAnimDecoderReset, (WebPAnimDecoder *)); | ||
| 10438 | DEF_DLL_FN (const WebPDemuxer *, WebPAnimDecoderGetDemuxer, | ||
| 10439 | (const WebPAnimDecoder *)); | ||
| 10405 | DEF_DLL_FN (void, WebPAnimDecoderDelete, (WebPAnimDecoder *)); | 10440 | DEF_DLL_FN (void, WebPAnimDecoderDelete, (WebPAnimDecoder *)); |
| 10406 | 10441 | ||
| 10407 | static bool | 10442 | static bool |
| @@ -10413,60 +10448,61 @@ init_webp_functions (void) | |||
| 10413 | && (library2 = w32_delayed_load (Qwebpdemux)))) | 10448 | && (library2 = w32_delayed_load (Qwebpdemux)))) |
| 10414 | return false; | 10449 | return false; |
| 10415 | 10450 | ||
| 10416 | LOAD_DLL_FN (library1, WebPGetInfo); | ||
| 10417 | LOAD_DLL_FN (library1, WebPGetFeaturesInternal); | 10451 | LOAD_DLL_FN (library1, WebPGetFeaturesInternal); |
| 10418 | LOAD_DLL_FN (library1, WebPDecodeRGBA); | 10452 | LOAD_DLL_FN (library1, WebPDecodeRGBA); |
| 10419 | LOAD_DLL_FN (library1, WebPDecodeRGB); | 10453 | LOAD_DLL_FN (library1, WebPDecodeRGB); |
| 10420 | LOAD_DLL_FN (library1, WebPFree); | 10454 | LOAD_DLL_FN (library1, WebPFree); |
| 10421 | LOAD_DLL_FN (library2, WebPDemuxGetI); | 10455 | LOAD_DLL_FN (library2, WebPDemuxGetI); |
| 10422 | LOAD_DLL_FN (library2, WebPDemuxInternal); | 10456 | LOAD_DLL_FN (library2, WebPAnimDecoderGetInfo); |
| 10423 | LOAD_DLL_FN (library2, WebPDemuxDelete); | ||
| 10424 | LOAD_DLL_FN (library2, WebPAnimDecoderGetNext); | 10457 | LOAD_DLL_FN (library2, WebPAnimDecoderGetNext); |
| 10425 | LOAD_DLL_FN (library2, WebPAnimDecoderNewInternal); | 10458 | LOAD_DLL_FN (library2, WebPAnimDecoderNewInternal); |
| 10426 | LOAD_DLL_FN (library2, WebPAnimDecoderOptionsInitInternal); | ||
| 10427 | LOAD_DLL_FN (library2, WebPAnimDecoderHasMoreFrames); | 10459 | LOAD_DLL_FN (library2, WebPAnimDecoderHasMoreFrames); |
| 10460 | LOAD_DLL_FN (library2, WebPAnimDecoderReset); | ||
| 10461 | LOAD_DLL_FN (library2, WebPAnimDecoderGetDemuxer); | ||
| 10428 | LOAD_DLL_FN (library2, WebPAnimDecoderDelete); | 10462 | LOAD_DLL_FN (library2, WebPAnimDecoderDelete); |
| 10429 | return true; | 10463 | return true; |
| 10430 | } | 10464 | } |
| 10431 | 10465 | ||
| 10432 | #undef WebPGetInfo | ||
| 10433 | #undef WebPGetFeatures | 10466 | #undef WebPGetFeatures |
| 10434 | #undef WebPDecodeRGBA | 10467 | #undef WebPDecodeRGBA |
| 10435 | #undef WebPDecodeRGB | 10468 | #undef WebPDecodeRGB |
| 10436 | #undef WebPFree | 10469 | #undef WebPFree |
| 10437 | #undef WebPDemuxGetI | 10470 | #undef WebPDemuxGetI |
| 10438 | #undef WebPDemux | 10471 | #undef WebPAnimDecoderGetInfo |
| 10439 | #undef WebPDemuxDelete | ||
| 10440 | #undef WebPAnimDecoderGetNext | 10472 | #undef WebPAnimDecoderGetNext |
| 10441 | #undef WebPAnimDecoderNew | 10473 | #undef WebPAnimDecoderNew |
| 10442 | #undef WebPAnimDecoderOptionsInit | ||
| 10443 | #undef WebPAnimDecoderHasMoreFrames | 10474 | #undef WebPAnimDecoderHasMoreFrames |
| 10475 | #undef WebPAnimDecoderReset | ||
| 10476 | #undef WebPAnimDecoderGetDemuxer | ||
| 10444 | #undef WebPAnimDecoderDelete | 10477 | #undef WebPAnimDecoderDelete |
| 10445 | 10478 | ||
| 10446 | #define WebPGetInfo fn_WebPGetInfo | ||
| 10447 | #define WebPGetFeatures(d,s,f) \ | 10479 | #define WebPGetFeatures(d,s,f) \ |
| 10448 | fn_WebPGetFeaturesInternal(d,s,f,WEBP_DECODER_ABI_VERSION) | 10480 | fn_WebPGetFeaturesInternal(d,s,f,WEBP_DECODER_ABI_VERSION) |
| 10449 | #define WebPDecodeRGBA fn_WebPDecodeRGBA | 10481 | #define WebPDecodeRGBA fn_WebPDecodeRGBA |
| 10450 | #define WebPDecodeRGB fn_WebPDecodeRGB | 10482 | #define WebPDecodeRGB fn_WebPDecodeRGB |
| 10451 | #define WebPFree fn_WebPFree | 10483 | #define WebPFree fn_WebPFree |
| 10452 | #define WebPDemuxGetI fn_WebPDemuxGetI | 10484 | #define WebPDemuxGetI fn_WebPDemuxGetI |
| 10453 | #define WebPDemux(d) \ | 10485 | #define WebPAnimDecoderGetInfo fn_WebPAnimDecoderGetInfo |
| 10454 | fn_WebPDemuxInternal(d,0,NULL,WEBP_DEMUX_ABI_VERSION) | ||
| 10455 | #define WebPDemuxDelete fn_WebPDemuxDelete | ||
| 10456 | #define WebPAnimDecoderGetNext fn_WebPAnimDecoderGetNext | 10486 | #define WebPAnimDecoderGetNext fn_WebPAnimDecoderGetNext |
| 10457 | #define WebPAnimDecoderNew(d,o) \ | 10487 | #define WebPAnimDecoderNew(d,o) \ |
| 10458 | fn_WebPAnimDecoderNewInternal(d,o,WEBP_DEMUX_ABI_VERSION) | 10488 | fn_WebPAnimDecoderNewInternal(d,o,WEBP_DEMUX_ABI_VERSION) |
| 10459 | #define WebPAnimDecoderOptionsInit(o) \ | ||
| 10460 | fn_WebPAnimDecoderOptionsInitInternal(o,WEBP_DEMUX_ABI_VERSION) | ||
| 10461 | #define WebPAnimDecoderHasMoreFrames fn_WebPAnimDecoderHasMoreFrames | 10489 | #define WebPAnimDecoderHasMoreFrames fn_WebPAnimDecoderHasMoreFrames |
| 10490 | #define WebPAnimDecoderReset fn_WebPAnimDecoderReset | ||
| 10491 | #define WebPAnimDecoderGetDemuxer fn_WebPAnimDecoderGetDemuxer | ||
| 10462 | #define WebPAnimDecoderDelete fn_WebPAnimDecoderDelete | 10492 | #define WebPAnimDecoderDelete fn_WebPAnimDecoderDelete |
| 10463 | 10493 | ||
| 10464 | #endif /* WINDOWSNT */ | 10494 | #endif /* WINDOWSNT */ |
| 10465 | 10495 | ||
| 10496 | /* Release webp_anim_handle resources. */ | ||
| 10466 | static void | 10497 | static void |
| 10467 | webp_destroy (struct anim_cache* cache) | 10498 | webp_destroy (union anim_handle *handle) |
| 10468 | { | 10499 | { |
| 10469 | WebPAnimDecoderDelete (cache->handle); | 10500 | struct webp_anim_handle *h = &handle->webp; |
| 10501 | WebPAnimDecoderDelete (h->dec); | ||
| 10502 | h->dec = NULL; | ||
| 10503 | xfree (h->contents); | ||
| 10504 | h->contents = NULL; | ||
| 10505 | h->timestamp = 0; | ||
| 10470 | } | 10506 | } |
| 10471 | 10507 | ||
| 10472 | /* Load WebP image IMG for use on frame F. Value is true if | 10508 | /* Load WebP image IMG for use on frame F. Value is true if |
| @@ -10475,171 +10511,228 @@ webp_destroy (struct anim_cache* cache) | |||
| 10475 | static bool | 10511 | static bool |
| 10476 | webp_load (struct frame *f, struct image *img) | 10512 | webp_load (struct frame *f, struct image *img) |
| 10477 | { | 10513 | { |
| 10478 | ptrdiff_t size = 0; | 10514 | /* Return value. */ |
| 10479 | uint8_t *contents; | 10515 | bool success = false; |
| 10480 | Lisp_Object file = Qnil; | 10516 | /* Owned copies and borrowed views of input WebP bitstream data and |
| 10481 | int frames = 0; | 10517 | decoded image/frame, respectively. IOW, contents_cpy and |
| 10482 | double delay = 0; | 10518 | decoded_cpy must always be freed, and contents and decoded must |
| 10483 | WebPAnimDecoder* anim = NULL; | 10519 | never be freed. */ |
| 10520 | uint8_t *contents_cpy = NULL; | ||
| 10521 | uint8_t const *contents = NULL; | ||
| 10522 | uint8_t *decoded_cpy = NULL; | ||
| 10523 | uint8_t *decoded = NULL; | ||
| 10484 | 10524 | ||
| 10485 | /* Open the WebP file. */ | 10525 | /* Non-nil :index suggests the image is animated; check the cache. */ |
| 10486 | Lisp_Object specified_file = image_spec_value (img->spec, QCfile, NULL); | 10526 | Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL); |
| 10487 | Lisp_Object specified_data = image_spec_value (img->spec, QCdata, NULL); | 10527 | struct anim_cache *cache = (NILP (image_number) ? NULL |
| 10528 | : anim_get_animation_cache (XCDR (img->spec))); | ||
| 10529 | struct webp_anim_handle *anim_handle = cache ? &cache->handle.webp : NULL; | ||
| 10530 | |||
| 10531 | /* Image spec inputs. */ | ||
| 10532 | Lisp_Object specified_data = Qnil; | ||
| 10533 | Lisp_Object specified_file = Qnil; | ||
| 10534 | /* Size of WebP contents. */ | ||
| 10535 | ptrdiff_t size = 0; | ||
| 10536 | /* WebP features parsed from bitstream headers. */ | ||
| 10537 | WebPBitstreamFeatures features = { 0 }; | ||
| 10488 | 10538 | ||
| 10489 | if (NILP (specified_data)) | 10539 | if (! (anim_handle && anim_handle->dec)) |
| 10490 | { | 10540 | /* If there is no cache entry, read in image contents. */ |
| 10491 | contents = (uint8_t *) slurp_image (specified_file, &size, "WebP"); | ||
| 10492 | if (contents == NULL) | ||
| 10493 | return false; | ||
| 10494 | } | ||
| 10495 | else | ||
| 10496 | { | 10541 | { |
| 10497 | if (!STRINGP (specified_data)) | 10542 | specified_data = image_spec_value (img->spec, QCdata, NULL); |
| 10543 | if (NILP (specified_data)) | ||
| 10498 | { | 10544 | { |
| 10499 | image_invalid_data_error (specified_data); | 10545 | /* Open the WebP file. */ |
| 10500 | return false; | 10546 | specified_file = image_spec_value (img->spec, QCfile, NULL); |
| 10547 | contents_cpy = (uint8_t *) slurp_image (specified_file, | ||
| 10548 | &size, "WebP"); | ||
| 10549 | if (!contents_cpy) | ||
| 10550 | goto cleanup; | ||
| 10551 | contents = contents_cpy; | ||
| 10552 | } | ||
| 10553 | else if (STRINGP (specified_data)) | ||
| 10554 | { | ||
| 10555 | contents = SDATA (specified_data); | ||
| 10556 | size = SBYTES (specified_data); | ||
| 10501 | } | 10557 | } |
| 10502 | contents = SDATA (specified_data); | ||
| 10503 | size = SBYTES (specified_data); | ||
| 10504 | } | ||
| 10505 | |||
| 10506 | /* Validate the WebP image header. */ | ||
| 10507 | if (!WebPGetInfo (contents, size, NULL, NULL)) | ||
| 10508 | { | ||
| 10509 | if (!NILP (file)) | ||
| 10510 | image_error ("Not a WebP file: `%s'", file); | ||
| 10511 | else | 10558 | else |
| 10512 | image_error ("Invalid header in WebP image data"); | 10559 | { |
| 10513 | goto webp_error1; | 10560 | image_invalid_data_error (specified_data); |
| 10514 | } | 10561 | goto cleanup; |
| 10515 | 10562 | } | |
| 10516 | Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL); | ||
| 10517 | ptrdiff_t idx = FIXNUMP (image_number) ? XFIXNAT (image_number) : 0; | ||
| 10518 | 10563 | ||
| 10519 | /* Get WebP features. */ | 10564 | /* Get WebP features. This can return various error codes while |
| 10520 | WebPBitstreamFeatures features; | 10565 | validating WebP headers, but we (currently) only distinguish |
| 10521 | VP8StatusCode result = WebPGetFeatures (contents, size, &features); | 10566 | success. */ |
| 10522 | switch (result) | 10567 | if (WebPGetFeatures (contents, size, &features) != VP8_STATUS_OK) |
| 10523 | { | 10568 | { |
| 10524 | case VP8_STATUS_OK: | 10569 | image_error (NILP (specified_data) |
| 10525 | break; | 10570 | ? "Error parsing WebP headers from file: `%s'" |
| 10526 | case VP8_STATUS_NOT_ENOUGH_DATA: | 10571 | : "Error parsing WebP headers from image data", |
| 10527 | case VP8_STATUS_OUT_OF_MEMORY: | 10572 | specified_file); |
| 10528 | case VP8_STATUS_INVALID_PARAM: | 10573 | goto cleanup; |
| 10529 | case VP8_STATUS_BITSTREAM_ERROR: | 10574 | } |
| 10530 | case VP8_STATUS_UNSUPPORTED_FEATURE: | ||
| 10531 | case VP8_STATUS_SUSPENDED: | ||
| 10532 | case VP8_STATUS_USER_ABORT: | ||
| 10533 | default: | ||
| 10534 | /* Error out in all other cases. */ | ||
| 10535 | if (!NILP (file)) | ||
| 10536 | image_error ("Error when interpreting WebP image data: `%s'", file); | ||
| 10537 | else | ||
| 10538 | image_error ("Error when interpreting WebP image data"); | ||
| 10539 | goto webp_error1; | ||
| 10540 | } | 10575 | } |
| 10541 | 10576 | ||
| 10542 | uint8_t *decoded = NULL; | 10577 | /* Dimensions of still image or animation frame. */ |
| 10543 | int width, height; | 10578 | int width = -1; |
| 10579 | int height = -1; | ||
| 10580 | /* Number of animation frames. */ | ||
| 10581 | int frames = -1; | ||
| 10582 | /* Current animation frame's duration in ms. */ | ||
| 10583 | int duration = -1; | ||
| 10544 | 10584 | ||
| 10545 | if (features.has_animation) | 10585 | if ((anim_handle && anim_handle->dec) || features.has_animation) |
| 10586 | /* Animated image. */ | ||
| 10546 | { | 10587 | { |
| 10547 | /* Animated image. */ | 10588 | if (!cache) |
| 10548 | int timestamp; | 10589 | /* If the lookup was initially skipped due to the absence of an |
| 10590 | :index, do it now. */ | ||
| 10591 | { | ||
| 10592 | cache = anim_get_animation_cache (XCDR (img->spec)); | ||
| 10593 | anim_handle = &cache->handle.webp; | ||
| 10594 | } | ||
| 10549 | 10595 | ||
| 10550 | struct anim_cache* cache = anim_get_animation_cache (XCDR (img->spec)); | 10596 | if (anim_handle->dec) |
| 10551 | /* Get the next frame from the animation cache. */ | 10597 | /* If WebPGetFeatures was skipped, get the already parsed |
| 10552 | if (cache->handle && cache->index == idx - 1) | 10598 | features from the cached decoder. */ |
| 10553 | { | 10599 | { |
| 10554 | WebPAnimDecoderGetNext (cache->handle, &decoded, ×tamp); | 10600 | WebPDemuxer const *dmux |
| 10555 | delay = timestamp; | 10601 | = WebPAnimDecoderGetDemuxer (anim_handle->dec); |
| 10556 | cache->index++; | 10602 | uint32_t const flags = WebPDemuxGetI (dmux, WEBP_FF_FORMAT_FLAGS); |
| 10557 | anim = cache->handle; | 10603 | features.has_alpha = !!(flags & ALPHA_FLAG); |
| 10558 | width = cache->width; | 10604 | features.has_animation = !!(flags & ANIMATION_FLAG); |
| 10559 | height = cache->height; | ||
| 10560 | frames = cache->frames; | ||
| 10561 | } | 10605 | } |
| 10562 | else | 10606 | else |
| 10563 | { | 10607 | /* If there was no decoder in the cache, create one now. */ |
| 10564 | /* Start a new cache entry. */ | 10608 | { |
| 10565 | if (cache->handle) | 10609 | /* If the data is from a Lisp string, copy it over so that it |
| 10566 | WebPAnimDecoderDelete (cache->handle); | 10610 | doesn't get garbage-collected. If it's fresh from a file, |
| 10611 | then another copy isn't needed to keep it alive. Either | ||
| 10612 | way, ownership transfers to the anim cache which frees | ||
| 10613 | memory during pruning. */ | ||
| 10614 | anim_handle->contents = (STRINGP (specified_data) | ||
| 10615 | ? (uint8_t *) xlispstrdup (specified_data) | ||
| 10616 | : contents_cpy); | ||
| 10617 | contents_cpy = NULL; | ||
| 10618 | contents = anim_handle->contents; | ||
| 10619 | cache->destructor = webp_destroy; | ||
| 10620 | |||
| 10621 | /* The WebPData docs can be interpreted as requiring it be | ||
| 10622 | allocated, initialized, and cleared via its dedicated API. | ||
| 10623 | However that seems to apply mostly to the mux API that we | ||
| 10624 | don't use; the demux API we use treats WebPData as | ||
| 10625 | read-only POD, so this should be fine. */ | ||
| 10626 | WebPData const webp_data = { .bytes = contents, .size = size }; | ||
| 10627 | /* We could ask for multithreaded decoding here. */ | ||
| 10628 | anim_handle->dec = WebPAnimDecoderNew (&webp_data, NULL); | ||
| 10629 | if (!anim_handle->dec) | ||
| 10630 | { | ||
| 10631 | image_error (NILP (specified_data) | ||
| 10632 | ? "Error parsing WebP file: `%s'" | ||
| 10633 | : "Error parsing WebP image data", | ||
| 10634 | specified_file); | ||
| 10635 | goto cleanup; | ||
| 10636 | } | ||
| 10567 | 10637 | ||
| 10568 | WebPData webp_data; | 10638 | /* Get the width/height of the total image. */ |
| 10569 | if (NILP (specified_data)) | 10639 | WebPAnimInfo info; |
| 10570 | /* If we got the data from a file, then we don't need to | 10640 | if (!WebPAnimDecoderGetInfo (anim_handle->dec, &info)) |
| 10571 | copy the data. */ | ||
| 10572 | webp_data.bytes = cache->temp = contents; | ||
| 10573 | else | ||
| 10574 | /* We got the data from a string, so copy it over so that | ||
| 10575 | it doesn't get garbage-collected. */ | ||
| 10576 | { | 10641 | { |
| 10577 | webp_data.bytes = xmalloc (size); | 10642 | image_error (NILP (specified_data) |
| 10578 | memcpy ((void*) webp_data.bytes, contents, size); | 10643 | ? ("Error getting global animation info " |
| 10644 | "from WebP file: `%s'") | ||
| 10645 | : ("Error getting global animation info " | ||
| 10646 | "from WebP image data"), | ||
| 10647 | specified_file); | ||
| 10648 | goto cleanup; | ||
| 10579 | } | 10649 | } |
| 10580 | /* In any case, we release the allocated memory when we | ||
| 10581 | purge the anim cache. */ | ||
| 10582 | webp_data.size = size; | ||
| 10583 | 10650 | ||
| 10651 | /* Other libwebp[demux] APIs (and WebPAnimInfo internally) | ||
| 10652 | store these values as int, so this should be safe. */ | ||
| 10653 | cache->width = info.canvas_width; | ||
| 10654 | cache->height = info.canvas_height; | ||
| 10655 | cache->frames = info.frame_count; | ||
| 10584 | /* This is used just for reporting by `image-cache-size'. */ | 10656 | /* This is used just for reporting by `image-cache-size'. */ |
| 10585 | cache->byte_size = size; | 10657 | cache->byte_size = size; |
| 10658 | } | ||
| 10586 | 10659 | ||
| 10587 | /* Get the width/height of the total image. */ | 10660 | width = cache->width; |
| 10588 | WebPDemuxer* demux = WebPDemux (&webp_data); | 10661 | height = cache->height; |
| 10589 | cache->width = width = WebPDemuxGetI (demux, WEBP_FF_CANVAS_WIDTH); | 10662 | frames = cache->frames; |
| 10590 | cache->height = height = WebPDemuxGetI (demux, | 10663 | |
| 10591 | WEBP_FF_CANVAS_HEIGHT); | 10664 | /* Desired frame number. */ |
| 10592 | cache->frames = frames = WebPDemuxGetI (demux, WEBP_FF_FRAME_COUNT); | 10665 | EMACS_INT idx = (FIXNUMP (image_number) |
| 10593 | cache->destructor = (void (*)(void *)) webp_destroy; | 10666 | ? min (XFIXNAT (image_number), frames) : 0); |
| 10594 | WebPDemuxDelete (demux); | 10667 | if (cache->index >= idx) |
| 10595 | 10668 | /* The decoder cannot rewind (nor be queried for the last | |
| 10596 | WebPAnimDecoderOptions dec_options; | 10669 | frame's decoded pixels and timestamp), so restart from |
| 10597 | WebPAnimDecoderOptionsInit (&dec_options); | 10670 | the first frame. We could avoid restarting when |
| 10598 | anim = WebPAnimDecoderNew (&webp_data, &dec_options); | 10671 | cache->index == idx by adding more fields to |
| 10599 | 10672 | webp_anim_handle, but it may not be worth it. */ | |
| 10600 | cache->handle = anim; | 10673 | { |
| 10601 | cache->index = idx; | 10674 | WebPAnimDecoderReset (anim_handle->dec); |
| 10602 | 10675 | anim_handle->timestamp = 0; | |
| 10603 | while (WebPAnimDecoderHasMoreFrames (anim)) { | 10676 | cache->index = -1; |
| 10604 | WebPAnimDecoderGetNext (anim, &decoded, ×tamp); | 10677 | } |
| 10605 | /* Each frame has its own delay, but we don't really support | 10678 | |
| 10606 | that. So just use the delay from the first frame. */ | 10679 | /* Decode until desired frame number. */ |
| 10607 | if (delay == 0) | 10680 | for (; |
| 10608 | delay = timestamp; | 10681 | (cache->index < idx |
| 10609 | /* Stop when we get to the desired index. */ | 10682 | && WebPAnimDecoderHasMoreFrames (anim_handle->dec)); |
| 10610 | if (idx-- == 0) | 10683 | cache->index++) |
| 10611 | break; | 10684 | { |
| 10612 | } | 10685 | int timestamp; |
| 10686 | if (!WebPAnimDecoderGetNext (anim_handle->dec, &decoded, ×tamp)) | ||
| 10687 | { | ||
| 10688 | image_error (NILP (specified_data) | ||
| 10689 | ? "Error decoding frame #%d from WebP file: `%s'" | ||
| 10690 | : "Error decoding frame #%d from WebP image data", | ||
| 10691 | cache->index + 1, specified_file); | ||
| 10692 | goto cleanup; | ||
| 10693 | } | ||
| 10694 | eassert (anim_handle->timestamp >= 0); | ||
| 10695 | eassert (timestamp >= anim_handle->timestamp); | ||
| 10696 | duration = timestamp - anim_handle->timestamp; | ||
| 10697 | anim_handle->timestamp = timestamp; | ||
| 10613 | } | 10698 | } |
| 10614 | } | 10699 | } |
| 10615 | else | 10700 | else |
| 10701 | /* Non-animated image. */ | ||
| 10616 | { | 10702 | { |
| 10617 | /* Non-animated image. */ | 10703 | /* Could performance be improved by using the 'advanced' |
| 10704 | WebPDecoderConfig API to request scaling/cropping as | ||
| 10705 | appropriate for Emacs frame and image dimensions, | ||
| 10706 | similarly to the SVG code? */ | ||
| 10618 | if (features.has_alpha) | 10707 | if (features.has_alpha) |
| 10619 | /* Linear [r0, g0, b0, a0, r1, g1, b1, a1, ...] order. */ | 10708 | /* Linear [r0, g0, b0, a0, r1, g1, b1, a1, ...] order. */ |
| 10620 | decoded = WebPDecodeRGBA (contents, size, &width, &height); | 10709 | decoded_cpy = WebPDecodeRGBA (contents, size, &width, &height); |
| 10621 | else | 10710 | else |
| 10622 | /* Linear [r0, g0, b0, r1, g1, b1, ...] order. */ | 10711 | /* Linear [r0, g0, b0, r1, g1, b1, ...] order. */ |
| 10623 | decoded = WebPDecodeRGB (contents, size, &width, &height); | 10712 | decoded_cpy = WebPDecodeRGB (contents, size, &width, &height); |
| 10713 | decoded = decoded_cpy; | ||
| 10624 | } | 10714 | } |
| 10625 | 10715 | ||
| 10626 | if (!decoded) | 10716 | if (!decoded) |
| 10627 | { | 10717 | { |
| 10628 | image_error ("Error when decoding WebP image data"); | 10718 | image_error (NILP (specified_data) |
| 10629 | goto webp_error1; | 10719 | ? "Error decoding WebP file: `%s'" |
| 10720 | : "Error decoding WebP image data", | ||
| 10721 | specified_file); | ||
| 10722 | goto cleanup; | ||
| 10630 | } | 10723 | } |
| 10631 | 10724 | ||
| 10632 | if (!(width <= INT_MAX && height <= INT_MAX | 10725 | if (!(width <= INT_MAX && height <= INT_MAX |
| 10633 | && check_image_size (f, width, height))) | 10726 | && check_image_size (f, width, height))) |
| 10634 | { | 10727 | { |
| 10635 | image_size_error (); | 10728 | image_size_error (); |
| 10636 | goto webp_error2; | 10729 | goto cleanup; |
| 10637 | } | 10730 | } |
| 10638 | 10731 | ||
| 10639 | /* Create the x image and pixmap. */ | 10732 | /* Create the x image and pixmap. */ |
| 10640 | Emacs_Pix_Container ximg; | 10733 | Emacs_Pix_Container ximg; |
| 10641 | if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, false)) | 10734 | if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, false)) |
| 10642 | goto webp_error2; | 10735 | goto cleanup; |
| 10643 | 10736 | ||
| 10644 | /* Find the background to use if the WebP image contains an alpha | 10737 | /* Find the background to use if the WebP image contains an alpha |
| 10645 | channel. */ | 10738 | channel. */ |
| @@ -10676,7 +10769,7 @@ webp_load (struct frame *f, struct image *img) | |||
| 10676 | img->corners[RIGHT_CORNER] | 10769 | img->corners[RIGHT_CORNER] |
| 10677 | = img->corners[LEFT_CORNER] + width; | 10770 | = img->corners[LEFT_CORNER] + width; |
| 10678 | 10771 | ||
| 10679 | uint8_t *p = decoded; | 10772 | uint8_t const *p = decoded; |
| 10680 | for (int y = 0; y < height; ++y) | 10773 | for (int y = 0; y < height; ++y) |
| 10681 | { | 10774 | { |
| 10682 | for (int x = 0; x < width; ++x) | 10775 | for (int x = 0; x < width; ++x) |
| @@ -10684,7 +10777,7 @@ webp_load (struct frame *f, struct image *img) | |||
| 10684 | int r, g, b; | 10777 | int r, g, b; |
| 10685 | /* The WebP alpha channel allows 256 levels of partial | 10778 | /* The WebP alpha channel allows 256 levels of partial |
| 10686 | transparency. Blend it with the background manually. */ | 10779 | transparency. Blend it with the background manually. */ |
| 10687 | if (features.has_alpha || anim) | 10780 | if (features.has_alpha || features.has_animation) |
| 10688 | { | 10781 | { |
| 10689 | float a = (float) p[3] / UINT8_MAX; | 10782 | float a = (float) p[3] / UINT8_MAX; |
| 10690 | r = (int)(a * p[0] + (1 - a) * bg_color.red) << 8; | 10783 | r = (int)(a * p[0] + (1 - a) * bg_color.red) << 8; |
| @@ -10714,29 +10807,31 @@ webp_load (struct frame *f, struct image *img) | |||
| 10714 | img->width = width; | 10807 | img->width = width; |
| 10715 | img->height = height; | 10808 | img->height = height; |
| 10716 | 10809 | ||
| 10717 | /* Return animation data. */ | 10810 | if (features.has_animation) |
| 10718 | img->lisp_data = Fcons (Qcount, | 10811 | /* Return animation metadata. */ |
| 10719 | Fcons (make_fixnum (frames), | 10812 | { |
| 10720 | img->lisp_data)); | 10813 | eassert (frames > 0); |
| 10721 | img->lisp_data = Fcons (Qdelay, | 10814 | eassert (duration >= 0); |
| 10722 | Fcons (make_float (delay / 1000), | 10815 | img->lisp_data = Fcons (Qcount, |
| 10723 | img->lisp_data)); | 10816 | Fcons (make_fixnum (frames), |
| 10724 | 10817 | img->lisp_data)); | |
| 10725 | /* Clean up. */ | 10818 | /* WebP spec: interpretation of no/small frame duration is |
| 10726 | if (!anim) | 10819 | implementation-defined. In practice browsers and libwebp tools |
| 10727 | WebPFree (decoded); | 10820 | map small durations to 100ms to protect against annoying |
| 10728 | if (NILP (specified_data) && !anim) | 10821 | images. For consistency across image types and user |
| 10729 | xfree (contents); | 10822 | configurability, we return a non-nil nonnumeric value that |
| 10730 | return true; | 10823 | image-multi-frame-p turns into image-default-frame-delay. */ |
| 10731 | 10824 | img->lisp_data | |
| 10732 | webp_error2: | 10825 | = Fcons (Qdelay, |
| 10733 | if (!anim) | 10826 | Fcons (duration ? make_float (duration / 1000.0) : Qt, |
| 10734 | WebPFree (decoded); | 10827 | img->lisp_data)); |
| 10828 | } | ||
| 10735 | 10829 | ||
| 10736 | webp_error1: | 10830 | success = true; |
| 10737 | if (NILP (specified_data)) | 10831 | cleanup: |
| 10738 | xfree (contents); | 10832 | WebPFree (decoded_cpy); |
| 10739 | return false; | 10833 | xfree (contents_cpy); |
| 10834 | return success; | ||
| 10740 | } | 10835 | } |
| 10741 | 10836 | ||
| 10742 | #endif /* HAVE_WEBP */ | 10837 | #endif /* HAVE_WEBP */ |
| @@ -12879,7 +12974,7 @@ static struct image_type const image_types[] = | |||
| 12879 | IMAGE_TYPE_INIT (init_xpm_functions) }, | 12974 | IMAGE_TYPE_INIT (init_xpm_functions) }, |
| 12880 | #endif | 12975 | #endif |
| 12881 | #if defined HAVE_WEBP | 12976 | #if defined HAVE_WEBP |
| 12882 | { SYMBOL_INDEX (Qwebp), webp_image_p, webp_load, image_clear_image, | 12977 | { SYMBOL_INDEX (Qwebp), webp_image_p, webp_load, gif_clear_image, |
| 12883 | IMAGE_TYPE_INIT (init_webp_functions) }, | 12978 | IMAGE_TYPE_INIT (init_webp_functions) }, |
| 12884 | #endif | 12979 | #endif |
| 12885 | { SYMBOL_INDEX (Qxbm), xbm_image_p, xbm_load, image_clear_image }, | 12980 | { SYMBOL_INDEX (Qxbm), xbm_image_p, xbm_load, image_clear_image }, |
| @@ -13159,7 +13254,6 @@ non-numeric, there is no explicit limit on the size of images. */); | |||
| 13159 | DEFSYM (QCanimate_buffer, ":animate-buffer"); | 13254 | DEFSYM (QCanimate_buffer, ":animate-buffer"); |
| 13160 | DEFSYM (QCanimate_tardiness, ":animate-tardiness"); | 13255 | DEFSYM (QCanimate_tardiness, ":animate-tardiness"); |
| 13161 | DEFSYM (QCanimate_position, ":animate-position"); | 13256 | DEFSYM (QCanimate_position, ":animate-position"); |
| 13162 | DEFSYM (QCanimate_multi_frame_data, ":animate-multi-frame-data"); | ||
| 13163 | 13257 | ||
| 13164 | defsubr (&Simage_transforms_p); | 13258 | defsubr (&Simage_transforms_p); |
| 13165 | 13259 | ||