aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLars Ingebrigtsen2022-04-11 14:38:27 +0200
committerLars Ingebrigtsen2022-04-11 14:38:27 +0200
commit8b7aaf3e56c63cae7e2affc249179e5022451595 (patch)
tree9dd4d6d8323d4946f070161d996dea0fdd856ff6 /src
parent5141234acf85fe232adcaa3b0278f7766eb0d250 (diff)
downloademacs-8b7aaf3e56c63cae7e2affc249179e5022451595.tar.gz
emacs-8b7aaf3e56c63cae7e2affc249179e5022451595.zip
Speed up GIF animations
* src/image.c (anim_prune_animation_cache): Tweak the destructor API. (gif_destroy): New function. (gif_load): Use a cache to avoid quadratic CPU usage for animated images (bug#45224). (webp_destroy): New function. (webp_load): Use it.
Diffstat (limited to 'src')
-rw-r--r--src/image.c290
1 files changed, 179 insertions, 111 deletions
diff --git a/src/image.c b/src/image.c
index a3c98684261..967263e2c60 100644
--- a/src/image.c
+++ b/src/image.c
@@ -8484,7 +8484,7 @@ anim_prune_animation_cache (void)
8484 else 8484 else
8485 { 8485 {
8486 if (cache->handle) 8486 if (cache->handle)
8487 cache->destructor (cache->handle); 8487 cache->destructor (cache);
8488 if (cache->temp) 8488 if (cache->temp)
8489 xfree (cache->temp); 8489 xfree (cache->temp);
8490 *pcache = cache->next; 8490 *pcache = cache->next;
@@ -8754,127 +8754,187 @@ static const int interlace_increment[] = {8, 8, 4, 2};
8754 8754
8755#define GIF_LOCAL_DESCRIPTOR_EXTENSION 249 8755#define GIF_LOCAL_DESCRIPTOR_EXTENSION 249
8756 8756
8757static void
8758gif_destroy (struct anim_cache* cache)
8759{
8760 int gif_err;
8761 gif_close (cache->handle, &gif_err);
8762}
8763
8757static bool 8764static bool
8758gif_load (struct frame *f, struct image *img) 8765gif_load (struct frame *f, struct image *img)
8759{ 8766{
8760 int rc, width, height, x, y, i, j; 8767 int rc, width, height, x, y, i, j;
8761 ColorMapObject *gif_color_map; 8768 ColorMapObject *gif_color_map;
8762 GifFileType *gif; 8769 GifFileType *gif = NULL;
8763 gif_memory_source memsrc; 8770 gif_memory_source memsrc;
8764 Lisp_Object specified_bg = image_spec_value (img->spec, QCbackground, NULL); 8771 Lisp_Object specified_bg = image_spec_value (img->spec, QCbackground, NULL);
8765 Lisp_Object specified_file = image_spec_value (img->spec, QCfile, NULL); 8772 Lisp_Object specified_file = image_spec_value (img->spec, QCfile, NULL);
8766 Lisp_Object specified_data = image_spec_value (img->spec, QCdata, NULL); 8773 Lisp_Object specified_data = image_spec_value (img->spec, QCdata, NULL);
8767 EMACS_INT idx; 8774 EMACS_INT idx = -1;
8768 int gif_err; 8775 int gif_err;
8776 struct anim_cache* cache = NULL;
8769 8777
8770 if (NILP (specified_data)) 8778 /* Which sub-image are we to display? */
8779 {
8780 Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL);
8781 idx = FIXNUMP (image_number) ? XFIXNAT (image_number) : 0;
8782 }
8783
8784 if (idx != -1)
8771 { 8785 {
8772 Lisp_Object file = image_find_image_file (specified_file); 8786 /* If this is an animated image, create a cache for it. */
8773 if (!STRINGP (file)) 8787 cache = anim_get_animation_cache (img->spec);
8788 if (cache->handle)
8774 { 8789 {
8775 image_error ("Cannot find image file `%s'", specified_file); 8790 /* We have an old cache entry, and it looks correct, so use
8776 return false; 8791 it. */
8792 if (cache->index == idx - 1)
8793 gif = cache->handle;
8777 } 8794 }
8795 }
8778 8796
8779 Lisp_Object encoded_file = ENCODE_FILE (file); 8797 /* If we don't have a cached entry, read the image. */
8798 if (! gif)
8799 {
8800 if (NILP (specified_data))
8801 {
8802 Lisp_Object file = image_find_image_file (specified_file);
8803 if (!STRINGP (file))
8804 {
8805 image_error ("Cannot find image file `%s'", specified_file);
8806 return false;
8807 }
8808
8809 Lisp_Object encoded_file = ENCODE_FILE (file);
8780#ifdef WINDOWSNT 8810#ifdef WINDOWSNT
8781 encoded_file = ansi_encode_filename (encoded_file); 8811 encoded_file = ansi_encode_filename (encoded_file);
8782#endif 8812#endif
8783 8813
8784 /* Open the GIF file. */ 8814 /* Open the GIF file. */
8785#if GIFLIB_MAJOR < 5 8815#if GIFLIB_MAJOR < 5
8786 gif = DGifOpenFileName (SSDATA (encoded_file)); 8816 gif = DGifOpenFileName (SSDATA (encoded_file));
8787#else 8817#else
8788 gif = DGifOpenFileName (SSDATA (encoded_file), &gif_err); 8818 gif = DGifOpenFileName (SSDATA (encoded_file), &gif_err);
8789#endif 8819#endif
8790 if (gif == NULL) 8820 if (gif == NULL)
8791 { 8821 {
8792#if HAVE_GIFERRORSTRING 8822#if HAVE_GIFERRORSTRING
8793 const char *errstr = GifErrorString (gif_err); 8823 const char *errstr = GifErrorString (gif_err);
8794 if (errstr) 8824 if (errstr)
8795 image_error ("Cannot open `%s': %s", file, build_string (errstr)); 8825 image_error ("Cannot open `%s': %s", file,
8796 else 8826 build_string (errstr));
8827 else
8797#endif 8828#endif
8798 image_error ("Cannot open `%s'", file); 8829 image_error ("Cannot open `%s'", file);
8799 return false; 8830 return false;
8831 }
8800 } 8832 }
8801 } 8833 else
8802 else
8803 {
8804 if (!STRINGP (specified_data))
8805 { 8834 {
8806 image_error ("Invalid image data `%s'", specified_data); 8835 if (!STRINGP (specified_data))
8807 return false; 8836 {
8808 } 8837 image_error ("Invalid image data `%s'", specified_data);
8838 return false;
8839 }
8809 8840
8810 /* Read from memory! */ 8841 /* Read from memory! */
8811 current_gif_memory_src = &memsrc; 8842 current_gif_memory_src = &memsrc;
8812 memsrc.bytes = SDATA (specified_data); 8843 memsrc.bytes = SDATA (specified_data);
8813 memsrc.len = SBYTES (specified_data); 8844 memsrc.len = SBYTES (specified_data);
8814 memsrc.index = 0; 8845 memsrc.index = 0;
8815 8846
8816#if GIFLIB_MAJOR < 5 8847#if GIFLIB_MAJOR < 5
8817 gif = DGifOpen (&memsrc, gif_read_from_memory); 8848 gif = DGifOpen (&memsrc, gif_read_from_memory);
8818#else 8849#else
8819 gif = DGifOpen (&memsrc, gif_read_from_memory, &gif_err); 8850 gif = DGifOpen (&memsrc, gif_read_from_memory, &gif_err);
8851#endif
8852 if (!gif)
8853 {
8854#if HAVE_GIFERRORSTRING
8855 const char *errstr = GifErrorString (gif_err);
8856 if (errstr)
8857 image_error ("Cannot open memory source `%s': %s",
8858 img->spec, build_string (errstr));
8859 else
8820#endif 8860#endif
8821 if (!gif) 8861 image_error ("Cannot open memory source `%s'", img->spec);
8862 return false;
8863 }
8864 }
8865
8866 /* Before reading entire contents, check the declared image size. */
8867 if (!check_image_size (f, gif->SWidth, gif->SHeight))
8868 {
8869 image_size_error ();
8870 goto gif_error;
8871 }
8872
8873 /* Read entire contents. */
8874 rc = DGifSlurp (gif);
8875 if (rc == GIF_ERROR || gif->ImageCount <= 0)
8822 { 8876 {
8823#if HAVE_GIFERRORSTRING 8877#if HAVE_GIFERRORSTRING
8824 const char *errstr = GifErrorString (gif_err); 8878 const char *errstr = GifErrorString (gif->Error);
8825 if (errstr) 8879 if (errstr)
8826 image_error ("Cannot open memory source `%s': %s", 8880 if (NILP (specified_data))
8827 img->spec, build_string (errstr)); 8881 image_error ("Error reading `%s' (%s)", img->spec,
8882 build_string (errstr));
8883 else
8884 image_error ("Error reading GIF data: %s",
8885 build_string (errstr));
8828 else 8886 else
8829#endif 8887#endif
8830 image_error ("Cannot open memory source `%s'", img->spec); 8888 if (NILP (specified_data))
8831 return false; 8889 image_error ("Error reading `%s'", img->spec);
8890 else
8891 image_error ("Error reading GIF data");
8892 goto gif_error;
8832 } 8893 }
8833 }
8834 8894
8835 /* Before reading entire contents, check the declared image size. */ 8895 width = img->width = gif->SWidth;
8836 if (!check_image_size (f, gif->SWidth, gif->SHeight)) 8896 height = img->height = gif->SHeight;
8897
8898 /* Check that the selected subimages fit. It's not clear whether
8899 the GIF spec requires this, but Emacs can crash if they don't fit. */
8900 for (j = 0; j <= idx; ++j)
8901 {
8902 struct SavedImage *subimage = gif->SavedImages + j;
8903 int subimg_width = subimage->ImageDesc.Width;
8904 int subimg_height = subimage->ImageDesc.Height;
8905 int subimg_top = subimage->ImageDesc.Top;
8906 int subimg_left = subimage->ImageDesc.Left;
8907 if (! (subimg_width >= 0 && subimg_height >= 0
8908 && 0 <= subimg_top && subimg_top <= height - subimg_height
8909 && 0 <= subimg_left && subimg_left <= width - subimg_width))
8910 {
8911 image_error ("Subimage does not fit in image");
8912 goto gif_error;
8913 }
8914 }
8915 }
8916 else
8837 { 8917 {
8838 image_size_error (); 8918 /* Cached image; set data. */
8839 goto gif_error; 8919 width = img->width = gif->SWidth;
8920 height = img->height = gif->SHeight;
8840 } 8921 }
8841 8922
8842 /* Read entire contents. */ 8923 if (idx < 0 || idx >= gif->ImageCount)
8843 rc = DGifSlurp (gif);
8844 if (rc == GIF_ERROR || gif->ImageCount <= 0)
8845 { 8924 {
8846#if HAVE_GIFERRORSTRING 8925 image_error ("Invalid image number `%s' in image `%s'",
8847 const char *errstr = GifErrorString (gif->Error); 8926 make_fixnum (idx), img->spec);
8848 if (errstr)
8849 if (NILP (specified_data))
8850 image_error ("Error reading `%s' (%s)", img->spec,
8851 build_string (errstr));
8852 else
8853 image_error ("Error reading GIF data: %s",
8854 build_string (errstr));
8855 else
8856#endif
8857 if (NILP (specified_data))
8858 image_error ("Error reading `%s'", img->spec);
8859 else
8860 image_error ("Error reading GIF data");
8861 goto gif_error; 8927 goto gif_error;
8862 } 8928 }
8863 8929
8864 /* Which sub-image are we to display? */ 8930 /* It's an animated image, so initalize the cache. */
8865 { 8931 if (cache && !cache->handle)
8866 Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL); 8932 {
8867 idx = FIXNUMP (image_number) ? XFIXNAT (image_number) : 0; 8933 cache->handle = gif;
8868 if (idx < 0 || idx >= gif->ImageCount) 8934 cache->destructor = (void (*)(void *)) &gif_destroy;
8869 { 8935 cache->width = width;
8870 image_error ("Invalid image number `%s' in image `%s'", 8936 cache->height = height;
8871 image_number, img->spec); 8937 }
8872 goto gif_error;
8873 }
8874 }
8875
8876 width = img->width = gif->SWidth;
8877 height = img->height = gif->SHeight;
8878 8938
8879 img->corners[TOP_CORNER] = gif->SavedImages[0].ImageDesc.Top; 8939 img->corners[TOP_CORNER] = gif->SavedImages[0].ImageDesc.Top;
8880 img->corners[LEFT_CORNER] = gif->SavedImages[0].ImageDesc.Left; 8940 img->corners[LEFT_CORNER] = gif->SavedImages[0].ImageDesc.Left;
@@ -8889,24 +8949,6 @@ gif_load (struct frame *f, struct image *img)
8889 goto gif_error; 8949 goto gif_error;
8890 } 8950 }
8891 8951
8892 /* Check that the selected subimages fit. It's not clear whether
8893 the GIF spec requires this, but Emacs can crash if they don't fit. */
8894 for (j = 0; j <= idx; ++j)
8895 {
8896 struct SavedImage *subimage = gif->SavedImages + j;
8897 int subimg_width = subimage->ImageDesc.Width;
8898 int subimg_height = subimage->ImageDesc.Height;
8899 int subimg_top = subimage->ImageDesc.Top;
8900 int subimg_left = subimage->ImageDesc.Left;
8901 if (! (subimg_width >= 0 && subimg_height >= 0
8902 && 0 <= subimg_top && subimg_top <= height - subimg_height
8903 && 0 <= subimg_left && subimg_left <= width - subimg_width))
8904 {
8905 image_error ("Subimage does not fit in image");
8906 goto gif_error;
8907 }
8908 }
8909
8910 /* Create the X image and pixmap. */ 8952 /* Create the X image and pixmap. */
8911 Emacs_Pix_Container ximg; 8953 Emacs_Pix_Container ximg;
8912 if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, 0)) 8954 if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, 0))
@@ -8944,11 +8986,6 @@ gif_load (struct frame *f, struct image *img)
8944 8986
8945 /* Read the GIF image into the X image. */ 8987 /* Read the GIF image into the X image. */
8946 8988
8947 /* FIXME: With the current implementation, loading an animated gif
8948 is quadratic in the number of animation frames, since each frame
8949 is a separate struct image. We must provide a way for a single
8950 gif_load call to construct and save all animation frames. */
8951
8952 init_color_table (); 8989 init_color_table ();
8953 8990
8954 unsigned long bgcolor UNINIT; 8991 unsigned long bgcolor UNINIT;
@@ -8963,7 +9000,19 @@ gif_load (struct frame *f, struct image *img)
8963#endif 9000#endif
8964 } 9001 }
8965 9002
8966 for (j = 0; j <= idx; ++j) 9003 int start_frame = 0;
9004
9005 /* We have animation data in the cache, so copy it over so that we
9006 can alter it. */
9007 int cache_image_size = width * height * ximg->bits_per_pixel / 8;
9008 if (cache && cache->temp)
9009 {
9010 memcpy (ximg->data, cache->temp, cache_image_size);
9011 start_frame = cache->index;
9012 cache->index = idx;
9013 }
9014
9015 for (j = start_frame; j <= idx; ++j)
8967 { 9016 {
8968 /* We use a local variable `raster' here because RasterBits is a 9017 /* We use a local variable `raster' here because RasterBits is a
8969 char *, which invites problems with bytes >= 0x80. */ 9018 char *, which invites problems with bytes >= 0x80. */
@@ -9074,6 +9123,15 @@ gif_load (struct frame *f, struct image *img)
9074 } 9123 }
9075 } 9124 }
9076 9125
9126 if (cache)
9127 {
9128 /* Allocate an area to store what we have computed so far. */
9129 if (! cache->temp)
9130 cache->temp = xmalloc (cache_image_size);
9131 /* Copy over the data to the cache. */
9132 memcpy (cache->temp, ximg->data, cache_image_size);
9133 }
9134
9077#ifdef COLOR_TABLE_SUPPORT 9135#ifdef COLOR_TABLE_SUPPORT
9078 img->colors = colors_in_color_table (&img->ncolors); 9136 img->colors = colors_in_color_table (&img->ncolors);
9079 free_color_table (); 9137 free_color_table ();
@@ -9114,17 +9172,20 @@ gif_load (struct frame *f, struct image *img)
9114 Fcons (make_fixnum (gif->ImageCount), 9172 Fcons (make_fixnum (gif->ImageCount),
9115 img->lisp_data)); 9173 img->lisp_data));
9116 9174
9117 if (gif_close (gif, &gif_err) == GIF_ERROR) 9175 if (!cache)
9118 { 9176 {
9177 if (gif_close (gif, &gif_err) == GIF_ERROR)
9178 {
9119#if HAVE_GIFERRORSTRING 9179#if HAVE_GIFERRORSTRING
9120 char const *error_text = GifErrorString (gif_err); 9180 char const *error_text = GifErrorString (gif_err);
9121 9181
9122 if (error_text) 9182 if (error_text)
9123 image_error ("Error closing `%s': %s", 9183 image_error ("Error closing `%s': %s",
9124 img->spec, build_string (error_text)); 9184 img->spec, build_string (error_text));
9125 else 9185 else
9126#endif 9186#endif
9127 image_error ("Error closing `%s'", img->spec); 9187 image_error ("Error closing `%s'", img->spec);
9188 }
9128 } 9189 }
9129 9190
9130 /* Maybe fill in the background field while we have ximg handy. */ 9191 /* Maybe fill in the background field while we have ximg handy. */
@@ -9138,7 +9199,8 @@ gif_load (struct frame *f, struct image *img)
9138 return true; 9199 return true;
9139 9200
9140 gif_error: 9201 gif_error:
9141 gif_close (gif, NULL); 9202 if (!cache)
9203 gif_close (gif, NULL);
9142 return false; 9204 return false;
9143} 9205}
9144 9206
@@ -9292,6 +9354,12 @@ init_webp_functions (void)
9292 9354
9293#endif /* WINDOWSNT */ 9355#endif /* WINDOWSNT */
9294 9356
9357static void
9358webp_destroy (struct anim_cache* cache)
9359{
9360 WebPAnimDecoderDelete (cache->handle);
9361}
9362
9295/* Load WebP image IMG for use on frame F. Value is true if 9363/* Load WebP image IMG for use on frame F. Value is true if
9296 successful. */ 9364 successful. */
9297 9365
@@ -9408,7 +9476,7 @@ webp_load (struct frame *f, struct image *img)
9408 cache->height = height = WebPDemuxGetI (demux, 9476 cache->height = height = WebPDemuxGetI (demux,
9409 WEBP_FF_CANVAS_HEIGHT); 9477 WEBP_FF_CANVAS_HEIGHT);
9410 cache->frames = frames = WebPDemuxGetI (demux, WEBP_FF_FRAME_COUNT); 9478 cache->frames = frames = WebPDemuxGetI (demux, WEBP_FF_FRAME_COUNT);
9411 cache->destructor = (void (*)(void *)) &WebPAnimDecoderDelete; 9479 cache->destructor = (void (*)(void *)) webp_destroy;
9412 WebPDemuxDelete (demux); 9480 WebPDemuxDelete (demux);
9413 9481
9414 WebPAnimDecoderOptions dec_options; 9482 WebPAnimDecoderOptions dec_options;