unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Alan Third <alan@idiocy.org>
To: Eli Zaretskii <eliz@gnu.org>
Cc: emacs-devel@gnu.org
Subject: Re: Linking to ImageMagick by default
Date: Thu, 27 Dec 2018 13:11:05 +0000	[thread overview]
Message-ID: <20181227131105.GA18792@breton.holly.idiocy.org> (raw)
In-Reply-To: <83efadcw3j.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 824 bytes --]

On Wed, Dec 19, 2018 at 06:36:16PM +0200, Eli Zaretskii wrote:
> I think you should use Fcons instead of list3 or list2.

Thanks Eli! I got there eventually using Fcons.

I’ve now run into another issue. Now that I’m stripping the image spec
down to the basics when I load an image in image mode and then use +
or - to resize, it doesn’t redraw.

It redraws correctly if you cause a redraw, by scrolling the window,
or making an animated gif animate, or similar.

I think that during redisplay something must be looking at the image
and deciding it hasn’t changed, but I can’t find anything like that.

Patch attached. It probably won’t build on Windows, unfortunately.
I’ve yet to look at that code properly, and I don’t have a Windows
build environment, but I expect it to work on X and NS.
-- 
Alan Third

[-- Attachment #2: 0001-Add-native-image-scaling.patch --]
[-- Type: text/plain, Size: 52631 bytes --]

From 8644aace35436d4e92f5c998003072b4b345f8ab Mon Sep 17 00:00:00 2001
From: Alan Third <alan@idiocy.org>
Date: Mon, 10 Dec 2018 19:43:08 +0000
Subject: [PATCH] Add native image scaling

* configure.ac: Test for XRender outside of xft checks.
* src/Makefile.in (XRENDER_LIBS): List XRender libs separately from
xft libs.
* lisp/image.el (image--get-imagemagick-and-warn): Remove ImageMagick
checks and on't flush the image cache as often.
* src/image.c (get_cache_spec):
(calc_image_spec): New function.
(Fimage_flush): Use the modified image spec.
(image_ascent):
(Fimage_size): Replace struct image with struct image_spec.
(make_image): Remove members that are image independent.
(scale_image_size):
(compute_image_size): Make available when image resizing of any sort
is available.
(lookup_image): Use new cache_spec for lookups and remove image
independent code.
* src/dispextern.h (struct image_spec): New struct.
(struct it):
(struct glyph): Replace an image_id with struct image_spec.
(struct glyph_string): Add struct image_spec.
(struct image): Remove members that are image independent.
(calc_image_spec): New function.
(image_ascent): Replace struct image with struct image_spec.
* src/dispnew.c (buffer_posn_from_coords):
(mode_line_string):
(marginal_area_string): Replace struct image.
* src/nsimage.m (ns_load_image):
([EmacsImage setSizeFromSpec:]): Remove NS specific image resizing.
* src/nsterm.m (ns_draw_fringe_bitmap): Remove GNUstep specific code.
(ns_dumpglyphs_image): Composite image according to sizes in new
image_prop struct.
* src/w32term.c (x_draw_image_foreground):
(x_draw_image_relief):
(w32_draw_image_foreground_1): Replace struct image with struct image_spec.
* src/xdisp.c (handle_single_display_spec):
(push_it):
(pop_it):
(dump_glyph):
(push_prefix_prop):
(calc_pixel_width_or_height):
(fill_image_glyph_string):
(produce_image_glyph):
(get_window_cursor_type): Replace struct image with struct image_spec.
(note_mouse_highlight): Use the image spec stored in the glyph struct
rather than the one attached to the image struct.
* src/xterm.c (x_composite_image): New function.
(x_draw_image_foreground): Replace struct image with struct image_spec
and use new x_composite_image function.
(x_draw_image_relief):
(x_draw_image_foreground_1):
(x_draw_image_glyph_string): Replace struct image with struct image_spec.
---
 configure.ac     |  14 +-
 lisp/image.el    |   3 -
 src/Makefile.in  |   3 +-
 src/dispextern.h |  48 +++---
 src/dispnew.c    |  32 +---
 src/image.c      | 374 +++++++++++++++++++++++++++++------------------
 src/nsimage.m    |  62 --------
 src/nsterm.h     |   1 -
 src/nsterm.m     |  36 ++---
 src/w32term.c    |   6 +-
 src/xdisp.c      |  86 ++++++-----
 src/xterm.c      | 108 +++++++++-----
 12 files changed, 417 insertions(+), 356 deletions(-)

diff --git a/configure.ac b/configure.ac
index 8b34c3b658..2b22e2cbc2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3241,6 +3241,17 @@ AC_DEFUN
   CFLAGS=$late_CFLAGS
 fi
 
+# Check for XRender
+HAVE_XRENDER=no
+if test "${HAVE_X11}" = "yes"; then
+  AC_CHECK_LIB(Xrender, XRenderQueryExtension, HAVE_XRENDER=yes)
+  if test $HAVE_XRENDER = yes; then
+    XRENDER_LIBS="-lXrender"
+    AC_SUBST(XRENDER_LIBS)
+    AC_DEFINE([HAVE_XRENDER], 1, [Define to 1 if XRender is available.])
+  fi
+fi
+
 ### Start of font-backend (under any platform) section.
 # (nothing here yet -- this is a placeholder)
 ### End of font-backend (under any platform) section.
@@ -3263,15 +3274,12 @@ AC_DEFUN
       EMACS_CHECK_MODULES([XFT], [xft >= 0.13.0], [], [HAVE_XFT=no])
       ## Because xterm.c uses XRenderQueryExtension when XFT is
       ## enabled, we also need to link to -lXrender.
-      HAVE_XRENDER=no
-      AC_CHECK_LIB(Xrender, XRenderQueryExtension, HAVE_XRENDER=yes)
       if test "$HAVE_XFT" != no && test "$HAVE_XRENDER" != no; then
 	OLD_CPPFLAGS="$CPPFLAGS"
 	OLD_CFLAGS="$CFLAGS"
 	OLD_LIBS="$LIBS"
 	CPPFLAGS="$CPPFLAGS $XFT_CFLAGS"
 	CFLAGS="$CFLAGS $XFT_CFLAGS"
-	XFT_LIBS="-lXrender $XFT_LIBS"
 	LIBS="$XFT_LIBS $LIBS"
 	AC_CHECK_HEADER(X11/Xft/Xft.h,
 	  AC_CHECK_LIB(Xft, XftFontOpen, HAVE_XFT=yes, , $XFT_LIBS) , ,
diff --git a/lisp/image.el b/lisp/image.el
index 74a23046e9..4843c2de1f 100644
--- a/lisp/image.el
+++ b/lisp/image.el
@@ -982,10 +982,7 @@ image--get-image
     image))
 
 (defun image--get-imagemagick-and-warn ()
-  (unless (or (fboundp 'imagemagick-types) (featurep 'ns))
-    (error "Cannot rescale images without ImageMagick support"))
   (let ((image (image--get-image)))
-    (image-flush image)
     (when (fboundp 'imagemagick-types)
       (plist-put (cdr image) :type 'imagemagick))
     image))
diff --git a/src/Makefile.in b/src/Makefile.in
index 6b2e54a160..809a484413 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -127,7 +127,8 @@ LIBIMAGE=
 
 XCB_LIBS=@XCB_LIBS@
 XFT_LIBS=@XFT_LIBS@
-LIBX_EXTRA=-lX11 $(XCB_LIBS) $(XFT_LIBS)
+XRENDER_LIBS=@XRENDER_LIBS@
+LIBX_EXTRA=-lX11 $(XCB_LIBS) $(XFT_LIBS) $(XRENDER_LIBS)
 
 FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
 FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
diff --git a/src/dispextern.h b/src/dispextern.h
index 4cd017603d..877948ee97 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -172,6 +172,30 @@ extern bool trace_redisplay_p EXTERNALLY_VISIBLE;
 
 
 \f
+/* Struct for use by display functions.  Contains the desired size of
+   the image, derived from the display properties.  */
+
+struct image_spec
+{
+  ptrdiff_t image_id;
+  int width;
+  int height;
+
+  /* Relief to draw around the image.  */
+  int relief;
+
+  /* Optional margins around the image.  This includes the relief.  */
+  int hmargin, vmargin;
+
+  /* Percent of image height used as ascent.  A value of
+     CENTERED_IMAGE_ASCENT means draw the image centered on the
+     line.  */
+  int ascent;
+#define DEFAULT_IMAGE_ASCENT 50
+#define CENTERED_IMAGE_ASCENT -1
+};
+
+\f
 /***********************************************************************
 			    Text positions
  ***********************************************************************/
@@ -505,7 +529,7 @@ struct glyph
     } cmp;
 
     /* Image ID for image glyphs (type == IMAGE_GLYPH).  */
-    int img_id;
+    struct image_spec image;
 
 #ifdef HAVE_XWIDGETS
     /* Xwidget reference (type == XWIDGET_GLYPH).  */
@@ -1361,6 +1385,7 @@ struct glyph_string
 
   /* Image, if any.  */
   struct image *img;
+  struct image_spec image;
 
   /* Xwidget.  */
   struct xwidget *xwidget;
@@ -2373,7 +2398,7 @@ struct it
       struct {
 	Lisp_Object object;
 	struct it_slice slice;
-	ptrdiff_t image_id;
+        struct image_spec image;
       } image;
       /* method == GET_FROM_STRETCH */
       struct {
@@ -2509,7 +2534,7 @@ struct it
   enum glyphless_display_method glyphless_method;
 
   /* If what == IT_IMAGE, the id of the image to display.  */
-  ptrdiff_t image_id;
+  struct image_spec image;
 
   /* If what == IT_XWIDGET.  */
   struct xwidget *xwidget;
@@ -3001,13 +3026,6 @@ struct image
 #define BOT_CORNER 2
 #define RIGHT_CORNER 3
 
-  /* Percent of image height used as ascent.  A value of
-     CENTERED_IMAGE_ASCENT means draw the image centered on the
-     line.  */
-  int ascent;
-#define DEFAULT_IMAGE_ASCENT 50
-#define CENTERED_IMAGE_ASCENT -1
-
   /* Lisp specification of this image.  */
   Lisp_Object spec;
 
@@ -3016,12 +3034,6 @@ struct image
      Used to allow fine-grained cache flushing.  */
   Lisp_Object dependencies;
 
-  /* Relief to draw around the image.  */
-  int relief;
-
-  /* Optional margins around the image.  This includes the relief.  */
-  int hmargin, vmargin;
-
   /* Reference to the type of the image.  */
   struct image_type *type;
 
@@ -3042,7 +3054,6 @@ struct image
   struct image *next, *prev;
 };
 
-
 /* Cache of images.  Each frame has a cache.  X frames with the same
    x_display_info share their caches.  */
 
@@ -3358,6 +3369,7 @@ void clear_image_caches (Lisp_Object);
 void mark_image_cache (struct image_cache *);
 bool valid_image_p (Lisp_Object);
 void prepare_image_for_display (struct frame *, struct image *);
+struct image_spec calc_image_spec (struct frame *f, Lisp_Object spec);
 ptrdiff_t lookup_image (struct frame *, Lisp_Object);
 
 #if defined (HAVE_X_WINDOWS) ||  defined (HAVE_NS)
@@ -3373,7 +3385,7 @@ RGB_PIXEL_COLOR image_background (struct image *, struct frame *,
 int image_background_transparent (struct image *, struct frame *,
                                   XImagePtr_or_DC mask);
 
-int image_ascent (struct image *, struct face *, struct glyph_slice *);
+int image_ascent (struct image_spec, struct face *, struct glyph_slice *);
 
 #endif
 
diff --git a/src/dispnew.c b/src/dispnew.c
index b628c694e8..4f374928eb 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -5117,9 +5117,6 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p
   struct text_pos startp;
   Lisp_Object string;
   struct glyph_row *row;
-#ifdef HAVE_WINDOW_SYSTEM
-  struct image *img = 0;
-#endif
   int x0, x1, to_x, it_vpos;
   void *itdata = NULL;
 
@@ -5213,17 +5210,8 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p
     }
 
 #ifdef HAVE_WINDOW_SYSTEM
-  if (it.what == IT_IMAGE)
-    {
-      /* Note that this ignores images that are fringe bitmaps,
-	 because their image ID is zero, and so IMAGE_OPT_FROM_ID will
-	 return NULL.  This is okay, since fringe bitmaps are not
-	 displayed in the text area, and so are never the object we
-	 are interested in.  */
-      img = IMAGE_OPT_FROM_ID (it.f, it.image_id);
-      if (img && !NILP (img->spec))
-	*object = img->spec;
-    }
+  if (it.what == IT_IMAGE && !NILP (it.object))
+    *object = it.object;
 #endif
 
   /* IT's vpos counts from the glyph row that includes the window's
@@ -5239,14 +5227,14 @@ buffer_posn_from_coords (struct window *w, int *x, int *y, struct display_pos *p
 	{
 	  struct glyph *glyph = row->glyphs[TEXT_AREA] + it.hpos;
 #ifdef HAVE_WINDOW_SYSTEM
-	  if (img)
+	  if (it.what == IT_IMAGE)
 	    {
 	      *dy -= row->ascent - glyph->ascent;
 	      *dx += glyph->slice.img.x;
 	      *dy += glyph->slice.img.y;
 	      /* Image slices positions are still relative to the entire image */
-	      *width = img->width;
-	      *height = img->height;
+	      *width = it.image.width;
+	      *height = it.image.height;
 	    }
 	  else
 #endif
@@ -5317,10 +5305,7 @@ mode_line_string (struct window *w, enum window_part part,
 #ifdef HAVE_WINDOW_SYSTEM
 	  if (glyph->type == IMAGE_GLYPH)
 	    {
-	      struct image *img;
-	      img = IMAGE_OPT_FROM_ID (WINDOW_XFRAME (w), glyph->u.img_id);
-	      if (img != NULL)
-		*object = img->spec;
+              *object = glyph->object;
 	      y0 -= row->ascent - glyph->ascent;
 	    }
 #endif
@@ -5404,10 +5389,7 @@ marginal_area_string (struct window *w, enum window_part part,
 #ifdef HAVE_WINDOW_SYSTEM
 	  if (glyph->type == IMAGE_GLYPH)
 	    {
-	      struct image *img;
-	      img = IMAGE_OPT_FROM_ID (WINDOW_XFRAME (w), glyph->u.img_id);
-	      if (img != NULL)
-		*object = img->spec;
+              *object = glyph->object;
 	      y0 -= row->ascent - glyph->ascent;
 	      x0 += glyph->slice.img.x;
 	      y0 += glyph->slice.img.y;
diff --git a/src/image.c b/src/image.c
index 218626f771..7900d3a2bc 100644
--- a/src/image.c
+++ b/src/image.c
@@ -901,11 +901,14 @@ or omitted means use the selected frame.  */)
   size = Qnil;
   if (valid_image_p (spec))
     {
+      struct image_spec image;
+      int width, height;
       struct frame *f = decode_window_system_frame (frame);
-      ptrdiff_t id = lookup_image (f, spec);
-      struct image *img = IMAGE_FROM_ID (f, id);
-      int width = img->width + 2 * img->hmargin;
-      int height = img->height + 2 * img->vmargin;
+
+      image = calc_image_spec (f, spec);
+
+      width = image.width + 2 * image.hmargin;
+      height = image.height + 2 * image.vmargin;
 
       if (NILP (pixels))
 	size = Fcons (make_float ((double) width / FRAME_COLUMN_WIDTH (f)),
@@ -984,7 +987,6 @@ make_image (Lisp_Object spec, EMACS_UINT hash)
   eassert (img->type != NULL);
   img->spec = spec;
   img->lisp_data = Qnil;
-  img->ascent = DEFAULT_IMAGE_ASCENT;
   img->hash = hash;
   img->corners[BOT_CORNER] = -1;  /* Full image */
   return img;
@@ -1082,19 +1084,19 @@ prepare_image_for_display (struct frame *f, struct image *img)
    drawn in face FACE.  */
 
 int
-image_ascent (struct image *img, struct face *face, struct glyph_slice *slice)
+image_ascent (struct image_spec img, struct face *face, struct glyph_slice *slice)
 {
   int height;
   int ascent;
 
-  if (slice->height == img->height)
-    height = img->height + img->vmargin;
+  if (slice->height == img.height)
+    height = img.height + img.vmargin;
   else if (slice->y == 0)
-    height = slice->height + img->vmargin;
+    height = slice->height + img.vmargin;
   else
     height = slice->height;
 
-  if (img->ascent == CENTERED_IMAGE_ASCENT)
+  if (img.ascent == CENTERED_IMAGE_ASCENT)
     {
       if (face->font)
 	{
@@ -1116,7 +1118,7 @@ image_ascent (struct image *img, struct face *face, struct glyph_slice *slice)
 	ascent = height / 2;
     }
   else
-    ascent = height * (img->ascent / 100.0);
+    ascent = height * (img.ascent / 100.0);
 
   return ascent;
 }
@@ -1453,6 +1455,46 @@ make_image_cache (void)
   return c;
 }
 
+/* Filter the SPEC so it can be used to look up images in the cache
+   without worrying about whether they are the same size, etc.  */
+
+static Lisp_Object
+get_cache_spec (Lisp_Object spec)
+{
+  Lisp_Object cache_spec, tail;
+
+  /* If type is imagemagick then the image will already be resized and
+     rotated, etc., so just return the full spec.
+
+     FIXME: I think we still want to strip out the likes of margin, as
+     that is not part of the image.  */
+  if (EQ (image_spec_value (spec, QCtype, NULL), Qimagemagick))
+    return spec;
+
+  cache_spec = Qnil;
+
+  for (tail = XCDR (spec);
+       CONSP (tail) && CONSP (XCDR (tail));
+       tail = XCDR (XCDR (tail)))
+    {
+      Lisp_Object property = XCAR (tail);
+      Lisp_Object value = XCAR (XCDR (tail));
+
+      /* FIXME: Surely there's a better way to do this?  */
+      if (!EQ (property, QCwidth)
+          && !EQ (property, QCheight)
+          && !EQ (property, QCmax_width)
+          && !EQ (property, QCmax_height)
+          && !EQ (property, QCscale)
+          && !EQ (property, QCmargin)
+          && !EQ (property, QCascent)
+          && !EQ (property, QCrelief))
+        cache_spec = Fcons (property, Fcons (value, cache_spec));
+    }
+  cache_spec = Fcons (XCAR (spec), cache_spec);
+
+  return cache_spec;
+}
 
 /* Find an image matching SPEC in the cache, and return it.  If no
    image is found, return NULL.  */
@@ -1661,6 +1703,8 @@ FRAME t means refresh the image on all frames.  */)
   if (!valid_image_p (spec))
     error ("Invalid image specification");
 
+  spec = get_cache_spec (spec);
+
   if (EQ (frame, Qt))
     {
       Lisp_Object tail;
@@ -1747,6 +1791,176 @@ postprocess_image (struct frame *f, struct image *img)
     }
 }
 
+#if defined (HAVE_IMAGEMAGICK) || defined (HAVE_XRENDER) || defined (HAVE_NS)
+/* Scale an image size by returning SIZE / DIVISOR * MULTIPLIER,
+   safely rounded and clipped to int range.  */
+
+static int
+scale_image_size (int size, size_t divisor, size_t multiplier)
+{
+  if (divisor != 0)
+    {
+      double s = size;
+      double scaled = s * multiplier / divisor + 0.5;
+      if (scaled < INT_MAX)
+	return scaled;
+    }
+  return INT_MAX;
+}
+
+/* Compute the desired size of an image with native size WIDTH x HEIGHT.
+   Use SPEC to deduce the size.  Store the desired size into
+   *D_WIDTH x *D_HEIGHT.  Store -1 x -1 if the native size is OK.  */
+static void
+compute_image_size (size_t width, size_t height,
+		    Lisp_Object spec,
+		    int *d_width, int *d_height)
+{
+  Lisp_Object value;
+  int desired_width = -1, desired_height = -1, max_width = -1, max_height = -1;
+  double scale = 1;
+
+  value = image_spec_value (spec, QCscale, NULL);
+  if (NUMBERP (value))
+    scale = XFLOATINT (value);
+
+  value = image_spec_value (spec, QCmax_width, NULL);
+  if (FIXNATP (value))
+    max_width = min (XFIXNAT (value), INT_MAX);
+
+  value = image_spec_value (spec, QCmax_height, NULL);
+  if (FIXNATP (value))
+    max_height = min (XFIXNAT (value), INT_MAX);
+
+  /* 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.  */
+  value = image_spec_value (spec, QCwidth, NULL);
+  if (FIXNATP (value))
+    {
+      desired_width = min (XFIXNAT (value) * scale, INT_MAX);
+      /* :width overrides :max-width. */
+      max_width = -1;
+    }
+
+  value = image_spec_value (spec, QCheight, NULL);
+  if (FIXNATP (value))
+    {
+      desired_height = min (XFIXNAT (value) * scale, INT_MAX);
+      /* :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)
+    goto out;
+
+  width = width * scale;
+  height = height * scale;
+
+  if (desired_width != -1)
+    /* Width known, calculate height. */
+    desired_height = scale_image_size (desired_width, width, height);
+  else if (desired_height != -1)
+    /* Height known, calculate width. */
+    desired_width = scale_image_size (desired_height, height, width);
+  else
+    {
+      desired_width = width;
+      desired_height = height;
+    }
+
+  if (max_width != -1 && desired_width > max_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)
+    {
+      /* The image is higher than :max-height. */
+      desired_height = max_height;
+      desired_width = scale_image_size (desired_height, height, width);
+    }
+
+ out:
+  *d_width = desired_width;
+  *d_height = desired_height;
+}
+#endif /* HAVE_IMAGEMAGICK || HAVE_XRENDER || HAVE_NS  */
+
+struct image_spec
+calc_image_spec (struct frame *f, Lisp_Object spec)
+{
+  struct image_spec img_spec;
+  struct image *img;
+
+  Lisp_Object ascent, margin, relief;
+  int relief_bound;
+
+  img_spec.image_id = lookup_image (f, spec);
+  img = IMAGE_OPT_FROM_ID (f, img_spec.image_id);
+
+#if defined (HAVE_XRENDER) || defined (HAVE_NS)
+  if (!EQ (image_spec_value (spec, QCtype, NULL), Qimagemagick))
+    {
+      int width, height;
+
+      compute_image_size (img->width, img->height, spec, &width, &height);
+
+      /* Set the final sizes.  We always maintain the aspect ratio.  */
+      img_spec.width = width;
+      img_spec.height = height;
+    }
+  else
+#endif
+    {
+      /* We don't have built-in image resizing or we're using
+         ImageMagick which does its own transforms.  */
+      img_spec.width = img->width;
+      img_spec.height = img->height;
+    }
+
+  /* Handle type independent image attributes
+     `:ascent ASCENT', `:margin MARGIN', `:relief RELIEF'.  */
+  ascent = image_spec_value (spec, QCascent, NULL);
+  if (FIXNUMP (ascent))
+    img_spec.ascent = XFIXNAT (ascent);
+  else if (EQ (ascent, Qcenter))
+    img_spec.ascent = CENTERED_IMAGE_ASCENT;
+  else
+    img_spec.ascent = DEFAULT_IMAGE_ASCENT;
+
+  margin = image_spec_value (spec, QCmargin, NULL);
+  if (FIXNUMP (margin))
+    img_spec.vmargin = img_spec.hmargin = XFIXNAT (margin);
+  else if (CONSP (margin))
+    {
+      img_spec.hmargin = XFIXNAT (XCAR (margin));
+      img_spec.vmargin = XFIXNAT (XCDR (margin));
+    }
+  else
+    {
+      img_spec.hmargin = 0;
+      img_spec.vmargin = 0;
+    }
+
+  relief = image_spec_value (spec, QCrelief, NULL);
+  relief_bound = INT_MAX - max (img_spec.hmargin, img_spec.vmargin);
+  if (RANGED_FIXNUMP (- relief_bound, relief, relief_bound))
+    {
+      img_spec.relief = XFIXNUM (relief);
+      img_spec.hmargin += eabs (img_spec.relief);
+      img_spec.vmargin += eabs (img_spec.relief);
+    }
+  else
+    img_spec.relief = 0;
+
+  return img_spec;
+}
 
 /* Return the id of image with Lisp specification SPEC on frame F.
    SPEC must be a valid Lisp image specification (see valid_image_p).  */
@@ -1756,15 +1970,18 @@ lookup_image (struct frame *f, Lisp_Object spec)
 {
   struct image *img;
   EMACS_UINT hash;
+  Lisp_Object cache_spec;
 
   /* F must be a window-system frame, and SPEC must be a valid image
      specification.  */
   eassert (FRAME_WINDOW_P (f));
   eassert (valid_image_p (spec));
 
+  cache_spec = get_cache_spec (spec);
+
   /* Look up SPEC in the hash table of the image cache.  */
-  hash = sxhash (spec, 0);
-  img = search_image_cache (f, spec, hash);
+  hash = sxhash (cache_spec, 0);
+  img = search_image_cache (f, cache_spec, hash);
   if (img && img->load_failed_p)
     {
       free_image (f, img);
@@ -1775,7 +1992,7 @@ lookup_image (struct frame *f, Lisp_Object spec)
   if (img == NULL)
     {
       block_input ();
-      img = make_image (spec, hash);
+      img = make_image (cache_spec, hash);
       cache_image (f, img);
       img->load_failed_p = ! img->type->load (f, img);
       img->frame_foreground = FRAME_FOREGROUND_PIXEL (f);
@@ -1797,35 +2014,9 @@ lookup_image (struct frame *f, Lisp_Object spec)
 	}
       else
 	{
-	  /* Handle image type independent image attributes
-	     `:ascent ASCENT', `:margin MARGIN', `:relief RELIEF',
+	  /* Handle image type independent image attribute
 	     `:background COLOR'.  */
-	  Lisp_Object ascent, margin, relief, bg;
-	  int relief_bound;
-
-	  ascent = image_spec_value (spec, QCascent, NULL);
-	  if (FIXNUMP (ascent))
-	    img->ascent = XFIXNAT (ascent);
-	  else if (EQ (ascent, Qcenter))
-	    img->ascent = CENTERED_IMAGE_ASCENT;
-
-	  margin = image_spec_value (spec, QCmargin, NULL);
-	  if (FIXNUMP (margin))
-	    img->vmargin = img->hmargin = XFIXNAT (margin);
-	  else if (CONSP (margin))
-	    {
-	      img->hmargin = XFIXNAT (XCAR (margin));
-	      img->vmargin = XFIXNAT (XCDR (margin));
-	    }
-
-	  relief = image_spec_value (spec, QCrelief, NULL);
-	  relief_bound = INT_MAX - max (img->hmargin, img->vmargin);
-	  if (RANGED_FIXNUMP (- relief_bound, relief, relief_bound))
-	    {
-	      img->relief = XFIXNUM (relief);
-	      img->hmargin += eabs (img->relief);
-	      img->vmargin += eabs (img->relief);
-	    }
+	  Lisp_Object bg;
 
 	  if (! img->background_valid)
 	    {
@@ -8101,105 +8292,6 @@ gif_load (struct frame *f, struct image *img)
 				 ImageMagick
 ***********************************************************************/
 
-/* Scale an image size by returning SIZE / DIVISOR * MULTIPLIER,
-   safely rounded and clipped to int range.  */
-
-static int
-scale_image_size (int size, size_t divisor, size_t multiplier)
-{
-  if (divisor != 0)
-    {
-      double s = size;
-      double scaled = s * multiplier / divisor + 0.5;
-      if (scaled < INT_MAX)
-	return scaled;
-    }
-  return INT_MAX;
-}
-
-/* Compute the desired size of an image with native size WIDTH x HEIGHT.
-   Use SPEC to deduce the size.  Store the desired size into
-   *D_WIDTH x *D_HEIGHT.  Store -1 x -1 if the native size is OK.  */
-static void
-compute_image_size (size_t width, size_t height,
-		    Lisp_Object spec,
-		    int *d_width, int *d_height)
-{
-  Lisp_Object value;
-  int desired_width = -1, desired_height = -1, max_width = -1, max_height = -1;
-  double scale = 1;
-
-  value = image_spec_value (spec, QCscale, NULL);
-  if (NUMBERP (value))
-    scale = XFLOATINT (value);
-
-  value = image_spec_value (spec, QCmax_width, NULL);
-  if (FIXNATP (value))
-    max_width = min (XFIXNAT (value), INT_MAX);
-
-  value = image_spec_value (spec, QCmax_height, NULL);
-  if (FIXNATP (value))
-    max_height = min (XFIXNAT (value), INT_MAX);
-
-  /* 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.  */
-  value = image_spec_value (spec, QCwidth, NULL);
-  if (FIXNATP (value))
-    {
-      desired_width = min (XFIXNAT (value) * scale, INT_MAX);
-      /* :width overrides :max-width. */
-      max_width = -1;
-    }
-
-  value = image_spec_value (spec, QCheight, NULL);
-  if (FIXNATP (value))
-    {
-      desired_height = min (XFIXNAT (value) * scale, INT_MAX);
-      /* :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)
-    goto out;
-
-  width = width * scale;
-  height = height * scale;
-
-  if (desired_width != -1)
-    /* Width known, calculate height. */
-    desired_height = scale_image_size (desired_width, width, height);
-  else if (desired_height != -1)
-    /* Height known, calculate width. */
-    desired_width = scale_image_size (desired_height, height, width);
-  else
-    {
-      desired_width = width;
-      desired_height = height;
-    }
-
-  if (max_width != -1 && desired_width > max_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)
-    {
-      /* The image is higher than :max-height. */
-      desired_height = max_height;
-      desired_width = scale_image_size (desired_height, height, width);
-    }
-
- out:
-  *d_width = desired_width;
-  *d_height = desired_height;
-}
-
 static bool imagemagick_image_p (Lisp_Object);
 static bool imagemagick_load (struct frame *, struct image *);
 static void imagemagick_clear_image (struct frame *, struct image *);
diff --git a/src/nsimage.m b/src/nsimage.m
index 0ae1b88edd..71da0201d1 100644
--- a/src/nsimage.m
+++ b/src/nsimage.m
@@ -126,8 +126,6 @@ Updated by Christian Limpach (chris@nice.ch)
       eImg = temp;
     }
 
-  [eImg setSizeFromSpec:XCDR (img->spec)];
-
   size = [eImg size];
   img->width = size.width;
   img->height = size.height;
@@ -524,66 +522,6 @@ - (BOOL)setFrame: (unsigned int) index
   return YES;
 }
 
-- (void)setSizeFromSpec: (Lisp_Object) spec
-{
-  NSSize size = [self size];
-  Lisp_Object value;
-  double scale = 1, aspect = size.width / size.height;
-  double width = -1, height = -1, max_width = -1, max_height = -1;
-
-  value = Fplist_get (spec, QCscale);
-  if (NUMBERP (value))
-    scale = XFLOATINT (value) ;
-
-  value = Fplist_get (spec, QCmax_width);
-  if (NUMBERP (value))
-    max_width = XFLOATINT (value);
-
-  value = Fplist_get (spec, QCmax_height);
-  if (NUMBERP (value))
-    max_height = XFLOATINT (value);
-
-  value = Fplist_get (spec, QCwidth);
-  if (NUMBERP (value))
-    {
-      width = XFLOATINT (value) * scale;
-      /* :width overrides :max-width. */
-      max_width = -1;
-    }
-
-  value = Fplist_get (spec, QCheight);
-  if (NUMBERP (value))
-    {
-      height = XFLOATINT (value) * scale;
-      /* :height overrides :max-height. */
-      max_height = -1;
-    }
-
-  if (width <= 0 && height <= 0)
-    {
-      width = size.width * scale;
-      height = size.height * scale;
-    }
-  else if (width > 0 && height <= 0)
-      height = width / aspect;
-  else if (height > 0 && width <= 0)
-      width = height * aspect;
-
-  if (max_width > 0 && width > max_width)
-    {
-      width = max_width;
-      height = max_width / aspect;
-    }
-
-  if (max_height > 0 && height > max_height)
-    {
-      height = max_height;
-      width = max_height * aspect;
-    }
-
-  [self setSize:NSMakeSize(width, height)];
-}
-
 - (instancetype)rotate: (double)rotation
 {
   EmacsImage *new_image;
diff --git a/src/nsterm.h b/src/nsterm.h
index 23460abc65..f64d78bd2b 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -648,7 +648,6 @@ typedef id instancetype;
 - (NSColor *)stippleMask;
 - (Lisp_Object)getMetadata;
 - (BOOL)setFrame: (unsigned int) index;
-- (void)setSizeFromSpec: (Lisp_Object) spec;
 - (instancetype)rotate: (double)rotation;
 @end
 
diff --git a/src/nsterm.m b/src/nsterm.m
index 6c285f0abb..0a2a184830 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -3121,7 +3121,6 @@ so some key presses (TAB) are swallowed by the system.  */
             [img setXBMColor: bm_color];
           }
 
-#ifdef NS_IMPL_COCOA
           // Note: For periodic images, the full image height is "h + hd".
           // By using the height h, a suitable part of the image is used.
           NSRect fromRect = NSMakeRect(0, 0, p->wd, p->h);
@@ -3134,13 +3133,6 @@ so some key presses (TAB) are swallowed by the system.  */
                  fraction: 1.0
                respectFlipped: YES
                     hints: nil];
-#else
-          {
-            NSPoint pt = imageRect.origin;
-            pt.y += p->h;
-            [img compositeToPoint: pt operation: NSCompositingOperationSourceOver];
-          }
-#endif
         }
       ns_reset_clipping (f);
     }
@@ -3821,13 +3813,15 @@ Function modeled after x_draw_glyph_string_box ().
 {
   EmacsImage *img = s->img->pixmap;
   int box_line_vwidth = max (s->face->box_line_width, 0);
-  int x = s->x, y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+  int x = s->x, y = s->ybase - image_ascent (s->image, s->face, &s->slice);
   int bg_x, bg_y, bg_height;
   int th;
   char raised_p;
   NSRect br;
   struct face *face;
   NSColor *tdCol;
+  double xscale = (double)s->img->width/s->image.width;
+  double yscale = (double)s->img->height/s->image.height;
 
   NSTRACE ("ns_dumpglyphs_image");
 
@@ -3841,8 +3835,8 @@ Function modeled after x_draw_glyph_string_box ().
   /* other terms have this, but was causing problems w/tabbar mode */
   /* - 2 * box_line_vwidth; */
 
-  if (s->slice.x == 0) x += s->img->hmargin;
-  if (s->slice.y == 0) y += s->img->vmargin;
+  if (s->slice.x == 0) x += s->image.hmargin;
+  if (s->slice.y == 0) y += s->image.vmargin;
 
   /* Draw BG: if we need larger area than image itself cleared, do that,
      otherwise, since we composite the image under NS (instead of mucking
@@ -3859,7 +3853,7 @@ Function modeled after x_draw_glyph_string_box ().
 
   [ns_lookup_indexed_color (NS_FACE_BACKGROUND (face), s->f) set];
 
-  if (bg_height > s->slice.height || s->img->hmargin || s->img->vmargin
+  if (bg_height > s->slice.height || s->image.hmargin || s->image.vmargin
       || s->img->mask || s->img->pixmap == 0 || s->width != s->background_width)
     {
       br = NSMakeRect (bg_x, bg_y, s->background_width, bg_height);
@@ -3877,9 +3871,9 @@ Function modeled after x_draw_glyph_string_box ().
     {
 #ifdef NS_IMPL_COCOA
       NSRect dr = NSMakeRect (x, y, s->slice.width, s->slice.height);
-      NSRect ir = NSMakeRect (s->slice.x,
-                              s->img->height - s->slice.y - s->slice.height,
-                              s->slice.width, s->slice.height);
+      NSRect ir = NSMakeRect (s->slice.x * xscale,
+                              (s->image.height - s->slice.y - s->slice.height) * yscale,
+                              s->slice.width * xscale , s->slice.height * yscale);
       [img drawInRect: dr
              fromRect: ir
              operation: NSCompositingOperationSourceOver
@@ -3914,7 +3908,7 @@ Function modeled after x_draw_glyph_string_box ().
   ns_draw_text_decoration (s, face, tdCol, br.size.width, br.origin.x);
 
   /* Draw relief, if requested */
-  if (s->img->relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
+  if (s->image.relief || s->hl ==DRAW_IMAGE_RAISED || s->hl ==DRAW_IMAGE_SUNKEN)
     {
       if (s->hl == DRAW_IMAGE_SUNKEN || s->hl == DRAW_IMAGE_RAISED)
         {
@@ -3924,8 +3918,8 @@ Function modeled after x_draw_glyph_string_box ().
         }
       else
         {
-          th = abs (s->img->relief);
-          raised_p = (s->img->relief > 0);
+          th = abs (s->image.relief);
+          raised_p = (s->image.relief > 0);
         }
 
       r.origin.x = x - th;
@@ -3934,9 +3928,9 @@ Function modeled after x_draw_glyph_string_box ().
       r.size.height = s->slice.height + 2*th-1;
       ns_draw_relief (r, th, raised_p,
                       s->slice.y == 0,
-                      s->slice.y + s->slice.height == s->img->height,
+                      s->slice.y + s->slice.height == s->image.height,
                       s->slice.x == 0,
-                      s->slice.x + s->slice.width == s->img->width, s);
+                      s->slice.x + s->slice.width == s->image.width, s);
     }
 
   /* If there is no mask, the background won't be seen,
@@ -3944,7 +3938,7 @@ Function modeled after x_draw_glyph_string_box ().
      Do this for all images, getting transparency right is not reliable.  */
   if (s->hl == DRAW_CURSOR)
     {
-      int thickness = abs (s->img->relief);
+      int thickness = abs (s->image.relief);
       if (thickness == 0) thickness = 1;
       ns_draw_box (br, thickness, FRAME_CURSOR_COLOR (s->f), 1, 1);
     }
diff --git a/src/w32term.c b/src/w32term.c
index 8d189ae32c..eb231b57f7 100644
--- a/src/w32term.c
+++ b/src/w32term.c
@@ -1850,7 +1850,7 @@ static void
 x_draw_image_foreground (struct glyph_string *s)
 {
   int x = s->x;
-  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+  int y = s->ybase - image_ascent (s->image, s->face, &s->slice);
 
   /* If first glyph of S has a left box line, start drawing it to the
      right of that line.  */
@@ -1943,7 +1943,7 @@ x_draw_image_relief (struct glyph_string *s)
   int extra_x, extra_y;
   RECT r;
   int x = s->x;
-  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+  int y = s->ybase - image_ascent (s->image, s->face, &s->slice);
 
   /* If first glyph of S has a left box line, start drawing it to the
      right of that line.  */
@@ -2015,7 +2015,7 @@ w32_draw_image_foreground_1 (struct glyph_string *s, HBITMAP pixmap)
   HDC hdc = CreateCompatibleDC (s->hdc);
   HGDIOBJ orig_hdc_obj = SelectObject (hdc, pixmap);
   int x = 0;
-  int y = s->ybase - s->y - image_ascent (s->img, s->face, &s->slice);
+  int y = s->ybase - s->y - image_ascent (s->image, s->face, &s->slice);
 
   /* If first glyph of S has a left box line, start drawing it to the
      right of that line.  */
diff --git a/src/xdisp.c b/src/xdisp.c
index 4201bdc4a7..6d17f64b7d 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -5197,7 +5197,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
 
 	  it->area = TEXT_AREA;
 	  it->what = IT_IMAGE;
-	  it->image_id = -1; /* no image */
+	  it->image.image_id = -1; /* no image */
 	  it->position = start_pos;
 	  it->object = NILP (object) ? it->w->contents : object;
 	  it->method = GET_FROM_IMAGE;
@@ -5356,7 +5356,7 @@ handle_single_display_spec (struct it *it, Lisp_Object spec, Lisp_Object object,
       else
 	{
 	  it->what = IT_IMAGE;
-	  it->image_id = lookup_image (it->f, value);
+          it->image = calc_image_spec (it->f, value);
 	  it->position = start_pos;
 	  it->object = NILP (object) ? it->w->contents : object;
 	  it->method = GET_FROM_IMAGE;
@@ -6103,7 +6103,7 @@ push_it (struct it *it, struct text_pos *position)
     {
     case GET_FROM_IMAGE:
       p->u.image.object = it->object;
-      p->u.image.image_id = it->image_id;
+      p->u.image.image = it->image;
       p->u.image.slice = it->slice;
       break;
     case GET_FROM_STRETCH:
@@ -6209,7 +6209,7 @@ pop_it (struct it *it)
   switch (it->method)
     {
     case GET_FROM_IMAGE:
-      it->image_id = p->u.image.image_id;
+      it->image = p->u.image.image;
       it->object = p->u.image.object;
       it->slice = p->u.image.slice;
       break;
@@ -19446,7 +19446,7 @@ dump_glyph (struct glyph_row *row, struct glyph *glyph, int area)
 		      ? '0'
 		      : '-'))),
 	       glyph->pixel_width,
-	       (unsigned int) glyph->u.img_id,
+	       (unsigned int) glyph->u.image.image_id,
 	       '.',
 	       glyph->face_id,
 	       glyph->left_box_line_p,
@@ -20775,7 +20775,7 @@ push_prefix_prop (struct it *it, Lisp_Object prop)
   else if (IMAGEP (prop))
     {
       it->what = IT_IMAGE;
-      it->image_id = lookup_image (it->f, prop);
+      it->image = calc_image_spec (it->f, prop);
       it->method = GET_FROM_IMAGE;
     }
 #endif /* HAVE_WINDOW_SYSTEM */
@@ -25596,12 +25596,8 @@ calc_pixel_width_or_height (double *res, struct it *it, Lisp_Object prop,
 	  /* '(image PROPS...)': width or height of the specified image.  */
 	  if (FRAME_WINDOW_P (it->f)
 	      && valid_image_p (prop))
-	    {
-	      ptrdiff_t id = lookup_image (it->f, prop);
-	      struct image *img = IMAGE_FROM_ID (it->f, id);
+	      return OK_PIXELS (width_p ? it->image.width : it->image.height);
 
-	      return OK_PIXELS (width_p ? img->width : img->height);
-	    }
 	  /* '(xwidget PROPS...)': dimensions of the specified xwidget.  */
 	  if (FRAME_WINDOW_P (it->f) && valid_xwidget_spec_p (prop))
 	    {
@@ -26104,7 +26100,8 @@ static void
 fill_image_glyph_string (struct glyph_string *s)
 {
   eassert (s->first_glyph->type == IMAGE_GLYPH);
-  s->img = IMAGE_FROM_ID (s->f, s->first_glyph->u.img_id);
+  s->img = IMAGE_FROM_ID (s->f, s->first_glyph->u.image.image_id);
+  s->image = s->first_glyph->u.image;
   eassert (s->img);
   s->slice = s->first_glyph->slice.img;
   s->face = FACE_FROM_ID (s->f, s->first_glyph->face_id);
@@ -27247,7 +27244,7 @@ produce_image_glyph (struct it *it)
   /* Make sure X resources of the face is loaded.  */
   prepare_face_for_display (it->f, face);
 
-  if (it->image_id < 0)
+  if (it->image.image_id < 0)
     {
       /* Fringe bitmap.  */
       it->ascent = it->phys_ascent = 0;
@@ -27257,60 +27254,60 @@ produce_image_glyph (struct it *it)
       return;
     }
 
-  img = IMAGE_FROM_ID (it->f, it->image_id);
+  img = IMAGE_FROM_ID (it->f, it->image.image_id);
   /* Make sure X resources of the image is loaded.  */
   prepare_image_for_display (it->f, img);
 
   slice.x = slice.y = 0;
-  slice.width = img->width;
-  slice.height = img->height;
+  slice.width = it->image.width;
+  slice.height = it->image.height;
 
   if (FIXNUMP (it->slice.x))
     slice.x = XFIXNUM (it->slice.x);
   else if (FLOATP (it->slice.x))
-    slice.x = XFLOAT_DATA (it->slice.x) * img->width;
+    slice.x = XFLOAT_DATA (it->slice.x) * it->image.width;
 
   if (FIXNUMP (it->slice.y))
     slice.y = XFIXNUM (it->slice.y);
   else if (FLOATP (it->slice.y))
-    slice.y = XFLOAT_DATA (it->slice.y) * img->height;
+    slice.y = XFLOAT_DATA (it->slice.y) * it->image.height;
 
   if (FIXNUMP (it->slice.width))
     slice.width = XFIXNUM (it->slice.width);
   else if (FLOATP (it->slice.width))
-    slice.width = XFLOAT_DATA (it->slice.width) * img->width;
+    slice.width = XFLOAT_DATA (it->slice.width) * it->image.width;
 
   if (FIXNUMP (it->slice.height))
     slice.height = XFIXNUM (it->slice.height);
   else if (FLOATP (it->slice.height))
-    slice.height = XFLOAT_DATA (it->slice.height) * img->height;
+    slice.height = XFLOAT_DATA (it->slice.height) * it->image.height;
 
-  if (slice.x >= img->width)
-    slice.x = img->width;
-  if (slice.y >= img->height)
-    slice.y = img->height;
-  if (slice.x + slice.width >= img->width)
-    slice.width = img->width - slice.x;
-  if (slice.y + slice.height > img->height)
-    slice.height = img->height - slice.y;
+  if (slice.x >= it->image.width)
+    slice.x = it->image.width;
+  if (slice.y >= it->image.height)
+    slice.y = it->image.height;
+  if (slice.x + slice.width >= it->image.width)
+    slice.width = it->image.width - slice.x;
+  if (slice.y + slice.height > it->image.height)
+    slice.height = it->image.height - slice.y;
 
   if (slice.width == 0 || slice.height == 0)
     return;
 
-  it->ascent = it->phys_ascent = glyph_ascent = image_ascent (img, face, &slice);
+  it->ascent = it->phys_ascent = glyph_ascent = image_ascent (it->image, face, &slice);
 
   it->descent = slice.height - glyph_ascent;
   if (slice.y == 0)
-    it->descent += img->vmargin;
-  if (slice.y + slice.height == img->height)
-    it->descent += img->vmargin;
+    it->descent += it->image.vmargin;
+  if (slice.y + slice.height == it->image.height)
+    it->descent += it->image.vmargin;
   it->phys_descent = it->descent;
 
   it->pixel_width = slice.width;
   if (slice.x == 0)
-    it->pixel_width += img->hmargin;
-  if (slice.x + slice.width == img->width)
-    it->pixel_width += img->hmargin;
+    it->pixel_width += it->image.hmargin;
+  if (slice.x + slice.width == it->image.width)
+    it->pixel_width += it->image.hmargin;
 
   /* It's quite possible for images to have an ascent greater than
      their height, so don't get confused in that case.  */
@@ -27325,13 +27322,13 @@ produce_image_glyph (struct it *it)
 	{
 	  if (slice.y == 0)
 	    it->ascent += face->box_line_width;
-	  if (slice.y + slice.height == img->height)
+	  if (slice.y + slice.height == it->image.height)
 	    it->descent += face->box_line_width;
 	}
 
       if (it->start_of_box_run_p && slice.x == 0)
 	it->pixel_width += eabs (face->box_line_width);
-      if (it->end_of_box_run_p && slice.x + slice.width == img->width)
+      if (it->end_of_box_run_p && slice.x + slice.width == it->image.width)
 	it->pixel_width += eabs (face->box_line_width);
     }
 
@@ -27388,7 +27385,7 @@ produce_image_glyph (struct it *it)
           glyph->padding_p = false;
 	  glyph->glyph_not_available_p = false;
 	  glyph->face_id = it->face_id;
-	  glyph->u.img_id = img->id;
+	  glyph->u.image = it->image;
 	  glyph->slice.img = slice;
 	  glyph->font_type = FONT_TYPE_UNKNOWN;
 	  if (it->bidi_p)
@@ -29326,15 +29323,15 @@ get_window_cursor_type (struct window *w, struct glyph *glyph, int *width,
 	      /* Using a block cursor on large images can be very annoying.
 		 So use a hollow cursor for "large" images.
 		 If image is not transparent (no mask), also use hollow cursor.  */
-	      struct image *img = IMAGE_OPT_FROM_ID (f, glyph->u.img_id);
+	      struct image *img = IMAGE_OPT_FROM_ID (f, glyph->u.image.image_id);
 	      if (img != NULL && IMAGEP (img->spec))
 		{
 		  /* Arbitrarily, interpret "Large" as >32x32 and >NxN
 		     where N = size of default frame font size.
 		     This should cover most of the "tiny" icons people may use.  */
 		  if (!img->mask
-		      || img->width > max (32, WINDOW_FRAME_COLUMN_WIDTH (w))
-		      || img->height > max (32, WINDOW_FRAME_LINE_HEIGHT (w)))
+		      || glyph->u.image.width > max (32, WINDOW_FRAME_COLUMN_WIDTH (w))
+		      || glyph->u.image.height > max (32, WINDOW_FRAME_LINE_HEIGHT (w)))
 		    cursor_type = HOLLOW_BOX_CURSOR;
 		}
 	    }
@@ -31482,11 +31479,10 @@ note_mouse_highlight (struct frame *f, int x, int y)
       /* Look for :pointer property on image.  */
       if (glyph != NULL && glyph->type == IMAGE_GLYPH)
 	{
-	  struct image *img = IMAGE_OPT_FROM_ID (f, glyph->u.img_id);
-	  if (img != NULL && IMAGEP (img->spec))
+	  if (IMAGEP (glyph->object))
 	    {
 	      Lisp_Object image_map, hotspot;
-	      if ((image_map = Fplist_get (XCDR (img->spec), QCmap),
+	      if ((image_map = Fplist_get (XCDR (glyph->object), QCmap),
 		   !NILP (image_map))
 		  && (hotspot = find_hot_spot (image_map,
 					       glyph->slice.img.x + dx,
@@ -31517,7 +31513,7 @@ note_mouse_highlight (struct frame *f, int x, int y)
 		    }
 		}
 	      if (NILP (pointer))
-		pointer = Fplist_get (XCDR (img->spec), QCpointer);
+		pointer = Fplist_get (XCDR (glyph->object), QCpointer);
 	    }
 	}
 #endif	/* HAVE_WINDOW_SYSTEM */
diff --git a/src/xterm.c b/src/xterm.c
index 943f4c3b3d..6a94916d4e 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -38,11 +38,6 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #include <X11/extensions/Xfixes.h>
 #endif
 
-/* Using Xft implies that XRender is available.  */
-#ifdef HAVE_XFT
-#include <X11/extensions/Xrender.h>
-#endif
-
 #ifdef HAVE_XDBE
 #include <X11/extensions/Xdbe.h>
 #endif
@@ -136,6 +131,10 @@ along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
 #include <X11/XKBlib.h>
 #endif
 
+#ifdef HAVE_XRENDER
+#include <X11/extensions/Xrender.h>
+#endif
+
 /* Default to using XIM if available.  */
 #ifdef USE_XIM
 bool use_xim = true;
@@ -2976,13 +2975,60 @@ x_draw_glyph_string_box (struct glyph_string *s)
 }
 
 
+static void
+x_composite_image (struct glyph_string *s, Pixmap dest,
+                   int srcX, int srcY, int dstX, int dstY,
+                   int width, int height)
+{
+#ifdef HAVE_XRENDER
+  Picture source, destination;
+  XRenderPictFormat *default_format, *source_format;
+  XRenderPictureAttributes source_attr, dest_attr;
+  double xscale = (double) s->img->width/s->image.width;
+  double yscale = (double) s->img->height/s->image.height;
+
+  XTransform tmat = {{{XDoubleToFixed (xscale), XDoubleToFixed (0), XDoubleToFixed (0)},
+                      {XDoubleToFixed (0), XDoubleToFixed (yscale), XDoubleToFixed (0)},
+                      {XDoubleToFixed (0), XDoubleToFixed (0), XDoubleToFixed (1)}}};
+
+  default_format = XRenderFindVisualFormat (s->display, DefaultVisual (s->display, 0));
+  /* FIXME: I doubt this is always correct.  There must be a way of
+     working out the format of the pixmap.  */
+  source_format = XRenderFindStandardFormat (s->display, PictStandardRGB24);
+
+  source = XRenderCreatePicture (s->display, s->img->pixmap, source_format,
+                                 0, &source_attr);
+  destination = XRenderCreatePicture (s->display, dest, default_format, 0, &dest_attr);
+
+  XRenderSetPictureFilter (s->display, source, FilterBest, 0, 0);
+  XRenderSetPictureTransform (s->display, source, &tmat);
+
+  /* FIXME: XRenderComposite supports masks, so can we just use the
+     mask in s->img->mask here?  */
+  XRenderComposite (s->display, PictOpOver, source, None, destination,
+                    srcX*xscale, srcY*yscale,
+                    0, 0,
+                    dstX, dstY,
+                    width, height);
+
+  XRenderFreePicture (s->display, source);
+  XRenderFreePicture (s->display, destination);
+#else
+  XCopyArea (s->display, s->img->pixmap,
+             dest, s->gc,
+             srcX, srcY
+             width, height, dstX, dstY);
+#endif
+}
+
+
 /* Draw foreground of image glyph string S.  */
 
 static void
 x_draw_image_foreground (struct glyph_string *s)
 {
   int x = s->x;
-  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+  int y = s->ybase - image_ascent (s->image, s->face, &s->slice);
 
   /* If first glyph of S has a left box line, start drawing it to the
      right of that line.  */
@@ -2994,9 +3040,9 @@ x_draw_image_foreground (struct glyph_string *s)
   /* If there is a margin around the image, adjust x- and y-position
      by that margin.  */
   if (s->slice.x == 0)
-    x += s->img->hmargin;
+    x += s->image.hmargin;
   if (s->slice.y == 0)
-    y += s->img->vmargin;
+    y += s->image.vmargin;
 
   if (s->img->pixmap)
     {
@@ -3024,10 +3070,8 @@ x_draw_image_foreground (struct glyph_string *s)
 	  image_rect.width = s->slice.width;
 	  image_rect.height = s->slice.height;
 	  if (x_intersect_rectangles (&clip_rect, &image_rect, &r))
-            XCopyArea (s->display, s->img->pixmap,
-                       FRAME_X_DRAWABLE (s->f), s->gc,
-		       s->slice.x + r.x - x, s->slice.y + r.y - y,
-		       r.width, r.height, r.x, r.y);
+            x_composite_image (s, FRAME_X_DRAWABLE (s->f), s->slice.x + r.x - x, s->slice.y + r.y - y,
+                               r.x, r.y, r.width, r.height);
 	}
       else
 	{
@@ -3039,10 +3083,8 @@ x_draw_image_foreground (struct glyph_string *s)
 	  image_rect.width = s->slice.width;
 	  image_rect.height = s->slice.height;
 	  if (x_intersect_rectangles (&clip_rect, &image_rect, &r))
-            XCopyArea (s->display, s->img->pixmap,
-                       FRAME_X_DRAWABLE (s->f), s->gc,
-		       s->slice.x + r.x - x, s->slice.y + r.y - y,
-		       r.width, r.height, r.x, r.y);
+            x_composite_image (s, FRAME_X_DRAWABLE (s->f), s->slice.x + r.x - x, s->slice.y + r.y - y,
+                               r.x, r.y, r.width, r.height);
 
 	  /* When the image has a mask, we can expect that at
 	     least part of a mouse highlight or a block cursor will
@@ -3052,7 +3094,7 @@ x_draw_image_foreground (struct glyph_string *s)
 	     nothing here for mouse-face.  */
 	  if (s->hl == DRAW_CURSOR)
 	    {
-	      int relief = eabs (s->img->relief);
+	      int relief = eabs (s->image.relief);
 	      x_draw_rectangle (s->f, s->gc,
 			      x - relief, y - relief,
 			      s->slice.width + relief*2 - 1,
@@ -3077,7 +3119,7 @@ x_draw_image_relief (struct glyph_string *s)
   int extra_x, extra_y;
   XRectangle r;
   int x = s->x;
-  int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+  int y = s->ybase - image_ascent (s->image, s->face, &s->slice);
 
   /* If first glyph of S has a left box line, start drawing it to the
      right of that line.  */
@@ -3089,9 +3131,9 @@ x_draw_image_relief (struct glyph_string *s)
   /* If there is a margin around the image, adjust x- and y-position
      by that margin.  */
   if (s->slice.x == 0)
-    x += s->img->hmargin;
+    x += s->image.hmargin;
   if (s->slice.y == 0)
-    y += s->img->vmargin;
+    y += s->image.vmargin;
 
   if (s->hl == DRAW_IMAGE_SUNKEN
       || s->hl == DRAW_IMAGE_RAISED)
@@ -3101,8 +3143,8 @@ x_draw_image_relief (struct glyph_string *s)
     }
   else
     {
-      thick = eabs (s->img->relief);
-      raised_p = s->img->relief > 0;
+      thick = eabs (s->image.relief);
+      raised_p = s->image.relief > 0;
     }
 
   x1 = x + s->slice.width - 1;
@@ -3128,9 +3170,9 @@ x_draw_image_relief (struct glyph_string *s)
     x -= thick + extra_x, left_p = true;
   if (s->slice.y == 0)
     y -= thick + extra_y, top_p = true;
-  if (s->slice.x + s->slice.width == s->img->width)
+  if (s->slice.x + s->slice.width == s->image.width)
     x1 += thick + extra_x, right_p = true;
-  if (s->slice.y + s->slice.height == s->img->height)
+  if (s->slice.y + s->slice.height == s->image.height)
     y1 += thick + extra_y, bot_p = true;
 
   x_setup_relief_colors (s);
@@ -3146,7 +3188,7 @@ static void
 x_draw_image_foreground_1 (struct glyph_string *s, Pixmap pixmap)
 {
   int x = 0;
-  int y = s->ybase - s->y - image_ascent (s->img, s->face, &s->slice);
+  int y = s->ybase - s->y - image_ascent (s->image, s->face, &s->slice);
 
   /* If first glyph of S has a left box line, start drawing it to the
      right of that line.  */
@@ -3158,9 +3200,9 @@ x_draw_image_foreground_1 (struct glyph_string *s, Pixmap pixmap)
   /* If there is a margin around the image, adjust x- and y-position
      by that margin.  */
   if (s->slice.x == 0)
-    x += s->img->hmargin;
+    x += s->image.hmargin;
   if (s->slice.y == 0)
-    y += s->img->vmargin;
+    y += s->image.vmargin;
 
   if (s->img->pixmap)
     {
@@ -3200,7 +3242,7 @@ x_draw_image_foreground_1 (struct glyph_string *s, Pixmap pixmap)
 	     nothing here for mouse-face.  */
 	  if (s->hl == DRAW_CURSOR)
 	    {
-	      int r = eabs (s->img->relief);
+	      int r = eabs (s->image.relief);
 	      x_draw_rectangle (s->f, s->gc, x - r, y - r,
 			      s->slice.width + r*2 - 1,
 			      s->slice.height + r*2 - 1);
@@ -3265,8 +3307,8 @@ x_draw_image_glyph_string (struct glyph_string *s)
      flickering.  */
   s->stippled_p = s->face->stipple != 0;
   if (height > s->slice.height
-      || s->img->hmargin
-      || s->img->vmargin
+      || s->image.hmargin
+      || s->image.vmargin
       || s->img->mask
       || s->img->pixmap == 0
       || s->width != s->background_width)
@@ -3338,8 +3380,8 @@ x_draw_image_glyph_string (struct glyph_string *s)
     {
       cairo_t *cr = x_begin_cr_clip (s->f, s->gc);
 
-      int x = s->x + s->img->hmargin;
-      int y = s->y + s->img->vmargin;
+      int x = s->x + s->image.hmargin;
+      int y = s->y + s->image.vmargin;
       int width = s->background_width;
 
       cairo_set_source_surface (cr, s->img->cr_data,
@@ -3363,7 +3405,7 @@ x_draw_image_glyph_string (struct glyph_string *s)
     x_draw_image_foreground (s);
 
   /* If we must draw a relief around the image, do it.  */
-  if (s->img->relief
+  if (s->image.relief
       || s->hl == DRAW_IMAGE_RAISED
       || s->hl == DRAW_IMAGE_SUNKEN)
     x_draw_image_relief (s);
-- 
2.19.1


  parent reply	other threads:[~2018-12-27 13:11 UTC|newest]

Thread overview: 42+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-02  1:33 Linking to ImageMagick by default Glenn Morris
2018-12-02 18:15 ` Paul Eggert
2018-12-05 13:30 ` Lars Ingebrigtsen
2018-12-05 15:28   ` Stefan Monnier
2018-12-06 11:06     ` Lars Ingebrigtsen
2018-12-05 17:24   ` Glenn Morris
2018-12-05 17:27     ` Lars Ingebrigtsen
2018-12-05 18:27     ` Daniel Pittman
2018-12-05 18:38       ` Lars Ingebrigtsen
2018-12-05 19:31     ` joakim
2018-12-05 22:39   ` Alan Third
2018-12-08 18:38     ` Alan Third
2018-12-08 21:24       ` Paul Eggert
2018-12-10 22:09         ` Alan Third
2018-12-19 16:03           ` Alan Third
2018-12-19 16:36             ` Eli Zaretskii
2018-12-19 16:45               ` Joseph Garvin
2018-12-27 15:06                 ` Alan Third
2018-12-27 13:11               ` Alan Third [this message]
2018-12-27 16:05                 ` Eli Zaretskii
2018-12-27 20:37                   ` Juri Linkov
2018-12-28  8:12                 ` Eli Zaretskii
2018-12-28 21:21                   ` Alan Third
2018-12-29  6:56                     ` Eli Zaretskii
2018-12-29 21:31                       ` Juri Linkov
2018-12-30 12:47                       ` Alan Third
2019-01-01 21:47                         ` [PATCH] Add native image scaling Alan Third
2019-01-02 16:11                           ` Eli Zaretskii
2019-01-02 21:12                             ` [PATCH v2] Add native image scaling (bug#33587) Alan Third
2019-01-04 14:31                               ` Eli Zaretskii
2019-01-04 19:09                                 ` Alan Third
2019-01-04 20:21                                   ` Eli Zaretskii
2019-01-04 22:45                                     ` Alan Third
2019-01-10 19:42                                       ` Alan Third
2019-01-10 19:50                                         ` Eli Zaretskii
2019-01-10 23:40                                         ` Paul Eggert
2019-01-06 16:26                                   ` Eli Zaretskii
2019-01-05 21:30                               ` Track mouse drags over an image (was: Add native image scaling) Roland Winkler
2019-01-08 18:20                                 ` Track mouse drags over an image Stefan Monnier
2019-01-11  4:55                                   ` Roland Winkler
2019-01-11 16:23                                     ` Stefan Monnier
2018-12-09 11:34       ` Linking to ImageMagick by default Elias Mårtenson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181227131105.GA18792@breton.holly.idiocy.org \
    --to=alan@idiocy.org \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).