along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
-#include <stdio.h>
+#include "sysstdio.h"
#include <unistd.h>
#ifdef HAVE_PNG
#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);
/* Functions to access the contents of a bitmap, given an id. */
int
-x_bitmap_height (FRAME_PTR f, ptrdiff_t id)
+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 (FRAME_PTR f, ptrdiff_t id)
+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)
ptrdiff_t
-x_bitmap_pixmap (FRAME_PTR f, ptrdiff_t id)
+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
#ifdef HAVE_X_WINDOWS
int
-x_bitmap_mask (FRAME_PTR f, ptrdiff_t id)
+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
/* Allocate a new bitmap record. Returns index of new record. */
static ptrdiff_t
-x_allocate_bitmap_record (FRAME_PTR f)
+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)
/* Add one reference to the reference count of the bitmap with id ID. */
void
-x_reference_bitmap (FRAME_PTR f, ptrdiff_t id)
+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 */
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
int xhot, yhot, result;
ptrdiff_t id;
Lisp_Object found;
- int fd;
char *filename;
/* Look for an existing bitmap with the same name. */
}
/* Search bitmap-file-path for the file, if appropriate. */
- fd = openp (Vx_bitmap_file_path, file, Qnil, &found, Qnil);
- if (fd < 0)
+ if (openp (Vx_bitmap_file_path, file, Qnil, &found, make_number (R_OK)) < 0)
return -1;
- emacs_close (fd);
filename = SSDATA (found);
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 */
/* Remove reference to bitmap with id number ID. */
void
-x_destroy_bitmap (FRAME_PTR f, ptrdiff_t id)
+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. */
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;
}
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;
DEFUN ("image-flush", Fimage_flush, Simage_flush,
1, 2, 0,
- doc: /* Fush the image with specification SPEC on frame FRAME.
+ doc: /* Flush the image with specification SPEC on frame FRAME.
This removes the image from the Emacs image cache. If SPEC specifies
an image file, the next redisplay of this image will read from the
current contents of that file.
}
/* 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;
else
{
file_found = ENCODE_FILE (file_found);
- close (fd);
+ if (fd != -2)
+ emacs_close (fd);
}
return file_found;
static unsigned char *
slurp_file (char *file, ptrdiff_t *size)
{
- FILE *fp = fopen (file, "rb");
+ FILE *fp = emacs_fopen (file, "rb");
unsigned char *buf = NULL;
struct stat st;
- if (fp && fstat (fileno (fp), &st) == 0
- && 0 <= st.st_size && st.st_size <= min (PTRDIFF_MAX, SIZE_MAX)
- && (buf = xmalloc (st.st_size),
- fread (buf, 1, st.st_size, fp) == st.st_size))
- {
- *size = st.st_size;
- fclose (fp);
- }
- else
+ if (fp)
{
- if (fp)
- fclose (fp);
- if (buf)
+ ptrdiff_t count = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (fclose_unwind, fp);
+
+ if (fstat (fileno (fp), &st) == 0
+ && 0 <= st.st_size && st.st_size < min (PTRDIFF_MAX, SIZE_MAX))
{
- xfree (buf);
- buf = NULL;
+ /* Report an error if we read past the purported EOF.
+ This can happen if the file grows as we read it. */
+ ptrdiff_t buflen = st.st_size;
+ buf = xmalloc (buflen + 1);
+ if (fread (buf, 1, buflen + 1, fp) == buflen)
+ *size = buflen;
+ else
+ {
+ xfree (buf);
+ buf = NULL;
+ }
}
+
+ unbind_to (count, Qnil);
}
return buf;
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) \
xpm_alloc_color (Display *dpy, Colormap cmap, char *color_name, XColor *color,
void *closure)
{
- return xpm_lookup_color ((struct frame *) closure, color_name, color);
+ return xpm_lookup_color (closure, color_name, color);
}
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;
XColor *colors, *p;
XImagePtr_or_DC ximg;
#ifdef HAVE_NTGUI
- HDC hdc;
HGDIOBJ prev;
#endif /* HAVE_NTGUI */
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
#ifndef HAVE_NTGUI
XImagePtr mask_img;
#else
- HDC frame_dc;
HGDIOBJ prev;
char *mask_img;
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
static void
png_read_from_memory (png_structp png_ptr, png_bytep data, png_size_t length)
{
- struct png_memory_storage *tbr
- = (struct png_memory_storage *) fn_png_get_io_ptr (png_ptr);
+ struct png_memory_storage *tbr = fn_png_get_io_ptr (png_ptr);
if (length > tbr->len - tbr->index)
fn_png_error (png_ptr, "Read error");
static void
png_read_from_file (png_structp png_ptr, png_bytep data, png_size_t length)
{
- FILE *fp = (FILE *) fn_png_get_io_ptr (png_ptr);
+ FILE *fp = fn_png_get_io_ptr (png_ptr);
if (fread (data, 1, length, fp) < length)
fn_png_error (png_ptr, "Read error");
}
/* Open the image file. */
- fp = fopen (SSDATA (file), "rb");
+ fp = emacs_fopen (SSDATA (file), "rb");
if (!fp)
{
image_error ("Cannot open image file `%s'", file, Qnil);
if (fread (sig, 1, sizeof sig, fp) != sizeof sig
|| fn_png_sig_cmp (sig, 0, sizeof sig))
{
- image_error ("Not a PNG file: `%s'", file, Qnil);
fclose (fp);
+ image_error ("Not a PNG file: `%s'", file, Qnil);
return 0;
}
}
/* Read image info. */
if (!NILP (specified_data))
- fn_png_set_read_fn (png_ptr, (void *) &tbr, png_read_from_memory);
+ fn_png_set_read_fn (png_ptr, &tbr, png_read_from_memory);
else
- fn_png_set_read_fn (png_ptr, (void *) fp, png_read_from_file);
+ fn_png_set_read_fn (png_ptr, fp, png_read_from_file);
fn_png_set_sig_bytes (png_ptr, sizeof sig);
fn_png_read_info (png_ptr, info_ptr);
static void
our_memory_skip_input_data (j_decompress_ptr cinfo, long int num_bytes)
{
- struct jpeg_source_mgr *src = (struct jpeg_source_mgr *) cinfo->src;
+ struct jpeg_source_mgr *src = cinfo->src;
if (src)
{
static void
jpeg_memory_src (j_decompress_ptr cinfo, JOCTET *data, ptrdiff_t len)
{
- struct jpeg_source_mgr *src;
+ struct jpeg_source_mgr *src = cinfo->src;
- if (cinfo->src == NULL)
+ if (! src)
{
/* First time for this JPEG object? */
- cinfo->src = (struct jpeg_source_mgr *)
- (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
- sizeof (struct jpeg_source_mgr));
- src = (struct jpeg_source_mgr *) cinfo->src;
+ src = cinfo->mem->alloc_small ((j_common_ptr) cinfo,
+ JPOOL_PERMANENT, sizeof *src);
+ cinfo->src = src;
src->next_input_byte = data;
}
- src = (struct jpeg_source_mgr *) cinfo->src;
src->init_source = our_common_init_source;
src->fill_input_buffer = our_memory_fill_input_buffer;
src->skip_input_data = our_memory_skip_input_data;
static void
jpeg_file_src (j_decompress_ptr cinfo, FILE *fp)
{
- struct jpeg_stdio_mgr *src;
+ struct jpeg_stdio_mgr *src = (struct jpeg_stdio_mgr *) cinfo->src;
- if (cinfo->src != NULL)
- src = (struct jpeg_stdio_mgr *) cinfo->src;
- else
+ if (! src)
{
/* First time for this JPEG object? */
- cinfo->src = (struct jpeg_source_mgr *)
- (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
- sizeof (struct jpeg_stdio_mgr));
- src = (struct jpeg_stdio_mgr *) cinfo->src;
- src->buffer = (JOCTET *)
- (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
- JPEG_STDIO_BUFFER_SIZE);
+ src = cinfo->mem->alloc_small ((j_common_ptr) cinfo,
+ JPOOL_PERMANENT, sizeof *src);
+ cinfo->src = (struct jpeg_source_mgr *) src;
+ src->buffer = cinfo->mem->alloc_small ((j_common_ptr) cinfo,
+ JPOOL_PERMANENT,
+ JPEG_STDIO_BUFFER_SIZE);
}
src->file = fp;
return 0;
}
- fp = fopen (SSDATA (file), "rb");
+ fp = emacs_fopen (SSDATA (file), "rb");
if (fp == NULL)
{
image_error ("Cannot open `%s'", file, Qnil);
#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];
}
delay |= ext->Bytes[1];
}
}
- img->lisp_data = Fcons (Qextension_data,
- Fcons (img->lisp_data, Qnil));
+ img->lisp_data = list2 (Qextension_data, img->lisp_data);
if (delay)
img->lisp_data
= Fcons (Qdelay,
#endif /* HAVE_GIF */
+#ifdef HAVE_IMAGEMAGICK
+
+/***********************************************************************
+ ImageMagick
+***********************************************************************/
+
+/* Scale an image size by returning SIZE / DIVISOR * MULTIPLIER,
+ safely rounded and clipped to int range. */
+
+static int
+scale_image_size (int size, size_t divisor, size_t multiplier)
+{
+ if (divisor != 0)
+ {
+ double s = size;
+ double scaled = s * multiplier / divisor + 0.5;
+ if (scaled < INT_MAX)
+ return scaled;
+ }
+ return INT_MAX;
+}
+
+/* Compute the desired size of an image with native size WIDTH x HEIGHT.
+ Use SPEC to deduce the size. Store the desired size into
+ *D_WIDTH x *D_HEIGHT. Store -1 x -1 if the native size is OK. */
static void
compute_image_size (size_t width, size_t height,
Lisp_Object spec,
unspecified should be calculated from the specified to preserve
aspect ratio. */
value = image_spec_value (spec, QCwidth, NULL);
- desired_width = (INTEGERP (value) ? XFASTINT (value) : -1);
+ desired_width = NATNUMP (value) ? min (XFASTINT (value), INT_MAX) : -1;
value = image_spec_value (spec, QCheight, NULL);
- desired_height = (INTEGERP (value) ? XFASTINT (value) : -1);
+ desired_height = NATNUMP (value) ? min (XFASTINT (value), INT_MAX) : -1;
if (desired_width == -1)
{
value = image_spec_value (spec, QCmax_width, NULL);
- if (INTEGERP (value) &&
- width > XFASTINT (value))
+ if (NATNUMP (value))
{
- /* The image is wider than :max-width. */
- desired_width = XFASTINT (value);
- if (desired_height == -1)
+ int max_width = min (XFASTINT (value), INT_MAX);
+ if (max_width < width)
{
- value = image_spec_value (spec, QCmax_height, NULL);
- if (INTEGERP (value))
+ /* The image is wider than :max-width. */
+ desired_width = max_width;
+ if (desired_height == -1)
{
- /* We have no specified height, but we have a
- :max-height value, so check that we satisfy both
- conditions. */
- desired_height = (double) desired_width / width * height;
- if (desired_height > XFASTINT (value))
+ desired_height = scale_image_size (desired_width,
+ width, height);
+ value = image_spec_value (spec, QCmax_height, NULL);
+ if (NATNUMP (value))
{
- desired_height = XFASTINT (value);
- desired_width = (double) desired_height / height * width;
+ int max_height = min (XFASTINT (value), INT_MAX);
+ if (max_height < desired_height)
+ {
+ desired_height = max_height;
+ desired_width = scale_image_size (desired_height,
+ height, width);
+ }
}
}
- else
- {
- /* We have no specified height and no specified
- max-height, so just compute the height. */
- desired_height = (double) desired_width / width * height;
- }
}
}
}
if (desired_height == -1)
{
value = image_spec_value (spec, QCmax_height, NULL);
- if (INTEGERP (value) &&
- height > XFASTINT (value))
- desired_height = XFASTINT (value);
+ if (NATNUMP (value))
+ {
+ int max_height = min (XFASTINT (value), INT_MAX);
+ if (max_height < height)
+ desired_height = max_height;
+ }
}
if (desired_width != -1 && desired_height == -1)
/* w known, calculate h. */
- desired_height = (double) desired_width / width * height;
+ desired_height = scale_image_size (desired_width, width, height);
if (desired_width == -1 && desired_height != -1)
/* h known, calculate w. */
- desired_width = (double) desired_height / height * width;
+ desired_width = scale_image_size (desired_height, height, width);
*d_width = desired_width;
*d_height = desired_height;
}
-/***********************************************************************
- ImageMagick
-***********************************************************************/
-#if defined (HAVE_IMAGEMAGICK)
-
static Lisp_Object Qimagemagick;
static bool imagemagick_image_p (Lisp_Object);
IMAGEMAGICK_WIDTH,
IMAGEMAGICK_MAX_HEIGHT,
IMAGEMAGICK_MAX_WIDTH,
+ IMAGEMAGICK_FORMAT,
IMAGEMAGICK_ROTATION,
IMAGEMAGICK_CROP,
IMAGEMAGICK_LAST
{":width", IMAGE_INTEGER_VALUE, 0},
{":max-height", IMAGE_INTEGER_VALUE, 0},
{":max-width", IMAGE_INTEGER_VALUE, 0},
+ {":format", IMAGE_SYMBOL_VALUE, 0},
{":rotation", IMAGE_NUMBER_VALUE, 0},
{":crop", IMAGE_DONT_CHECK_VALUE_TYPE, 0}
};
description = (char *) MagickRelinquishMemory (description);
}
+/* Possibly give ImageMagick some extra help to determine the image
+ type by supplying a "dummy" filename based on the Content-Type. */
+
+static char *
+imagemagick_filename_hint (Lisp_Object spec, char hint_buffer[MaxTextExtent])
+{
+ Lisp_Object symbol = intern ("image-format-suffixes");
+ Lisp_Object val = find_symbol_value (symbol);
+ Lisp_Object format;
+
+ if (! CONSP (val))
+ return NULL;
+
+ format = image_spec_value (spec, intern (":format"), NULL);
+ val = Fcar_safe (Fcdr_safe (Fassq (format, val)));
+ if (! STRINGP (val))
+ return NULL;
+
+ /* 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)
+ 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
XImagePtr ximg;
int x, y;
MagickWand *image_wand;
- MagickWand *ping_wand;
PixelIterator *iterator;
PixelWand **pixels, *bg_wand = NULL;
MagickPixelPacket pixel;
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.
Interface :index is same as for GIF. First we "ping" the image to see how
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 ();
- 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
- ? 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. */
init_color_table ();
-#ifdef HAVE_MAGICKEXPORTIMAGEPIXELS
+#if defined (HAVE_MAGICKEXPORTIMAGEPIXELS) && ! defined (HAVE_NS)
if (imagemagick_render_type != 0)
{
/* Magicexportimage is normally faster than pixelpushing. This
/* Copy pixels from the imagemagick image structure to the x image map. */
iterator = NewPixelIterator (image_wand);
- if (iterator == (PixelIterator *) NULL)
+ if (! iterator)
{
#ifdef COLOR_TABLE_SUPPORT
free_color_table ();
for (y = 0; y < image_height; y++)
{
pixels = PixelGetNextIteratorRow (iterator, &width);
- if (pixels == (PixelWand **) NULL)
+ if (! pixels)
break;
for (x = 0; x < (long) width; x++)
{
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
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 ((char *) 0, PATH_BITMAPS);
+ Vx_bitmap_file_path = decode_env_path (0, PATH_BITMAPS);
DEFVAR_LISP ("image-cache-eviction-delay", Vimage_cache_eviction_delay,
doc: /* Maximum time after which images are removed from the cache.