#endif
#include <setjmp.h>
-
#include <c-ctype.h>
-/* 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"
#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 and libgif 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;
#endif /* HAVE_NTGUI */
#ifdef HAVE_NS
#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);
/* 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);
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
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)
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
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
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)
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. */
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
#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;
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 */
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)
{
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;
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
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. */
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;
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;
}
/* 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;
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) \
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;
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 \
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;
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
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)
#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
#ifdef HAVE_GIF
+/* Giflib before 5.0 didn't define these macros. */
+#ifndef GIFLIB_MAJOR
+#define GIFLIB_MAJOR 4
+#endif
+
#if defined (HAVE_NTGUI)
+
+/* 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
+
/* winuser.h might define DrawText to DrawTextA or DrawTextW.
Undefine before redefining to avoid a preprocessor warning. */
#ifdef DrawText
#endif /* HAVE_NTGUI */
-
#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)
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;
}
#define fn_DGifSlurp DGifSlurp
#define fn_DGifOpen DGifOpen
#define fn_DGifOpenFileName DGifOpenFileName
+#if 5 <= GIFLIB_MAJOR
+# define fn_GifErrorString GifErrorString
+#endif
#endif /* WINDOWSNT */
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))
{
}
/* 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
{
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. */
{
while (subimg_height <= row)
{
- lint_assume (pass < 3);
+ assume (pass < 3);
row = interlace_start[++pass];
}
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. */
+ 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 MagickWand *animation_cache = NULL;
-static int animation_index = 0;
+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 || animation_cache == NULL)
- composite_wand = MagickGetImage (super_wand);
+ if (ino == 0 || cache->wand == NULL || cache->index > ino)
+ {
+ composite_wand = MagickGetImage (super_wand);
+ if (cache->wand)
+ DestroyMagickWand (cache->wand);
+ }
else
- composite_wand = animation_cache;
+ composite_wand = cache->wand;
dest_width = MagickGetImageWidth (composite_wand);
dest_height = MagickGetImageHeight (composite_wand);
- for (i = max (1, animation_index + 1); i <= ino; i++)
+ for (i = max (1, cache->index + 1); i <= ino; i++)
{
MagickWand *sub_wand;
PixelIterator *source_iterator, *dest_iterator;
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);
- /* Sanity check. The sub-image should not be bigger than the
- base image. */
- if (source_height + source_top > dest_height)
- {
- DestroyMagickWand (composite_wand);
- DestroyMagickWand (sub_wand);
- animation_cache = NULL;
- image_error ("Inconsinstent animation size", Qnil, Qnil);
- return NULL;
- }
-
+ /* This flag says how to handle transparent pixels. */
dispose = MagickGetImageDispose (sub_wand);
source_iterator = NewPixelIterator (sub_wand);
{
DestroyMagickWand (composite_wand);
DestroyMagickWand (sub_wand);
- animation_cache = NULL;
+ cache->wand = NULL;
image_error ("Imagemagick pixel iterator creation failed",
Qnil, Qnil);
return NULL;
DestroyMagickWand (composite_wand);
DestroyMagickWand (sub_wand);
DestroyPixelIterator (source_iterator);
- animation_cache = NULL;
+ cache->wand = NULL;
image_error ("Imagemagick pixel iterator creation failed",
Qnil, Qnil);
return NULL;
}
- /* The sub-image may not start at origo, so move the destination
+ /* The sub-image may not start at origin, so move the destination
iterator to where the sub-image should start. */
- PixelSetIteratorRow (dest_iterator, source_top);
+ 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
- does in some pictures. */
+ also does in some pictures. */
if (x + source_left > dest_width)
break;
/* Normally we only copy over non-transparent pixels,
PixelSetMagickColor (dest[x + source_left], &pixel);
}
}
- PixelSyncIterator(dest_iterator);
+ PixelSyncIterator (dest_iterator);
}
DestroyPixelIterator (source_iterator);
/* Cache a copy for the next iteration. The current wand will be
destroyed by the caller. */
- animation_cache = CloneMagickWand (composite_wand);
- animation_index = ino;
+ cache->wand = CloneMagickWand (composite_wand);
+ cache->index = ino;
return composite_wand;
}
XImagePtr ximg;
int x, y;
MagickWand *image_wand;
- MagickWand *ping_wand;
PixelIterator *iterator;
PixelWand **pixels, *bg_wand = NULL;
MagickPixelPacket pixel;
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) */
+ image_wand = NewMagickWand ();
if (filename)
- status = MagickPingImage (ping_wand, filename);
+ status = MagickReadImage (image_wand, filename);
else
{
filename_hint = imagemagick_filename_hint (img->spec, hint_buffer);
- MagickSetFilename (ping_wand, filename_hint);
- status = MagickPingImageBlob (ping_wand, contents, size);
+ 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)
- status = MagickReadImage (image_wand, filename);
- else
- {
- MagickSetFilename (image_wand, filename_hint);
- status = MagickReadImageBlob (image_wand, contents, size);
- }
-
- if (status == MagickFalse)
- {
- imagemagick_error (image_wand);
- goto imagemagick_error;
- }
-
/* If we have an animated image, get the new wand based on the
"super-wand". */
if (MagickGetNumberImages (image_wand) > 1)
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 *));
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);
#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 */
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 ();
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
);
#endif