summaryrefslogtreecommitdiff
path: root/src/image.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/image.c')
-rw-r--r--src/image.c1931
1 files changed, 1575 insertions, 356 deletions
diff --git a/src/image.c b/src/image.c
index c2e76d5bfcd..c0a7b85cb3b 100644
--- a/src/image.c
+++ b/src/image.c
@@ -20,6 +20,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <config.h>
#include <fcntl.h>
+#include <math.h>
#include <unistd.h>
/* Include this before including <setjmp.h> to work around bugs with
@@ -30,6 +31,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <setjmp.h>
+#include <math.h>
#include <stdint.h>
#include <c-ctype.h>
#include <flexmember.h>
@@ -78,27 +80,40 @@ typedef struct x_bitmap_record Bitmap_Record;
#endif /* !USE_CAIRO */
#endif /* HAVE_X_WINDOWS */
-#ifdef USE_CAIRO
-#define GET_PIXEL image_pix_context_get_pixel
-#define PUT_PIXEL image_pix_container_put_pixel
-#define NO_PIXMAP 0
-
-#define PIX_MASK_RETAIN 0
-#define PIX_MASK_DRAW 255
-
+#if defined(USE_CAIRO) || defined(HAVE_NS)
#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+#ifndef HAVE_NS
#define ARGB_TO_ULONG(a, r, g, b) (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
+#endif
#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff)
#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff)
#define BLUE_FROM_ULONG(color) ((color) & 0xff)
#define RED16_FROM_ULONG(color) (RED_FROM_ULONG (color) * 0x101)
#define GREEN16_FROM_ULONG(color) (GREEN_FROM_ULONG (color) * 0x101)
#define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG (color) * 0x101)
+#endif
+
+#ifdef USE_CAIRO
+#define GET_PIXEL image_pix_context_get_pixel
+#define PUT_PIXEL image_pix_container_put_pixel
+#define NO_PIXMAP 0
+
+#define PIX_MASK_RETAIN 0
+#define PIX_MASK_DRAW 255
static unsigned long image_alloc_image_color (struct frame *, struct image *,
Lisp_Object, unsigned long);
#endif /* USE_CAIRO */
+#if defined HAVE_PGTK && defined HAVE_IMAGEMAGICK
+/* In pgtk, we don't want to create scaled image. If we create scaled
+ * image on scale=2.0 environment, the created image is half size and
+ * Gdk scales it back, and the result is blurry. To avoid this, we
+ * hold original size image as far as we can, and let Gdk to scale it
+ * when it is shown. */
+# define DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE
+#endif
+
#ifdef HAVE_NTGUI
/* We need (or want) w32.h only when we're _not_ compiling for Cygwin. */
@@ -129,12 +144,37 @@ typedef struct ns_bitmap_record Bitmap_Record;
#endif /* HAVE_NS */
+#ifdef HAVE_PGTK
+typedef struct pgtk_bitmap_record Bitmap_Record;
+#endif /* HAVE_PGTK */
+
#if (defined HAVE_X_WINDOWS \
&& ! (defined HAVE_NTGUI || defined USE_CAIRO || defined HAVE_NS))
/* W32_TODO : Color tables on W32. */
# define COLOR_TABLE_SUPPORT 1
#endif
+#ifdef HAVE_HAIKU
+#include "haiku_support.h"
+typedef struct haiku_bitmap_record Bitmap_Record;
+
+#define GET_PIXEL(ximg, x, y) haiku_get_pixel (ximg, x, y)
+#define PUT_PIXEL haiku_put_pixel
+#define NO_PIXMAP 0
+
+#define PIX_MASK_RETAIN 0
+#define PIX_MASK_DRAW 1
+
+#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color) ((color) & 0xff)
+#define RED16_FROM_ULONG(color) (RED_FROM_ULONG (color) * 0x101)
+#define GREEN16_FROM_ULONG(color) (GREEN_FROM_ULONG (color) * 0x101)
+#define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG (color) * 0x101)
+
+#endif
+
static void image_disable_image (struct frame *, struct image *);
static void image_edge_detection (struct frame *, struct image *, Lisp_Object,
Lisp_Object);
@@ -396,6 +436,34 @@ image_reference_bitmap (struct frame *f, ptrdiff_t id)
++FRAME_DISPLAY_INFO (f)->bitmaps[id - 1].refcount;
}
+#ifdef HAVE_PGTK
+static cairo_pattern_t *
+image_create_pattern_from_pixbuf (struct frame *f, GdkPixbuf * pixbuf)
+{
+ GdkPixbuf *pb = gdk_pixbuf_add_alpha (pixbuf, TRUE, 255, 255, 255);
+ cairo_surface_t *surface =
+ cairo_surface_create_similar_image (cairo_get_target
+ (f->output_data.pgtk->cr_context),
+ CAIRO_FORMAT_A1,
+ gdk_pixbuf_get_width (pb),
+ gdk_pixbuf_get_height (pb));
+
+ cairo_t *cr = cairo_create (surface);
+ gdk_cairo_set_source_pixbuf (cr, pb, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ cairo_pattern_t *pat = cairo_pattern_create_for_surface (surface);
+ cairo_pattern_set_extend (pat, CAIRO_EXTEND_REPEAT);
+
+ cairo_surface_destroy (surface);
+ g_object_unref (pb);
+
+ return pat;
+}
+#endif
+
/* Create a bitmap for frame F from a HEIGHT x WIDTH array of bits at BITS. */
ptrdiff_t
@@ -430,6 +498,72 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
return -1;
#endif
+#ifdef HAVE_PGTK
+ GdkPixbuf *pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+ FALSE,
+ 8,
+ width,
+ height);
+ {
+ char *sp = bits;
+ int mask = 0x01;
+ unsigned char *buf = gdk_pixbuf_get_pixels (pixbuf);
+ int rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ for (int y = 0; y < height; y++)
+ {
+ unsigned char *dp = buf + rowstride * y;
+ for (int x = 0; x < width; x++)
+ {
+ if (*sp & mask)
+ {
+ *dp++ = 0xff;
+ *dp++ = 0xff;
+ *dp++ = 0xff;
+ }
+ else
+ {
+ *dp++ = 0x00;
+ *dp++ = 0x00;
+ *dp++ = 0x00;
+ }
+ if ((mask <<= 1) >= 0x100)
+ {
+ mask = 0x01;
+ sp++;
+ }
+ }
+ if (mask != 0x01)
+ {
+ mask = 0x01;
+ sp++;
+ }
+ }
+ }
+#endif /* HAVE_PGTK */
+
+#ifdef HAVE_HAIKU
+ void *bitmap, *stipple;
+ int bytes_per_line, x, y;
+
+ bitmap = BBitmap_new (width, height, false);
+
+ if (!bitmap)
+ return -1;
+
+ bytes_per_line = (width + 7) / 8;
+ stipple = xmalloc (height * bytes_per_line);
+ memcpy (stipple, bits, height * bytes_per_line);
+
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ PUT_PIXEL (bitmap, x, y, ((bits[8] >> (x % 8)) & 1
+ ? f->foreground_pixel
+ : f->background_pixel));
+ bits += bytes_per_line;
+ }
+#endif
+
id = image_allocate_bitmap_record (f);
#ifdef HAVE_NS
@@ -437,6 +571,23 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
dpyinfo->bitmaps[id - 1].depth = 1;
#endif
+#ifdef HAVE_PGTK
+ dpyinfo->bitmaps[id - 1].img = pixbuf;
+ dpyinfo->bitmaps[id - 1].depth = 1;
+ dpyinfo->bitmaps[id - 1].pattern =
+ image_create_pattern_from_pixbuf (f, pixbuf);
+#endif
+
+#ifdef HAVE_HAIKU
+ dpyinfo->bitmaps[id - 1].img = bitmap;
+ dpyinfo->bitmaps[id - 1].depth = 1;
+ dpyinfo->bitmaps[id - 1].stipple_bits = stipple;
+ dpyinfo->bitmaps[id - 1].stipple_foreground
+ = f->foreground_pixel & 0xffffffff;
+ dpyinfo->bitmaps[id - 1].stipple_background
+ = f->background_pixel & 0xffffffff;
+#endif
+
dpyinfo->bitmaps[id - 1].file = NULL;
dpyinfo->bitmaps[id - 1].height = height;
dpyinfo->bitmaps[id - 1].width = width;
@@ -460,24 +611,55 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
return id;
}
+#if defined HAVE_HAIKU || defined HAVE_NS
+static char *slurp_file (int, ptrdiff_t *);
+static Lisp_Object image_find_image_fd (Lisp_Object, int *);
+static bool xbm_read_bitmap_data (struct frame *, char *, char *,
+ int *, int *, char **, bool);
+#endif
+
/* Create bitmap from file FILE for frame F. */
ptrdiff_t
image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
{
-#ifdef HAVE_NTGUI
+#if defined (HAVE_NTGUI)
return -1; /* W32_TODO : bitmap support */
#else
Display_Info *dpyinfo = FRAME_DISPLAY_INFO (f);
#endif
#ifdef HAVE_NS
- ptrdiff_t id;
- void *bitmap = ns_image_from_file (file);
+ ptrdiff_t id, size;
+ int fd, width, height, rc;
+ char *contents, *data;
+ void *bitmap;
- if (!bitmap)
+ if (!STRINGP (image_find_image_fd (file, &fd)))
+ return -1;
+
+ contents = slurp_file (fd, &size);
+
+ if (!contents)
+ return -1;
+
+ rc = xbm_read_bitmap_data (f, contents, contents + size,
+ &width, &height, &data, 0);
+
+ if (!rc)
+ {
+ xfree (contents);
return -1;
+ }
+
+ bitmap = ns_image_from_XBM (data, width, height, 0, 0);
+ if (!bitmap)
+ {
+ xfree (contents);
+ xfree (data);
+ return -1;
+ }
id = image_allocate_bitmap_record (f);
dpyinfo->bitmaps[id - 1].img = bitmap;
@@ -486,6 +668,32 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object 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);
+
+ xfree (contents);
+ xfree (data);
+ return id;
+#endif
+
+#ifdef HAVE_PGTK
+ GError *err = NULL;
+ ptrdiff_t id;
+ void * bitmap = gdk_pixbuf_new_from_file (SSDATA (file), &err);
+
+ if (!bitmap)
+ {
+ g_error_free (err);
+ return -1;
+ }
+
+ id = image_allocate_bitmap_record (f);
+
+ dpyinfo->bitmaps[id - 1].img = bitmap;
+ dpyinfo->bitmaps[id - 1].refcount = 1;
+ dpyinfo->bitmaps[id - 1].file = xlispstrdup (file);
+ dpyinfo->bitmaps[id - 1].height = gdk_pixbuf_get_width (bitmap);
+ dpyinfo->bitmaps[id - 1].width = gdk_pixbuf_get_height (bitmap);
+ dpyinfo->bitmaps[id - 1].pattern
+ = image_create_pattern_from_pixbuf (f, bitmap);
return id;
#endif
@@ -536,6 +744,89 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
return id;
#endif /* HAVE_X_WINDOWS */
+
+#ifdef HAVE_HAIKU
+ ptrdiff_t id, size;
+ int fd, width, height, rc, bytes_per_line, x, y;
+ char *contents, *data, *tmp;
+ void *bitmap;
+ Lisp_Object found;
+
+ /* Look for an existing bitmap with the same name. */
+ for (id = 0; id < dpyinfo->bitmaps_last; ++id)
+ {
+ if (dpyinfo->bitmaps[id].refcount
+ && dpyinfo->bitmaps[id].file
+ && !strcmp (dpyinfo->bitmaps[id].file, SSDATA (file)))
+ {
+ ++dpyinfo->bitmaps[id].refcount;
+ return id + 1;
+ }
+ }
+
+ /* Search bitmap-file-path for the file, if appropriate. */
+ if (openp (Vx_bitmap_file_path, file, Qnil, &found,
+ make_fixnum (R_OK), false, false)
+ < 0)
+ return -1;
+
+ if (!STRINGP (image_find_image_fd (file, &fd))
+ && !STRINGP (image_find_image_fd (found, &fd)))
+ return -1;
+
+ contents = slurp_file (fd, &size);
+
+ if (!contents)
+ return -1;
+
+ rc = xbm_read_bitmap_data (f, contents, contents + size,
+ &width, &height, &data, 0);
+
+ if (!rc)
+ {
+ xfree (contents);
+ return -1;
+ }
+
+ bitmap = BBitmap_new (width, height, false);
+
+ if (!bitmap)
+ {
+ xfree (contents);
+ xfree (data);
+ return -1;
+ }
+
+ id = image_allocate_bitmap_record (f);
+
+ dpyinfo->bitmaps[id - 1].img = bitmap;
+ dpyinfo->bitmaps[id - 1].depth = 1;
+ dpyinfo->bitmaps[id - 1].file = xlispstrdup (file);
+ dpyinfo->bitmaps[id - 1].height = height;
+ dpyinfo->bitmaps[id - 1].width = width;
+ dpyinfo->bitmaps[id - 1].refcount = 1;
+ dpyinfo->bitmaps[id - 1].stipple_foreground
+ = f->foreground_pixel & 0xffffffff;
+ dpyinfo->bitmaps[id - 1].stipple_background
+ = f->background_pixel & 0xffffffff;
+ dpyinfo->bitmaps[id - 1].stipple_bits = data;
+
+ bytes_per_line = (width + 7) / 8;
+ tmp = data;
+
+ for (y = 0; y < height; y++)
+ {
+ for (x = 0; x < width; x++)
+ PUT_PIXEL (bitmap, x, y, ((tmp[x / 8] >> (x % 8)) & 1
+ ? f->foreground_pixel
+ : f->background_pixel));
+
+ tmp += bytes_per_line;
+ }
+
+ xfree (contents);
+ return id;
+#endif
}
/* Free bitmap B. */
@@ -561,6 +852,18 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record *bm)
ns_release_object (bm->img);
#endif
+#ifdef HAVE_PGTK
+ if (bm->pattern != NULL)
+ cairo_pattern_destroy (bm->pattern);
+#endif
+
+#ifdef HAVE_HAIKU
+ BBitmap_free (bm->img);
+
+ if (bm->stipple_bits)
+ xfree (bm->stipple_bits);
+#endif
+
if (bm->file)
{
xfree (bm->file);
@@ -1016,7 +1319,7 @@ parse_image_spec (Lisp_Object spec, struct image_keyword *keywords,
return false;
maybe_done:
- if (EQ (XCDR (plist), Qnil))
+ if (NILP (XCDR (plist)))
{
/* Check that all mandatory fields are present. */
for (i = 0; i < nkeywords; ++i)
@@ -1321,7 +1624,6 @@ image_ascent (struct image *img, struct face *face, struct glyph_slice *slice)
return ascent;
}
-
/* Image background colors. */
@@ -1345,6 +1647,7 @@ four_corners_best (Emacs_Pix_Context pimg, int *corners,
corner_pixels[3] = GET_PIXEL (pimg, corners[LEFT_CORNER], corners[BOT_CORNER] - 1);
}
else
+
{
/* Get the colors at the corner_pixels of pimg. */
corner_pixels[0] = GET_PIXEL (pimg, 0, 0);
@@ -1631,13 +1934,50 @@ search_image_cache (struct frame *f, Lisp_Object spec, EMACS_UINT hash,
}
+/* Filter out image elements that don't affect display, but will
+ disrupt finding the image in the cache. This should perhaps be
+ user-configurable, but for now it's hard-coded (but new elements
+ can be added at will). */
+static Lisp_Object
+filter_image_spec (Lisp_Object spec)
+{
+ Lisp_Object out = Qnil;
+
+ /* Skip past the `image' element. */
+ if (CONSP (spec))
+ spec = XCDR (spec);
+
+ while (CONSP (spec))
+ {
+ Lisp_Object key = XCAR (spec);
+ spec = XCDR (spec);
+ if (CONSP (spec))
+ {
+ Lisp_Object value = XCAR (spec);
+ spec = XCDR (spec);
+
+ /* Some animation-related data doesn't affect display, but
+ breaks the image cache. Filter those out. */
+ if (!(EQ (key, QCanimate_buffer)
+ || EQ (key, QCanimate_tardiness)
+ || EQ (key, QCanimate_position)
+ || EQ (key, QCanimate_multi_frame_data)))
+ {
+ out = Fcons (value, out);
+ out = Fcons (key, out);
+ }
+ }
+ }
+ return out;
+}
+
/* Search frame F for an image with spec SPEC, and free it. */
static void
uncache_image (struct frame *f, Lisp_Object spec)
{
struct image *img;
- EMACS_UINT hash = sxhash (spec);
+ EMACS_UINT hash = sxhash (filter_image_spec (spec));
/* Because the background colors are based on the current face, we
can have multiple copies of an image with the same spec. We want
@@ -1834,6 +2174,11 @@ image_size_in_bytes (struct image *img)
if (img->mask)
size += w32_image_size (img->mask);
+#elif defined HAVE_HAIKU
+ if (img->pixmap)
+ size += BBitmap_bytes_length (img->pixmap);
+ if (img->mask)
+ size += BBitmap_bytes_length (img->mask);
#endif
return size;
@@ -1964,8 +2309,8 @@ postprocess_image (struct frame *f, struct image *img)
tem = XCDR (conversion);
if (CONSP (tem))
image_edge_detection (f, img,
- Fplist_get (tem, QCmatrix),
- Fplist_get (tem, QCcolor_adjustment));
+ plist_get (tem, QCmatrix),
+ plist_get (tem, QCcolor_adjustment));
}
}
}
@@ -1975,14 +2320,16 @@ postprocess_image (struct frame *f, struct image *img)
safely rounded and clipped to int range. */
static int
-scale_image_size (int size, size_t divisor, size_t multiplier)
+scale_image_size (int size, double divisor, double multiplier)
{
if (divisor != 0)
{
- double s = size;
- double scaled = s * multiplier / divisor + 0.5;
+ double scaled = size * multiplier / divisor;
if (scaled < INT_MAX)
- return scaled;
+ {
+ /* Use ceil, as rounding can discard fractional SVG pixels. */
+ return ceil (scaled);
+ }
}
return INT_MAX;
}
@@ -2003,84 +2350,77 @@ image_get_dimension (struct image *img, Lisp_Object symbol)
if (FIXNATP (value))
return min (XFIXNAT (value), INT_MAX);
if (CONSP (value) && NUMBERP (CAR (value)) && EQ (Qem, CDR (value)))
- return min (img->face_font_size * XFLOATINT (CAR (value)), INT_MAX);
+ return scale_image_size (img->face_font_size, 1, XFLOATINT (CAR (value)));
return -1;
}
/* Compute the desired size of an image with native size WIDTH x HEIGHT.
- Use SPEC to deduce the size. Store the desired size into
+ Use IMG 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,
+compute_image_size (double width, double height,
struct image *img,
int *d_width, int *d_height)
{
- Lisp_Object value;
- int int_value;
- int desired_width = -1, desired_height = -1, max_width = -1, max_height = -1;
double scale = 1;
-
- value = image_spec_value (img->spec, QCscale, NULL);
+ Lisp_Object value = image_spec_value (img->spec, QCscale, NULL);
if (NUMBERP (value))
- scale = XFLOATINT (value);
-
- int_value = image_get_dimension (img, QCmax_width);
- if (int_value >= 0)
- max_width = int_value;
-
- int_value = image_get_dimension (img, QCmax_height);
- if (int_value >= 0)
- max_height = int_value;
+ {
+ double dval = XFLOATINT (value);
+ if (0 <= dval)
+ scale = dval;
+ }
/* If width and/or height is set in the display spec assume we want
to scale to those values. If either h or w is unspecified, the
unspecified should be calculated from the specified to preserve
aspect ratio. */
- int_value = image_get_dimension (img, QCwidth);
- if (int_value >= 0)
+ int desired_width = image_get_dimension (img, QCwidth), max_width;
+ if (desired_width < 0)
+ max_width = image_get_dimension (img, QCmax_width);
+ else
{
- desired_width = int_value * scale;
+ desired_width = scale_image_size (desired_width, 1, scale);
/* :width overrides :max-width. */
max_width = -1;
}
- int_value = image_get_dimension (img, QCheight);
- if (int_value >= 0)
+ int desired_height = image_get_dimension (img, QCheight), max_height;
+ if (desired_height < 0)
+ max_height = image_get_dimension (img, QCmax_height);
+ else
{
- desired_height = int_value * scale;
+ desired_height = scale_image_size (desired_height, 1, scale);
/* :height overrides :max-height. */
max_height = -1;
}
/* If we have both width/height set explicitly, we skip past all the
aspect ratio-preserving computations below. */
- if (desired_width != -1 && desired_height != -1)
+ if (0 <= desired_width && 0 <= desired_height)
goto out;
- width = width * scale;
- height = height * scale;
-
- if (desired_width != -1)
+ if (0 <= desired_width)
/* Width known, calculate height. */
desired_height = scale_image_size (desired_width, width, height);
- else if (desired_height != -1)
+ else if (0 <= desired_height)
/* Height known, calculate width. */
desired_width = scale_image_size (desired_height, height, width);
else
{
- desired_width = width;
- desired_height = height;
+ desired_width = scale_image_size (width, 1, scale);
+ desired_height = scale_image_size (height, 1, scale);
}
- if (max_width != -1 && desired_width > max_width)
+ if (0 <= max_width && max_width < desired_width)
{
/* The image is wider than :max-width. */
desired_width = max_width;
desired_height = scale_image_size (desired_width, width, height);
}
- if (max_height != -1 && desired_height > max_height)
+ if (0 <= max_height && max_height < desired_height)
{
/* The image is higher than :max-height. */
desired_height = max_height;
@@ -2163,10 +2503,11 @@ compute_image_size (size_t width, size_t height,
finally move the origin back to the top left of the image, which
may now be a different corner.
- Note that different GUI backends (X, Cairo, w32, NS) want the
- transform matrix defined as transform from the original image to
- the transformed image, while others want the matrix to describe the
- transform of the space, which boils down to inverting the matrix.
+ Note that different GUI backends (X, Cairo, w32, NS, Haiku) want
+ the transform matrix defined as transform from the original image
+ to the transformed image, while others want the matrix to describe
+ the transform of the space, which boils down to inverting the
+ matrix.
It's possible to pre-calculate the matrix multiplications and just
generate one transform matrix that will do everything we need in a
@@ -2211,7 +2552,24 @@ compute_image_rotation (struct image *img, double *rotation)
static void
image_set_transform (struct frame *f, struct image *img)
{
-# ifdef HAVE_IMAGEMAGICK
+ bool flip;
+
+#if defined HAVE_HAIKU
+ matrix3x3 identity = {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 },
+ };
+
+ img->original_width = img->width;
+ img->original_height = img->height;
+ img->use_bilinear_filtering = false;
+
+ memcpy (&img->transform, identity, sizeof identity);
+#endif
+
+# if (defined HAVE_IMAGEMAGICK \
+ && !defined DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE)
/* ImageMagick images already have the correct transform. */
if (EQ (image_spec_value (img->spec, QCtype, NULL), Qimagemagick))
return;
@@ -2244,7 +2602,10 @@ image_set_transform (struct frame *f, struct image *img)
double rotation = 0.0;
compute_image_rotation (img, &rotation);
-# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS
+ /* Determine flipping. */
+ flip = !NILP (image_spec_value (img->spec, QCflip, NULL));
+
+# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_HAIKU
/* We want scale up operations to use a nearest neighbor filter to
show real pixels instead of munging them, but scale down
operations to use a blended filter, to avoid aliasing and the like.
@@ -2258,6 +2619,10 @@ image_set_transform (struct frame *f, struct image *img)
smoothing = !NILP (s);
# endif
+#ifdef HAVE_HAIKU
+ img->use_bilinear_filtering = smoothing;
+#endif
+
/* Perform scale transformation. */
matrix3x3 matrix
@@ -2267,7 +2632,7 @@ image_set_transform (struct frame *f, struct image *img)
: img->width / (double) width),
[1][1] = (!IEEE_FLOATING_POINT && height == 0 ? DBL_MAX
: img->height / (double) height),
-# elif defined HAVE_NTGUI || defined HAVE_NS
+# elif defined HAVE_NTGUI || defined HAVE_NS || defined HAVE_HAIKU
[0][0] = (!IEEE_FLOATING_POINT && img->width == 0 ? DBL_MAX
: width / (double) img->width),
[1][1] = (!IEEE_FLOATING_POINT && img->height == 0 ? DBL_MAX
@@ -2282,26 +2647,65 @@ image_set_transform (struct frame *f, struct image *img)
/* Perform rotation transformation. */
int rotate_flag = -1;
- if (rotation == 0)
+
+ /* Haiku needs this, since the transformation is done on the basis
+ of the view, and not the image. */
+#ifdef HAVE_HAIKU
+ int extra_tx, extra_ty;
+
+ extra_tx = 0;
+ extra_ty = 0;
+#endif
+
+ if (rotation == 0 && !flip)
rotate_flag = 0;
else
{
# if (defined USE_CAIRO || defined HAVE_XRENDER \
- || defined HAVE_NTGUI || defined HAVE_NS)
+ || defined HAVE_NTGUI || defined HAVE_NS \
+ || defined HAVE_HAIKU)
int cos_r, sin_r;
- if (rotation == 90)
+ if (rotation == 0)
+ {
+ /* FLIP is always true here. As this will rotate by 0
+ degrees, it has no visible effect. Applying only
+ translation matrix to the image would be sufficient for
+ horizontal flipping, but writing special handling for
+ this case would increase code complexity somewhat. */
+ cos_r = 1;
+ sin_r = 0;
+ rotate_flag = 1;
+
+#ifdef HAVE_HAIKU
+ extra_tx = width;
+ extra_ty = 0;
+#endif
+ }
+ else if (rotation == 90)
{
width = img->height;
height = img->width;
cos_r = 0;
sin_r = 1;
rotate_flag = 1;
+
+#ifdef HAVE_HAIKU
+ if (!flip)
+ extra_ty = height;
+ extra_tx = 0;
+#endif
}
else if (rotation == 180)
{
cos_r = -1;
sin_r = 0;
rotate_flag = 1;
+
+#ifdef HAVE_HAIKU
+ if (!flip)
+ extra_tx = width;
+ extra_ty = height;
+#endif
}
else if (rotation == 270)
{
@@ -2310,6 +2714,13 @@ image_set_transform (struct frame *f, struct image *img)
cos_r = 0;
sin_r = -1;
rotate_flag = 1;
+
+#ifdef HAVE_HAIKU
+ extra_tx = width;
+
+ if (flip)
+ extra_ty = height;
+#endif
}
if (0 < rotate_flag)
@@ -2330,9 +2741,14 @@ image_set_transform (struct frame *f, struct image *img)
matrix3x3 v;
matrix3x3_mult (rot, u, v);
- /* 3. Translate back. */
+ /* 3. Translate back. Flip horizontally if requested. */
t[2][0] = width * -.5;
t[2][1] = height * -.5;
+ if (flip)
+ {
+ t[0][0] = -t[0][0];
+ t[2][0] = -t[2][0];
+ }
matrix3x3_mult (t, v, matrix);
# else
/* 1. Translate so (0, 0) is in the center of the image. */
@@ -2350,9 +2766,10 @@ image_set_transform (struct frame *f, struct image *img)
matrix3x3 v;
matrix3x3_mult (u, rot, v);
- /* 3. Translate back. */
+ /* 3. Translate back. Flip horizontally if requested. */
t[2][0] = width * .5;
t[2][1] = height * .5;
+ if (flip) t[0][0] = -t[0][0];
matrix3x3_mult (v, t, matrix);
# endif
img->width = width;
@@ -2413,7 +2830,17 @@ image_set_transform (struct frame *f, struct image *img)
img->xform.eM22 = matrix[1][1];
img->xform.eDx = matrix[2][0];
img->xform.eDy = matrix[2][1];
-# endif
+# elif defined HAVE_HAIKU
+ /* Store the transform in the struct image for later. */
+ memcpy (&img->transform, &matrix, sizeof matrix);
+
+ /* Also add the extra translations. */
+ if (rotate_flag)
+ {
+ img->transform[0][2] = extra_tx;
+ img->transform[1][2] = extra_ty;
+ }
+#endif
}
#endif /* HAVE_IMAGEMAGICK || HAVE_NATIVE_TRANSFORMS */
@@ -2435,8 +2862,8 @@ lookup_image (struct frame *f, Lisp_Object spec, int face_id)
face_id = DEFAULT_FACE_ID;
struct face *face = FACE_FROM_ID (f, face_id);
- unsigned long foreground = FACE_COLOR_TO_PIXEL (face->foreground, f);
- unsigned long background = FACE_COLOR_TO_PIXEL (face->background, f);
+ unsigned long foreground = face->foreground;
+ unsigned long background = face->background;
int font_size = face->font->pixel_size;
char *font_family = SSDATA (face->lface[LFACE_FAMILY_INDEX]);
@@ -2446,7 +2873,7 @@ lookup_image (struct frame *f, Lisp_Object spec, int face_id)
eassert (valid_image_p (spec));
/* Look up SPEC in the hash table of the image cache. */
- hash = sxhash (spec);
+ hash = sxhash (filter_image_spec (spec));
img = search_image_cache (f, spec, hash, foreground, background,
font_size, font_family, false);
if (img && img->load_failed_p)
@@ -2585,6 +3012,92 @@ cache_image (struct frame *f, struct image *img)
}
+#if defined (HAVE_WEBP) || defined (HAVE_GIF)
+
+/* To speed animations up, we keep a cache (based on EQ-ness of the
+ image spec/object) where we put the animator iterator. */
+
+struct anim_cache
+{
+ Lisp_Object spec;
+ /* For webp, this will be an iterator, and for libgif, a gif handle. */
+ void *handle;
+ /* If we need to maintain temporary data of some sort. */
+ void *temp;
+ /* A function to call to free the handle. */
+ void (*destructor) (void *);
+ int index, width, height, frames;
+ struct timespec update_time;
+ struct anim_cache *next;
+};
+
+static struct anim_cache *anim_cache = NULL;
+
+static struct anim_cache *
+anim_create_cache (Lisp_Object spec)
+{
+ struct anim_cache *cache = xmalloc (sizeof (struct anim_cache));
+ cache->handle = NULL;
+ cache->temp = NULL;
+
+ cache->index = -1;
+ cache->next = NULL;
+ cache->spec = spec;
+ return cache;
+}
+
+/* Discard cached images that haven't been used for a minute. */
+static void
+anim_prune_animation_cache (void)
+{
+ struct anim_cache **pcache = &anim_cache;
+ struct timespec old = timespec_sub (current_timespec (),
+ make_timespec (60, 0));
+
+ while (*pcache)
+ {
+ struct anim_cache *cache = *pcache;
+ if (timespec_cmp (old, cache->update_time) <= 0)
+ pcache = &cache->next;
+ else
+ {
+ if (cache->handle)
+ cache->destructor (cache);
+ if (cache->temp)
+ xfree (cache->temp);
+ *pcache = cache->next;
+ xfree (cache);
+ }
+ }
+}
+
+static struct anim_cache *
+anim_get_animation_cache (Lisp_Object spec)
+{
+ struct anim_cache *cache;
+ struct anim_cache **pcache = &anim_cache;
+
+ anim_prune_animation_cache ();
+
+ while (1)
+ {
+ cache = *pcache;
+ if (! cache)
+ {
+ *pcache = cache = anim_create_cache (spec);
+ break;
+ }
+ if (EQ (spec, cache->spec))
+ break;
+ pcache = &cache->next;
+ }
+
+ cache->update_time = current_timespec ();
+ return cache;
+}
+
+#endif /* HAVE_WEBP || HAVE_GIF */
+
/* Call FN on every image in the image cache of frame F. Used to mark
Lisp Objects in the image cache. */
@@ -2611,6 +3124,11 @@ mark_image_cache (struct image_cache *c)
if (c->images[i])
mark_image (c->images[i]);
}
+
+#if defined HAVE_WEBP || defined HAVE_GIF
+ for (struct anim_cache *cache = anim_cache; cache; cache = cache->next)
+ mark_object (cache->spec);
+#endif
}
@@ -2655,13 +3173,12 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
{
Display *display = FRAME_X_DISPLAY (f);
Drawable drawable = FRAME_X_DRAWABLE (f);
- Screen *screen = FRAME_X_SCREEN (f);
eassert (input_blocked_p ());
if (depth <= 0)
- depth = DefaultDepthOfScreen (screen);
- *ximg = XCreateImage (display, DefaultVisualOfScreen (screen),
+ depth = FRAME_DISPLAY_INFO (f)->n_planes;
+ *ximg = XCreateImage (display, FRAME_X_VISUAL (f),
depth, ZPixmap, 0, NULL, width, height,
depth > 16 ? 32 : depth > 8 ? 16 : 8, 0);
if (*ximg == NULL)
@@ -2713,12 +3230,11 @@ x_create_xrender_picture (struct frame *f, Emacs_Pixmap pixmap, int depth)
{
Picture p;
Display *display = FRAME_X_DISPLAY (f);
- int event_basep, error_basep;
- if (XRenderQueryExtension (display, &event_basep, &error_basep))
+ if (FRAME_DISPLAY_INFO (f)->xrender_supported_p)
{
if (depth <= 0)
- depth = DefaultDepthOfScreen (FRAME_X_SCREEN (f));
+ depth = FRAME_DISPLAY_INFO (f)->n_planes;
if (depth == 32 || depth == 24 || depth == 8 || depth == 4 || depth == 1)
{
/* FIXME: Do we need to handle all possible bit depths?
@@ -2820,6 +3336,30 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
return 1;
#endif /* HAVE_X_WINDOWS */
+#ifdef HAVE_HAIKU
+ if (depth == 0)
+ depth = 24;
+
+ if (depth != 24 && depth != 1)
+ {
+ *pimg = NULL;
+ image_error ("Invalid image bit depth specified");
+ return 0;
+ }
+
+ *pixmap = BBitmap_new (width, height, depth == 1);
+
+ if (*pixmap == NO_PIXMAP)
+ {
+ *pimg = NULL;
+ image_error ("Unable to create pixmap", Qnil, Qnil);
+ return 0;
+ }
+
+ *pimg = *pixmap;
+ return 1;
+#endif
+
#ifdef HAVE_NTGUI
BITMAPINFOHEADER *header;
@@ -2960,7 +3500,7 @@ static void
gui_put_x_image (struct frame *f, Emacs_Pix_Container pimg,
Emacs_Pixmap pixmap, int width, int height)
{
-#ifdef USE_CAIRO
+#if defined USE_CAIRO || defined HAVE_HAIKU
eassert (pimg == pixmap);
#elif defined HAVE_X_WINDOWS
GC gc;
@@ -2972,14 +3512,6 @@ gui_put_x_image (struct frame *f, Emacs_Pix_Container pimg,
XFreeGC (FRAME_X_DISPLAY (f), gc);
#endif /* HAVE_X_WINDOWS */
-#ifdef HAVE_NTGUI
-#if 0 /* I don't think this is necessary looking at where it is used. */
- HDC hdc = get_frame_dc (f);
- SetDIBits (hdc, pixmap, 0, height, pimg->data, &(pimg->info), DIB_RGB_COLORS);
- release_frame_dc (f, hdc);
-#endif
-#endif /* HAVE_NTGUI */
-
#ifdef HAVE_NS
eassert (pimg == pixmap);
ns_retain_object (pimg);
@@ -3087,7 +3619,7 @@ image_unget_x_image_or_dc (struct image *img, bool mask_p,
static Emacs_Pix_Container
image_get_x_image (struct frame *f, struct image *img, bool mask_p)
{
-#ifdef USE_CAIRO
+#if defined USE_CAIRO || defined (HAVE_HAIKU)
return !mask_p ? img->pixmap : img->mask;
#elif defined HAVE_X_WINDOWS
XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
@@ -3194,7 +3726,7 @@ slurp_file (int fd, ptrdiff_t *size)
if (fp)
{
- ptrdiff_t count = SPECPDL_INDEX ();
+ specpdl_ref count = SPECPDL_INDEX ();
record_unwind_protect_ptr (fclose_unwind, fp);
if (fstat (fileno (fp), &st) == 0
@@ -3246,6 +3778,8 @@ enum xbm_keyword_index
XBM_ALGORITHM,
XBM_HEURISTIC_MASK,
XBM_MASK,
+ XBM_DATA_WIDTH,
+ XBM_DATA_HEIGHT,
XBM_LAST
};
@@ -3267,7 +3801,9 @@ static const struct image_keyword xbm_format[XBM_LAST] =
{":relief", IMAGE_INTEGER_VALUE, 0},
{":conversion", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
{":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
- {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0}
+ {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
+ {":data-width", IMAGE_POSITIVE_INTEGER_VALUE, 0},
+ {":data-height", IMAGE_POSITIVE_INTEGER_VALUE, 0}
};
/* Tokens returned from xbm_scan. */
@@ -3289,8 +3825,8 @@ enum xbm_token
an entry `:file FILENAME' where FILENAME is a string.
If the specification is for a bitmap loaded from memory it must
- contain `:width WIDTH', `:height HEIGHT', and `:data DATA', where
- WIDTH and HEIGHT are integers > 0. DATA may be:
+ contain `:data-width WIDTH', `:data-height HEIGHT', and `:data DATA',
+ where WIDTH and HEIGHT are integers > 0. DATA may be:
1. a string large enough to hold the bitmap data, i.e. it must
have a size >= (WIDTH + 7) / 8 * HEIGHT
@@ -3300,9 +3836,7 @@ enum xbm_token
3. a vector of strings or bool-vectors, one for each line of the
bitmap.
- 4. a string containing an in-memory XBM file. WIDTH and HEIGHT
- may not be specified in this case because they are defined in the
- XBM file.
+ 4. a string containing an in-memory XBM file.
Both the file and data forms may contain the additional entries
`:background COLOR' and `:foreground COLOR'. If not present,
@@ -3322,13 +3856,13 @@ xbm_image_p (Lisp_Object object)
if (kw[XBM_FILE].count)
{
- if (kw[XBM_WIDTH].count || kw[XBM_HEIGHT].count || kw[XBM_DATA].count)
+ if (kw[XBM_DATA].count)
return 0;
}
else if (kw[XBM_DATA].count && xbm_file_p (kw[XBM_DATA].value))
{
/* In-memory XBM file. */
- if (kw[XBM_WIDTH].count || kw[XBM_HEIGHT].count || kw[XBM_FILE].count)
+ if (kw[XBM_FILE].count)
return 0;
}
else
@@ -3337,14 +3871,14 @@ xbm_image_p (Lisp_Object object)
int width, height, stride;
/* Entries for `:width', `:height' and `:data' must be present. */
- if (!kw[XBM_WIDTH].count
- || !kw[XBM_HEIGHT].count
+ if (!kw[XBM_DATA_WIDTH].count
+ || !kw[XBM_DATA_HEIGHT].count
|| !kw[XBM_DATA].count)
return 0;
data = kw[XBM_DATA].value;
- width = XFIXNAT (kw[XBM_WIDTH].value);
- height = XFIXNAT (kw[XBM_HEIGHT].value);
+ width = XFIXNAT (kw[XBM_DATA_WIDTH].value);
+ height = XFIXNAT (kw[XBM_DATA_HEIGHT].value);
if (!kw[XBM_STRIDE].count)
stride = width;
@@ -3470,6 +4004,48 @@ xbm_scan (char **s, char *end, char *sval, int *ival)
*ival = value;
return overflow ? XBM_TK_OVERFLOW : XBM_TK_NUMBER;
}
+ /* Character literal. XBM images typically contain hex escape
+ sequences and not actual characters, so we only try to handle
+ that here. */
+ else if (c == '\'')
+ {
+ int value = 0, digit;
+ bool overflow = false;
+
+ if (*s == end)
+ return 0;
+
+ c = *(*s)++;
+
+ if (c != '\\' || *s == end)
+ return 0;
+
+ c = *(*s)++;
+
+ if (c == 'x')
+ {
+ while (*s < end)
+ {
+ c = *(*s)++;
+
+ if (c == '\'')
+ {
+ *ival = value;
+ return overflow ? XBM_TK_OVERFLOW : XBM_TK_NUMBER;
+ }
+
+ digit = char_hexdigit (c);
+
+ if (digit < 0)
+ return 0;
+
+ overflow |= INT_MULTIPLY_WRAPV (value, 16, &value);
+ value += digit;
+ }
+ }
+
+ return 0;
+ }
else if (c_isalpha (c) || c == '_')
{
*sval++ = c;
@@ -3547,10 +4123,8 @@ convert_mono_to_color_image (struct frame *f, struct image *img,
release_frame_dc (f, hdc);
old_prev = SelectObject (old_img_dc, img->pixmap);
new_prev = SelectObject (new_img_dc, new_pixmap);
- /* Windows convention for mono bitmaps is black = background,
- white = foreground. */
- SetTextColor (new_img_dc, background);
- SetBkColor (new_img_dc, foreground);
+ SetTextColor (new_img_dc, foreground);
+ SetBkColor (new_img_dc, background);
BitBlt (new_img_dc, 0, 0, img->width, img->height, old_img_dc,
0, 0, SRCCOPY);
@@ -3595,7 +4169,7 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data,
data,
img->width, img->height,
fg, bg,
- DefaultDepthOfScreen (FRAME_X_SCREEN (f)));
+ FRAME_DISPLAY_INFO (f)->n_planes);
# if !defined USE_CAIRO && defined HAVE_XRENDER
if (img->pixmap)
img->picture = x_create_xrender_picture (f, img->pixmap, 0);
@@ -3610,6 +4184,21 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data,
convert_mono_to_color_image (f, img, fg, bg);
#elif defined HAVE_NS
img->pixmap = ns_image_from_XBM (data, img->width, img->height, fg, bg);
+#elif defined HAVE_HAIKU
+ img->pixmap = BBitmap_new (img->width, img->height, 0);
+
+ if (img->pixmap)
+ {
+ int bytes_per_line = (img->width + 7) / 8;
+
+ for (int y = 0; y < img->height; y++)
+ {
+ for (int x = 0; x < img->width; x++)
+ PUT_PIXEL (img->pixmap, x, y,
+ (data[x / 8] >> (x % 8)) & 1 ? fg : bg);
+ data += bytes_per_line;
+ }
+ }
#endif
}
@@ -3794,6 +4383,7 @@ xbm_load_image (struct frame *f, struct image *img, char *contents, char *end)
rc = xbm_read_bitmap_data (f, contents, end, &img->width, &img->height,
&data, 0);
+
if (rc)
{
unsigned long foreground = img->face_foreground;
@@ -3912,8 +4502,8 @@ xbm_load (struct frame *f, struct image *img)
/* Get specified width, and height. */
if (!in_memory_file_p)
{
- img->width = XFIXNAT (fmt[XBM_WIDTH].value);
- img->height = XFIXNAT (fmt[XBM_HEIGHT].value);
+ img->width = XFIXNAT (fmt[XBM_DATA_WIDTH].value);
+ img->height = XFIXNAT (fmt[XBM_DATA_HEIGHT].value);
eassert (img->width > 0 && img->height > 0);
if (!check_image_size (f, img->width, img->height))
{
@@ -4015,6 +4605,13 @@ xbm_load (struct frame *f, struct image *img)
XPM images
***********************************************************************/
+#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK)
+
+static bool xpm_image_p (Lisp_Object object);
+static bool xpm_load (struct frame *f, struct image *img);
+
+#endif /* HAVE_XPM || HAVE_NS */
+
#ifdef HAVE_XPM
#ifdef HAVE_NTGUI
/* Indicate to xpm.h that we don't have Xlib. */
@@ -4038,7 +4635,7 @@ xbm_load (struct frame *f, struct image *img)
#endif /* not HAVE_NTGUI */
#endif /* HAVE_XPM */
-#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS
+#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
/* Indices of image specification fields in xpm_format, below. */
@@ -4058,7 +4655,7 @@ enum xpm_keyword_index
XPM_LAST
};
-#if defined HAVE_XPM || defined HAVE_NS
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
/* Vector of image_keyword structures describing the format
of valid XPM image specifications. */
@@ -4076,7 +4673,7 @@ static const struct image_keyword xpm_format[XPM_LAST] =
{":color-symbols", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
{":background", IMAGE_STRING_OR_NIL_VALUE, 0}
};
-#endif /* HAVE_XPM || HAVE_NS */
+#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK */
#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
@@ -4116,9 +4713,9 @@ struct xpm_cached_color
};
/* The hash table used for the color cache, and its bucket vector
- size. */
+ size (which should be prime). */
-#define XPM_COLOR_CACHE_BUCKETS 1001
+#define XPM_COLOR_CACHE_BUCKETS 1009
static struct xpm_cached_color **xpm_color_cache;
/* Initialize the color cache. */
@@ -4300,7 +4897,7 @@ init_xpm_functions (void)
#endif /* WINDOWSNT */
-#if defined HAVE_XPM || defined HAVE_NS
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
/* Value is true if COLOR_SYMBOLS is a valid color symbols list
for XPM images. Such a list must consist of conses whose car and
cdr are strings. */
@@ -4336,9 +4933,9 @@ xpm_image_p (Lisp_Object object)
&& (! fmt[XPM_COLOR_SYMBOLS].count
|| xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value)));
}
-#endif /* HAVE_XPM || HAVE_NS */
+#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK */
-#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS */
+#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU */
#if defined HAVE_XPM && defined HAVE_X_WINDOWS && !defined USE_GTK
ptrdiff_t
@@ -4419,8 +5016,10 @@ xpm_load (struct frame *f, struct image *img)
#ifndef HAVE_NTGUI
attrs.visual = FRAME_X_VISUAL (f);
attrs.colormap = FRAME_X_COLORMAP (f);
+ attrs.depth = FRAME_DISPLAY_INFO (f)->n_planes;
attrs.valuemask |= XpmVisual;
attrs.valuemask |= XpmColormap;
+ attrs.valuemask |= XpmDepth;
#endif /* HAVE_NTGUI */
#ifdef ALLOC_XPM_COLORS
@@ -4707,9 +5306,11 @@ xpm_load (struct frame *f, struct image *img)
#endif /* HAVE_XPM && !USE_CAIRO */
#if (defined USE_CAIRO && defined HAVE_XPM) \
- || (defined HAVE_NS && !defined HAVE_XPM)
+ || (defined HAVE_NS && !defined HAVE_XPM) \
+ || (defined HAVE_HAIKU && !defined HAVE_XPM) \
+ || (defined HAVE_PGTK && !defined HAVE_XPM)
-/* XPM support functions for NS where libxpm is not available, and for
+/* XPM support functions for NS and Haiku where libxpm is not available, and for
Cairo. Only XPM version 3 (without any extensions) is supported. */
static void xpm_put_color_table_v (Lisp_Object, const char *,
@@ -4906,7 +5507,7 @@ xpm_load_image (struct frame *f,
Lisp_Object (*get_color_table) (Lisp_Object, const char *, int);
Lisp_Object frame, color_symbols, color_table;
int best_key;
-#ifndef HAVE_NS
+#if !defined (HAVE_NS)
bool have_mask = false;
#endif
Emacs_Pix_Container ximg = NULL, mask_img = NULL;
@@ -5446,7 +6047,7 @@ lookup_rgb_color (struct frame *f, int r, int g, int b)
{
#ifdef HAVE_NTGUI
return PALETTERGB (r >> 8, g >> 8, b >> 8);
-#elif defined USE_CAIRO || defined HAVE_NS
+#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
return RGB_TO_ULONG (r >> 8, g >> 8, b >> 8);
#else
xsignal1 (Qfile_error,
@@ -5519,7 +6120,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
p = colors;
for (y = 0; y < img->height; ++y)
{
-#if !defined USE_CAIRO && !defined HAVE_NS
+#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU
Emacs_Color *row = p;
for (x = 0; x < img->width; ++x, ++p)
p->pixel = GET_PIXEL (ximg, x, y);
@@ -5527,7 +6128,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
{
FRAME_TERMINAL (f)->query_colors (f, row, img->width);
}
-#else /* USE_CAIRO || HAVE_NS */
+#else /* USE_CAIRO || HAVE_NS || HAVE_HAIKU */
for (x = 0; x < img->width; ++x, ++p)
{
p->pixel = GET_PIXEL (ximg, x, y);
@@ -5763,7 +6364,7 @@ image_edge_detection (struct frame *f, struct image *img,
}
-#if defined HAVE_X_WINDOWS || defined USE_CAIRO
+#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_HAIKU
static void
image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap pixmap,
int x, int y, unsigned int width, unsigned int height,
@@ -5797,9 +6398,11 @@ image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap pixmap,
XDrawLine (dpy, pixmap, gc, x, y, x + width - 1, y + height - 1);
XDrawLine (dpy, pixmap, gc, x, y + height - 1, x + width - 1, y);
XFreeGC (dpy, gc);
-#endif /* HAVE_X_WINDOWS */
+#elif HAVE_HAIKU
+ be_draw_cross_on_pixmap (pixmap, x, y, width, height, color);
+#endif
}
-#endif /* HAVE_X_WINDOWS || USE_CAIRO */
+#endif /* HAVE_X_WINDOWS || USE_CAIRO || HAVE_HAIKU */
/* Transform image IMG on frame F so that it looks disabled. */
@@ -5842,17 +6445,17 @@ image_disable_image (struct frame *f, struct image *img)
#ifndef HAVE_NTGUI
#ifndef HAVE_NS /* TODO: NS support, however this not needed for toolbars */
-#ifndef USE_CAIRO
+#if !defined USE_CAIRO && !defined HAVE_HAIKU
#define CrossForeground(f) BLACK_PIX_DEFAULT (f)
#define MaskForeground(f) WHITE_PIX_DEFAULT (f)
-#else /* USE_CAIRO */
+#else /* USE_CAIRO || HAVE_HAIKU */
#define CrossForeground(f) 0
#define MaskForeground(f) PIX_MASK_DRAW
-#endif /* USE_CAIRO */
+#endif /* USE_CAIRO || HAVE_HAIKU */
-#ifndef USE_CAIRO
+#if !defined USE_CAIRO && !defined HAVE_HAIKU
image_sync_to_pixmaps (f, img);
-#endif /* !USE_CAIRO */
+#endif /* !USE_CAIRO && !HAVE_HAIKU */
image_pixmap_draw_cross (f, img->pixmap, 0, 0, img->width, img->height,
CrossForeground (f));
if (img->mask)
@@ -6415,15 +7018,16 @@ image_can_use_native_api (Lisp_Object type)
return w32_can_use_native_image_api (type);
# elif defined HAVE_NS
return ns_can_use_native_image_api (type);
+# elif defined HAVE_HAIKU
+ return haiku_can_use_native_image_api (type);
# else
return false;
# endif
}
/*
- * These functions are actually defined in the OS-native implementation
- * file. Currently, for Windows GDI+ interface, w32image.c, but other
- * operating systems can follow suit.
+ * These functions are actually defined in the OS-native implementation file.
+ * Currently, for Windows GDI+ interface, w32image.c, and nsimage.m for macOS.
*/
/* Indices of image specification fields in native format, below. */
@@ -6489,6 +7093,9 @@ native_image_load (struct frame *f, struct image *img)
# elif defined HAVE_NS
return ns_load_image (f, img, image_file,
image_spec_value (img->spec, QCdata, NULL));
+# elif defined HAVE_HAIKU
+ return haiku_load_image (f, img, image_file,
+ image_spec_value (img->spec, QCdata, NULL));
# else
return 0;
# endif
@@ -8233,24 +8840,30 @@ gif_image_p (Lisp_Object object)
# undef DrawText
# endif
-/* 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 <gif_lib.h>
# endif /* HAVE_NTGUI */
-/* Giflib before 5.0 didn't define these macros. */
+/* Giflib before 4.1.6 didn't define these macros. */
# ifndef GIFLIB_MAJOR
# define GIFLIB_MAJOR 4
# endif
+# ifndef GIFLIB_MINOR
+# define GIFLIB_MINOR 0
+# endif
+# ifndef GIFLIB_RELEASE
+# define GIFLIB_RELEASE 0
+# endif
+/* Giflib before 5.0 didn't define these macros. */
+# if GIFLIB_MAJOR < 5
+# define DISPOSAL_UNSPECIFIED 0 /* No disposal specified. */
+# define DISPOSE_DO_NOT 1 /* Leave image in place. */
+# define DISPOSE_BACKGROUND 2 /* Set area too background color. */
+# define DISPOSE_PREVIOUS 3 /* Restore to previous content. */
+# define NO_TRANSPARENT_COLOR -1
+# endif
/* GifErrorString is declared to return char const * when GIFLIB_MAJOR
and GIFLIB_MINOR indicate 5.1 or later. Do not bother using it in
@@ -8273,6 +8886,8 @@ DEF_DLL_FN (GifFileType *, DGifOpenFileName, (const char *));
# else
DEF_DLL_FN (GifFileType *, DGifOpen, (void *, InputFunc, int *));
DEF_DLL_FN (GifFileType *, DGifOpenFileName, (const char *, int *));
+DEF_DLL_FN (int, DGifSavedExtensionToGCB,
+ (GifFileType *, int, GraphicsControlBlock *));
# endif
# if HAVE_GIFERRORSTRING
DEF_DLL_FN (char const *, GifErrorString, (int));
@@ -8290,6 +8905,9 @@ init_gif_functions (void)
LOAD_DLL_FN (library, DGifSlurp);
LOAD_DLL_FN (library, DGifOpen);
LOAD_DLL_FN (library, DGifOpenFileName);
+# if GIFLIB_MAJOR >= 5
+ LOAD_DLL_FN (library, DGifSavedExtensionToGCB);
+# endif
# if HAVE_GIFERRORSTRING
LOAD_DLL_FN (library, GifErrorString);
# endif
@@ -8300,12 +8918,18 @@ init_gif_functions (void)
# undef DGifOpen
# undef DGifOpenFileName
# undef DGifSlurp
+# if GIFLIB_MAJOR >= 5
+# undef DGifSavedExtensionToGCB
+# endif
# undef GifErrorString
# define DGifCloseFile fn_DGifCloseFile
# define DGifOpen fn_DGifOpen
# define DGifOpenFileName fn_DGifOpenFileName
# define DGifSlurp fn_DGifSlurp
+# if GIFLIB_MAJOR >= 5
+# define DGifSavedExtensionToGCB fn_DGifSavedExtensionToGCB
+# endif
# define GifErrorString fn_GifErrorString
# endif /* WINDOWSNT */
@@ -8364,120 +8988,191 @@ static const int interlace_increment[] = {8, 8, 4, 2};
#define GIF_LOCAL_DESCRIPTOR_EXTENSION 249
+static void
+gif_destroy (struct anim_cache* cache)
+{
+ int gif_err;
+ gif_close (cache->handle, &gif_err);
+}
+
static bool
gif_load (struct frame *f, struct image *img)
{
int rc, width, height, x, y, i, j;
ColorMapObject *gif_color_map;
- GifFileType *gif;
+ GifFileType *gif = NULL;
gif_memory_source memsrc;
Lisp_Object specified_bg = image_spec_value (img->spec, QCbackground, NULL);
Lisp_Object specified_file = image_spec_value (img->spec, QCfile, NULL);
Lisp_Object specified_data = image_spec_value (img->spec, QCdata, NULL);
- EMACS_INT idx;
+ unsigned long *pixmap = NULL;
+ EMACS_INT idx = -1;
int gif_err;
+ struct anim_cache* cache = NULL;
+ /* Which sub-image are we to display? */
+ Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL);
- if (NILP (specified_data))
+ idx = FIXNUMP (image_number) ? XFIXNAT (image_number) : 0;
+
+ if (!NILP (image_number))
{
- Lisp_Object file = image_find_image_file (specified_file);
- if (!STRINGP (file))
+ /* If this is an animated image, create a cache for it. */
+ cache = anim_get_animation_cache (img->spec);
+ /* We have an old cache entry, so use it. */
+ if (cache->handle)
{
- image_error ("Cannot find image file `%s'", specified_file);
- return 0;
+ gif = cache->handle;
+ pixmap = cache->temp;
+ /* We're out of sync, so start from the beginning. */
+ if (cache->index != idx - 1)
+ cache->index = -1;
}
+ }
- Lisp_Object encoded_file = ENCODE_FILE (file);
+ /* If we don't have a cached entry, read the image. */
+ if (! gif)
+ {
+ if (NILP (specified_data))
+ {
+ Lisp_Object file = image_find_image_file (specified_file);
+ if (!STRINGP (file))
+ {
+ image_error ("Cannot find image file `%s'", specified_file);
+ return false;
+ }
+
+ Lisp_Object encoded_file = ENCODE_FILE (file);
#ifdef WINDOWSNT
- encoded_file = ansi_encode_filename (encoded_file);
+ encoded_file = ansi_encode_filename (encoded_file);
#endif
- /* Open the GIF file. */
+ /* Open the GIF file. */
#if GIFLIB_MAJOR < 5
- gif = DGifOpenFileName (SSDATA (encoded_file));
+ gif = DGifOpenFileName (SSDATA (encoded_file));
#else
- gif = DGifOpenFileName (SSDATA (encoded_file), &gif_err);
+ gif = DGifOpenFileName (SSDATA (encoded_file), &gif_err);
#endif
- if (gif == NULL)
- {
+ if (gif == NULL)
+ {
#if HAVE_GIFERRORSTRING
- const char *errstr = GifErrorString (gif_err);
- if (errstr)
- image_error ("Cannot open `%s': %s", file, build_string (errstr));
- else
+ const char *errstr = GifErrorString (gif_err);
+ if (errstr)
+ image_error ("Cannot open `%s': %s", file,
+ build_string (errstr));
+ else
#endif
- image_error ("Cannot open `%s'", file);
-
- return 0;
+ image_error ("Cannot open `%s'", file);
+ return false;
+ }
}
- }
- else
- {
- if (!STRINGP (specified_data))
+ else
{
- image_error ("Invalid image data `%s'", specified_data);
- return 0;
- }
+ if (!STRINGP (specified_data))
+ {
+ image_error ("Invalid image data `%s'", specified_data);
+ return false;
+ }
- /* Read from memory! */
- current_gif_memory_src = &memsrc;
- memsrc.bytes = SDATA (specified_data);
- memsrc.len = SBYTES (specified_data);
- memsrc.index = 0;
+ /* Read from memory! */
+ current_gif_memory_src = &memsrc;
+ memsrc.bytes = SDATA (specified_data);
+ memsrc.len = SBYTES (specified_data);
+ memsrc.index = 0;
#if GIFLIB_MAJOR < 5
- gif = DGifOpen (&memsrc, gif_read_from_memory);
+ gif = DGifOpen (&memsrc, gif_read_from_memory);
#else
- gif = DGifOpen (&memsrc, gif_read_from_memory, &gif_err);
+ gif = DGifOpen (&memsrc, gif_read_from_memory, &gif_err);
+#endif
+ if (!gif)
+ {
+#if HAVE_GIFERRORSTRING
+ const char *errstr = GifErrorString (gif_err);
+ if (errstr)
+ image_error ("Cannot open memory source `%s': %s",
+ img->spec, build_string (errstr));
+ else
#endif
- if (!gif)
+ image_error ("Cannot open memory source `%s'", img->spec);
+ return false;
+ }
+ }
+
+ /* Before reading entire contents, check the declared image size. */
+ if (!check_image_size (f, gif->SWidth, gif->SHeight))
+ {
+ image_size_error ();
+ goto gif_error;
+ }
+
+ /* Read entire contents. */
+ rc = DGifSlurp (gif);
+ if (rc == GIF_ERROR || gif->ImageCount <= 0)
{
#if HAVE_GIFERRORSTRING
- const char *errstr = GifErrorString (gif_err);
+ const char *errstr = GifErrorString (gif->Error);
if (errstr)
- image_error ("Cannot open memory source `%s': %s",
- img->spec, build_string (errstr));
+ if (NILP (specified_data))
+ image_error ("Error reading `%s' (%s)", img->spec,
+ build_string (errstr));
+ else
+ image_error ("Error reading GIF data: %s",
+ build_string (errstr));
else
#endif
- image_error ("Cannot open memory source `%s'", img->spec);
- return 0;
+ if (NILP (specified_data))
+ image_error ("Error reading `%s'", img->spec);
+ else
+ image_error ("Error reading GIF data");
+ goto gif_error;
+ }
+
+ width = img->width = gif->SWidth;
+ height = img->height = gif->SHeight;
+
+ /* Check that the selected subimages fit. It's not clear whether
+ the GIF spec requires this, but Emacs can crash if they don't fit. */
+ for (j = 0; j < gif->ImageCount; ++j)
+ {
+ struct SavedImage *subimage = gif->SavedImages + j;
+ int subimg_width = subimage->ImageDesc.Width;
+ int subimg_height = subimage->ImageDesc.Height;
+ int subimg_top = subimage->ImageDesc.Top;
+ int subimg_left = subimage->ImageDesc.Left;
+ if (subimg_width < 0
+ || subimg_height < 0
+ || subimg_top < 0
+ || subimg_left < 0
+ || subimg_top + subimg_height > height
+ || subimg_left + subimg_width > width)
+ {
+ image_error ("Subimage does not fit in image");
+ goto gif_error;
+ }
}
}
-
- /* Before reading entire contents, check the declared image size. */
- if (!check_image_size (f, gif->SWidth, gif->SHeight))
+ else
{
- image_size_error ();
- gif_close (gif, NULL);
- return 0;
+ /* Cached image; set data. */
+ width = img->width = gif->SWidth;
+ height = img->height = gif->SHeight;
}
- /* Read entire contents. */
- rc = DGifSlurp (gif);
- if (rc == GIF_ERROR || gif->ImageCount <= 0)
+ if (idx < 0 || idx >= gif->ImageCount)
{
- if (NILP (specified_data))
- image_error ("Error reading `%s'", img->spec);
- else
- image_error ("Error reading GIF data");
- gif_close (gif, NULL);
- return 0;
+ image_error ("Invalid image number `%s' in image `%s'",
+ make_fixnum (idx), img->spec);
+ goto gif_error;
}
- /* Which sub-image are we to display? */
- {
- Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL);
- idx = FIXNUMP (image_number) ? XFIXNAT (image_number) : 0;
- if (idx < 0 || idx >= gif->ImageCount)
- {
- image_error ("Invalid image number `%s' in image `%s'",
- image_number, img->spec);
- gif_close (gif, NULL);
- return 0;
- }
- }
-
- width = img->width = gif->SWidth;
- height = img->height = gif->SHeight;
+ /* It's an animated image, so initialize the cache. */
+ if (cache && !cache->handle)
+ {
+ cache->handle = gif;
+ cache->destructor = (void (*)(void *)) &gif_destroy;
+ cache->width = width;
+ cache->height = height;
+ }
img->corners[TOP_CORNER] = gif->SavedImages[0].ImageDesc.Top;
img->corners[LEFT_CORNER] = gif->SavedImages[0].ImageDesc.Left;
@@ -8489,35 +9184,21 @@ gif_load (struct frame *f, struct image *img)
if (!check_image_size (f, width, height))
{
image_size_error ();
- gif_close (gif, NULL);
- return 0;
- }
-
- /* Check that the selected subimages fit. It's not clear whether
- the GIF spec requires this, but Emacs can crash if they don't fit. */
- for (j = 0; j <= idx; ++j)
- {
- struct SavedImage *subimage = gif->SavedImages + j;
- int subimg_width = subimage->ImageDesc.Width;
- int subimg_height = subimage->ImageDesc.Height;
- int subimg_top = subimage->ImageDesc.Top;
- int subimg_left = subimage->ImageDesc.Left;
- if (! (subimg_width >= 0 && subimg_height >= 0
- && 0 <= subimg_top && subimg_top <= height - subimg_height
- && 0 <= subimg_left && subimg_left <= width - subimg_width))
- {
- image_error ("Subimage does not fit in image");
- gif_close (gif, NULL);
- return 0;
- }
+ goto gif_error;
}
/* Create the X image and pixmap. */
Emacs_Pix_Container ximg;
if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, 0))
+ goto gif_error;
+
+ /* We construct the (possibly composited animated) image in this
+ buffer. */
+ if (!pixmap)
{
- gif_close (gif, NULL);
- return 0;
+ pixmap = xmalloc (width * height * sizeof (unsigned long));
+ if (cache)
+ cache->temp = pixmap;
}
/* Clear the part of the screen image not covered by the image.
@@ -8534,29 +9215,25 @@ gif_load (struct frame *f, struct image *img)
frame_bg = lookup_rgb_color (f, color.red, color.green, color.blue);
}
#endif /* USE_CAIRO */
+
for (y = 0; y < img->corners[TOP_CORNER]; ++y)
for (x = 0; x < width; ++x)
- PUT_PIXEL (ximg, x, y, frame_bg);
+ *(pixmap + x + y * width) = frame_bg;
for (y = img->corners[BOT_CORNER]; y < height; ++y)
for (x = 0; x < width; ++x)
- PUT_PIXEL (ximg, x, y, frame_bg);
+ *(pixmap + x + y * width) = frame_bg;
for (y = img->corners[TOP_CORNER]; y < img->corners[BOT_CORNER]; ++y)
{
for (x = 0; x < img->corners[LEFT_CORNER]; ++x)
- PUT_PIXEL (ximg, x, y, frame_bg);
+ *(pixmap + x + y * width) = frame_bg;
for (x = img->corners[RIGHT_CORNER]; x < width; ++x)
- PUT_PIXEL (ximg, x, y, frame_bg);
+ *(pixmap + x + y * width) = frame_bg;
}
/* Read the GIF image into the X image. */
- /* FIXME: With the current implementation, loading an animated gif
- is quadratic in the number of animation frames, since each frame
- is a separate struct image. We must provide a way for a single
- gif_load call to construct and save all animation frames. */
-
init_color_table ();
unsigned long bgcolor UNINIT;
@@ -8571,19 +9248,34 @@ gif_load (struct frame *f, struct image *img)
#endif
}
- for (j = 0; j <= idx; ++j)
+ int start_frame = 0;
+
+ /* We have animation data in the cache. */
+ if (cache && cache->temp)
+ {
+ start_frame = cache->index + 1;
+ if (start_frame > idx)
+ start_frame = 0;
+ cache->index = idx;
+ }
+
+ for (j = start_frame; j <= idx; ++j)
{
/* We use a local variable `raster' here because RasterBits is a
char *, which invites problems with bytes >= 0x80. */
struct SavedImage *subimage = gif->SavedImages + j;
unsigned char *raster = (unsigned char *) subimage->RasterBits;
- int transparency_color_index = -1;
- int disposal = 0;
int subimg_width = subimage->ImageDesc.Width;
int subimg_height = subimage->ImageDesc.Height;
int subimg_top = subimage->ImageDesc.Top;
int subimg_left = subimage->ImageDesc.Left;
+ /* From gif89a spec: 1 = "keep in place", 2 = "restore
+ to background". Treat any other value like 2. */
+ int disposal = DISPOSAL_UNSPECIFIED;
+ int transparency_color_index = NO_TRANSPARENT_COLOR;
+
+#if GIFLIB_MAJOR < 5
/* Find the Graphic Control Extension block for this sub-image.
Extract the disposal method and transparency color. */
for (i = 0; i < subimage->ExtensionBlockCount; i++)
@@ -8594,24 +9286,37 @@ gif_load (struct frame *f, struct image *img)
&& extblock->ByteCount == 4
&& extblock->Bytes[0] & 1)
{
- /* From gif89a spec: 1 = "keep in place", 2 = "restore
- to background". Treat any other value like 2. */
disposal = (extblock->Bytes[0] >> 2) & 7;
transparency_color_index = (unsigned char) extblock->Bytes[3];
break;
}
}
+#else
+ GraphicsControlBlock gcb;
+ DGifSavedExtensionToGCB (gif, j, &gcb);
+ disposal = gcb.DisposalMode;
+ transparency_color_index = gcb.TransparentColor;
+#endif
/* We can't "keep in place" the first subimage. */
if (j == 0)
- disposal = 2;
+ disposal = DISPOSE_BACKGROUND;
- /* For disposal == 0, the spec says "No disposal specified. The
- decoder is not required to take any action." In practice, it
- seems we need to treat this like "keep in place", see e.g.
+ /* For disposal == 0 (DISPOSAL_UNSPECIFIED), the spec says
+ "No disposal specified. The decoder is not required to take
+ any action." In practice, it seems we need to treat this
+ like "keep in place" (DISPOSE_DO_NOT), see e.g.
https://upload.wikimedia.org/wikipedia/commons/3/37/Clock.gif */
- if (disposal == 0)
- disposal = 1;
+ if (disposal == DISPOSAL_UNSPECIFIED)
+ disposal = DISPOSE_DO_NOT;
+
+ /* This is not quite correct -- the specification is unclear,
+ but I think we're supposed to restore to the frame before the
+ previous frame? And we don't have that data at this point.
+ But DISPOSE_DO_NOT is less wrong than substituting the
+ background, so do that for now. */
+ if (disposal == DISPOSE_PREVIOUS)
+ disposal = DISPOSE_DO_NOT;
gif_color_map = subimage->ImageDesc.ColorMap;
if (!gif_color_map)
@@ -8650,10 +9355,10 @@ gif_load (struct frame *f, struct image *img)
for (x = 0; x < subimg_width; x++)
{
int c = raster[y * subimg_width + x];
- if (transparency_color_index != c || disposal != 1)
+ if (transparency_color_index != c || disposal != DISPOSE_DO_NOT)
{
- PUT_PIXEL (ximg, x + subimg_left, row + subimg_top,
- pixel_colors[c]);
+ *(pixmap + x + subimg_left + (y + subimg_top) * width) =
+ pixel_colors[c];
}
}
}
@@ -8664,15 +9369,21 @@ gif_load (struct frame *f, struct image *img)
for (x = 0; x < subimg_width; ++x)
{
int c = raster[y * subimg_width + x];
- if (transparency_color_index != c || disposal != 1)
+ if (transparency_color_index != c || disposal != DISPOSE_DO_NOT)
{
- PUT_PIXEL (ximg, x + subimg_left, y + subimg_top,
- pixel_colors[c]);
+ *(pixmap + x + subimg_left + (y + subimg_top) * width) =
+ pixel_colors[c];
}
}
}
}
+ /* We now have the complete image (possibly composed from a series
+ of animated frames) in pixmap. Put it into ximg. */
+ for (y = 0; y < height; ++y)
+ for (x = 0; x < width; ++x)
+ PUT_PIXEL (ximg, x, y, *(pixmap + x + y * width));
+
#ifdef COLOR_TABLE_SUPPORT
img->colors = colors_in_color_table (&img->ncolors);
free_color_table ();
@@ -8701,11 +9412,11 @@ gif_load (struct frame *f, struct image *img)
}
}
img->lisp_data = list2 (Qextension_data, img->lisp_data);
- if (delay)
- img->lisp_data
- = Fcons (Qdelay,
- Fcons (make_float (delay / 100.0),
- img->lisp_data));
+ img->lisp_data
+ = Fcons (Qdelay,
+ /* Default GIF delay is 1/15th of a second. */
+ Fcons (make_float (delay? delay / 100.0: 1.0 / 15),
+ img->lisp_data));
}
if (gif->ImageCount > 1)
@@ -8713,17 +9424,22 @@ gif_load (struct frame *f, struct image *img)
Fcons (make_fixnum (gif->ImageCount),
img->lisp_data));
- if (gif_close (gif, &gif_err) == GIF_ERROR)
+ if (!cache)
{
+ if (pixmap)
+ xfree (pixmap);
+ if (gif_close (gif, &gif_err) == GIF_ERROR)
+ {
#if HAVE_GIFERRORSTRING
- char const *error_text = GifErrorString (gif_err);
+ char const *error_text = GifErrorString (gif_err);
- if (error_text)
- image_error ("Error closing `%s': %s",
- img->spec, build_string (error_text));
- else
+ if (error_text)
+ image_error ("Error closing `%s': %s",
+ img->spec, build_string (error_text));
+ else
#endif
- image_error ("Error closing `%s'", img->spec);
+ image_error ("Error closing `%s'", img->spec);
+ }
}
/* Maybe fill in the background field while we have ximg handy. */
@@ -8734,14 +9450,453 @@ gif_load (struct frame *f, struct image *img)
/* Put ximg into the image. */
image_put_x_image (f, img, ximg, 0);
- return 1;
+ return true;
+
+ gif_error:
+ if (pixmap)
+ xfree (pixmap);
+ gif_close (gif, NULL);
+ if (cache)
+ {
+ cache->handle = NULL;
+ cache->temp = NULL;
+ }
+ return false;
}
#endif /* HAVE_GIF */
+#ifdef HAVE_WEBP
+
+
+/***********************************************************************
+ WebP
+ ***********************************************************************/
+
+#include "webp/decode.h"
+#include "webp/demux.h"
+
+/* Indices of image specification fields in webp_format, below. */
+
+enum webp_keyword_index
+{
+ WEBP_TYPE,
+ WEBP_DATA,
+ WEBP_FILE,
+ WEBP_ASCENT,
+ WEBP_MARGIN,
+ WEBP_RELIEF,
+ WEBP_ALGORITHM,
+ WEBP_HEURISTIC_MASK,
+ WEBP_MASK,
+ WEBP_INDEX,
+ WEBP_BACKGROUND,
+ WEBP_LAST
+};
+
+/* Vector of image_keyword structures describing the format
+ of valid user-defined image specifications. */
+
+static const struct image_keyword webp_format[WEBP_LAST] =
+{
+ {":type", IMAGE_SYMBOL_VALUE, 1},
+ {":data", IMAGE_STRING_VALUE, 0},
+ {":file", IMAGE_STRING_VALUE, 0},
+ {":ascent", IMAGE_ASCENT_VALUE, 0},
+ {":margin", IMAGE_NON_NEGATIVE_INTEGER_VALUE_OR_PAIR, 0},
+ {":relief", IMAGE_INTEGER_VALUE, 0},
+ {":conversion", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
+ {":heuristic-mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
+ {":mask", IMAGE_DONT_CHECK_VALUE_TYPE, 0},
+ {":index", IMAGE_NON_NEGATIVE_INTEGER_VALUE, 0},
+ {":background", IMAGE_STRING_OR_NIL_VALUE, 0}
+};
+
+/* Return true if OBJECT is a valid WebP image specification. */
+
+static bool
+webp_image_p (Lisp_Object object)
+{
+ struct image_keyword fmt[WEBP_LAST];
+ memcpy (fmt, webp_format, sizeof fmt);
+
+ if (!parse_image_spec (object, fmt, WEBP_LAST, Qwebp))
+ return false;
+
+ /* Must specify either the :data or :file keyword. */
+ return fmt[WEBP_FILE].count + fmt[WEBP_DATA].count == 1;
+}
+
+#ifdef WINDOWSNT
+
+/* WebP library details. */
+
+DEF_DLL_FN (int, WebPGetInfo, (const uint8_t *, size_t, int *, int *));
+/* WebPGetFeatures is a static inline function defined in WebP's
+ decode.h. Since we cannot use that with dynamically-loaded libwebp
+ DLL, we instead load the internal function it calls and redirect to
+ that through a macro. */
+DEF_DLL_FN (VP8StatusCode, WebPGetFeaturesInternal,
+ (const uint8_t *, size_t, WebPBitstreamFeatures *, int));
+DEF_DLL_FN (uint8_t *, WebPDecodeRGBA, (const uint8_t *, size_t, int *, int *));
+DEF_DLL_FN (uint8_t *, WebPDecodeRGB, (const uint8_t *, size_t, int *, int *));
+DEF_DLL_FN (void, WebPFree, (void *));
+DEF_DLL_FN (uint32_t, WebPDemuxGetI, (const WebPDemuxer *, WebPFormatFeature));
+DEF_DLL_FN (WebPDemuxer *, WebPDemuxInternal,
+ (const WebPData *, int, WebPDemuxState *, int));
+DEF_DLL_FN (void, WebPDemuxDelete, (WebPDemuxer *));
+DEF_DLL_FN (int, WebPAnimDecoderGetNext,
+ (WebPAnimDecoder *, uint8_t **, int *));
+DEF_DLL_FN (WebPAnimDecoder *, WebPAnimDecoderNewInternal,
+ (const WebPData *, const WebPAnimDecoderOptions *, int));
+DEF_DLL_FN (int, WebPAnimDecoderOptionsInitInternal,
+ (WebPAnimDecoderOptions *, int));
+DEF_DLL_FN (int, WebPAnimDecoderHasMoreFrames, (const WebPAnimDecoder *));
+DEF_DLL_FN (void, WebPAnimDecoderDelete, (WebPAnimDecoder *));
+
+static bool
+init_webp_functions (void)
+{
+ HMODULE library1, library2;
+
+ if (!((library1 = w32_delayed_load (Qwebp))
+ && (library2 = w32_delayed_load (Qwebpdemux))))
+ return false;
+
+ LOAD_DLL_FN (library1, WebPGetInfo);
+ LOAD_DLL_FN (library1, WebPGetFeaturesInternal);
+ LOAD_DLL_FN (library1, WebPDecodeRGBA);
+ LOAD_DLL_FN (library1, WebPDecodeRGB);
+ LOAD_DLL_FN (library1, WebPFree);
+ LOAD_DLL_FN (library2, WebPDemuxGetI);
+ LOAD_DLL_FN (library2, WebPDemuxInternal);
+ LOAD_DLL_FN (library2, WebPDemuxDelete);
+ LOAD_DLL_FN (library2, WebPAnimDecoderGetNext);
+ LOAD_DLL_FN (library2, WebPAnimDecoderNewInternal);
+ LOAD_DLL_FN (library2, WebPAnimDecoderOptionsInitInternal);
+ LOAD_DLL_FN (library2, WebPAnimDecoderHasMoreFrames);
+ LOAD_DLL_FN (library2, WebPAnimDecoderDelete);
+ return true;
+}
+
+#undef WebPGetInfo
+#undef WebPGetFeatures
+#undef WebPDecodeRGBA
+#undef WebPDecodeRGB
+#undef WebPFree
+#undef WebPDemuxGetI
+#undef WebPDemux
+#undef WebPDemuxDelete
+#undef WebPAnimDecoderGetNext
+#undef WebPAnimDecoderNew
+#undef WebPAnimDecoderOptionsInit
+#undef WebPAnimDecoderHasMoreFrames
+#undef WebPAnimDecoderDelete
+
+#define WebPGetInfo fn_WebPGetInfo
+#define WebPGetFeatures(d,s,f) \
+ fn_WebPGetFeaturesInternal(d,s,f,WEBP_DECODER_ABI_VERSION)
+#define WebPDecodeRGBA fn_WebPDecodeRGBA
+#define WebPDecodeRGB fn_WebPDecodeRGB
+#define WebPFree fn_WebPFree
+#define WebPDemuxGetI fn_WebPDemuxGetI
+#define WebPDemux(d) \
+ fn_WebPDemuxInternal(d,0,NULL,WEBP_DEMUX_ABI_VERSION)
+#define WebPDemuxDelete fn_WebPDemuxDelete
+#define WebPAnimDecoderGetNext fn_WebPAnimDecoderGetNext
+#define WebPAnimDecoderNew(d,o) \
+ fn_WebPAnimDecoderNewInternal(d,o,WEBP_DEMUX_ABI_VERSION)
+#define WebPAnimDecoderOptionsInit(o) \
+ fn_WebPAnimDecoderOptionsInitInternal(o,WEBP_DEMUX_ABI_VERSION)
+#define WebPAnimDecoderHasMoreFrames fn_WebPAnimDecoderHasMoreFrames
+#define WebPAnimDecoderDelete fn_WebPAnimDecoderDelete
+
+#endif /* WINDOWSNT */
+
+static void
+webp_destroy (struct anim_cache* cache)
+{
+ WebPAnimDecoderDelete (cache->handle);
+}
+
+/* Load WebP image IMG for use on frame F. Value is true if
+ successful. */
+
+static bool
+webp_load (struct frame *f, struct image *img)
+{
+ ptrdiff_t size = 0;
+ uint8_t *contents;
+ Lisp_Object file = Qnil;
+ int frames = 0;
+ double delay = 0;
+ WebPAnimDecoder* anim = NULL;
+
+ /* Open the WebP file. */
+ Lisp_Object specified_file = image_spec_value (img->spec, QCfile, NULL);
+ Lisp_Object specified_data = image_spec_value (img->spec, QCdata, NULL);
+
+ if (NILP (specified_data))
+ {
+ int fd;
+ file = image_find_image_fd (specified_file, &fd);
+ if (!STRINGP (file))
+ {
+ image_error ("Cannot find image file `%s'", specified_file);
+ return false;
+ }
+
+ contents = (uint8_t *) slurp_file (fd, &size);
+ if (contents == NULL)
+ {
+ image_error ("Error loading WebP image `%s'", file);
+ return false;
+ }
+ }
+ else
+ {
+ if (!STRINGP (specified_data))
+ {
+ image_error ("Invalid image data `%s'", specified_data);
+ return false;
+ }
+ contents = SDATA (specified_data);
+ size = SBYTES (specified_data);
+ }
+
+ /* Validate the WebP image header. */
+ if (!WebPGetInfo (contents, size, NULL, NULL))
+ {
+ if (!NILP (file))
+ image_error ("Not a WebP file: `%s'", file);
+ else
+ image_error ("Invalid header in WebP image data");
+ goto webp_error1;
+ }
+
+ Lisp_Object image_number = image_spec_value (img->spec, QCindex, NULL);
+ ptrdiff_t idx = FIXNUMP (image_number) ? XFIXNAT (image_number) : 0;
+
+ /* Get WebP features. */
+ WebPBitstreamFeatures features;
+ VP8StatusCode result = WebPGetFeatures (contents, size, &features);
+ switch (result)
+ {
+ case VP8_STATUS_OK:
+ break;
+ case VP8_STATUS_NOT_ENOUGH_DATA:
+ case VP8_STATUS_OUT_OF_MEMORY:
+ case VP8_STATUS_INVALID_PARAM:
+ case VP8_STATUS_BITSTREAM_ERROR:
+ case VP8_STATUS_UNSUPPORTED_FEATURE:
+ case VP8_STATUS_SUSPENDED:
+ case VP8_STATUS_USER_ABORT:
+ default:
+ /* Error out in all other cases. */
+ if (!NILP (file))
+ image_error ("Error when interpreting WebP image data: `%s'", file);
+ else
+ image_error ("Error when interpreting WebP image data");
+ goto webp_error1;
+ }
+
+ uint8_t *decoded = NULL;
+ int width, height;
+
+ if (features.has_animation)
+ {
+ /* Animated image. */
+ int timestamp;
+
+ struct anim_cache* cache = anim_get_animation_cache (img->spec);
+ /* Get the next frame from the animation cache. */
+ if (cache->handle && cache->index == idx - 1)
+ {
+ WebPAnimDecoderGetNext (cache->handle, &decoded, &timestamp);
+ delay = timestamp;
+ cache->index++;
+ anim = cache->handle;
+ width = cache->width;
+ height = cache->height;
+ frames = cache->frames;
+ }
+ else
+ {
+ /* Start a new cache entry. */
+ if (cache->handle)
+ WebPAnimDecoderDelete (cache->handle);
+
+ WebPData webp_data;
+ if (NILP (specified_data))
+ /* If we got the data from a file, then we don't need to
+ copy the data. */
+ webp_data.bytes = cache->temp = contents;
+ else
+ /* We got the data from a string, so copy it over so that
+ it doesn't get garbage-collected. */
+ {
+ webp_data.bytes = xmalloc (size);
+ memcpy ((void*) webp_data.bytes, contents, size);
+ }
+ /* In any case, we release the allocated memory when we
+ purge the anim cache. */
+ webp_data.size = size;
+
+ /* Get the width/height of the total image. */
+ WebPDemuxer* demux = WebPDemux (&webp_data);
+ cache->width = width = WebPDemuxGetI (demux, WEBP_FF_CANVAS_WIDTH);
+ cache->height = height = WebPDemuxGetI (demux,
+ WEBP_FF_CANVAS_HEIGHT);
+ cache->frames = frames = WebPDemuxGetI (demux, WEBP_FF_FRAME_COUNT);
+ cache->destructor = (void (*)(void *)) webp_destroy;
+ WebPDemuxDelete (demux);
+
+ WebPAnimDecoderOptions dec_options;
+ WebPAnimDecoderOptionsInit (&dec_options);
+ anim = WebPAnimDecoderNew (&webp_data, &dec_options);
+
+ cache->handle = anim;
+ cache->index = idx;
+
+ while (WebPAnimDecoderHasMoreFrames (anim)) {
+ WebPAnimDecoderGetNext (anim, &decoded, &timestamp);
+ /* Each frame has its own delay, but we don't really support
+ that. So just use the delay from the first frame. */
+ if (delay == 0)
+ delay = timestamp;
+ /* Stop when we get to the desired index. */
+ if (idx-- == 0)
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* Non-animated image. */
+ if (features.has_alpha)
+ /* Linear [r0, g0, b0, a0, r1, g1, b1, a1, ...] order. */
+ decoded = WebPDecodeRGBA (contents, size, &width, &height);
+ else
+ /* Linear [r0, g0, b0, r1, g1, b1, ...] order. */
+ decoded = WebPDecodeRGB (contents, size, &width, &height);
+ }
+
+ if (!decoded)
+ {
+ image_error ("Error when decoding WebP image data");
+ goto webp_error1;
+ }
+
+ if (!(width <= INT_MAX && height <= INT_MAX
+ && check_image_size (f, width, height)))
+ {
+ image_size_error ();
+ goto webp_error2;
+ }
+
+ /* Create the x image and pixmap. */
+ Emacs_Pix_Container ximg, mask_img = NULL;
+ if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, false))
+ goto webp_error2;
+
+ /* Create an image and pixmap serving as mask if the WebP image
+ contains an alpha channel. */
+ if (features.has_alpha
+ && !image_create_x_image_and_pixmap (f, img, width, height, 1,
+ &mask_img, true))
+ {
+ image_destroy_x_image (ximg);
+ image_clear_image_1 (f, img, CLEAR_IMAGE_PIXMAP);
+ goto webp_error2;
+ }
+
+ /* Fill the X image and mask from WebP data. */
+ init_color_table ();
+
+ img->corners[TOP_CORNER] = 0;
+ img->corners[LEFT_CORNER] = 0;
+ img->corners[BOT_CORNER]
+ = img->corners[TOP_CORNER] + height;
+ img->corners[RIGHT_CORNER]
+ = img->corners[LEFT_CORNER] + width;
+
+ uint8_t *p = decoded;
+ for (int y = 0; y < height; ++y)
+ {
+ for (int x = 0; x < width; ++x)
+ {
+ int r = *p++ << 8;
+ int g = *p++ << 8;
+ int b = *p++ << 8;
+ PUT_PIXEL (ximg, x, y, lookup_rgb_color (f, r, g, b));
+
+ /* An alpha channel associates variable transparency with an
+ image. WebP allows up to 256 levels of partial transparency.
+ We handle this like with PNG (which see), using the frame's
+ background color to combine the image with. */
+ if (features.has_alpha || anim)
+ {
+ if (mask_img)
+ PUT_PIXEL (mask_img, x, y, *p > 0 ? PIX_MASK_DRAW : PIX_MASK_RETAIN);
+ ++p;
+ }
+ }
+ }
+
+#ifdef COLOR_TABLE_SUPPORT
+ /* Remember colors allocated for this image. */
+ img->colors = colors_in_color_table (&img->ncolors);
+ free_color_table ();
+#endif /* COLOR_TABLE_SUPPORT */
+
+ /* Put ximg into the image. */
+ image_put_x_image (f, img, ximg, 0);
+
+ /* Same for the mask. */
+ if (mask_img)
+ {
+ /* Fill in the background_transparent field while we have the
+ mask handy. Casting avoids a GCC warning. */
+ image_background_transparent (img, f, (Emacs_Pix_Context)mask_img);
+
+ image_put_x_image (f, img, mask_img, 1);
+ }
+
+ img->width = width;
+ img->height = height;
+
+ /* Return animation data. */
+ img->lisp_data = Fcons (Qcount,
+ Fcons (make_fixnum (frames),
+ img->lisp_data));
+ img->lisp_data = Fcons (Qdelay,
+ Fcons (make_float (delay / 1000),
+ img->lisp_data));
+
+ /* Clean up. */
+ if (!anim)
+ WebPFree (decoded);
+ if (NILP (specified_data) && !anim)
+ xfree (contents);
+ return true;
+
+ webp_error2:
+ if (!anim)
+ WebPFree (decoded);
+
+ webp_error1:
+ if (NILP (specified_data))
+ xfree (contents);
+ return false;
+}
+
+#endif /* HAVE_WEBP */
+
+
#ifdef HAVE_IMAGEMAGICK
+
/***********************************************************************
ImageMagick
***********************************************************************/
@@ -8895,7 +10050,7 @@ imagemagick_filename_hint (Lisp_Object spec, char hint_buffer[MaxTextExtent])
(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
+ compute all the preceding images to be able to display a particular
sub-image.
Computing all the preceding images is too slow, so we maintain a
@@ -9117,11 +10272,15 @@ imagemagick_load_image (struct frame *f, struct image *img,
PixelWand **pixels, *bg_wand = NULL;
MagickPixelPacket pixel;
Lisp_Object image;
+#ifndef DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE
Lisp_Object value;
+#endif
Lisp_Object crop;
EMACS_INT ino;
int desired_width, desired_height;
+#ifndef DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE
double rotation;
+#endif
char hint_buffer[MaxTextExtent];
char *filename_hint = NULL;
imagemagick_initialize ();
@@ -9238,9 +10397,13 @@ imagemagick_load_image (struct frame *f, struct image *img,
PixelSetBlue (bg_wand, (double) bgcolor.blue / 65535);
}
+#ifndef DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE
compute_image_size (MagickGetImageWidth (image_wand),
MagickGetImageHeight (image_wand),
img, &desired_width, &desired_height);
+#else
+ desired_width = desired_height = -1;
+#endif
if (desired_width != -1 && desired_height != -1)
{
@@ -9284,6 +10447,7 @@ imagemagick_load_image (struct frame *f, struct image *img,
}
}
+#ifndef DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE
/* Furthermore :rotation. we need background color and angle for
rotation. */
/*
@@ -9302,6 +10466,7 @@ imagemagick_load_image (struct frame *f, struct image *img,
goto imagemagick_error;
}
}
+#endif
/* Set the canvas background color to the frame or specified
background, and flatten the image. Note: as of ImageMagick
@@ -9339,7 +10504,8 @@ imagemagick_load_image (struct frame *f, struct image *img,
init_color_table ();
-#if defined (HAVE_MAGICKEXPORTIMAGEPIXELS) && ! defined (HAVE_NS)
+#if defined (HAVE_MAGICKEXPORTIMAGEPIXELS) && \
+ ! defined (HAVE_NS) && ! defined (HAVE_HAIKU)
if (imagemagick_render_type != 0)
{
/* Magicexportimage is normally faster than pixelpushing. This
@@ -9432,8 +10598,8 @@ imagemagick_load_image (struct frame *f, struct image *img,
color_scale * pixel.red,
color_scale * pixel.green,
color_scale * pixel.blue));
- }
- }
+ }
+ }
DestroyPixelIterator (iterator);
}
@@ -9669,6 +10835,10 @@ DEF_DLL_FN (gboolean, rsvg_handle_close, (RsvgHandle *, GError **));
DEF_DLL_FN (void, rsvg_handle_set_dpi_x_y,
(RsvgHandle * handle, double dpi_x, double dpi_y));
+# if LIBRSVG_CHECK_VERSION (2, 52, 1)
+DEF_DLL_FN (gboolean, rsvg_handle_get_intrinsic_size_in_pixels,
+ (RsvgHandle *, gdouble *, gdouble *));
+# endif
# if LIBRSVG_CHECK_VERSION (2, 46, 0)
DEF_DLL_FN (void, rsvg_handle_get_intrinsic_dimensions,
(RsvgHandle *, gboolean *, RsvgLength *, gboolean *,
@@ -9676,14 +10846,15 @@ DEF_DLL_FN (void, rsvg_handle_get_intrinsic_dimensions,
DEF_DLL_FN (gboolean, rsvg_handle_get_geometry_for_layer,
(RsvgHandle *, const char *, const RsvgRectangle *,
RsvgRectangle *, RsvgRectangle *, GError **));
+# else
+DEF_DLL_FN (void, rsvg_handle_get_dimensions,
+ (RsvgHandle *, RsvgDimensionData *));
# endif
# if LIBRSVG_CHECK_VERSION (2, 48, 0)
DEF_DLL_FN (gboolean, rsvg_handle_set_stylesheet,
(RsvgHandle *, const guint8 *, gsize, GError **));
# endif
-DEF_DLL_FN (void, rsvg_handle_get_dimensions,
- (RsvgHandle *, RsvgDimensionData *));
DEF_DLL_FN (GdkPixbuf *, rsvg_handle_get_pixbuf, (RsvgHandle *));
DEF_DLL_FN (int, gdk_pixbuf_get_width, (const GdkPixbuf *));
DEF_DLL_FN (int, gdk_pixbuf_get_height, (const GdkPixbuf *));
@@ -9731,14 +10902,18 @@ init_svg_functions (void)
LOAD_DLL_FN (library, rsvg_handle_close);
#endif
LOAD_DLL_FN (library, rsvg_handle_set_dpi_x_y);
+#if LIBRSVG_CHECK_VERSION (2, 52, 1)
+ LOAD_DLL_FN (library, rsvg_handle_get_intrinsic_size_in_pixels);
+#endif
#if LIBRSVG_CHECK_VERSION (2, 46, 0)
LOAD_DLL_FN (library, rsvg_handle_get_intrinsic_dimensions);
LOAD_DLL_FN (library, rsvg_handle_get_geometry_for_layer);
+#else
+ LOAD_DLL_FN (library, rsvg_handle_get_dimensions);
#endif
#if LIBRSVG_CHECK_VERSION (2, 48, 0)
LOAD_DLL_FN (library, rsvg_handle_set_stylesheet);
#endif
- LOAD_DLL_FN (library, rsvg_handle_get_dimensions);
LOAD_DLL_FN (library, rsvg_handle_get_pixbuf);
LOAD_DLL_FN (gdklib, gdk_pixbuf_get_width);
@@ -9773,11 +10948,15 @@ init_svg_functions (void)
# undef g_clear_error
# undef g_object_unref
# undef g_type_init
+# if LIBRSVG_CHECK_VERSION (2, 52, 1)
+# undef rsvg_handle_get_intrinsic_size_in_pixels
+# endif
# if LIBRSVG_CHECK_VERSION (2, 46, 0)
# undef rsvg_handle_get_intrinsic_dimensions
# undef rsvg_handle_get_geometry_for_layer
+# else
+# undef rsvg_handle_get_dimensions
# endif
-# undef rsvg_handle_get_dimensions
# if LIBRSVG_CHECK_VERSION (2, 48, 0)
# undef rsvg_handle_set_stylesheet
# endif
@@ -9807,13 +10986,18 @@ init_svg_functions (void)
# if ! GLIB_CHECK_VERSION (2, 36, 0)
# define g_type_init fn_g_type_init
# endif
+# if LIBRSVG_CHECK_VERSION (2, 52, 1)
+# define rsvg_handle_get_intrinsic_size_in_pixels \
+ fn_rsvg_handle_get_intrinsic_size_in_pixels
+# endif
# if LIBRSVG_CHECK_VERSION (2, 46, 0)
# define rsvg_handle_get_intrinsic_dimensions \
fn_rsvg_handle_get_intrinsic_dimensions
# define rsvg_handle_get_geometry_for_layer \
fn_rsvg_handle_get_geometry_for_layer
+# else
+# define rsvg_handle_get_dimensions fn_rsvg_handle_get_dimensions
# endif
-# define rsvg_handle_get_dimensions fn_rsvg_handle_get_dimensions
# if LIBRSVG_CHECK_VERSION (2, 48, 0)
# define rsvg_handle_set_stylesheet fn_rsvg_handle_set_stylesheet
# endif
@@ -10043,72 +11227,90 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
/* Get the image dimensions. */
#if LIBRSVG_CHECK_VERSION (2, 46, 0)
- RsvgRectangle zero_rect, viewbox, out_logical_rect;
-
- /* Try the intrinsic dimensions first. */
- gboolean has_width, has_height, has_viewbox;
- RsvgLength iwidth, iheight;
- double dpi = FRAME_DISPLAY_INFO (f)->resx;
-
- rsvg_handle_get_intrinsic_dimensions (rsvg_handle,
- &has_width, &iwidth,
- &has_height, &iheight,
- &has_viewbox, &viewbox);
+ gdouble gviewbox_width = 0, gviewbox_height = 0;
+ gboolean has_viewbox = FALSE;
+# if LIBRSVG_CHECK_VERSION (2, 52, 1)
+ has_viewbox = rsvg_handle_get_intrinsic_size_in_pixels (rsvg_handle,
+ &gviewbox_width,
+ &gviewbox_height);
+# endif
- if (has_width && has_height)
- {
- /* Success! We can use these values directly. */
- viewbox_width = svg_css_length_to_pixels (iwidth, dpi, img->face_font_size);
- viewbox_height = svg_css_length_to_pixels (iheight, dpi, img->face_font_size);
- }
- else if (has_width && has_viewbox)
- {
- viewbox_width = svg_css_length_to_pixels (iwidth, dpi, img->face_font_size);
- viewbox_height = svg_css_length_to_pixels (iwidth, dpi, img->face_font_size)
- * viewbox.height / viewbox.width;
- }
- else if (has_height && has_viewbox)
+ if (has_viewbox)
{
- viewbox_height = svg_css_length_to_pixels (iheight, dpi, img->face_font_size);
- viewbox_width = svg_css_length_to_pixels (iheight, dpi, img->face_font_size)
- * viewbox.width / viewbox.height;
- }
- else if (has_viewbox)
- {
- viewbox_width = viewbox.width;
- viewbox_height = viewbox.height;
+ viewbox_width = gviewbox_width;
+ viewbox_height = gviewbox_height;
}
else
{
- /* We haven't found a usable set of sizes, so try working out
- the visible area. */
- rsvg_handle_get_geometry_for_layer (rsvg_handle, NULL,
- &zero_rect, &viewbox,
- &out_logical_rect, NULL);
- viewbox_width = viewbox.x + viewbox.width;
- viewbox_height = viewbox.y + viewbox.height;
- }
+ RsvgRectangle zero_rect, viewbox, out_logical_rect;
- if (viewbox_width == 0 || viewbox_height == 0)
-#endif
- {
- /* The functions used above to get the geometry of the visible
- area of the SVG are only available in librsvg 2.46 and above,
- so in certain circumstances this code path can result in some
- parts of the SVG being cropped. */
- RsvgDimensionData dimension_data;
+ /* Try the intrinsic dimensions first. */
+ gboolean has_width, has_height;
+ RsvgLength iwidth, iheight;
+ double dpi = FRAME_DISPLAY_INFO (f)->resx;
- rsvg_handle_get_dimensions (rsvg_handle, &dimension_data);
+ rsvg_handle_get_intrinsic_dimensions (rsvg_handle,
+ &has_width, &iwidth,
+ &has_height, &iheight,
+ &has_viewbox, &viewbox);
- viewbox_width = dimension_data.width;
- viewbox_height = dimension_data.height;
- }
+ if (has_width && has_height)
+ {
+ /* Success! We can use these values directly. */
+ viewbox_width = svg_css_length_to_pixels (iwidth, dpi,
+ img->face_font_size);
+ viewbox_height = svg_css_length_to_pixels (iheight, dpi,
+ img->face_font_size);
+ }
+ else if (has_width && has_viewbox)
+ {
+ viewbox_width = svg_css_length_to_pixels (iwidth, dpi,
+ img->face_font_size);
+ viewbox_height = viewbox_width * viewbox.height / viewbox.width;
+ }
+ else if (has_height && has_viewbox)
+ {
+ viewbox_height = svg_css_length_to_pixels (iheight, dpi,
+ img->face_font_size);
+ viewbox_width = viewbox_height * viewbox.width / viewbox.height;
+ }
+ else if (has_viewbox)
+ {
+ viewbox_width = viewbox.width;
+ viewbox_height = viewbox.height;
+ }
+ else
+ viewbox_width = viewbox_height = 0;
+
+ if (! (0 < viewbox_width && 0 < viewbox_height))
+ {
+ /* We haven't found a usable set of sizes, so try working out
+ the visible area. */
+ rsvg_handle_get_geometry_for_layer (rsvg_handle, NULL,
+ &zero_rect, &viewbox,
+ &out_logical_rect, NULL);
+ viewbox_width = viewbox.x + viewbox.width;
+ viewbox_height = viewbox.y + viewbox.height;
+ }
+ }
+#else
+ /* In librsvg before 2.46.0, guess the viewbox from the image dimensions. */
+ RsvgDimensionData dimension_data;
+ rsvg_handle_get_dimensions (rsvg_handle, &dimension_data);
+ viewbox_width = dimension_data.width;
+ viewbox_height = dimension_data.height;
+#endif
+#ifdef HAVE_NATIVE_TRANSFORMS
compute_image_size (viewbox_width, viewbox_height, img,
&width, &height);
- width *= FRAME_SCALE_FACTOR (f);
- height *= FRAME_SCALE_FACTOR (f);
+ width = scale_image_size (width, 1, FRAME_SCALE_FACTOR (f));
+ height = scale_image_size (height, 1, FRAME_SCALE_FACTOR (f));
+#else
+ width = viewbox_width;
+ height = viewbox_height;
+#endif
if (! check_image_size (f, width, height))
{
@@ -10309,7 +11511,7 @@ svg_load_image (struct frame *f, struct image *img, char *contents,
#endif
/* FIXME: Use error->message so the user knows what is the actual
problem with the image. */
- image_error ("Error parsing SVG image `%s'", img->spec);
+ image_error ("Error parsing SVG image");
g_clear_error (&err);
return 0;
}
@@ -10451,7 +11653,7 @@ gs_load (struct frame *f, struct image *img)
block_input ();
img->pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_DRAWABLE (f),
img->width, img->height,
- DefaultDepthOfScreen (FRAME_X_SCREEN (f)));
+ FRAME_DISPLAY_INFO (f)->n_planes);
unblock_input ();
}
@@ -10524,7 +11726,7 @@ x_kill_gs_process (Pixmap pixmap, struct frame *f)
/* On displays with a mutable colormap, figure out the colors
allocated for the image by looking at the pixels of an XImage for
img->pixmap. */
- if (x_mutable_colormap (FRAME_X_VISUAL (f)))
+ if (x_mutable_colormap (FRAME_X_VISUAL_INFO (f)))
{
XImage *ximg;
@@ -10555,16 +11757,6 @@ x_kill_gs_process (Pixmap pixmap, struct frame *f)
free_color_table ();
#endif
XDestroyImage (ximg);
-
-#if 0 /* This doesn't seem to be the case. If we free the colors
- here, we get a BadAccess later in image_clear_image when
- freeing the colors. */
- /* We have allocated colors once, but Ghostscript has also
- allocated colors on behalf of us. So, to get the
- reference counts right, free them once. */
- if (img->ncolors)
- x_free_colors (f, img->colors, img->ncolors);
-#endif
}
else
image_error ("Cannot get X image of `%s'; colors will not be freed",
@@ -10633,13 +11825,11 @@ The list of capabilities can include one or more of the following:
if (FRAME_WINDOW_P (f))
{
#ifdef HAVE_NATIVE_TRANSFORMS
-# if defined HAVE_IMAGEMAGICK || defined (USE_CAIRO) || defined (HAVE_NS)
+# if defined HAVE_IMAGEMAGICK || defined (USE_CAIRO) || defined (HAVE_NS) \
+ || defined (HAVE_HAIKU)
return list2 (Qscale, Qrotate90);
# elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER)
- int event_basep, error_basep;
-
- if (XRenderQueryExtension (FRAME_X_DISPLAY (f),
- &event_basep, &error_basep))
+ if (FRAME_DISPLAY_INFO (f)->xrender_supported_p)
return list2 (Qscale, Qrotate90);
# elif defined (HAVE_NTGUI)
return (w32_image_rotations_p ()
@@ -10723,10 +11913,14 @@ static struct image_type const image_types[] =
{ SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image,
IMAGE_TYPE_INIT (init_jpeg_functions) },
#endif
-#if defined HAVE_XPM || defined HAVE_NS
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
{ SYMBOL_INDEX (Qxpm), xpm_image_p, xpm_load, image_clear_image,
IMAGE_TYPE_INIT (init_xpm_functions) },
#endif
+#if defined HAVE_WEBP
+ { SYMBOL_INDEX (Qwebp), webp_image_p, webp_load, image_clear_image,
+ IMAGE_TYPE_INIT (init_webp_functions) },
+#endif
{ SYMBOL_INDEX (Qxbm), xbm_image_p, xbm_load, image_clear_image },
{ SYMBOL_INDEX (Qpbm), pbm_image_p, pbm_load, image_clear_image },
};
@@ -10801,6 +11995,7 @@ non-numeric, there is no explicit limit on the size of images. */);
DEFSYM (QCtransform_smoothing, ":transform-smoothing");
DEFSYM (QCcolor_adjustment, ":color-adjustment");
DEFSYM (QCmask, ":mask");
+ DEFSYM (QCflip, ":flip");
/* Other symbols. */
DEFSYM (Qlaplace, "laplace");
@@ -10867,7 +12062,8 @@ non-numeric, there is no explicit limit on the size of images. */);
DEFSYM (Qxbm, "xbm");
add_image_type (Qxbm);
-#if defined (HAVE_XPM) || defined (HAVE_NS)
+#if defined (HAVE_XPM) || defined (HAVE_NS) \
+ || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
DEFSYM (Qxpm, "xpm");
add_image_type (Qxpm);
#endif
@@ -10892,6 +12088,13 @@ non-numeric, there is no explicit limit on the size of images. */);
add_image_type (Qpng);
#endif
+#if defined (HAVE_WEBP) || (defined (HAVE_NATIVE_IMAGE_API) \
+ && defined (HAVE_HAIKU))
+ DEFSYM (Qwebp, "webp");
+ DEFSYM (Qwebpdemux, "webpdemux");
+ add_image_type (Qwebp);
+#endif
+
#if defined (HAVE_IMAGEMAGICK)
DEFSYM (Qimagemagick, "imagemagick");
add_image_type (Qimagemagick);
@@ -10913,8 +12116,19 @@ non-numeric, there is no explicit limit on the size of images. */);
#endif /* HAVE_NTGUI */
#endif /* HAVE_RSVG */
+#ifdef HAVE_NS
+ DEFSYM (Qheic, "heic");
+ add_image_type (Qheic);
+#endif
+
#if HAVE_NATIVE_IMAGE_API
DEFSYM (Qnative_image, "native-image");
+
+# if defined HAVE_NTGUI || defined HAVE_HAIKU
+ DEFSYM (Qbmp, "bmp");
+ add_image_type (Qbmp);
+# endif
+
# ifdef HAVE_NTGUI
DEFSYM (Qgdiplus, "gdiplus");
DEFSYM (Qshlwapi, "shlwapi");
@@ -10937,6 +12151,11 @@ non-numeric, there is no explicit limit on the size of images. */);
defsubr (&Slookup_image);
#endif
+ DEFSYM (QCanimate_buffer, ":animate-buffer");
+ DEFSYM (QCanimate_tardiness, ":animate-tardiness");
+ DEFSYM (QCanimate_position, ":animate-position");
+ DEFSYM (QCanimate_multi_frame_data, ":animate-multi-frame-data");
+
defsubr (&Simage_transforms_p);
DEFVAR_BOOL ("cross-disabled-images", cross_disabled_images,