X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/da5707e8ec8df0a8a9bb1a1f12c6b701314845cc..287d70764c83f6054eb2a0ede942eda01c20fcdf:/src/image.c diff --git a/src/image.c b/src/image.c index 99bb9d8c13..38a9227729 100644 --- a/src/image.c +++ b/src/image.c @@ -30,13 +30,8 @@ along with GNU Emacs. If not, see . */ #endif #include - #include -/* This makes the fields of a Display accessible, in Xlib header files. */ - -#define XLIB_ILLEGAL_ACCESS - #include "lisp.h" #include "frame.h" #include "window.h" @@ -92,10 +87,12 @@ typedef struct w32_bitmap_record Bitmap_Record; #define x_defined_color w32_defined_color #define DefaultDepthOfScreen(screen) (one_w32_display_info.n_cbits) -/* Version of libpng that we were compiled with, or -1 if no PNG - support was compiled in. This is tested by w32-win.el to correctly - set up the alist used to search for PNG libraries. */ -Lisp_Object Qlibpng_version; +/* Versions of libpng, libgif, and libjpeg that we were compiled with, + or -1 if no PNG/GIF support was compiled in. This is tested by + w32-win.el to correctly set up the alist used to search for the + respective image libraries. */ +Lisp_Object Qlibpng_version, Qlibgif_version, Qlibjpeg_version; + #endif /* HAVE_NTGUI */ #ifdef HAVE_NS @@ -145,7 +142,7 @@ static Lisp_Object QCmax_width, QCmax_height; #ifdef HAVE_NS /* Use with images created by ns_image_for_XPM. */ -unsigned long +static unsigned long XGetPixel (XImagePtr ximage, int x, int y) { return ns_get_pixel (ximage, x, y); @@ -153,7 +150,7 @@ XGetPixel (XImagePtr ximage, int x, int y) /* Use with images created by ns_image_for_XPM; alpha set to 1; pixel is assumed to be in RGB form. */ -void +static void XPutPixel (XImagePtr ximage, int x, int y, unsigned long pixel) { ns_put_pixel (ximage, x, y, pixel); @@ -163,16 +160,16 @@ XPutPixel (XImagePtr ximage, int x, int y, unsigned long pixel) /* Functions to access the contents of a bitmap, given an id. */ -int +static int x_bitmap_height (struct frame *f, ptrdiff_t id) { - return FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].height; + return FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].height; } -int +static int x_bitmap_width (struct frame *f, ptrdiff_t id) { - return FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].width; + return FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].width; } #if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI) @@ -180,7 +177,7 @@ ptrdiff_t x_bitmap_pixmap (struct frame *f, ptrdiff_t id) { /* HAVE_NTGUI needs the explicit cast here. */ - return (ptrdiff_t) FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].pixmap; + return (ptrdiff_t) FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].pixmap; } #endif @@ -188,7 +185,7 @@ x_bitmap_pixmap (struct frame *f, ptrdiff_t id) int x_bitmap_mask (struct frame *f, ptrdiff_t id) { - return FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].mask; + return FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].mask; } #endif @@ -197,7 +194,7 @@ x_bitmap_mask (struct frame *f, ptrdiff_t id) static ptrdiff_t x_allocate_bitmap_record (struct frame *f) { - Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f); ptrdiff_t i; if (dpyinfo->bitmaps_last < dpyinfo->bitmaps_size) @@ -218,7 +215,7 @@ x_allocate_bitmap_record (struct frame *f) void x_reference_bitmap (struct frame *f, ptrdiff_t id) { - ++FRAME_X_DISPLAY_INFO (f)->bitmaps[id - 1].refcount; + ++FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].refcount; } /* Create a bitmap for frame F from a HEIGHT x WIDTH array of bits at BITS. */ @@ -226,7 +223,7 @@ x_reference_bitmap (struct frame *f, ptrdiff_t id) ptrdiff_t x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsigned int height) { - Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f); ptrdiff_t id; #ifdef HAVE_X_WINDOWS @@ -240,8 +237,8 @@ x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsi #ifdef HAVE_NTGUI Pixmap bitmap; bitmap = CreateBitmap (width, height, - FRAME_X_DISPLAY_INFO (XFRAME (frame))->n_planes, - FRAME_X_DISPLAY_INFO (XFRAME (frame))->n_cbits, + FRAME_DISPLAY_INFO (XFRAME (frame))->n_planes, + FRAME_DISPLAY_INFO (XFRAME (frame))->n_cbits, bits); if (! bitmap) return -1; @@ -285,7 +282,7 @@ x_create_bitmap_from_data (struct frame *f, char *bits, unsigned int width, unsi ptrdiff_t x_create_bitmap_from_file (struct frame *f, Lisp_Object file) { - Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f); #ifdef HAVE_NTGUI return -1; /* W32_TODO : bitmap support */ @@ -302,11 +299,10 @@ x_create_bitmap_from_file (struct frame *f, Lisp_Object file) id = x_allocate_bitmap_record (f); dpyinfo->bitmaps[id - 1].img = bitmap; dpyinfo->bitmaps[id - 1].refcount = 1; - dpyinfo->bitmaps[id - 1].file = xmalloc (SBYTES (file) + 1); + dpyinfo->bitmaps[id - 1].file = xlispstrdup (file); dpyinfo->bitmaps[id - 1].depth = 1; dpyinfo->bitmaps[id - 1].height = ns_image_width (bitmap); dpyinfo->bitmaps[id - 1].width = ns_image_height (bitmap); - strcpy (dpyinfo->bitmaps[id - 1].file, SSDATA (file)); return id; #endif @@ -345,11 +341,10 @@ x_create_bitmap_from_file (struct frame *f, Lisp_Object file) dpyinfo->bitmaps[id - 1].pixmap = bitmap; dpyinfo->bitmaps[id - 1].have_mask = 0; dpyinfo->bitmaps[id - 1].refcount = 1; - dpyinfo->bitmaps[id - 1].file = xmalloc (SBYTES (file) + 1); + dpyinfo->bitmaps[id - 1].file = xlispstrdup (file); dpyinfo->bitmaps[id - 1].depth = 1; dpyinfo->bitmaps[id - 1].height = height; dpyinfo->bitmaps[id - 1].width = width; - strcpy (dpyinfo->bitmaps[id - 1].file, SSDATA (file)); return id; #endif /* HAVE_X_WINDOWS */ @@ -386,7 +381,7 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record *bm) void x_destroy_bitmap (struct frame *f, ptrdiff_t id) { - Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f); if (id > 0) { @@ -461,7 +456,7 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id) unsigned long x, y, xp, xm, yp, ym; GC gc; - Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f); if (!(id > 0)) return; @@ -565,7 +560,6 @@ static void x_emboss (struct frame *, struct image *); static void x_build_heuristic_mask (struct frame *, struct image *, Lisp_Object); #ifdef WINDOWSNT -extern Lisp_Object Vlibrary_cache; #define CACHE_IMAGE_TYPE(type, status) \ do { Vlibrary_cache = Fcons (Fcons (type, status), Vlibrary_cache); } while (0) #else @@ -1044,7 +1038,7 @@ void prepare_image_for_display (struct frame *f, struct image *img) { /* We're about to display IMG, so set its timestamp to `now'. */ - img->timestamp = current_emacs_time (); + img->timestamp = current_timespec (); /* If IMG doesn't have a pixmap yet, load it now, using the image type dependent loader function. */ @@ -1362,14 +1356,12 @@ static void cache_image (struct frame *f, struct image *img); struct image_cache * make_image_cache (void) { - struct image_cache *c = xzalloc (sizeof *c); - int size; + struct image_cache *c = xmalloc (sizeof *c); - size = 50; - c->images = xmalloc (size * sizeof *c->images); - c->size = size; - size = IMAGE_CACHE_BUCKETS_SIZE * sizeof *c->buckets; - c->buckets = xzalloc (size); + c->size = 50; + c->used = c->refcount = 0; + c->images = xmalloc (c->size * sizeof *c->images); + c->buckets = xzalloc (IMAGE_CACHE_BUCKETS_SIZE * sizeof *c->buckets); return c; } @@ -1485,7 +1477,7 @@ clear_image_cache (struct frame *f, Lisp_Object filter) else if (INTEGERP (Vimage_cache_eviction_delay)) { /* Free cache based on timestamp. */ - EMACS_TIME old, t; + struct timespec old, t; double delay; ptrdiff_t nimages = 0; @@ -1500,13 +1492,13 @@ clear_image_cache (struct frame *f, Lisp_Object filter) delay = 1600 * delay / nimages / nimages; delay = max (delay, 1); - t = current_emacs_time (); - old = sub_emacs_time (t, EMACS_TIME_FROM_DOUBLE (delay)); + t = current_timespec (); + old = timespec_sub (t, dtotimespec (delay)); for (i = 0; i < c->used; ++i) { struct image *img = c->images[i]; - if (img && EMACS_TIME_LT (img->timestamp, old)) + if (img && timespec_cmp (img->timestamp, old) < 0) { free_image (f, img); ++nfreed; @@ -1529,7 +1521,7 @@ clear_image_cache (struct frame *f, Lisp_Object filter) clear_current_matrices (fr); } - ++windows_or_buffers_changed; + windows_or_buffers_changed = 19; } unblock_input (); @@ -1769,7 +1761,7 @@ lookup_image (struct frame *f, Lisp_Object spec) } /* We're using IMG, so set its timestamp to `now'. */ - img->timestamp = current_emacs_time (); + img->timestamp = current_timespec (); /* Value is the image id. */ return img->id; @@ -2465,7 +2457,7 @@ xbm_image_p (Lisp_Object object) } else if (BOOL_VECTOR_P (elt)) { - if (XBOOL_VECTOR (elt)->size < width) + if (bool_vector_size (elt) < width) return 0; } else @@ -2480,7 +2472,7 @@ xbm_image_p (Lisp_Object object) } else if (BOOL_VECTOR_P (data)) { - if (XBOOL_VECTOR (data)->size / height < width) + if (bool_vector_size (data) / height < width) return 0; } else @@ -2717,10 +2709,13 @@ xbm_read_bitmap_data (struct frame *f, unsigned char *contents, unsigned char *e LA1 = xbm_scan (&s, end, buffer, &value) #define expect(TOKEN) \ - if (LA1 != (TOKEN)) \ - goto failure; \ - else \ - match () + do \ + { \ + if (LA1 != (TOKEN)) \ + goto failure; \ + match (); \ + } \ + while (0) #define expect_ident(IDENT) \ if (LA1 == XBM_TK_IDENT && strcmp (buffer, (IDENT)) == 0) \ @@ -3031,13 +3026,13 @@ xbm_load (struct frame *f, struct image *img) if (STRINGP (line)) memcpy (p, SDATA (line), nbytes); else - memcpy (p, XBOOL_VECTOR (line)->data, nbytes); + memcpy (p, bool_vector_data (line), nbytes); } } else if (STRINGP (data)) bits = SSDATA (data); else - bits = (char *) XBOOL_VECTOR (data)->data; + bits = (char *) bool_vector_data (data); #ifdef HAVE_NTGUI { @@ -3428,7 +3423,7 @@ xpm_image_p (Lisp_Object object) ptrdiff_t x_create_bitmap_from_xpm_data (struct frame *f, const char **bits) { - Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f); ptrdiff_t id; int rc; XpmAttributes attrs; @@ -3595,6 +3590,12 @@ xpm_load (struct frame *f, struct image *img) } #ifdef HAVE_NTGUI +#ifdef WINDOWSNT + /* FILE is encoded in UTF-8, but image libraries on Windows + support neither UTF-8 nor UTF-16 encoded file names. So we + need to re-encode it in ANSI. */ + file = ansi_encode_filename (file); +#endif /* XpmReadFileToPixmap is not available in the Windows port of libxpm. But XpmReadFileToImage almost does what we want. */ rc = fn_XpmReadFileToImage (&hdc, SDATA (file), @@ -3980,10 +3981,13 @@ xpm_load_image (struct frame *f, LA1 = xpm_scan (&s, end, &beg, &len) #define expect(TOKEN) \ - if (LA1 != (TOKEN)) \ - goto failure; \ - else \ - match () + do \ + { \ + if (LA1 != (TOKEN)) \ + goto failure; \ + match (); \ + } \ + while (0) #define expect_ident(IDENT) \ if (LA1 == XPM_TK_IDENT \ @@ -4329,7 +4333,7 @@ lookup_rgb_color (struct frame *f, int r, int g, int b) two orders of magnitude. Freeing colors on TrueColor visuals is a nop, and pixel colors specify RGB values directly. See also the Xlib spec, chapter 3.1. */ - dpyinfo = FRAME_X_DISPLAY_INFO (f); + dpyinfo = FRAME_DISPLAY_INFO (f); if (dpyinfo->red_bits > 0) { unsigned long pr, pg, pb; @@ -4824,7 +4828,7 @@ x_edge_detection (struct frame *f, struct image *img, Lisp_Object matrix, static void x_disable_image (struct frame *f, struct image *img) { - Display_Info *dpyinfo = FRAME_X_DISPLAY_INFO (f); + Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f); #ifdef HAVE_NTGUI int n_planes = dpyinfo->n_planes * dpyinfo->n_cbits; #else @@ -4936,7 +4940,7 @@ x_build_heuristic_mask (struct frame *f, struct image *img, Lisp_Object how) int row_width; #endif /* HAVE_NTGUI */ int x, y; - bool rc, use_img_background; + bool use_img_background; unsigned long bg = 0; if (img->mask) @@ -4945,9 +4949,8 @@ x_build_heuristic_mask (struct frame *f, struct image *img, Lisp_Object how) #ifndef HAVE_NTGUI #ifndef HAVE_NS /* Create an image and pixmap serving as mask. */ - rc = image_create_x_image_and_pixmap (f, img, img->width, img->height, 1, - &mask_img, 1); - if (!rc) + if (! image_create_x_image_and_pixmap (f, img, img->width, img->height, 1, + &mask_img, 1)) return; #endif /* !HAVE_NS */ #else @@ -5109,6 +5112,27 @@ pbm_image_p (Lisp_Object object) } +/* Get next char skipping comments in Netpbm header. Returns -1 at + end of input. */ + +static int +pbm_next_char (unsigned char **s, unsigned char *end) +{ + int c = -1; + + while (*s < end && (c = *(*s)++, c == '#')) + { + /* Skip to the next line break. */ + while (*s < end && (c = *(*s)++, c != '\n' && c != '\r')) + ; + + c = -1; + } + + return c; +} + + /* Scan a decimal number from *S and return it. Advance *S while reading the number. END is the end of the string. Value is -1 at end of input. */ @@ -5118,28 +5142,16 @@ pbm_scan_number (unsigned char **s, unsigned char *end) { int c = 0, val = -1; - while (*s < end) - { - /* Skip white-space. */ - while (*s < end && (c = *(*s)++, c_isspace (c))) - ; + /* Skip white-space. */ + while ((c = pbm_next_char (s, end)) != -1 && c_isspace (c)) + ; - if (c == '#') - { - /* Skip comment to end of line. */ - while (*s < end && (c = *(*s)++, c != '\n')) - ; - } - else if (c_isdigit (c)) - { - /* Read decimal number. */ - val = c - '0'; - while (*s < end && (c = *(*s)++, c_isdigit (c))) - val = 10 * val + c - '0'; - break; - } - else - break; + if (c_isdigit (c)) + { + /* Read decimal number. */ + val = c - '0'; + while ((c = pbm_next_char (s, end)) != -1 && c_isdigit (c)) + val = 10 * val + c - '0'; } return val; @@ -6962,6 +6974,9 @@ tiff_load (struct frame *f, struct image *img) image_error ("Cannot find image file `%s'", specified_file, Qnil); return 0; } +#ifdef WINDOWSNT + file = ansi_encode_filename (file); +#endif /* Try to open the image file. */ tiff = fn_TIFFOpen (SSDATA (file), "r"); @@ -7208,6 +7223,7 @@ gif_image_p (Lisp_Object object) #ifdef HAVE_GIF #if defined (HAVE_NTGUI) + /* winuser.h might define DrawText to DrawTextA or DrawTextW. Undefine before redefining to avoid a preprocessor warning. */ #ifdef DrawText @@ -7218,20 +7234,38 @@ gif_image_p (Lisp_Object object) #include #undef DrawText +/* Giflib before 5.0 didn't define these macros (used only if HAVE_NTGUI). */ +#ifndef GIFLIB_MINOR +#define GIFLIB_MINOR 0 +#endif +#ifndef GIFLIB_RELEASE +#define GIFLIB_RELEASE 0 +#endif + #else /* HAVE_NTGUI */ #include #endif /* HAVE_NTGUI */ +/* Giflib before 5.0 didn't define these macros. */ +#ifndef GIFLIB_MAJOR +#define GIFLIB_MAJOR 4 +#endif #ifdef WINDOWSNT /* GIF library details. */ DEF_IMGLIB_FN (int, DGifCloseFile, (GifFileType *)); DEF_IMGLIB_FN (int, DGifSlurp, (GifFileType *)); +#if GIFLIB_MAJOR < 5 DEF_IMGLIB_FN (GifFileType *, DGifOpen, (void *, InputFunc)); DEF_IMGLIB_FN (GifFileType *, DGifOpenFileName, (const char *)); +#else +DEF_IMGLIB_FN (GifFileType *, DGifOpen, (void *, InputFunc, int *)); +DEF_IMGLIB_FN (GifFileType *, DGifOpenFileName, (const char *, int *)); +DEF_IMGLIB_FN (char *, GifErrorString, (int)); +#endif static bool init_gif_functions (void) @@ -7245,6 +7279,9 @@ init_gif_functions (void) LOAD_IMGLIB_FN (library, DGifSlurp); LOAD_IMGLIB_FN (library, DGifOpen); LOAD_IMGLIB_FN (library, DGifOpenFileName); +#if GIFLIB_MAJOR >= 5 + LOAD_IMGLIB_FN (library, GifErrorString); +#endif return 1; } @@ -7254,6 +7291,9 @@ init_gif_functions (void) #define fn_DGifSlurp DGifSlurp #define fn_DGifOpen DGifOpen #define fn_DGifOpenFileName DGifOpenFileName +#if 5 <= GIFLIB_MAJOR +# define fn_GifErrorString GifErrorString +#endif #endif /* WINDOWSNT */ @@ -7310,6 +7350,9 @@ gif_load (struct frame *f, struct image *img) Lisp_Object specified_data = image_spec_value (img->spec, QCdata, NULL); unsigned long bgcolor = 0; EMACS_INT idx; +#if GIFLIB_MAJOR >= 5 + int gif_err; +#endif if (NILP (specified_data)) { @@ -7319,14 +7362,27 @@ gif_load (struct frame *f, struct image *img) image_error ("Cannot find image file `%s'", specified_file, Qnil); return 0; } +#ifdef WINDOWSNT + file = ansi_encode_filename (file); +#endif /* Open the GIF file. */ +#if GIFLIB_MAJOR < 5 gif = fn_DGifOpenFileName (SSDATA (file)); if (gif == NULL) { image_error ("Cannot open `%s'", file, Qnil); return 0; } +#else + gif = fn_DGifOpenFileName (SSDATA (file), &gif_err); + if (gif == NULL) + { + image_error ("Cannot open `%s': %s", + file, build_string (fn_GifErrorString (gif_err))); + return 0; + } +#endif } else { @@ -7342,12 +7398,22 @@ gif_load (struct frame *f, struct image *img) memsrc.len = SBYTES (specified_data); memsrc.index = 0; +#if GIFLIB_MAJOR < 5 gif = fn_DGifOpen (&memsrc, gif_read_from_memory); if (!gif) { image_error ("Cannot open memory source `%s'", img->spec, Qnil); return 0; } +#else + gif = fn_DGifOpen (&memsrc, gif_read_from_memory, &gif_err); + if (!gif) + { + image_error ("Cannot open memory source `%s': %s", + img->spec, build_string (fn_GifErrorString (gif_err))); + return 0; + } +#endif } /* Before reading entire contents, check the declared image size. */ @@ -7527,10 +7593,7 @@ gif_load (struct frame *f, struct image *img) y++, row += interlace_increment[pass]) { while (subimg_height <= row) - { - lint_assume (pass < 3); - row = interlace_start[++pass]; - } + row = interlace_start[++pass]; for (x = 0; x < subimg_width; x++) { @@ -7845,37 +7908,232 @@ imagemagick_error (MagickWand *wand) } /* Possibly give ImageMagick some extra help to determine the image - type by supplying a "dummy" filename based on the Content-Type. */ + type by supplying a "dummy" filename based on the Content-Type. */ -static char* -imagemagick_filename_hint (Lisp_Object spec) +static char * +imagemagick_filename_hint (Lisp_Object spec, char hint_buffer[MaxTextExtent]) { - Lisp_Object format = image_spec_value (spec, intern (":format"), NULL); - Lisp_Object val, symbol = intern ("image-format-suffixes"); - const char *prefix = "/tmp/foo."; - char *name; + Lisp_Object symbol = intern ("image-format-suffixes"); + Lisp_Object val = find_symbol_value (symbol); + Lisp_Object format; - if (NILP (Fboundp (symbol))) - return NULL; - - val = Fassq (format, Fsymbol_value (symbol)); if (! CONSP (val)) return NULL; - val = Fcdr (val); - if (! CONSP (val)) - return NULL; - - val = Fcar (val); + format = image_spec_value (spec, intern (":format"), NULL); + val = Fcar_safe (Fcdr_safe (Fassq (format, val))); if (! STRINGP (val)) return NULL; - name = xmalloc (strlen (prefix) + SBYTES (val) + 1); - strcpy (name, prefix); - strcat (name, SSDATA (val)); - return name; + /* It's OK to truncate the hint if it has MaxTextExtent or more bytes, + as ImageMagick would ignore the extra bytes anyway. */ + snprintf (hint_buffer, MaxTextExtent, "/tmp/foo.%s", SSDATA (val)); + return hint_buffer; +} + +/* Animated images (e.g., GIF89a) are composed from one "master image" + (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 + sub-image. + + Computing all the preceding images is too slow, so we maintain a + cache of previously computed images. We have to maintain a cache + separate from the image cache, because the images may be scaled + before display. */ + +struct animation_cache +{ + MagickWand *wand; + int index; + struct timespec update_time; + struct animation_cache *next; + char signature[FLEXIBLE_ARRAY_MEMBER]; +}; + +static struct animation_cache *animation_cache = NULL; + +static struct animation_cache * +imagemagick_create_cache (char *signature) +{ + struct animation_cache *cache + = xmalloc (offsetof (struct animation_cache, signature) + + strlen (signature) + 1); + cache->wand = 0; + cache->index = 0; + cache->next = 0; + strcpy (cache->signature, signature); + return cache; +} + +/* Discard cached images that haven't been used for a minute. */ +static void +imagemagick_prune_animation_cache (void) +{ + struct animation_cache **pcache = &animation_cache; + struct timespec old = timespec_sub (current_timespec (), + make_timespec (60, 0)); + + while (*pcache) + { + struct animation_cache *cache = *pcache; + if (timespec_cmp (old, cache->update_time) <= 0) + pcache = &cache->next; + else + { + if (cache->wand) + DestroyMagickWand (cache->wand); + *pcache = cache->next; + xfree (cache); + } + } +} + +static struct animation_cache * +imagemagick_get_animation_cache (MagickWand *wand) +{ + char *signature = MagickGetImageSignature (wand); + struct animation_cache *cache; + struct animation_cache **pcache = &animation_cache; + + imagemagick_prune_animation_cache (); + + while (1) + { + cache = *pcache; + if (! cache) + { + *pcache = cache = imagemagick_create_cache (signature); + break; + } + if (strcmp (signature, cache->signature) == 0) + break; + pcache = &cache->next; + } + + DestroyString (signature); + cache->update_time = current_timespec (); + return cache; +} + +static MagickWand * +imagemagick_compute_animated_image (MagickWand *super_wand, int ino) +{ + int i; + MagickWand *composite_wand; + size_t dest_width, dest_height; + struct animation_cache *cache = imagemagick_get_animation_cache (super_wand); + + MagickSetIteratorIndex (super_wand, 0); + + if (ino == 0 || cache->wand == NULL || cache->index > ino) + { + composite_wand = MagickGetImage (super_wand); + if (cache->wand) + DestroyMagickWand (cache->wand); + } + else + composite_wand = cache->wand; + + dest_width = MagickGetImageWidth (composite_wand); + dest_height = MagickGetImageHeight (composite_wand); + + for (i = max (1, cache->index + 1); i <= ino; i++) + { + MagickWand *sub_wand; + PixelIterator *source_iterator, *dest_iterator; + PixelWand **source, **dest; + size_t source_width, source_height; + ssize_t source_left, source_top; + MagickPixelPacket pixel; + DisposeType dispose; + ptrdiff_t lines = 0; + + MagickSetIteratorIndex (super_wand, i); + sub_wand = MagickGetImage (super_wand); + + MagickGetImagePage (sub_wand, &source_width, &source_height, + &source_left, &source_top); + + /* This flag says how to handle transparent pixels. */ + dispose = MagickGetImageDispose (sub_wand); + + source_iterator = NewPixelIterator (sub_wand); + if (! source_iterator) + { + DestroyMagickWand (composite_wand); + DestroyMagickWand (sub_wand); + cache->wand = NULL; + image_error ("Imagemagick pixel iterator creation failed", + Qnil, Qnil); + return NULL; + } + + dest_iterator = NewPixelIterator (composite_wand); + if (! dest_iterator) + { + DestroyMagickWand (composite_wand); + DestroyMagickWand (sub_wand); + DestroyPixelIterator (source_iterator); + cache->wand = NULL; + image_error ("Imagemagick pixel iterator creation failed", + Qnil, Qnil); + return NULL; + } + + /* The sub-image may not start at origin, so move the destination + iterator to where the sub-image should start. */ + if (source_top > 0) + { + PixelSetIteratorRow (dest_iterator, source_top); + lines = source_top; + } + + while ((source = PixelGetNextIteratorRow (source_iterator, &source_width)) + != NULL) + { + ptrdiff_t x; + + /* Sanity check. This shouldn't happen, but apparently + does in some pictures. */ + if (++lines >= dest_height) + break; + + dest = PixelGetNextIteratorRow (dest_iterator, &dest_width); + for (x = 0; x < source_width; x++) + { + /* Sanity check. This shouldn't happen, but apparently + also does in some pictures. */ + if (x + source_left > dest_width - 1) + break; + /* Normally we only copy over non-transparent pixels, + but if the disposal method is "Background", then we + copy over all pixels. */ + if (dispose == BackgroundDispose || + PixelGetAlpha (source[x])) + { + PixelGetMagickColor (source[x], &pixel); + PixelSetMagickColor (dest[x + source_left], &pixel); + } + } + PixelSyncIterator (dest_iterator); + } + + DestroyPixelIterator (source_iterator); + DestroyPixelIterator (dest_iterator); + DestroyMagickWand (sub_wand); + } + + /* Cache a copy for the next iteration. The current wand will be + destroyed by the caller. */ + cache->wand = CloneMagickWand (composite_wand); + cache->index = ino; + + return composite_wand; } + /* Helper function for imagemagick_load, which does the actual loading given contents and size, apart from frame and image structures, passed from imagemagick_load. Uses librimagemagick to do most of @@ -7898,7 +8156,6 @@ imagemagick_load_image (struct frame *f, struct image *img, XImagePtr ximg; int x, y; MagickWand *image_wand; - MagickWand *ping_wand; PixelIterator *iterator; PixelWand **pixels, *bg_wand = NULL; MagickPixelPacket pixel; @@ -7909,6 +8166,7 @@ imagemagick_load_image (struct frame *f, struct image *img, int desired_width, desired_height; double rotation; int pixelwidth; + char hint_buffer[MaxTextExtent]; char *filename_hint = NULL; /* Handle image index for image types who can contain more than one image. @@ -7920,57 +8178,48 @@ imagemagick_load_image (struct frame *f, struct image *img, MagickWandGenesis (); image = image_spec_value (img->spec, QCindex, NULL); ino = INTEGERP (image) ? XFASTINT (image) : 0; - ping_wand = NewMagickWand (); - /* MagickSetResolution (ping_wand, 2, 2); (Bug#10112) */ - - if (! filename) - filename_hint = imagemagick_filename_hint (img->spec); - - if (filename_hint) - MagickSetFilename (ping_wand, filename_hint); + image_wand = NewMagickWand (); - status = filename - ? MagickPingImage (ping_wand, filename) - : MagickPingImageBlob (ping_wand, contents, size); + if (filename) + status = MagickReadImage (image_wand, filename); + else + { + filename_hint = imagemagick_filename_hint (img->spec, hint_buffer); + MagickSetFilename (image_wand, filename_hint); + status = MagickReadImageBlob (image_wand, contents, size); + } if (status == MagickFalse) { - imagemagick_error (ping_wand); - DestroyMagickWand (ping_wand); + imagemagick_error (image_wand); + DestroyMagickWand (image_wand); return 0; } - if (ino < 0 || ino >= MagickGetNumberImages (ping_wand)) + if (ino < 0 || ino >= MagickGetNumberImages (image_wand)) { image_error ("Invalid image number `%s' in image `%s'", image, img->spec); - DestroyMagickWand (ping_wand); + DestroyMagickWand (image_wand); return 0; } - if (MagickGetNumberImages (ping_wand) > 1) + if (MagickGetNumberImages (image_wand) > 1) img->lisp_data = Fcons (Qcount, - Fcons (make_number (MagickGetNumberImages (ping_wand)), + Fcons (make_number (MagickGetNumberImages (image_wand)), img->lisp_data)); - DestroyMagickWand (ping_wand); - - /* Now we know how many images are inside the file. If it's not a - bundle, the number is one. Load the image data. */ - - image_wand = NewMagickWand (); - - if (filename_hint) - MagickSetFilename (image_wand, filename_hint); - - if ((filename - ? MagickReadImage (image_wand, filename) - : MagickReadImageBlob (image_wand, contents, size)) - == MagickFalse) + /* If we have an animated image, get the new wand based on the + "super-wand". */ + if (MagickGetNumberImages (image_wand) > 1) { - imagemagick_error (image_wand); - goto imagemagick_error; + MagickWand *super_wand = image_wand; + image_wand = imagemagick_compute_animated_image (super_wand, ino); + if (! image_wand) + image_wand = super_wand; + else + DestroyMagickWand (super_wand); } /* Retrieve the frame's background color, for use later. */ @@ -8207,16 +8456,11 @@ imagemagick_load_image (struct frame *f, struct image *img, /* `MagickWandTerminus' terminates the imagemagick environment. */ MagickWandTerminus (); - if (filename_hint) - free (filename_hint); - return 1; imagemagick_error: DestroyMagickWand (image_wand); if (bg_wand) DestroyPixelWand (bg_wand); - if (filename_hint) - free (filename_hint); MagickWandTerminus (); /* TODO more cleanup. */ @@ -8247,6 +8491,9 @@ imagemagick_load (struct frame *f, struct image *img) image_error ("Cannot find image file `%s'", file_name, Qnil); return 0; } +#ifdef WINDOWSNT + file = ansi_encode_filename (file); +#endif success_p = imagemagick_load_image (f, img, 0, 0, SSDATA (file)); } /* Else its not a file, its a lisp object. Load the image from a @@ -8414,7 +8661,9 @@ DEF_IMGLIB_FN (int, gdk_pixbuf_get_n_channels, (const GdkPixbuf *)); DEF_IMGLIB_FN (gboolean, gdk_pixbuf_get_has_alpha, (const GdkPixbuf *)); DEF_IMGLIB_FN (int, gdk_pixbuf_get_bits_per_sample, (const GdkPixbuf *)); +#if ! GLIB_CHECK_VERSION (2, 36, 0) DEF_IMGLIB_FN (void, g_type_init, (void)); +#endif DEF_IMGLIB_FN (void, g_object_unref, (gpointer)); DEF_IMGLIB_FN (void, g_error_free, (GError *)); @@ -8446,7 +8695,9 @@ init_svg_functions (void) LOAD_IMGLIB_FN (gdklib, gdk_pixbuf_get_has_alpha); LOAD_IMGLIB_FN (gdklib, gdk_pixbuf_get_bits_per_sample); +#if ! GLIB_CHECK_VERSION (2, 36, 0) LOAD_IMGLIB_FN (gobject, g_type_init); +#endif LOAD_IMGLIB_FN (gobject, g_object_unref); LOAD_IMGLIB_FN (glib, g_error_free); @@ -8471,7 +8722,9 @@ init_svg_functions (void) #define fn_gdk_pixbuf_get_has_alpha gdk_pixbuf_get_has_alpha #define fn_gdk_pixbuf_get_bits_per_sample gdk_pixbuf_get_bits_per_sample +#if ! GLIB_CHECK_VERSION (2, 36, 0) #define fn_g_type_init g_type_init +#endif #define fn_g_object_unref g_object_unref #define fn_g_error_free g_error_free #endif /* !WINDOWSNT */ @@ -8556,9 +8809,12 @@ svg_load_image (struct frame *f, /* Pointer to emacs frame structure. * int x; int y; - /* g_type_init is a glib function that must be called prior to using - gnome type library functions. */ +#if ! GLIB_CHECK_VERSION (2, 36, 0) + /* g_type_init is a glib function that must be called prior to + using gnome type library functions (obsolete since 2.36.0). */ fn_g_type_init (); +#endif + /* Make a handle to a new rsvg object. */ rsvg_handle = fn_rsvg_handle_new (); @@ -9169,6 +9425,24 @@ non-numeric, there is no explicit limit on the size of images. */); make_number (PNG_LIBPNG_VER) #else make_number (-1) +#endif + ); + DEFSYM (Qlibgif_version, "libgif-version"); + Fset (Qlibgif_version, +#ifdef HAVE_GIF + make_number (GIFLIB_MAJOR * 10000 + + GIFLIB_MINOR * 100 + + GIFLIB_RELEASE) +#else + make_number (-1) +#endif + ); + DEFSYM (Qlibjpeg_version, "libjpeg-version"); + Fset (Qlibjpeg_version, +#if HAVE_JPEG + make_number (JPEG_LIB_VERSION) +#else + make_number (-1) #endif ); #endif @@ -9243,7 +9517,7 @@ A cross is always drawn on black & white displays. */); DEFVAR_LISP ("x-bitmap-file-path", Vx_bitmap_file_path, doc: /* List of directories to search for window system bitmap files. */); - Vx_bitmap_file_path = decode_env_path (0, PATH_BITMAPS); + Vx_bitmap_file_path = decode_env_path (0, PATH_BITMAPS, 0); DEFVAR_LISP ("image-cache-eviction-delay", Vimage_cache_eviction_delay, doc: /* Maximum time after which images are removed from the cache.