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 v2] Add native image scaling (bug#33587) Date: Wed, 2 Jan 2019 21:12:41 +0000 Message-ID: <20190102211241.GA53734@breton.holly.idiocy.org> References: <8336qb3upt.fsf@gnu.org> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Trace: blaine.gmane.org 1546463467 24711 195.159.176.226 (2 Jan 2019 21:11:07 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Wed, 2 Jan 2019 21:11:07 +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 Wed Jan 02 22:11:03 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 1genn1-0006EH-DM for ged-emacs-devel@m.gmane.org; Wed, 02 Jan 2019 22:10:59 +0100 Original-Received: from localhost ([127.0.0.1]:47394 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1genp7-0000Sp-Jg for ged-emacs-devel@m.gmane.org; Wed, 02 Jan 2019 16:13:09 -0500 Original-Received: from eggs.gnu.org ([208.118.235.92]:58628) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1genoy-0000Se-LH for emacs-devel@gnu.org; Wed, 02 Jan 2019 16:13:04 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1genou-00049Z-GM for emacs-devel@gnu.org; Wed, 02 Jan 2019 16:13:00 -0500 Original-Received: from mail-wm1-x32d.google.com ([2a00:1450:4864:20::32d]:56270) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1genol-0003ag-UY; Wed, 02 Jan 2019 16:12:50 -0500 Original-Received: by mail-wm1-x32d.google.com with SMTP id y139so27574113wmc.5; Wed, 02 Jan 2019 13:12:46 -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:content-transfer-encoding:in-reply-to :user-agent; bh=CMpaukxrmR99HFqgiBkGieSD3GSOLQMJixElc94fuKk=; b=Ypg2XDspvZipm6g9pGbvjMn+oa8ksZ18AZLb7Ao4CNqsrL8RrbI+U3CljwUJdchakc w2PfWC04fzzF9AqiAEzfxCFMZDKd5xpYfFSQx/8GjL5lOMZ5h7NETUuYHaR9H3PKeKYR JXph2dHxSWMGXyfGVB6cDccff4tIBkhlD226u0k/TyFkHcvt+WOBupEX0UABsjnnf8JR 659wRPWRUeQJvNQ3q05peHRWvERzWGgtMSXYB+AgWCUTAyX0GO33qvDhM9B5Jsydb+4s vRTLlXa9igutfr00Huw0hlQJdIM59nLjTGac7Hb+r3NMYY+6SNkRI+5ZMc/gLVfQgsuG BYEQ== 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 :content-transfer-encoding:in-reply-to:user-agent; bh=CMpaukxrmR99HFqgiBkGieSD3GSOLQMJixElc94fuKk=; b=MxctmSiMHkUw7q2mtpqcpb3M2ak4TbKyM/G+rpw9DtvlLCU+zQKB+W1llmwZWWOF2r jN2UlqRCvX8ahBWk9+k9j7g3vJ19mHqTcE5Qb2s72z5yTZFWQvzpte+WsJdwhSMdyNJ/ 8zjG692v09Umc/D9SSjUZ3rkeqUiBRvdaGeO2PoLB5r5mSfuVSW2pQuxQsxJpV6gCKCi qHAvc+q8b4uUgU3SoG0riAr6DGQH8ab3ZesVs1GCcke7XHZwIjL3qyx7PdjW4tBP/gMF bq3y8O/rA0u31J5l7PVnxOr/9SP0L5P9QEM1YbfcrnKsSC4pdHdpu7uTwzj4XE4Nj0As IkdA== X-Gm-Message-State: AA+aEWbvTsHAQzwBrVEVjLhOC7RlZks5equnrW66WTpomLMoTdn6rvSr hzky/mgeYO+BUs3pMyB7WIibcACvsn0= X-Google-Smtp-Source: AFSGD/XIUkQ8yP06oPNwLFvq/BmEIMrcbwciMMDFhWyOd1YdzGOniX+s0jawxocdda6ndbof8odfWw== X-Received: by 2002:a1c:9c0a:: with SMTP id f10mr37103426wme.73.1546463564396; Wed, 02 Jan 2019 13:12:44 -0800 (PST) Original-Received: from breton.holly.idiocy.org (ip6-2001-08b0-03f8-8129-e406-fb25-76e1-0b15.holly.idiocy.org. [2001:8b0:3f8:8129:e406:fb25:76e1:b15]) by smtp.gmail.com with ESMTPSA id y8sm40568295wmg.13.2019.01.02.13.12.43 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Wed, 02 Jan 2019 13:12:43 -0800 (PST) Content-Disposition: inline In-Reply-To: <8336qb3upt.fsf@gnu.org> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::32d 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:232106 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. * doc/lispref/display.texi (Image Descriptors): Document image-scaling-p and add resizing descriptors. (ImageMagick Images): Remove resizing descriptors. --- I think this is the final version. I would appreciate if someone who knows their way around image handling would be able to test it. I’m particularly concerned that I’ve probably broken masks. I can’t find any examples of how to use them online, and they don’t work at all in NS, so I don’t know if they get any use. configure.ac | 14 +- doc/lispref/display.texi | 88 ++++++----- etc/NEWS | 6 + lisp/image.el | 4 +- src/Makefile.in | 3 +- src/dispextern.h | 13 ++ src/image.c | 312 ++++++++++++++++++++++++++------------- src/nsimage.m | 68 +-------- src/nsterm.h | 2 +- src/nsterm.m | 8 - src/xterm.c | 58 ++++++-- 11 files changed, 343 insertions(+), 233 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/doc/lispref/display.texi b/doc/lispref/display.texi index 19424ecc7e..f311cd73df 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -5112,6 +5112,45 @@ Image Descriptors @var{relief} is negative, shadows are drawn so that the image appears as a pressed button; otherwise, it appears as an unpressed button. +@item :width @var{width}, :height @var{height} +The @code{:width} and @code{:height} keywords are used for scaling the +image. If only one of them is specified, the other one will be +calculated so as to preserve the aspect ratio. If both are specified, +aspect ratio may not be preserved. + +@item :max-width @var{max-width}, :max-height @var{max-height} +The @code{:max-width} and @code{:max-height} keywords are used for +scaling if the size of the image of the image exceeds these values. +If @code{:width} is set it will have precedence over @code{max-width}, +and if @code{:height} is set it will have precedence over +@code{max-height}, but you can otherwise mix these keywords as you +wish. @code{:max-width} and @code{:max-height} will always preserve +the aspect ratio. + +If both @code{:width} and @code{:max-height} has been set (but +@code{:height} has not been set), then @code{:max-height} will have +precedence. The same is the case for the opposite combination: The +``max'' keyword has precedence. That is, if you have a 200x100 image +and specify that @code{:width} should be 400 and @code{:max-height} +should be 150, you'll end up with an image that is 300x150: Preserving +the aspect ratio and not exceeding the ``max'' setting. This +combination of parameters is a useful way of saying ``display this +image as large as possible, but no larger than the available display +area''. + +@item :scale @var{scale} +This should be a number, where values higher than 1 means to increase +the size, and lower means to decrease the size. For instance, a value +of 0.25 will make the image a quarter size of what it originally was. +If the scaling makes the image larger than specified by +@code{:max-width} or @code{:max-height}, the resulting size will not +exceed those two values. If both @code{:scale} and +@code{:height}/@code{:width} are specified, the height/width will be +adjusted by the specified scaling factor. + +@item :index @var{frame} +@xref{Multi-Frame Images}. + @item :conversion @var{algorithm} This specifies a conversion algorithm that should be applied to the image before it is displayed; the value, @var{algorithm}, specifies @@ -5251,6 +5290,16 @@ Image Descriptors (@pxref{Input Focus}). @end defun +@defun image-scaling-p &optional frame +This function returns @code{t} if @var{frame} supports image scaling. +@var{frame} @code{nil} or omitted means to use the selected frame +(@pxref{Input Focus}). + +If image scaling is not supported, @code{:width}, @code{:height}, +@code{:scale}, @code{:max-width} and @code{:max-height} will only be +usable through ImageMagick, if available (@pxref{ImageMagick Images}). +@end defun + @node XBM Images @subsection XBM Images @cindex XBM @@ -5387,42 +5436,6 @@ ImageMagick Images supports transparency. If the value is @code{nil}, it defaults to the frame's background color. -@item :width @var{width}, :height @var{height} -The @code{:width} and @code{:height} keywords are used for scaling the -image. If only one of them is specified, the other one will be -calculated so as to preserve the aspect ratio. If both are specified, -aspect ratio may not be preserved. - -@item :max-width @var{max-width}, :max-height @var{max-height} -The @code{:max-width} and @code{:max-height} keywords are used for -scaling if the size of the image of the image exceeds these values. -If @code{:width} is set it will have precedence over @code{max-width}, -and if @code{:height} is set it will have precedence over -@code{max-height}, but you can otherwise mix these keywords as you -wish. @code{:max-width} and @code{:max-height} will always preserve -the aspect ratio. - -If both @code{:width} and @code{:max-height} has been set (but -@code{:height} has not been set), then @code{:max-height} will have -precedence. The same is the case for the opposite combination: The -``max'' keyword has precedence. That is, if you have a 200x100 image -and specify that @code{:width} should be 400 and @code{:max-height} -should be 150, you'll end up with an image that is 300x150: Preserving -the aspect ratio and not exceeding the ``max'' setting. This -combination of parameters is a useful way of saying ``display this -image as large as possible, but no larger than the available display -area''. - -@item :scale @var{scale} -This should be a number, where values higher than 1 means to increase -the size, and lower means to decrease the size. For instance, a value -of 0.25 will make the image a quarter size of what it originally was. -If the scaling makes the image larger than specified by -@code{:max-width} or @code{:max-height}, the resulting size will not -exceed those two values. If both @code{:scale} and -@code{:height}/@code{:width} are specified, the height/width will be -adjusted by the specified scaling factor. - @item :format @var{type} The value, @var{type}, should be a symbol specifying the type of the image data, as found in @code{image-format-suffixes}. This is used @@ -5431,9 +5444,6 @@ ImageMagick Images @item :rotation @var{angle} Specifies a rotation angle in degrees. - -@item :index @var{frame} -@xref{Multi-Frame Images}. @end table @node SVG Images 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..677689cae2 100644 --- a/src/image.c +++ b/src/image.c @@ -408,8 +408,13 @@ x_destroy_all_bitmaps (Display_Info *dpyinfo) dpyinfo->bitmaps_last = 0; } +#ifndef HAVE_XRENDER +/* Required for the definition of x_create_x_image_and_pixmap below. */ +typedef void Picture; +#endif + static bool x_create_x_image_and_pixmap (struct frame *, int, int, int, - XImagePtr *, Pixmap *); + XImagePtr *, Pixmap *, Picture *); static void x_destroy_x_image (XImagePtr ximg); #ifdef HAVE_NTGUI @@ -472,7 +477,8 @@ 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, NULL); unblock_input (); if (!result) @@ -1011,6 +1017,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 +1760,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 +1956,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)) @@ -1976,12 +2133,15 @@ x_check_image_size (XImagePtr ximg, int width, int height) static bool x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth, - XImagePtr *ximg, Pixmap *pixmap) + XImagePtr *ximg, Pixmap *pixmap, Picture *picture) { #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 +2178,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 +2338,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 +2368,13 @@ 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 +#else + NULL +#endif + ); } /* Put X image XIMG into image IMG on frame F, as a mask if and only @@ -8101,105 +8283,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 +9899,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 +10160,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