From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Alan Third Newsgroups: gmane.emacs.devel Subject: [PATCH] Add native image scaling Date: Tue, 1 Jan 2019 21:47:19 +0000 Message-ID: <20190101214719.GA18292@breton.holly.idiocy.org> References: <20181230124759.GA77761@breton.holly.idiocy.org> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: blaine.gmane.org 1546379221 24996 195.159.176.226 (1 Jan 2019 21:47:01 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Tue, 1 Jan 2019 21:47:01 +0000 (UTC) User-Agent: Mutt/1.10.1 (2018-07-13) Cc: emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Jan 01 22:46:57 2019 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1geRsD-0006Kj-TW for ged-emacs-devel@m.gmane.org; Tue, 01 Jan 2019 22:46:54 +0100 Original-Received: from localhost ([127.0.0.1]:40420 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1geRuK-0004li-PX for ged-emacs-devel@m.gmane.org; Tue, 01 Jan 2019 16:49:04 -0500 Original-Received: from eggs.gnu.org ([208.118.235.92]:48859) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1geRt1-0004az-D1 for emacs-devel@gnu.org; Tue, 01 Jan 2019 16:47:53 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1geRsx-0005AR-Qj for emacs-devel@gnu.org; Tue, 01 Jan 2019 16:47:43 -0500 Original-Received: from mail-wr1-x436.google.com ([2a00:1450:4864:20::436]:46943) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1geRsg-0004ed-6w; Tue, 01 Jan 2019 16:47:22 -0500 Original-Received: by mail-wr1-x436.google.com with SMTP id l9so28840745wrt.13; Tue, 01 Jan 2019 13:47:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=googlemail.com; s=20161025; h=sender:date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=jJ3qcZCuMiPWxijO2sHPdNLiNKwa1PFbRFDDdF+gCfI=; b=f4Y3mA2D4KfnTxUOv9hY/8fxJos4AB9SrQDa01UnGuQbs8yW2Y3wHz85F6+w5EZl5w kPSPnFxCtrPy5uKjSDvIFvZMs7ZObBFHLA8yeIlebeXzrpBQBLd/X5TZXW1bnYkkwiuC 2tXW7SjAT5Og3BvrqAf0/6N04njrGY0YcnMk2M77rEkmCL6HiPEhN1x8VzfvGIv0/d/q DdEdyzN3KaWiX+l9HKEKG+Zlm9OLcp/lnYL3CL1Ss/Ethp+RWEzDKYvB6zkG9KzAGJ/o DT+ziAsPtPtDc4bPfTu9xXRu8rqw1rul97hNY4IVxl7Bw65fB6USD+syEVVka93iNShV C7HQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:from:to:cc:subject:message-id :references:mime-version:content-disposition:in-reply-to:user-agent; bh=jJ3qcZCuMiPWxijO2sHPdNLiNKwa1PFbRFDDdF+gCfI=; b=dk+ycQVxwvUW3RnDkfP+mZkOsv2jbYKIlE6d4hKbt8uxP+z4oWCeSeLO/ijP9D/IP6 PCCaETa88cvzM9ikNbtXO9quurmOeoNOsUVXqwzjxHADKe0RjK6ktAchh/HuOidH4zto Tzcp0NcR7ta2+bP1N3RuMWcWcZCBBAdmL2ZYKjrae4UuBNqyEg6KZOMu5xeXcRKMCKiN nN62ORb+iW5fGZYt53eMN16LRPcK5iFU/1hwVPzcrosWIxEvvJ1bOJ3gB7m94gMTSonT rCq3j8k8iamtHsQOny9/SvHVfaQ4jOIiGVgMPd/2iG5WIIuOOP8fWrlwqCkPrlocd1JY s2gQ== X-Gm-Message-State: AJcUukc7MENYuduGK2P7rAgqA8lcmF4wwqMpCsglw/EKvfPNm+WNJlf4 v66II5MsDfUR7rLOjynE/ggHRp07lFM= X-Google-Smtp-Source: ALg8bN6b88AmhEl3E+v+xSAJUZteuO+Mcf2420+Nqk9lqFXvMNwplGXE6ykK+pCtipFzcgyEMKJ+HA== X-Received: by 2002:a5d:4250:: with SMTP id s16mr37551550wrr.253.1546379240142; Tue, 01 Jan 2019 13:47:20 -0800 (PST) Original-Received: from breton.holly.idiocy.org (ip6-2001-08b0-03f8-8129-e11e-0646-dc01-9336.holly.idiocy.org. [2001:8b0:3f8:8129:e11e:646:dc01:9336]) by smtp.gmail.com with ESMTPSA id o4sm36611162wrq.66.2019.01.01.13.47.19 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 01 Jan 2019 13:47:19 -0800 (PST) Content-Disposition: inline In-Reply-To: <20181230124759.GA77761@breton.holly.idiocy.org> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::436 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:232070 Archived-At: * 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): Allow resizing if native scaling is available. * src/dispextern.h: Add XRender and image scaling stuff. (struct image): Add XRender Pictures. * src/image.c (x_create_bitmap_mask): (image_create_x_image_and_pixmap): Handle XRender Picture. (scale_image_size): (compute_image_size): Make available when any form of scaling is enabled. (x_set_image_size): New function. (lookup_image): Set image size. (x_create_x_image_and_pixmap): Create XRender Picture when necessary. (x_put_x_image): Handle the case where desired size != actual size. (free_image): Free XRender Pictures. (Fimage_scaling_p): New function. (syms_of_image): Add image-scaling-p. * src/nsimage.m (ns_load_image): Remove NS specific resizing. ([EmacsImage setSizeFromSpec:]): Remove method. (ns_image_set_size): New function. * src/nsterm.m (ns_draw_fringe_bitmap): Cocoa and GNUstep both have the same compositing functions, so remove unnecessary difference. * src/xterm.c (x_composite_image): New function. (x_draw_image_foreground): Use new x_composite_image function. --- This is a completely rewritten version that doesn't do anything different with the image cache. I think this is pretty complete as it is, excepting Windows support. I don't know how to add Windows support to match the way it's done in NS and X, however if we can't resize the images up-front, it should be possible to add some size info to the image struct and do the resize on display. I'm still unsure about image.el calling image-flush on every image resize, but I have to assume it's in there for a reason. configure.ac | 14 ++- etc/NEWS | 6 + lisp/image.el | 4 +- src/Makefile.in | 3 +- src/dispextern.h | 13 ++ src/image.c | 314 ++++++++++++++++++++++++++++++++--------------- src/nsimage.m | 68 +--------- src/nsterm.h | 2 +- src/nsterm.m | 8 -- src/xterm.c | 58 +++++++-- 10 files changed, 298 insertions(+), 192 deletions(-) diff --git a/configure.ac b/configure.ac index 91fa417308..0c59fe0619 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/etc/NEWS b/etc/NEWS index 75e2c1bf98..69e14773a7 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1442,6 +1442,12 @@ that is non-nil, it will look for a file name handler for the current buffer's 'default-directory' and invoke that file name handler to make the process. That way 'make-process' can start remote processes. ++++ +** Emacs now supports resizing images without ImageMagick on X window +systems where the XRender extension is available, and on the NS port. +The new function 'image-scaling-p' can be used to test whether any +given frame supports resizing. + * Changes in Emacs 27.1 on Non-Free Operating Systems diff --git a/lisp/image.el b/lisp/image.el index 5727d8fbce..2e84e47b5c 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -982,8 +982,8 @@ image--get-image image)) (defun image--get-imagemagick-and-warn () - (unless (or (fboundp 'imagemagick-types) (featurep 'ns)) - (error "Cannot rescale images without ImageMagick support")) + (unless (or (fboundp 'imagemagick-types) (image-scaling-p)) + (error "Cannot rescale images on this terminal")) (let ((image (image--get-image))) (image-flush image) (when (fboundp 'imagemagick-types) diff --git a/src/Makefile.in b/src/Makefile.in index e9831e9299..f409ed4db2 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 5774e3e951..b064875ac4 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -31,6 +31,9 @@ along with GNU Emacs. If not, see . */ #include #endif /* USE_X_TOOLKIT */ +#ifdef HAVE_XRENDER +#include +#endif #else /* !HAVE_X_WINDOWS */ /* X-related stuff used by non-X gui code. */ @@ -2935,6 +2938,11 @@ struct redisplay_interface #ifdef HAVE_WINDOW_SYSTEM +#if defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER) \ + || defined (HAVE_NS) +#define HAVE_NATIVE_SCALING +#endif + /* Structure describing an image. Specific image formats like XBM are converted into this form, so that display only has to deal with this type of image. */ @@ -2958,6 +2966,11 @@ struct image and the latter is outdated. NULL means the X image has been synchronized to Pixmap. */ XImagePtr ximg, mask_img; + +#ifdef HAVE_NATIVE_SCALING + /* Picture versions of pixmap and mask for compositing. */ + Picture picture, mask_picture; +#endif #endif /* Colors allocated for this image, if any. Allocated via xmalloc. */ diff --git a/src/image.c b/src/image.c index 87e0c071ee..9c78755bbc 100644 --- a/src/image.c +++ b/src/image.c @@ -408,8 +408,13 @@ x_destroy_all_bitmaps (Display_Info *dpyinfo) dpyinfo->bitmaps_last = 0; } +#ifdef HAVE_XRENDER +static bool x_create_x_image_and_pixmap (struct frame *, int, int, int, + XImagePtr *, Pixmap *, Picture *); +#else static bool x_create_x_image_and_pixmap (struct frame *, int, int, int, XImagePtr *, Pixmap *); +#endif static void x_destroy_x_image (XImagePtr ximg); #ifdef HAVE_NTGUI @@ -472,7 +477,11 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id) return; } - result = x_create_x_image_and_pixmap (f, width, height, 1, &mask_img, &mask); + result = x_create_x_image_and_pixmap (f, width, height, 1, &mask_img, &mask +#ifdef HAVE_XRENDER + , NULL +#endif + ); unblock_input (); if (!result) @@ -1011,6 +1020,13 @@ free_image (struct frame *f, struct image *img) c->images[img->id] = NULL; +#ifdef HAVE_XRENDER + if (img->picture) + XRenderFreePicture (FRAME_X_DISPLAY (f), img->picture); + if (img->mask_picture) + XRenderFreePicture (FRAME_X_DISPLAY (f), img->mask_picture); +#endif + /* Windows NT redefines 'free', but in this file, we need to avoid the redefinition. */ #ifdef WINDOWSNT @@ -1747,6 +1763,147 @@ postprocess_image (struct frame *f, struct image *img) } } +#if defined (HAVE_IMAGEMAGICK) || defined (HAVE_NATIVE_SCALING) +/* 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; +} + +#ifdef HAVE_NATIVE_SCALING +static void +x_set_image_size (struct frame *f, struct image *img) +{ +#ifdef HAVE_IMAGEMAGICK + /* ImageMagick images are already the correct size. */ + if (!imagemagick_image_p (img->spec)) +#endif + { + int width, height; + + compute_image_size (img->width, img->height, img->spec, &width, &height); + +#ifdef HAVE_NS + ns_image_set_size (img->pixmap, width, height); + img->width = width; + img->height = height; +#endif + +#ifdef HAVE_XRENDER + if (img->picture) + { + double xscale = (double) img->width/width; + double yscale = (double) img->height/height; + + XTransform tmat = {{{XDoubleToFixed (xscale), XDoubleToFixed (0), XDoubleToFixed (0)}, + {XDoubleToFixed (0), XDoubleToFixed (yscale), XDoubleToFixed (0)}, + {XDoubleToFixed (0), XDoubleToFixed (0), XDoubleToFixed (1)}}}; + + XRenderSetPictureFilter (FRAME_X_DISPLAY (f), img->picture, FilterBest, 0, 0); + XRenderSetPictureTransform (FRAME_X_DISPLAY (f), img->picture, &tmat); + + img->width = width; + img->height = height; + } +#endif + } +} +#endif +#endif /* HAVE_IMAGEMAGICK || HAVE_XRENDER || HAVE_NS */ + /* Return the id of image with Lisp specification SPEC on frame F. SPEC must be a valid Lisp image specification (see valid_image_p). */ @@ -1802,6 +1959,9 @@ lookup_image (struct frame *f, Lisp_Object spec) `:background COLOR'. */ Lisp_Object ascent, margin, relief, bg; int relief_bound; +#ifdef HAVE_NATIVE_SCALING + x_set_image_size (f, img); +#endif ascent = image_spec_value (spec, QCascent, NULL); if (FIXNUMP (ascent)) @@ -1975,13 +2135,21 @@ x_check_image_size (XImagePtr ximg, int width, int height) should indicate the bit depth of the image. */ static bool +#ifdef HAVE_XRENDER +x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, + XImagePtr *ximg, Pixmap *pixmap, Picture *picture) +#else x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, XImagePtr *ximg, Pixmap *pixmap) +#endif { #ifdef HAVE_X_WINDOWS Display *display = FRAME_X_DISPLAY (f); Drawable drawable = FRAME_X_DRAWABLE (f); Screen *screen = FRAME_X_SCREEN (f); +#ifdef HAVE_XRENDER + int event_basep, error_basep; +#endif eassert (input_blocked_p ()); @@ -2018,6 +2186,21 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, return 0; } +#ifdef HAVE_XRENDER + if (picture && XRenderQueryExtension (display, &event_basep, &error_basep)) + { + XRenderPictFormat *format; + XRenderPictureAttributes attr; + + /* FIXME: Do we need to handle all possible bit depths? */ + format = XRenderFindStandardFormat (display, + depth > 24 ? PictStandardARGB32 + : depth > 8 ? PictStandardRGB24 + : PictStandardA8); + *picture = XRenderCreatePicture (display, *pixmap, format, 0, &attr); + } +#endif + return 1; #endif /* HAVE_X_WINDOWS */ @@ -2163,7 +2346,8 @@ x_put_x_image (struct frame *f, XImagePtr ximg, Pixmap pixmap, int width, int he eassert (input_blocked_p ()); gc = XCreateGC (FRAME_X_DISPLAY (f), pixmap, 0, NULL); - XPutImage (FRAME_X_DISPLAY (f), pixmap, gc, ximg, 0, 0, 0, 0, width, height); + XPutImage (FRAME_X_DISPLAY (f), pixmap, gc, ximg, 0, 0, 0, 0, + ximg->width, ximg->height); XFreeGC (FRAME_X_DISPLAY (f), gc); #endif /* HAVE_X_WINDOWS */ @@ -2192,7 +2376,11 @@ image_create_x_image_and_pixmap (struct frame *f, struct image *img, eassert ((!mask_p ? img->pixmap : img->mask) == NO_PIXMAP); return x_create_x_image_and_pixmap (f, width, height, depth, ximg, - !mask_p ? &img->pixmap : &img->mask); + !mask_p ? &img->pixmap : &img->mask +#ifdef HAVE_XRENDER + , !mask_p ? &img->picture : &img->mask_picture +#endif + ); } /* Put X image XIMG into image IMG on frame F, as a mask if and only @@ -8101,105 +8289,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 *); @@ -9816,6 +9905,25 @@ DEFUN ("lookup-image", Flookup_image, Slookup_image, 1, 1, 0, Initialization ***********************************************************************/ +DEFUN ("image-scaling-p", Fimage_scaling_p, Simage_scaling_p, 0, 1, 0, + doc: /* Test whether FRAME supports resizing images. +Return t if FRAME supports native scaling, nil otherwise. */) + (Lisp_Object frame) +{ +#ifdef HAVE_NS + return Qt; +#elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER) + int event_basep, error_basep; + + if (XRenderQueryExtension + (FRAME_X_DISPLAY (decode_window_system_frame (frame)), + &event_basep, &error_basep)) + return Qt; +#endif + + return Qnil; +} + DEFUN ("init-image-library", Finit_image_library, Sinit_image_library, 1, 1, 0, doc: /* Initialize image library implementing image type TYPE. Return non-nil if TYPE is a supported image type. @@ -10058,6 +10166,8 @@ non-numeric, there is no explicit limit on the size of images. */); defsubr (&Slookup_image); #endif + defsubr (&Simage_scaling_p); + DEFVAR_BOOL ("cross-disabled-images", cross_disabled_images, doc: /* Non-nil means always draw a cross over disabled images. Disabled images are those having a `:conversion disabled' property. diff --git a/src/nsimage.m b/src/nsimage.m index 7879c5891d..f16910de08 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; @@ -151,6 +149,12 @@ Updated by Christian Limpach (chris@nice.ch) return [(id)img size].height; } +void +ns_image_set_size (void *img, int width, int height) +{ + [(EmacsImage *)img setSize:NSMakeSize (width, height)]; +} + unsigned long ns_get_pixel (void *img, int x, int y) { @@ -524,66 +528,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 089cbccbf0..78ce608554 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 @@ -1197,6 +1196,7 @@ extern bool ns_load_image (struct frame *f, struct image *img, Lisp_Object spec_file, Lisp_Object spec_data); extern int ns_image_width (void *img); extern int ns_image_height (void *img); +extern void ns_image_set_size (void *img, int width, int height); extern unsigned long ns_get_pixel (void *img, int x, int y); extern void ns_put_pixel (void *img, int x, int y, unsigned long argb); extern void ns_set_alpha (void *img, int x, int y, unsigned char a); diff --git a/src/nsterm.m b/src/nsterm.m index 016c044760..cbb2d2a5ce 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); } diff --git a/src/xterm.c b/src/xterm.c index e9cebcebba..fbbf61d320 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -38,11 +38,6 @@ along with GNU Emacs. If not, see . */ #include #endif -/* Using Xft implies that XRender is available. */ -#ifdef HAVE_XFT -#include -#endif - #ifdef HAVE_XDBE #include #endif @@ -2976,6 +2971,46 @@ 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 + if (s->img->picture) + { + Picture destination; + XRenderPictFormat *default_format; + XRenderPictureAttributes attr; + + /* FIXME: Should we do this each time or would it make sense to + store destination in the frame struct? */ + default_format = XRenderFindVisualFormat (s->display, + DefaultVisual (s->display, 0)); + destination = XRenderCreatePicture (s->display, dest, + default_format, 0, &attr); + + /* FIXME: It may make sense to use PictOpSrc instead of + PictOpOver, as I don't know if we care about alpha values too + much here. */ + XRenderComposite (s->display, PictOpOver, + s->img->picture, s->img->mask_picture, destination, + srcX, srcY, + srcX, srcY, + dstX, dstY, + width, height); + + XRenderFreePicture (s->display, destination); + } + else +#endif + XCopyArea (s->display, s->img->pixmap, + dest, s->gc, + srcX, srcY, + width, height, dstX, dstY); +} + + /* Draw foreground of image glyph string S. */ static void @@ -3007,6 +3042,7 @@ x_draw_image_foreground (struct glyph_string *s) trust on the shape extension to be available (XShapeCombineRegion). So, compute the rectangle to draw manually. */ + /* FIXME: Do we need to do this when using XRender compositing? */ unsigned long mask = (GCClipMask | GCClipXOrigin | GCClipYOrigin | GCFunction); XGCValues xgcv; @@ -3024,10 +3060,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 +3073,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 -- 2.19.1 -- Alan Third