#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);
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) \
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 \
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
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. */
-static MagickWand *animation_cache = NULL;
-static int animation_index = 0;
+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 || 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;
- for (i = max (1, animation_index + 1); i <= ino; i++)
+ 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, dest_width;
+ 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);
{
DestroyMagickWand (composite_wand);
DestroyMagickWand (sub_wand);
+ cache->wand = NULL;
image_error ("Imagemagick pixel iterator creation failed",
Qnil, Qnil);
return NULL;
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++)
{
- if (dispose == BackgroundDispose)
+ /* 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], &pixel);
- }
- else
- {
- /* Copy over non-transparent pixels. */
- if (PixelGetAlpha (source[x]))
- {
- PixelGetMagickColor (source[x], &pixel);
- PixelSetMagickColor (dest[x], &pixel);
- }
+ 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)