From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: stephen.berman@gmx.net, juri@linkov.net, bug-gnu-emacs@gnu.org Resent-Date: Thu, 07 Mar 2024 06:09:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: 69602@debbugs.gnu.org Cc: Stephen Berman , Juri Linkov X-Debbugs-Original-To: bug-gnu-emacs@gnu.org X-Debbugs-Original-Xcc: Stephen Berman , Juri Linkov Received: via spool by submit@debbugs.gnu.org id=B.17097916848045 (code B ref -1); Thu, 07 Mar 2024 06:09:02 +0000 Received: (at submit) by debbugs.gnu.org; 7 Mar 2024 06:08:04 +0000 Received: from localhost ([127.0.0.1]:52298 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri6vD-00025g-MH for submit@debbugs.gnu.org; Thu, 07 Mar 2024 01:08:04 -0500 Received: from lists.gnu.org ([209.51.188.17]:39178) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri6vA-00025G-SK for submit@debbugs.gnu.org; Thu, 07 Mar 2024 01:08:01 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ri6ue-0002KN-LC for bug-gnu-emacs@gnu.org; Thu, 07 Mar 2024 01:07:29 -0500 Received: from out-187.mta0.migadu.com ([91.218.175.187]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ri6uc-0007gw-0Z for bug-gnu-emacs@gnu.org; Thu, 07 Mar 2024 01:07:28 -0500 X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1709791640; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6iyDiCXdKMIDhabY5HAMfUpbIcBNW+2dnQI/8J9Xdas=; b=T1MgK+/Ug6ReujtODDWLNv8h5QU5I0M1LHnsYsS//gkauJkJWGlOLsztQqAVO6AxBlXFIX SrwkBjGDWOK9Hc3o3tbWQL+EbnNtoff/IgLftUFF244oEhQOEv9lKsqIgcdUw7Q4c+aZmb NRBjnhyZUMd8kx0uZecUjtnvWuj9uig= From: Joseph Turner Date: Wed, 06 Mar 2024 21:37:50 -0800 Message-ID: <87msramv72.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: text/plain Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Received-SPF: pass client-ip=91.218.175.187; envelope-from=joseph@breatheoutbreathe.in; helo=out-187.mta0.migadu.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: -1.4 (-) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -2.4 (--) Currently, when running `image-increase-size' or `image-decrease-size' on an image with a :map property, the image scales but the image map does not. For example, run the following snippet: (with-current-buffer (get-buffer-create "*image-properties-test*") (let ((svg "\n\n\n\n\n= \norggraphview\n\n\n\na\n\= n\nHover me!\n\n\n\n\n\n") (map '(((circle (85 . 85) . 80) "1" (help-echo "Surprise!")))) (inhibit-read-only t)) (erase-buffer) (insert-image (create-image svg 'svg t :map map)) (goto-char (point-min)) (pop-to-buffer (current-buffer)))) Hovering the circle alters the pointer style and displays the tooltip. Now run `M-x image-increase-size' or press "i +". While the image becomes larger, the area which activates the tooltip remains the same. See earlier discussion here: https://yhetil.org/emacs-devel/87r0gng41l.fsf@ushin.org/T/#t While a proper solution perhaps belongs on the C side, the following workaround adds an :unscaled-map property to images and sets :map according to :unscaled-map and :scale whenever :scale changes. This workaround does not (yet) handle :rotation. --8<---------------cut here---------------start------------->8--- (defun image--scale-map (map factor) "Scale MAP by FACTOR, destructively modifying it." (unless (=3D 1 factor) (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) (pcase-exhaustive type ('rect (setf (caar coords) (round (* (caar coords) factor))) (setf (cdar coords) (round (* (cdar coords) factor))) (setf (cadr coords) (round (* (cadr coords) factor))) (setf (cddr coords) (round (* (cddr coords) factor)))) ('circle (setf (caar coords) (round (* (caar coords) factor))) (setf (cdar coords) (round (* (cdar coords) factor))) (setf (cdr coords) (round (* (cdr coords) factor)))) ('poly (dotimes (i (length coords)) (aset coords i (round (* (aref coords i) factor)))))))) map) (defun image--create-image-add-unscaled-map (orig-fun file-or-data &optional type data-p &rest props) "Add :unscaled-map property to image returned by ORIG-FUN and return it. Intended to be used as :around advice for `create-image'." (let ((image (apply orig-fun file-or-data type data-p props))) (when-let ((map (image-property image :map))) (setq image (nconc image (list :unscaled-map (copy-tree map t)))) (when-let* ((props-scale (plist-get props :scale)) ((numberp props-scale))) (setf (image-property image :unscaled-map) (image--scale-map (image-property image :unscaled-map) (/ 1.0 props-scale))))) image)) (advice-add #'create-image :around #'image--create-image-add-unscaled-map) (defun image--change-size-scale-map (_factor &optional position) "Scale :map property of image at point to fit its :scale. Intended to be used as :after advice for `image--change-size'." (when-let* ((image (image--get-imagemagick-and-warn position)) (map (image-property image :map)) (unscaled-map (image-property image :unscaled-map)) (scale (image-property image :scale))) (setf (image-property image :map) ;; TODO: Instead of copying `:unscaled-map', reuse the :map vecto= r? (image--scale-map (copy-tree unscaled-map t) scale)))) (advice-add #'image--change-size :after #'image--change-size-scale-map) --8<---------------cut here---------------end--------------->8--- Thank you! Joseph In GNU Emacs 29.1 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.37, cairo version 1.16.0) Windowing system distributor 'The X.Org Foundation', version 11.0.12101007 System Description: Debian GNU/Linux 12 (bookworm) From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 07 Mar 2024 07:05:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Joseph Turner Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.170979508913983 (code B ref 69602); Thu, 07 Mar 2024 07:05:01 +0000 Received: (at 69602) by debbugs.gnu.org; 7 Mar 2024 07:04:49 +0000 Received: from localhost ([127.0.0.1]:52356 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri7o9-0003dS-4B for submit@debbugs.gnu.org; Thu, 07 Mar 2024 02:04:49 -0500 Received: from eggs.gnu.org ([209.51.188.92]:50130) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri7o7-0003dF-2w for 69602@debbugs.gnu.org; Thu, 07 Mar 2024 02:04:48 -0500 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ri7nU-0000Ns-P2; Thu, 07 Mar 2024 02:04:08 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-Version:References:Subject:In-Reply-To:To:From: Date; bh=uQxCLQTEz7Ssg4ccavf5IHZyg1DqxxQlFX2lvtAHGiU=; b=idVBkOSvbevV7hWye/hb wvIbG1jz29+3lNd3cwJmoOx2UmBxdHtXebLBeXAKe4FpztryXKrLFKtUEkZqp9ZeARlTGYEInxWRA KkeDfKlNuH/tUbjI+NAcnXkuKwzV/NFIuXLwFfJuUUtQuqcBuyuBPTqL92hh56/0ME2qq0Vi40qEr t2sIyw1BBIfL9C/Owex4mxRYLTyTH5+b5CqYwguTXtu6ML9c4XXp4HNRDQY1ApUyUNQDXnq+FuqyC jdlIA1vEb+iaS5SbZf3Hlkyt9lS1YKjllaJsITW4nAm87Sz+KpTvuYpRCcxKMwo/C+CThHU+roq4w B5ZAqD4u7Qy7kw==; Date: Thu, 07 Mar 2024 09:04:06 +0200 Message-Id: <86jzmejzfd.fsf@gnu.org> From: Eli Zaretskii In-Reply-To: <87msramv72.fsf@breatheoutbreathe.in> (bug-gnu-emacs@gnu.org) References: <87msramv72.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: text/plain; charset=gb2312 Content-Transfer-Encoding: quoted-printable X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > Cc: Stephen Berman , Juri Linkov > Date: Wed, 06 Mar 2024 21:37:50 -0800 > From: Joseph Turner via "Bug reports for GNU Emacs, > the Swiss army knife of text editors" >=20 > Currently, when running `image-increase-size' or `image-decrease-size' > on an image with a :map property, the image scales but the image map > does not. For example, run the following snippet: >=20 > (with-current-buffer (get-buffer-create "*image-properties-test*") > (let ((svg "\n\n\n\n= \n\norggraphview\n\n\n\na\n\n\nHover me!\n\n\n\n\n\n") > (map '(((circle (85 . 85) . 80) "1" (help-echo "Surprise!")))) > (inhibit-read-only t)) > (erase-buffer) > (insert-image (create-image svg 'svg t :map map)) > (goto-char (point-min)) > (pop-to-buffer (current-buffer)))) >=20 > Hovering the circle alters the pointer style and displays the tooltip. >=20 > Now run `M-x image-increase-size' or press "i +". While the image > becomes larger, the area which activates the tooltip remains the same. >=20 > See earlier discussion here: >=20 > https://yhetil.org/emacs-devel/87r0gng41l.fsf@ushin.org/T/#t >=20 > While a proper solution perhaps belongs on the C side, the following > workaround adds an :unscaled-map property to images and sets :map > according to :unscaled-map and :scale whenever :scale changes. The ELisp manual says about :map: Note that the map's coordinates should reflect the displayed image after all transforms have been done (rotation, scaling and so on), and also note that Emacs (by default) performs auto-scaling of images, so to make things match up, you should either specify =A1=AE:scale 1.0=A1=AF when creating the image, or use the result of =A1=AEimage-compute-scaling-factor=A1=AF to compute the elements of th= e map. Can this technique help? From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 07 Mar 2024 07:40:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.170979718417064 (code B ref 69602); Thu, 07 Mar 2024 07:40:01 +0000 Received: (at 69602) by debbugs.gnu.org; 7 Mar 2024 07:39:44 +0000 Received: from localhost ([127.0.0.1]:52370 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri8Lw-0004RA-EQ for submit@debbugs.gnu.org; Thu, 07 Mar 2024 02:39:44 -0500 Received: from out-177.mta1.migadu.com ([95.215.58.177]:24861) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri8Lu-0004Qw-1y for 69602@debbugs.gnu.org; Thu, 07 Mar 2024 02:39:42 -0500 References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1709797113; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=HIOVWfFDUbDLQUiJSzRY1cpxpAZV3ggMn0ecUQdj4Fo=; b=bqkP2n6yYOnicZ3qIUg9W9GC+7ku3R/evxEe5ApoXa27uOvCKSIxcyk8Ks1jTJEn3VtVTP v9L2R0MWgTQ/dO5PzfOxFSDHOo7k2Uh9P5AEuwlwI66lBPPfyOY9kFkwa2NZK8iYaCnJ8+ oAKViehIkAgjx9o5BvsFJm/l2lu7wGU= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Joseph Turner Date: Wed, 06 Mar 2024 23:14:21 -0800 In-reply-to: <86jzmejzfd.fsf@gnu.org> Message-ID: <87a5namqyz.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT X-Spam-Score: -0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) Eli Zaretskii writes: > The ELisp manual says about :map: > > Note that the map's coordinates should reflect the displayed image > after all transforms have been done (rotation, scaling and so on), > and also note that Emacs (by default) performs auto-scaling of > images, so to make things match up, you should either specify > =E2=80=98:scale 1.0=E2=80=99 when creating the image, or use the res= ult of > =E2=80=98image-compute-scaling-factor=E2=80=99 to compute the elemen= ts of the map. > > Can this technique help? Thank you for your help! When the user runs `image-increase-size', where should third-party code recompute :map to fit the new image scale? There's no `image-after-change-size-hook' nor `image-after-rotate-hook'. Joseph From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 07 Mar 2024 07:57:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Joseph Turner Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.170979818518880 (code B ref 69602); Thu, 07 Mar 2024 07:57:02 +0000 Received: (at 69602) by debbugs.gnu.org; 7 Mar 2024 07:56:25 +0000 Received: from localhost ([127.0.0.1]:52380 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri8c5-0004uR-Cm for submit@debbugs.gnu.org; Thu, 07 Mar 2024 02:56:25 -0500 Received: from eggs.gnu.org ([209.51.188.92]:40538) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri8c3-0004uF-IT for 69602@debbugs.gnu.org; Thu, 07 Mar 2024 02:56:24 -0500 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ri8bR-0002Kq-9U; Thu, 07 Mar 2024 02:55:45 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=/FGb5XUZoQoK4Ol3gXxh3BeSFkENrUIqiuF0zRTfsKw=; b=NejuQ73n2UZlyoXQkO0Y sr8tKhpXPK0/g5cjama8U+2vCuA7EpmkGd68p8vku5h4qt1J92yRlEOs47qE//NcDNh+VZ6grg1rK snbjMo2URhiaNcxr83ecOaS19USnxwRCaPRiXhyv6wzkQ0VWxtPngKu5l74xhtHKYX1fHOpCTxa5z UYeqbqZtxp6t+HikQasWSJj3KlzFm8rv9aJcAIA8gjZd1Nv7GQutEJLtJ9zKysIqcSxmh14hdBoyC rzw5s8hh67jB+bFrVgZEUSbDeIzMdKHWtGvYfvYKIBbyGd4SXnm9usODwQfqdPiFR5KXO41d+Bz7l ESOdEM5hjkmyMA==; Date: Thu, 07 Mar 2024 09:55:42 +0200 Message-Id: <86cys63281.fsf@gnu.org> From: Eli Zaretskii In-Reply-To: <87a5namqyz.fsf@breatheoutbreathe.in> (message from Joseph Turner on Wed, 06 Mar 2024 23:14:21 -0800) References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Joseph Turner > Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net > Date: Wed, 06 Mar 2024 23:14:21 -0800 > > Eli Zaretskii writes: > > The ELisp manual says about :map: > > > > Note that the map's coordinates should reflect the displayed image > > after all transforms have been done (rotation, scaling and so on), > > and also note that Emacs (by default) performs auto-scaling of > > images, so to make things match up, you should either specify > > ‘:scale 1.0’ when creating the image, or use the result of > > ‘image-compute-scaling-factor’ to compute the elements of the map. > > > > Can this technique help? > > Thank you for your help! > > When the user runs `image-increase-size', where should third-party code > recompute :map to fit the new image scale? > > There's no `image-after-change-size-hook' nor `image-after-rotate-hook'. I think the idea is to define the value of :map such that it runs image-compute-scaling-factor as part of computing the coordinates of the map. From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 07 Mar 2024 08:20:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.170979954821019 (code B ref 69602); Thu, 07 Mar 2024 08:20:02 +0000 Received: (at 69602) by debbugs.gnu.org; 7 Mar 2024 08:19:08 +0000 Received: from localhost ([127.0.0.1]:52395 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri8y4-0005Sx-0w for submit@debbugs.gnu.org; Thu, 07 Mar 2024 03:19:08 -0500 Received: from out-187.mta1.migadu.com ([95.215.58.187]:11747) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri8y1-0005ST-U5 for 69602@debbugs.gnu.org; Thu, 07 Mar 2024 03:19:06 -0500 References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1709799478; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=zHtFHzageNbT3UdYR8/pWQJJbRIQoZAVBPwRAcl1CX0=; b=k3CEuUPKyRvTvovEuPYicCseUNGnT3jycsPqvPjJx/+QkqXTTJOG4+sDhpE4I0TF5Ctc2I UtHi4HSdAaW/C2lYQ1TOaiYJHvUN0W0fNth0CdnUPJdTLjggJmC7NwGaj667SeDivnmFH5 x5W80LvqaMvmyM9EIr/aSA6A1+MrNnA= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Joseph Turner Date: Thu, 07 Mar 2024 00:08:57 -0800 In-reply-to: <86cys63281.fsf@gnu.org> Message-ID: <87wmqelakx.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT X-Spam-Score: -0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) Eli Zaretskii writes: >> From: Joseph Turner >> Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net >> Date: Wed, 06 Mar 2024 23:14:21 -0800 >> >> Eli Zaretskii writes: >> > The ELisp manual says about :map: >> > >> > Note that the map's coordinates should reflect the displayed image >> > after all transforms have been done (rotation, scaling and so on), >> > and also note that Emacs (by default) performs auto-scaling of >> > images, so to make things match up, you should either specify >> > =E2=80=98:scale 1.0=E2=80=99 when creating the image, or use the = result of >> > =E2=80=98image-compute-scaling-factor=E2=80=99 to compute the ele= ments of the map. >> > >> > Can this technique help? >> >> Thank you for your help! >> >> When the user runs `image-increase-size', where should third-party code >> recompute :map to fit the new image scale? >> >> There's no `image-after-change-size-hook' nor `image-after-rotate-hook'. > > I think the idea is to define the value of :map such that it runs > image-compute-scaling-factor as part of computing the coordinates of > the map. Sorry, I don't understand. When creating an image, we set its :map property according to the return value of `image-compute-scaling-factor'. Once the image is inserted into the buffer, the user may run `image-increase-size' or `image-rotate', which changes how the image is displayed but not its :map. Now, we need to rerun `image-compute-scaling-factor' and recompute :map. However, there is no hook which runs after the user runs those commands, so AFAICT there's no way for our code to know when to recompute :map. Thanks! Joseph From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 07 Mar 2024 09:29:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Joseph Turner Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.170980372417160 (code B ref 69602); Thu, 07 Mar 2024 09:29:02 +0000 Received: (at 69602) by debbugs.gnu.org; 7 Mar 2024 09:28:44 +0000 Received: from localhost ([127.0.0.1]:52552 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riA3P-0004Sh-HL for submit@debbugs.gnu.org; Thu, 07 Mar 2024 04:28:43 -0500 Received: from eggs.gnu.org ([209.51.188.92]:53598) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riA3M-0004SD-Fx for 69602@debbugs.gnu.org; Thu, 07 Mar 2024 04:28:41 -0500 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1riA2k-00062V-G5; Thu, 07 Mar 2024 04:28:03 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=rCQasw3NBQ9OUPIfberVHYN0IUb7GREN2BVpK3DumXo=; b=XH1K8WRaJ9lsxASEaIZw oBQzL9A4Dd7ElvsGxQ1m5poQvthpu8w8+Vh0c5VbqA79PJhlV9skTu9wc1dbhp6QO2s4WiKSssSY1 kB3u4DQZ/G0uS33gMG2xJfHPrzqbTpsrCgHOJ8PAnQqNjjYLThuTmwSeI80qWYwiKQEFzCWRSwwmD Ou8UrBKzA3TYhig00tw16PzAZNCW8BvGNRCrNUwXClEefjZmplTah92GopW3VVKHg0UphecguJ3xc Q9Gnfl/5xsvKqjWH5bu4cOGV+tV1mQBy0/vkQJJGtZoikqRNW6kYchgY78DWm6Wgl1SnhkO2uxdgg lOaeSf5NZOFxwQ==; Date: Thu, 07 Mar 2024 11:27:53 +0200 Message-Id: <86bk7q2xye.fsf@gnu.org> From: Eli Zaretskii In-Reply-To: <87wmqelakx.fsf@breatheoutbreathe.in> (message from Joseph Turner on Thu, 07 Mar 2024 00:08:57 -0800) References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Joseph Turner > Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net > Date: Thu, 07 Mar 2024 00:08:57 -0800 > > > Eli Zaretskii writes: > > >> > Note that the map's coordinates should reflect the displayed image > >> > after all transforms have been done (rotation, scaling and so on), > >> > and also note that Emacs (by default) performs auto-scaling of > >> > images, so to make things match up, you should either specify > >> > ‘:scale 1.0’ when creating the image, or use the result of > >> > ‘image-compute-scaling-factor’ to compute the elements of the map. > >> > > >> > Can this technique help? > >> > >> Thank you for your help! > >> > >> When the user runs `image-increase-size', where should third-party code > >> recompute :map to fit the new image scale? > >> > >> There's no `image-after-change-size-hook' nor `image-after-rotate-hook'. > > > > I think the idea is to define the value of :map such that it runs > > image-compute-scaling-factor as part of computing the coordinates of > > the map. > > Sorry, I don't understand. I hoped :map allows its value to be a form that is evaluated when the image is being processed, in which case that form could call image-compute-scaling-factor when it produces the coordinates. If that doesn't work, then... > When creating an image, we set its :map property according to the return > value of `image-compute-scaling-factor'. Once the image is inserted into > the buffer, the user may run `image-increase-size' or `image-rotate', > which changes how the image is displayed but not its :map. > > Now, we need to rerun `image-compute-scaling-factor' and recompute :map. > However, there is no hook which runs after the user runs those commands, > so AFAICT there's no way for our code to know when to recompute :map. ...AFAIU, when an image is rescaled, we call image-transform-properties to produce the updated image properties. So I guess you'd like that function to recompute the coordinates in :map according to the transform? IOW, I don't understand why you think the problem can only be solved in C: AFAIK almost all of the machinery that performs image transforms is implemented in Lisp, and each time an image is rescaled, we basically re-process the image descriptor anew. From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 08 Mar 2024 00:08:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.17098564578650 (code B ref 69602); Fri, 08 Mar 2024 00:08:01 +0000 Received: (at 69602) by debbugs.gnu.org; 8 Mar 2024 00:07:37 +0000 Received: from localhost ([127.0.0.1]:55504 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riNlu-0002FQ-N3 for submit@debbugs.gnu.org; Thu, 07 Mar 2024 19:07:37 -0500 Received: from out-174.mta1.migadu.com ([95.215.58.174]:59037) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riNlp-0002FA-QG for 69602@debbugs.gnu.org; Thu, 07 Mar 2024 19:07:33 -0500 References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1709856382; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=/ad6zV8Ml+xgtMYcTNgDrjwzy/KGK55dz3wkPPkD6o8=; b=bykiTefWlsVsJog37PiX54LIqTgoxS+B/MFbx3da/veLV3tGH1Q3JebWmTAqQfycgac8ef Uqpqb3R5lcz9+GNFJ01bCy3XF99PaztP4W4+ppZm9zZZld0n/UoYXwDuVwCSsJMRNLXP0Q tT4ZBe6rDveGAz/X4VDS4yTU9VefB9s= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Joseph Turner Date: Thu, 07 Mar 2024 05:53:11 -0800 In-reply-to: <86bk7q2xye.fsf@gnu.org> Message-ID: <871q8lk2o7.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Migadu-Flow: FLOW_OUT X-Spam-Score: 1.1 (+) X-Spam-Report: Spam detection software, running on the system "debbugs.gnu.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: Eli Zaretskii writes: >> From: Joseph Turner >> Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net >> Date: Thu, 07 Mar 2024 00:08:57 -0800 >> >> Eli Zaretskii w [...] Content analysis details: (1.1 points, 10.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 1.1 DATE_IN_PAST_06_12 Date: is 6 to 12 hours before Received: date -0.0 SPF_PASS SPF: sender matches SPF record 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 T_SCC_BODY_TEXT_LINE No description available. X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: 0.1 (/) --=-=-= Content-Type: text/plain Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: >> From: Joseph Turner >> Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net >> Date: Thu, 07 Mar 2024 00:08:57 -0800 >> >> Eli Zaretskii writes: > I hoped :map allows its value to be a form that is evaluated when the > image is being processed, in which case that form could call > image-compute-scaling-factor when it produces the coordinates. Thanks! Would this require a change in C? > If that doesn't work, then... > >> When creating an image, we set its :map property according to the return >> value of `image-compute-scaling-factor'. Once the image is inserted into >> the buffer, the user may run `image-increase-size' or `image-rotate', >> which changes how the image is displayed but not its :map. >> >> Now, we need to rerun `image-compute-scaling-factor' and recompute :map. What I said here is wrong. `image-compute-scaling-factor' is not useful for recomputing :map, but `image--current-scaling' is. >> However, there is no hook which runs after the user runs those commands, >> so AFAICT there's no way for our code to know when to recompute :map. > > ...AFAIU, when an image is rescaled, we call > image-transform-properties to produce the updated image properties. There are two ways to rescale an image, `image-transform-properties' (defined in image-mode.el; works only on file-backed images in image-mode) and `image--change-size' (defined in image.el; works on any image object in any mode). For now, I'd like to focus on improving `image.el'. > So I guess you'd like that function to recompute the coordinates in > :map according to the transform? > > IOW, I don't understand why you think the problem can only be solved > in C: AFAIK almost all of the machinery that performs image transforms > is implemented in Lisp, and each time an image is rescaled, we > basically re-process the image descriptor anew. The attached patch adds two hooks in `image.el' which allow packages to recompute an image's map after it's rescaled or rotated. The following demonstrates `image-after-change-size-hooks': (progn (defun image--scale-map (map factor) "Scale MAP by FACTOR, destructively modifying it." (when (and factor (/=3D 1 factor)) (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) (pcase-exhaustive type ('rect (setf (caar coords) (round (* (caar coords) factor))) (setf (cdar coords) (round (* (cdar coords) factor))) (setf (cadr coords) (round (* (cadr coords) factor))) (setf (cddr coords) (round (* (cddr coords) factor)))) ('circle (setf (caar coords) (round (* (caar coords) factor))) (setf (cdar coords) (round (* (cdar coords) factor))) (setf (cdr coords) (round (* (cdr coords) factor)))) ('poly (dotimes (i (length coords)) (aset coords i (round (* (aref coords i) factor)))))))) map) (defun image-rescale-image-map () "Recalculate and set :map property of image at point. Assumes that image has an :unscaled-map property." (when-let* ((image (image--get-imagemagick-and-warn)) (unscaled-image (image--image-without-parameters image)) (unscaled-map (image-property image :unscaled-map)) (scale (image--current-scaling image unscaled-image))) (setf (image-property image :map) (image--scale-map (copy-tree unscaled-map t) scale)))) (with-current-buffer (get-buffer-create "*image-properties-test*") (let* ((svg-string "\n\n\n\n\n\norggraphview\n\n\n\na\n\n\nHover me!\n\n\n\n\n\n") (scale 0.75) ; Adjust initial image scale (unscaled-map '(((circle (85 . 85) . 80) "1" (help-echo "Surpris= e!")))) (map (image--scale-map (copy-tree unscaled-map t) scale)) (image (create-image svg-string 'svg t :scale scale :map map :unscaled-map unscaled-map)= )) (add-hook 'image-after-change-size-hooks #'image-rescale-image-map ni= l t) (erase-buffer) (insert-image image) (goto-char (point-min)) (pop-to-buffer (current-buffer))))) After applying the attached patch, evaluate the above form, press "i +" and "i -" repeatedly to see the image and its map scale together. Thanks! Joseph --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Add-image-after-change-size-hooks-image-after-rotate.patch >From ace6449374eb92f820388c5d111daaa2dbd89835 Mon Sep 17 00:00:00 2001 From: Joseph Turner Date: Thu, 7 Mar 2024 15:24:49 -0800 Subject: [PATCH] Add image-after-change-size-hooks, image-after-rotate-hooks With these hooks, image properties, notably :map, may now be recalculated and set when an image is rotated or rescaled. See #69602. * lisp/image.el (image-after-change-size-hooks): Add hooks variable. (image--change-size): Run hooks at end of function. (image-after-rotate-hooks): Add hooks variable. (image-rotate): Run hooks at end of function. --- lisp/image.el | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lisp/image.el b/lisp/image.el index d2f8868b60e..053fb436cd5 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -1286,7 +1286,11 @@ image--change-size (new-image (image--image-without-parameters image)) (scale (image--current-scaling image new-image))) (setcdr image (cdr new-image)) - (plist-put (cdr image) :scale (* scale factor)))) + (plist-put (cdr image) :scale (* scale factor)) + (run-hooks 'image-after-change-size-hooks))) + +(defvar image-after-change-size-hooks nil + "Hooks run after image is rescaled.") (advice-add #'image--change-size :after #'image--change-size-scale-map) @@ -1355,9 +1359,13 @@ image-rotate ;; We don't want to exceed 360 degrees rotation, ;; because it's not seen as valid in Exif data. 360)))) + (run-hooks 'image-after-rotate-hooks) (set-transient-map image--repeat-map nil nil "Use %k for further adjustments")) +(defvar image-after-rotate-hooks nil + "Hooks run after image is rotated.") + (defun image-save () "Save the image under point. This writes the original image data to a file. Rotating or -- 2.41.0 --=-=-=-- From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 08 Mar 2024 07:15:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii , 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.170988204522950 (code B ref 69602); Fri, 08 Mar 2024 07:15:01 +0000 Received: (at 69602) by debbugs.gnu.org; 8 Mar 2024 07:14:05 +0000 Received: from localhost ([127.0.0.1]:56196 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riUQf-0005y6-3H for submit@debbugs.gnu.org; Fri, 08 Mar 2024 02:14:05 -0500 Received: from out-170.mta0.migadu.com ([91.218.175.170]:64184) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riUQb-0005xU-RQ for 69602@debbugs.gnu.org; Fri, 08 Mar 2024 02:14:03 -0500 References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1709882003; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=6YzNXRgAo97h3eBQ0FRhzSdqtV2cYIauW1sdmjM4PPk=; b=GmiQiQVWQGukbTdmmKEUvtizMmjX2Tgc0tED6PKG4Zld8SNUfiJ2NjxSYyOPpzI4JPnAbr cBRgOg79uQz/XekEyKUfmCoHafTLvV8XP8CZOFkp4fd4CXzStrvDX/Y8Qonolqsb6Gg5oP LmLtnJs1ynpQqx/D6sf5Wmi/Te8Thr0= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Joseph Turner Date: Thu, 07 Mar 2024 23:02:24 -0800 In-reply-to: <871q8lk2o7.fsf@breatheoutbreathe.in> Message-ID: <87o7bpi4c1.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Migadu-Flow: FLOW_OUT X-Spam-Score: 0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain Content-Transfer-Encoding: quoted-printable Joseph Turner writes: > The attached patch adds two hooks in `image.el' which allow packages to > recompute an image's map after it's rescaled or rotated. In this new attached patch, rescaling image maps is handled within image.el. No new hooks are added. This change should be backwards compatible, except that :unscaled-map is now a reserved image property. If we decide to install this patch, I'll work on modifying image maps inside of `image-rotate', as well as update the manual and NEWS. Test it out! (with-current-buffer (get-buffer-create "*image-properties-test*") (let* ((svg-string "\n\n\n\n\n\norggraphview\n\n\n\na\n\n\nHover me!\n\n\n\n\n\n") (map '(((circle (85 . 85) . 80) "1" (help-echo "Surprise!")))) (image (create-image svg-string 'svg t :map map))) (erase-buffer) (insert-image image) (goto-char (point-min)) (pop-to-buffer (current-buffer)))) Thank you! Joseph --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Recalculate-map-when-image-scale-changes.patch >From 7a3d9fa5cc08c40696ad65101d62cb4babb4dc76 Mon Sep 17 00:00:00 2001 From: Joseph Turner Date: Thu, 7 Mar 2024 21:55:00 -0800 Subject: [PATCH] Recalculate :map when image :scale changes Now, when rescaling an image with a :map using `image-increase-size' or `image-decrease-size', the image map scales along with the image. Image map coordinates are integers, so when scaling :map, coordinates must be rounded. To prevent an image from drifting from its map after repeatedly scaling up and down, `create-image' now stores the original :unscaled-map, which is combined with the image's scale after resizing to recalculate :map. * lisp/image.el (create-image): Add :unscaled-map image property (image--delayed-change-size): Fix comment (image--change-size): Also scale image map (image--scale-map): Add function to scale an image map --- lisp/image.el | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/lisp/image.el b/lisp/image.el index 2ebce59a98c..c72332172f0 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -533,6 +533,13 @@ create-image ('t t) ('nil nil) (func (funcall func image))))))) + ;; Add unscaled map. + (when-let ((map (plist-get props :map))) + (setq image (nconc image + (list :unscaled-map + (image--scale-map + (copy-tree map t) + (/ 1.0 (image-property image :scale))))))) image))) (defun image--default-smoothing (image) @@ -1185,7 +1192,7 @@ image-increase-size (defun image--delayed-change-size (size position) ;; Wait for a bit of idle-time before actually performing the change, ;; so as to batch together sequences of closely consecutive size changes. - ;; `image--change-size' just changes one value in a plist. The actual + ;; `image--change-size' just changes :scale and :map. The actual ;; image resizing happens later during redisplay. So if those ;; consecutive calls happen without any redisplay between them, ;; the costly operation of image resizing should happen only once. @@ -1267,9 +1274,34 @@ image--get-imagemagick-and-warn (defun image--change-size (factor &optional position) (let* ((image (image--get-imagemagick-and-warn position)) (new-image (image--image-without-parameters image)) - (scale (image--current-scaling image new-image))) + (unscaled-map (image-property image :unscaled-map)) + (scale (image--current-scaling image new-image)) + (new-scale (* scale factor))) (setcdr image (cdr new-image)) - (plist-put (cdr image) :scale (* scale factor)))) + (plist-put (cdr image) :scale new-scale) + (when unscaled-map + (setf (image-property image :map) + (image--scale-map (copy-tree unscaled-map t) new-scale))))) + +(defun image--scale-map (map factor) + "Scale MAP by FACTOR, destructively modifying it." + (unless (= 1 factor) + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (setf (caar coords) (round (* (caar coords) factor))) + (setf (cdar coords) (round (* (cdar coords) factor))) + (setf (cadr coords) (round (* (cadr coords) factor))) + (setf (cddr coords) (round (* (cddr coords) factor)))) + ('circle + (setf (caar coords) (round (* (caar coords) factor))) + (setf (cdar coords) (round (* (cdar coords) factor))) + (setf (cdr coords) (round (* (cdr coords) factor)))) + ('poly + (dotimes (i (length coords)) + (aset coords i + (round (* (aref coords i) factor)))))))) + map) (defun image--image-without-parameters (image) (cons (pop image) -- 2.41.0 --=-=-=-- From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 08 Mar 2024 07:26:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Joseph Turner Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.170988271124075 (code B ref 69602); Fri, 08 Mar 2024 07:26:01 +0000 Received: (at 69602) by debbugs.gnu.org; 8 Mar 2024 07:25:11 +0000 Received: from localhost ([127.0.0.1]:56224 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riUbP-0006GF-6G for submit@debbugs.gnu.org; Fri, 08 Mar 2024 02:25:11 -0500 Received: from eggs.gnu.org ([209.51.188.92]:60380) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riUbM-0006Fy-GM for 69602@debbugs.gnu.org; Fri, 08 Mar 2024 02:25:09 -0500 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1riUak-0003xd-Dw; Fri, 08 Mar 2024 02:24:30 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=AFxkn93R0e9dv6zkSPXeyOCAHGLHiEiHNT0DdGu5ofw=; b=P/oTBwN+/HIR ie0FZNpVevv9tMZeBYi/NsbAWUfcd1NwsGWvaQLLeirS4JtDLnpaRHyKYIyq8UcmeBk/LI+tSs6ns j/fsjPtZ899rc8/oYgmxat5LRIgG+ewMLqhupZlxMADMh+s8Zpt08FM8WKf2Fb5y4gCeF7caYQrqu vK5tQY0feRoWogcRB/pf5cDeuSLIM/kLsPX7/2MBQStoz7C/GvmdZQw1cva9t32m5ifM3XOfn7cQL m2U9GRhlmpv/q4UYrKrJv1dwUyK/Oq363fidT1GEPzefhqBt1JsOkVfgbjhHPWL2VUHmFJ8lvbqPb 1Da0mq0jQWxW7/xMQPhQVA==; Date: Fri, 08 Mar 2024 09:24:28 +0200 Message-Id: <86frx11903.fsf@gnu.org> From: Eli Zaretskii In-Reply-To: <871q8lk2o7.fsf@breatheoutbreathe.in> (message from Joseph Turner on Thu, 07 Mar 2024 05:53:11 -0800) References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Joseph Turner > Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net > Date: Thu, 07 Mar 2024 05:53:11 -0800 > > > IOW, I don't understand why you think the problem can only be solved > > in C: AFAIK almost all of the machinery that performs image transforms > > is implemented in Lisp, and each time an image is rescaled, we > > basically re-process the image descriptor anew. > > The attached patch adds two hooks in `image.el' which allow packages to > recompute an image's map after it's rescaled or rotated. Thanks, but please accompany the code change with suitable changes for NEWS and the ELisp manual. Bonus points for adding tests for this to our test suite. Also, I think we don't need the "after" part in the names of these two hooks: their doc strings explicitly document that they are run after the transformation, and image-change-size-hook is easier to remember, since the name basically says "a hook run when image is resized". And finally, please mention the hooks in the doc strings of public functions that perform size-changes and rotations of images, as we usually do with other hooks. From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 08 Mar 2024 08:33:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Joseph Turner Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.170988672321866 (code B ref 69602); Fri, 08 Mar 2024 08:33:01 +0000 Received: (at 69602) by debbugs.gnu.org; 8 Mar 2024 08:32:03 +0000 Received: from localhost ([127.0.0.1]:56526 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riVe6-0005gc-OM for submit@debbugs.gnu.org; Fri, 08 Mar 2024 03:32:03 -0500 Received: from eggs.gnu.org ([209.51.188.92]:53052) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riVe4-0005g6-A6 for 69602@debbugs.gnu.org; Fri, 08 Mar 2024 03:32:01 -0500 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1riVdQ-0002e9-EV; Fri, 08 Mar 2024 03:31:20 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=jE7bj9fgumHVLnEeqQEsrOvBqeUqGIzTUgVmSibC698=; b=fHJAU+NWIuKa EWmw79QSMpoa8lSZtGdXssEXIPUYU1mrJZB/p8FPxiAwfvzDSxTb+zy0B12QpFMTofUuL8a7952Wh tbZPZ4rCZ5SDwwyNenxHUuUTN0Ul9oZggbMXM+bPCMs0ZwPjHL3bCj1nlTD1MqiwAQY4quvhxkZ2H r3ai+mdjc/707c0O5/VYlPPbBrN/BqJliOVVHzHKLQ6Ac5Su6omPRcaAZmzadBdvJUpTtl4rzTZVo 48in6ecC6qwkb/ihSnpEeCnh5MNPzMYanpxpX7AJ0WUSyEzGiarGrU1JUOaQVrCytOoKX/4Q7j9Cq 9xqyhy31iRu943uuS8bWpw==; Date: Fri, 08 Mar 2024 10:31:12 +0200 Message-Id: <867cid15wv.fsf@gnu.org> From: Eli Zaretskii In-Reply-To: <87o7bpi4c1.fsf@breatheoutbreathe.in> (message from Joseph Turner on Thu, 07 Mar 2024 23:02:24 -0800) References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Joseph Turner > Date: Thu, 07 Mar 2024 23:02:24 -0800 > > Joseph Turner writes: > > > The attached patch adds two hooks in `image.el' which allow packages to > > recompute an image's map after it's rescaled or rotated. > > In this new attached patch, rescaling image maps is handled within > image.el. No new hooks are added. This change should be backwards > compatible, except that :unscaled-map is now a reserved image property. Are we sure no Lisp program out there would want the coordinates in :map to remain unchanged under these transformation? Maybe we should add a variable to control this, so that the change could be truly backward-compatible? > If we decide to install this patch, I'll work on modifying image maps > inside of `image-rotate', as well as update the manual and NEWS. Thanks. From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 08 Mar 2024 09:02:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.170988849226469 (code B ref 69602); Fri, 08 Mar 2024 09:02:01 +0000 Received: (at 69602) by debbugs.gnu.org; 8 Mar 2024 09:01:32 +0000 Received: from localhost ([127.0.0.1]:56571 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riW6e-0006sq-79 for submit@debbugs.gnu.org; Fri, 08 Mar 2024 04:01:32 -0500 Received: from out-174.mta0.migadu.com ([91.218.175.174]:26490) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riW6a-0006sI-RJ for 69602@debbugs.gnu.org; Fri, 08 Mar 2024 04:01:30 -0500 References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1709888420; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=4Z2RXJx0CSOO/92SmVSK19oFKXmRpsZD/Nf35MwXVJQ=; b=FmGy0bPrUcVkG4gIbpfUfa6Tt/QVNP3fSWNUAsIEh4In8z8w9xKkvgoYUWAXPn5xpiY0+Y suunFcLM2ChcBFUXy6iLQeZn23RviCQE3OobloEI2VOdoX0KRaVvD34+8IyFCi7mIUHp5v yDzY8DB0FmmWCaCzC65KEtb0ljuoqr8= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Joseph Turner Date: Fri, 08 Mar 2024 00:39:16 -0800 In-reply-to: <867cid15wv.fsf@gnu.org> Message-ID: <87bk7phzdz.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Migadu-Flow: FLOW_OUT X-Spam-Score: 0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain Eli Zaretskii writes: >> From: Joseph Turner >> Date: Thu, 07 Mar 2024 23:02:24 -0800 >> >> Joseph Turner writes: >> >> > The attached patch adds two hooks in `image.el' which allow packages to >> > recompute an image's map after it's rescaled or rotated. >> >> In this new attached patch, rescaling image maps is handled within >> image.el. No new hooks are added. This change should be backwards >> compatible, except that :unscaled-map is now a reserved image property. > > Are we sure no Lisp program out there would want the coordinates in > :map to remain unchanged under these transformation? Maybe we should > add a variable to control this, so that the change could be truly > backward-compatible? Good point. Thanks! What if explicitly passing :unscaled-map 'no-recalculate in `create-image' prevented :map from being recalculated later? If not, what kind of variable did you have in mind? A special variable let-bound the call? Or another argument to `create-image'? See patches. --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0002-Allow-map-to-not-be-recalculated-after-image-rescali.patch >From feafa5a4e967254fc58c2ad223cd7033d4c4be02 Mon Sep 17 00:00:00 2001 From: Joseph Turner Date: Fri, 8 Mar 2024 00:53:44 -0800 Subject: [PATCH 2/2] Allow :map to not be recalculated after image rescaling * lisp/image.el (create-image): Don't append :unscaled-map if passed in. (image--image-without-parameters): Don't rescale :map when :unscaled-map is 'no-recalculate. --- lisp/image.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lisp/image.el b/lisp/image.el index c72332172f0..b7de9817009 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -534,7 +534,8 @@ create-image ('nil nil) (func (funcall func image))))))) ;; Add unscaled map. - (when-let ((map (plist-get props :map))) + (when-let ((map (plist-get props :map)) + ((not (plist-member props :unscaled-map)))) (setq image (nconc image (list :unscaled-map (image--scale-map @@ -1279,7 +1280,8 @@ image--change-size (new-scale (* scale factor))) (setcdr image (cdr new-image)) (plist-put (cdr image) :scale new-scale) - (when unscaled-map + (when (and unscaled-map + (not (eq 'no-recalculate unscaled-map))) (setf (image-property image :map) (image--scale-map (copy-tree unscaled-map t) new-scale))))) -- 2.41.0 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Recalculate-map-when-image-scale-changes.patch >From 7a3d9fa5cc08c40696ad65101d62cb4babb4dc76 Mon Sep 17 00:00:00 2001 From: Joseph Turner Date: Thu, 7 Mar 2024 21:55:00 -0800 Subject: [PATCH 1/2] Recalculate :map when image :scale changes Now, when rescaling an image with a :map using `image-increase-size' or `image-decrease-size', the image map scales along with the image. Image map coordinates are integers, so when scaling :map, coordinates must be rounded. To prevent an image from drifting from its map after repeatedly scaling up and down, `create-image' now stores the original :unscaled-map, which is combined with the image's scale after resizing to recalculate :map. * lisp/image.el (create-image): Add :unscaled-map image property (image--delayed-change-size): Fix comment (image--change-size): Also scale image map (image--scale-map): Add function to scale an image map --- lisp/image.el | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/lisp/image.el b/lisp/image.el index 2ebce59a98c..c72332172f0 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -533,6 +533,13 @@ create-image ('t t) ('nil nil) (func (funcall func image))))))) + ;; Add unscaled map. + (when-let ((map (plist-get props :map))) + (setq image (nconc image + (list :unscaled-map + (image--scale-map + (copy-tree map t) + (/ 1.0 (image-property image :scale))))))) image))) (defun image--default-smoothing (image) @@ -1185,7 +1192,7 @@ image-increase-size (defun image--delayed-change-size (size position) ;; Wait for a bit of idle-time before actually performing the change, ;; so as to batch together sequences of closely consecutive size changes. - ;; `image--change-size' just changes one value in a plist. The actual + ;; `image--change-size' just changes :scale and :map. The actual ;; image resizing happens later during redisplay. So if those ;; consecutive calls happen without any redisplay between them, ;; the costly operation of image resizing should happen only once. @@ -1267,9 +1274,34 @@ image--get-imagemagick-and-warn (defun image--change-size (factor &optional position) (let* ((image (image--get-imagemagick-and-warn position)) (new-image (image--image-without-parameters image)) - (scale (image--current-scaling image new-image))) + (unscaled-map (image-property image :unscaled-map)) + (scale (image--current-scaling image new-image)) + (new-scale (* scale factor))) (setcdr image (cdr new-image)) - (plist-put (cdr image) :scale (* scale factor)))) + (plist-put (cdr image) :scale new-scale) + (when unscaled-map + (setf (image-property image :map) + (image--scale-map (copy-tree unscaled-map t) new-scale))))) + +(defun image--scale-map (map factor) + "Scale MAP by FACTOR, destructively modifying it." + (unless (= 1 factor) + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (setf (caar coords) (round (* (caar coords) factor))) + (setf (cdar coords) (round (* (cdar coords) factor))) + (setf (cadr coords) (round (* (cadr coords) factor))) + (setf (cddr coords) (round (* (cddr coords) factor)))) + ('circle + (setf (caar coords) (round (* (caar coords) factor))) + (setf (cdar coords) (round (* (cdar coords) factor))) + (setf (cdr coords) (round (* (cdr coords) factor)))) + ('poly + (dotimes (i (length coords)) + (aset coords i + (round (* (aref coords i) factor)))))))) + map) (defun image--image-without-parameters (image) (cons (pop image) -- 2.41.0 --=-=-=-- From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 08 Mar 2024 11:52:03 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Joseph Turner Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.170989869515858 (code B ref 69602); Fri, 08 Mar 2024 11:52:03 +0000 Received: (at 69602) by debbugs.gnu.org; 8 Mar 2024 11:51:35 +0000 Received: from localhost ([127.0.0.1]:56912 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riYlD-00047h-EZ for submit@debbugs.gnu.org; Fri, 08 Mar 2024 06:51:35 -0500 Received: from eggs.gnu.org ([209.51.188.92]:55368) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1riYlA-00047O-3y for 69602@debbugs.gnu.org; Fri, 08 Mar 2024 06:51:33 -0500 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1riYkP-0005wy-HY; Fri, 08 Mar 2024 06:50:53 -0500 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=Y4+hTl8G1i3Nef1br6ml/ie0nAIWxtaH/Bz5m0CEB8I=; b=TscGgJYvrUEW LHtPNKytBhhyebPPc70PZWozsTwBBkp6AZFvgmuRfMpO4nu1nF3K1SscvEBE9Mx64GDNoRGQJF/gf QYN4eFkOI5P8dQHuhG1uiAbpK79hDUdRRhz/HLiTk2hEn4LpfwpmUqOw+bTxSHvukcrhuuWTNMkmK OPNNtVSEZdes+uErKSWaK3bQP9jvMZzY/CE2QtMQGtXEsTttiGwztEit93AfJ+ZHff7WU1yDH0K3q ZCJLFxdXJv5lFzvG9QR1ZiSd10EXp8zM+u7R/J+kwfhth2V877zwk2A9H0IEyZXQC74++G99uSKQx 2OgY+ZU2AlK9p37vyiQ5PA==; Date: Fri, 08 Mar 2024 13:50:38 +0200 Message-Id: <8634t10woh.fsf@gnu.org> From: Eli Zaretskii In-Reply-To: <87bk7phzdz.fsf@breatheoutbreathe.in> (message from Joseph Turner on Fri, 08 Mar 2024 00:39:16 -0800) References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> <87bk7phzdz.fsf@breatheoutbreathe.in> X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Joseph Turner > Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net > Date: Fri, 08 Mar 2024 00:39:16 -0800 > > > Are we sure no Lisp program out there would want the coordinates in > > :map to remain unchanged under these transformation? Maybe we should > > add a variable to control this, so that the change could be truly > > backward-compatible? > > Good point. Thanks! > > What if explicitly passing :unscaled-map 'no-recalculate in > `create-image' prevented :map from being recalculated later? I think that's less desirable, since some images could be created outside of control of a Lisp program that wants to control the coordinates. > If not, what kind of variable did you have in mind? A special variable > let-bound the call? Or another argument to `create-image'? I had in mind a special variable (we'd need to mention it in NEWS and in the doc string of the relevant functions). Adding an argument is a heavier change, and I think it is not justified in this case, because I do agree with you that most, if not all, applications would want the coordinates to scale together with the image. Thanks. From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 21 Mar 2024 06:53:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.17110039251590 (code B ref 69602); Thu, 21 Mar 2024 06:53:01 +0000 Received: (at 69602) by debbugs.gnu.org; 21 Mar 2024 06:52:05 +0000 Received: from localhost ([127.0.0.1]:33894 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rnCHT-0000PW-T2 for submit@debbugs.gnu.org; Thu, 21 Mar 2024 02:52:05 -0400 Received: from out-170.mta1.migadu.com ([95.215.58.170]:51069) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rnCHO-0000Or-JV for 69602@debbugs.gnu.org; Thu, 21 Mar 2024 02:52:02 -0400 References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> <87bk7phzdz.fsf@breatheoutbreathe.in> <8634t10woh.fsf@gnu.org> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1711003872; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=Bb43HJaGxyLUKyP9CDifZmqT8aTrn87o94oFChy5rpk=; b=SStopa4Dj5sFBEYOsfBg0y+hFYmwECIAs0kpHikcVfwL9ztwmzfZJqSN+IA6d/p7oo0Fb4 xRYOo+66mNMYYAd2D5pt2KpFD+I0KNuwHluekDPQD980/HDwa+btnMoJlyqqmVOO5rzNq+ k6MDQBkASpfjHzfhuqijhnhzGZVdKZ0= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Joseph Turner Date: Wed, 20 Mar 2024 23:45:34 -0700 In-reply-to: <8634t10woh.fsf@gnu.org> Message-ID: <87il1g5b8m.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Migadu-Flow: FLOW_OUT X-Spam-Score: 0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain Eli Zaretskii writes: >> From: Joseph Turner >> Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net >> Date: Fri, 08 Mar 2024 00:39:16 -0800 >> >> > Are we sure no Lisp program out there would want the coordinates in >> > :map to remain unchanged under these transformation? Maybe we should >> > add a variable to control this, so that the change could be truly >> > backward-compatible? >> >> Good point. Thanks! >> >> What if explicitly passing :unscaled-map 'no-recalculate in >> `create-image' prevented :map from being recalculated later? > > I think that's less desirable, since some images could be created > outside of control of a Lisp program that wants to control the > coordinates. > >> If not, what kind of variable did you have in mind? A special variable >> let-bound the call? Or another argument to `create-image'? > > I had in mind a special variable (we'd need to mention it in NEWS and > in the doc string of the relevant functions). Adding an argument is a > heavier change, and I think it is not justified in this case, because > I do agree with you that most, if not all, applications would want the > coordinates to scale together with the image. Thanks for your feedback and your patience! Please see attached patch. Joseph --=-=-= Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename=0001-Recompute-map-when-image-scale-rotation-or-flip-chan.patch Content-Transfer-Encoding: quoted-printable >From 7b454df8f7335751c96c946ca09711b16c1de193 Mon Sep 17 00:00:00 2001 From: Joseph Turner Date: Thu, 7 Mar 2024 21:55:00 -0800 Subject: [PATCH] Recompute :map when image :scale, :rotation, or :flip chan= ges Now, when transforming an image, its :map is recomputed to fit. Image map coordinates are integers, so when computing :map, coordinates are rounded. To prevent an image from drifting from its map after repeated transformations, `create-image' now adds a new image property :original-map, which is combined with the image's transformation parameters to recompute :map. * lisp/image.el (image-recompute-map-p): Add user option to control whether :map is recomputed when an image is transformed. (create-image): Create :map from :original-map and vice versa. (image--delayed-change-size): Fix comment. (image--change-size, image-rotate, image-flip-horizontally, image-flip-vertically): Recompute image map after transformation and mention image-recompute-map-p in docstring. (image--compute-map): Add function to compute a map from original map. (image--compute-map): Add function to compute an original map from map. (image--scale-map): Add function to scale a map based on :scale. (image--rotate-map): Add function to rotate a map based on :rotation. (image--rotate-coord): Add function to rotate a map coordinate pair. (image--flip-map): Add function to flip a map based on :flip. (image-increase-size, image-decrease-size, image-mouse-increase-size, image-mouse-decrease-size): Mention image-recompute-map-p in docstring. * etc/NEWS: Add NEWS entry. * doc/lispref/display.texi (Image Descriptors): Document :original-map and new user option image-recompute-map-p. * test/lisp/image-tests.el (image--compute-map-and-original-map): Test `image--compute-map' and `image--compute-original-map'. (image-create-image-with-map): Test that `create-image' adds :map and/or :original-map as appropriate. (image-transform-map): Test functions related to transforming maps. --- doc/lispref/display.texi | 24 +++++ etc/NEWS | 12 +++ lisp/image.el | 221 ++++++++++++++++++++++++++++++++++++--- test/lisp/image-tests.el | 111 ++++++++++++++++++++ 4 files changed, 356 insertions(+), 12 deletions(-) diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 10cf5ce89e2..8335a02b5c5 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -6055,6 +6055,30 @@ Image Descriptors when creating the image, or use the result of @code{image-compute-scaling-factor} to compute the elements of the map. + +When an image's @code{:scale}, @code{:rotation}, or @code{:flip} is +changed, @code{:map} will be recomputed based on the value of +@code{:original-map} and the values of those transformation. + +@item :original-map @var{original-map} +@cindex original image map +This specifies the untransformed image map which will be used to +recompute @code{:map} after the image's @code{:scale}, @code{:rotation}, +or @code{:flip} is changed. + +If @code{:original-map} is not specified when creating an image with +@code{create-image}, it will be computed based on the supplied +@code{:map}, as well as any of @code{:scale}, @code{:rotation}, or +@code{:flip} which are non-nil. + +Conversely, if @code{:original-map} is specified but @code{:map} is not, +@code{:map} will be computed based on @code{:original-map}, +@code{:scale}, @code{:rotation}, and @code{:flip}. + +@defopt image-recompute-map-p +Set this user option to nil to prevent Emacs from automatically +recomputing an image @code{:map} based on its @code{:original-map}. +@end defopt @end table =20 @defun image-mask-p spec &optional frame diff --git a/etc/NEWS b/etc/NEWS index 06856602ea8..cbd97b495b2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1292,6 +1292,18 @@ without specifying a file, like this: (notifications-notify :title "I am playing music" :app-icon 'multimedia-player) =20 +** Image + ++++ +*** Image :map property is now recomputed when image is transformed. +Now images with clickable maps work as expected after you run commands +such as `image-increase-size', `image-decrease-size', `image-rotate', +`image-flip-horizontally', and `image-flip-vertically'. + ++++ +*** New user option 'image-recompute-map-p' +Set this option to nil to prevent Emacs from recomputing image maps. + ** Image Dired =20 *** New user option 'image-dired-thumb-naming'. diff --git a/lisp/image.el b/lisp/image.el index 2ebce59a98c..ed94b9eb621 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -533,6 +533,16 @@ create-image ('t t) ('nil nil) (func (funcall func image))))))) + ;; Add original map from map. + (when (and (plist-get props :map) + (not (plist-get props :original-map))) + (setq image (nconc image (list :original-map + (image--compute-original-map image)= )))) + ;; Add map from original map. + (when (and (plist-get props :original-map) + (not (plist-get props :map))) + (setq image (nconc image (list :map + (image--compute-map image))))) image))) =20 (defun image--default-smoothing (image) @@ -1173,7 +1183,10 @@ image-increase-size If N is 3, then the image size will be increased by 30%. More generally, the image size is multiplied by 1 plus N divided by 10. N defaults to 2, which increases the image size by 20%. -POSITION can be a buffer position or a marker, and defaults to point." +POSITION can be a buffer position or a marker, and defaults to point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "P") (image--delayed-change-size (if n (1+ (/ (prefix-numeric-value n) 10.0)) @@ -1185,7 +1198,7 @@ image-increase-size (defun image--delayed-change-size (size position) ;; Wait for a bit of idle-time before actually performing the change, ;; so as to batch together sequences of closely consecutive size changes. - ;; `image--change-size' just changes one value in a plist. The actual + ;; `image--change-size' just changes two values in a plist. The actual ;; image resizing happens later during redisplay. So if those ;; consecutive calls happen without any redisplay between them, ;; the costly operation of image resizing should happen only once. @@ -1196,7 +1209,10 @@ image-decrease-size If N is 3, then the image size will be decreased by 30%. More generally, the image size is multiplied by 1 minus N divided by 10. N defaults to 2, which decreases the image size by 20%. -POSITION can be a buffer position or a marker, and defaults to point." +POSITION can be a buffer position or a marker, and defaults to point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "P") (image--delayed-change-size (if n (- 1 (/ (prefix-numeric-value n) 10.0)) @@ -1208,7 +1224,10 @@ image-decrease-size (defun image-mouse-increase-size (&optional event) "Increase the image size using the mouse-gesture EVENT. This increases the size of the image at the position specified by -EVENT, if any, by the default factor used by `image-increase-size'." +EVENT, if any, by the default factor used by `image-increase-size'. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "e") (when (listp event) (save-window-excursion @@ -1218,7 +1237,10 @@ image-mouse-increase-size (defun image-mouse-decrease-size (&optional event) "Decrease the image size using the mouse-gesture EVENT. This decreases the size of the image at the position specified by -EVENT, if any, by the default factor used by `image-decrease-size'." +EVENT, if any, by the default factor used by `image-decrease-size'. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "e") (when (listp event) (save-window-excursion @@ -1269,7 +1291,9 @@ image--change-size (new-image (image--image-without-parameters image)) (scale (image--current-scaling image new-image))) (setcdr image (cdr new-image)) - (plist-put (cdr image) :scale (* scale factor)))) + (plist-put (cdr image) :scale (* scale factor)) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image))))) =20 (defun image--image-without-parameters (image) (cons (pop image) @@ -1296,7 +1320,10 @@ image-rotate If nil, ANGLE defaults to 90. Interactively, rotate the image 90 degrees clockwise with no prefix argument, and counter-clockwise with a prefix argument. Note that most image types support -rotations by only multiples of 90 degrees." +rotations by only multiples of 90 degrees. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive (and current-prefix-arg '(-90))) (let ((image (image--get-imagemagick-and-warn))) (setf (image-property image :rotation) @@ -1304,7 +1331,9 @@ image-rotate (or angle 90)) ;; We don't want to exceed 360 degrees rotation, ;; because it's not seen as valid in Exif data. - 360)))) + 360))) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image)))) (set-transient-map image--repeat-map nil nil "Use %k for further adjustments")) =20 @@ -1325,23 +1354,191 @@ image-save (read-file-name "Write image to file: "))))) =20 (defun image-flip-horizontally () - "Horizontally flip the image under point." + "Horizontally flip the image under point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive) (let ((image (image--get-image))) (image-flush image) (setf (image-property image :flip) - (not (image-property image :flip))))) + (not (image-property image :flip))) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image))))) =20 (defun image-flip-vertically () - "Vertically flip the image under point." + "Vertically flip the image under point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive) (let ((image (image--get-image))) (image-rotate 180) (setf (image-property image :flip) - (not (image-property image :flip))))) + (not (image-property image :flip))) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image))))) =20 (define-obsolete-function-alias 'image-refresh #'image-flush "29.1") =20 +;;; Map transformation + +(defcustom image-recompute-map-p t + "Recompute image map when scaling, rotating, or flipping an image." + :type 'boolean + :version "30.1") + +(defun image--compute-map (image) + "Compute map for IMAGE suitable to be used as its :map property. +Return a copy of :original-image transformed based on IMAGE's :scale, +:rotation, and :flip. When IMAGE's :original-map is nil, return nil. +When :rotation is not a multiple of 90, return copy of :original-map." + (pcase-let* ((original-map (image-property image :original-map)) + (map (copy-tree original-map t)) + (scale (or (image-property image :scale) 1)) + (rotation (or (image-property image :rotation) 0)) + (flip (image-property image :flip)) + ((and size `(,width . ,height)) (image-size image t))) + (when (and ; Handle only 90-degree rotations + (zerop (mod rotation 1)) + (zerop (% (truncate rotation) 90))) + ;; SIZE fits MAP after transformations. Scale MAP before + ;; flip and rotate operations, since both need MAP to fit SIZE. + (image--scale-map map scale) + ;; In rendered images, rotation is always applied before flip. + (image--rotate-map + map rotation (if (or (=3D 90 rotation) (=3D 270 rotation)) + ;; If rotated =C2=B190=C2=B0, swap width and heigh= t. + (cons height width) + size)) + ;; After rotation, there's no need to swap width and height. + (image--flip-map map flip size)) + map)) + +(defun image--compute-original-map (image) + "Return original map for IMAGE. +If IMAGE lacks :map property, return nil. +When :rotation is not a multiple of 90, return copy of :map." + (when (image-property image :map) + (let* ((image-copy (copy-tree image t)) + (map (image-property image-copy :map)) + (scale (or (image-property image-copy :scale) 1)) + (rotation (or (image-property image-copy :rotation) 0)) + (flip (image-property image-copy :flip)) + (size (image-size image-copy t))) + (when (and ; Handle only 90-degree rotations + (zerop (mod rotation 1)) + (zerop (% (truncate rotation) 90))) + ;; In rendered images, rotation is always applied before flip. + ;; To undo the transformation, flip before rotating. + ;; SIZE fits MAP before it is transformed back to ORIGINAL-MAP. + ;; Therefore, scale MAP after flip and rotate operations, since + ;; both need MAP to fit SIZE. + (image--flip-map map flip size) + (image--rotate-map map (- rotation) size) + (image--scale-map map (/ 1.0 scale))) + map))) + +(defun image--scale-map (map scale) + "Scale MAP according to SCALE. +Destructively modifies and returns MAP." + (unless (=3D 1 scale) + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (setf (caar coords) (round (* (caar coords) scale))) + (setf (cdar coords) (round (* (cdar coords) scale))) + (setf (cadr coords) (round (* (cadr coords) scale))) + (setf (cddr coords) (round (* (cddr coords) scale)))) + ('circle + (setf (caar coords) (round (* (caar coords) scale))) + (setf (cdar coords) (round (* (cdar coords) scale))) + (setcdr coords (round (* (cdr coords) scale)))) + ('poly + (dotimes (i (length coords)) + (aset coords i + (round (* (aref coords i) scale)))))))) + map) + +(defun image--rotate-map (map rotation size) + "Rotate MAP according to ROTATION and SIZE. +Destructively modifies and returns MAP." + (unless (zerop rotation) + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (let ( x0 y0 ; New upper left corner + x1 y1) ; New bottom right corner + (pcase (truncate (mod rotation 360)) ; Set new corners to... + (90 ; ...old bottom left and upper right + (setq x0 (caar coords) y0 (cddr coords) + x1 (cadr coords) y1 (cdar coords))) + (180 ; ...old bottom right and upper left + (setq x0 (cadr coords) y0 (cddr coords) + x1 (caar coords) y1 (cdar coords))) + (270 ; ...old upper right and bottom left + (setq x0 (cadr coords) y0 (cdar coords) + x1 (caar coords) y1 (cddr coords)))) + (setcar coords (image--rotate-coord x0 y0 rotation size)) + (setcdr coords (image--rotate-coord x1 y1 rotation size)))) + ('circle + (setcar coords (image--rotate-coord + (caar coords) (cdar coords) rotation size))) + ('poly + (dotimes (i (length coords)) + (when (=3D 0 (% i 2)) + (pcase-let ((`(,x . ,y) + (image--rotate-coord + (aref coords i) (aref coords (1+ i)) rotation s= ize))) + (aset coords i x) + (aset coords (1+ i) y)))))))) + map) + +(defun image--rotate-coord (x y angle size) + "Rotate coordinates X and Y by ANGLE in image of SIZE. +ANGLE must be a multiple of 90. Returns a cons cell of rounded +coordinates (X1 Y1)." + (pcase-let* ((radian (thread-first angle (* float-pi) (/ 180.0))) + (`(,width . ,height) size) + ;; y is positive, but we are in the bottom-right quadrant + (y (- y)) + ;; Rotate clockwise + (x1 (+ (* (sin radian) y) (* (cos radian) x))) + (y1 (- (* (cos radian) y) (* (sin radian) x))) + ;; Translate image back into bottom-right quadrant + (`(,x1 . ,y1) + (pcase (truncate (mod angle 360)) + (90 ; Translate right by height + (cons (+ x1 height) y1)) + (180 ; Translate right by width and down by height + (cons (+ x1 width) (- y1 height))) + (270 ; Translate down by width + (cons x1 (- y1 width))))) + ;; Invert y1 to make both x1 and y1 positive + (y1 (- y1))) + (cons (round x1) (round y1)))) + +(defun image--flip-map (map flip size) + "Horizontally flip MAP according to FLIP and SIZE. +Destructively modifies and returns MAP." + (when flip + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (let ((x0 (- (car size) (cadr coords))) + (y0 (cdar coords)) + (x1 (- (car size) (caar coords))) + (y1 (cddr coords))) + (setcar coords (cons x0 y0)) + (setcdr coords (cons x1 y1)))) + ('circle + (setf (caar coords) (- (car size) (caar coords)))) + ('poly + (dotimes (i (length coords)) + (when (=3D 0 (% i 2)) + (aset coords i (- (car size) (aref coords i))))))))) + map) + (provide 'image) =20 ;;; image.el ends here diff --git a/test/lisp/image-tests.el b/test/lisp/image-tests.el index 80142d6d6de..1e409190865 100644 --- a/test/lisp/image-tests.el +++ b/test/lisp/image-tests.el @@ -153,4 +153,115 @@ image-rotate (image-rotate -154.5) (should (equal image '(image :rotation 91.0))))) =20 +;;;; Transforming maps + +(ert-deftest image-create-image-with-map () + "Test that `create-image' correctly adds :map and/or :original-map." + (let ((data "foo") + (map '(((circle (1 . 1) . 1) a))) + (original-map '(((circle (2 . 2) . 2) a))) + (original-map-other '(((circle (3 . 3) . 3) a)))) + ;; Generate :original-map from :map. + (let* ((image (create-image data 'svg t :map map :scale 0.5)) + (got-original-map (image-property image :original-map))) + (should (equal got-original-map original-map))) + ;; Generate :map from :original-map. + (let* ((image (create-image + data 'svg t :original-map original-map :scale 0.5)) + (got-map (image-property image :map))) + (should (equal got-map map))) + ;; Use :original-map if both it and :map are specified. + (let* ((image (create-image + data 'svg t :map map + :original-map original-map-other :scale 0.5)) + (got-original-map (image-property image :original-map))) + (should (equal got-original-map original-map-other))))) + +(ert-deftest image--compute-map-and-original-map () + "Test `image--compute-map' and `image--compute-original-map'." + (let* ((svg-string "ABC") + (original-map + '(((circle (41 . 29) . 24) "a" (help-echo "A")) + ((rect (5 . 101) 77 . 149) "b" (help-echo "B")) + ((poly . [161 29 160 22 154 15 146 10 136 7 125 5 114 7 104 10= 96 15 91 22 89 29 91 37 96 43 104 49 114 52 125 53 136 52 146 49 154 43 16= 0 37]) "c" (help-echo "C")))) + (scaled-map + '(((circle (82 . 58) . 48) "a" (help-echo "A")) + ((rect (10 . 202) 154 . 298) "b" (help-echo "B")) + ((poly . [322 58 320 44 308 30 292 20 272 14 250 10 228 14 208= 20 192 30 182 44 178 58 182 74 192 86 208 98 228 104 250 106 272 104 292 9= 8 308 86 320 74]) "c" (help-echo "C")))) + (flipped-map + '(((circle (125 . 29) . 24) "a" (help-echo "A")) + ((rect (89 . 101) 161 . 149) "b" (help-echo "B")) + ((poly . [5 29 6 22 12 15 20 10 30 7 41 5 52 7 62 10 70 15 75 = 22 77 29 75 37 70 43 62 49 52 52 41 53 30 52 20 49 12 43 6 37]) "c" (help-e= cho "C")))) + (rotated-map + '(((circle (126 . 41) . 24) "a" (help-echo "A")) + ((rect (6 . 5) 54 . 77) "b" (help-echo "B")) + ((poly . [126 161 133 160 140 154 145 146 148 136 150 125 148 = 114 145 104 140 96 133 91 126 89 118 91 112 96 106 104 103 114 102 125 103 = 136 106 146 112 154 118 160]) "c" (help-echo "C")))) + (scaled-rotated-flipped-map + '(((circle (58 . 82) . 48) "a" (help-echo "A")) + ((rect (202 . 10) 298 . 154) "b" (help-echo "B")) + ((poly . [58 322 44 320 30 308 20 292 14 272 10 250 14 228 20 = 208 30 192 44 182 58 178 74 182 86 192 98 208 104 228 106 250 104 272 98 29= 2 86 308 74 320]) "c" (help-echo "C")))) + (image (create-image svg-string 'svg t :map scaled-rotated-flippe= d-map + :scale 2 :rotation 90 :flip t))) + ;; Test that `image--compute-original-map' correctly generates + ;; original-map when creating an already transformed image. + (should (equal (image-property image :original-map) + original-map)) + (setf (image-property image :flip) nil) + (setf (image-property image :rotation) 0) + (setf (image-property image :scale) 2) + (should (equal (image--compute-map image) + scaled-map)) + (setf (image-property image :scale) 1) + (setf (image-property image :rotation) 90) + (should (equal (image--compute-map image) + rotated-map)) + (setf (image-property image :rotation) 0) + (setf (image-property image :flip) t) + (should (equal (image--compute-map image) + flipped-map)) + (setf (image-property image :scale) 2) + (setf (image-property image :rotation) 90) + (should (equal (image--compute-map image) + scaled-rotated-flipped-map)) + + ;; Uncomment to test manually by interactively transforming the + ;; image and checking the map boundaries by hovering them. + + ;; (with-current-buffer (get-buffer-create "*test image map*") + ;; (erase-buffer) + ;; (insert-image image) + ;; (goto-char (point-min)) + ;; (pop-to-buffer (current-buffer))) + )) + +(ert-deftest image-transform-map () + "Test functions related to transforming image maps." + (let ((map '(((circle (4 . 3) . 2) "circle") + ((rect (3 . 6) 8 . 8) "rect") + ((poly . [6 11 7 13 2 14]) "poly"))) + (width 10) + (height 15)) + (should (equal (image--scale-map (copy-tree map t) 2) + '(((circle (8 . 6) . 4) "circle") + ((rect (6 . 12) 16 . 16) "rect") + ((poly . [12 22 14 26 4 28]) "poly")))) + (should (equal (image--rotate-map (copy-tree map t) 90 `(,width . ,hei= ght)) + '(((circle (12 . 4) . 2) "circle") + ((rect (7 . 3) 9 . 8) "rect") + ((poly . [4 6 2 7 1 2]) "poly")))) + (should (equal (image--flip-map (copy-tree map t) t `(,width . ,height= )) + '(((circle (6 . 3) . 2) "circle") + ((rect (2 . 6) 7 . 8) "rect") + ((poly . [4 11 3 13 8 14]) "poly")))) + (let ((copy (copy-tree map t))) + (image--scale-map copy 2) + ;; Scale size because the map has been scaled. + (image--rotate-map copy 90 `(,(* 2 width) . ,(* 2 height))) + ;; Swap width and height because the map has been flipped. + (image--flip-map copy t `(,(* 2 height) . ,(* 2 width))) + (should (equal copy + '(((circle (6 . 8) . 4) "circle") + ((rect (12 . 6) 16 . 16) "rect") + ((poly . [22 12 26 14 28 4]) "poly"))))))) + ;;; image-tests.el ends here --=20 2.41.0 --=-=-=-- From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 21 Mar 2024 12:02:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Joseph Turner Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.17110224634026 (code B ref 69602); Thu, 21 Mar 2024 12:02:02 +0000 Received: (at 69602) by debbugs.gnu.org; 21 Mar 2024 12:01:03 +0000 Received: from localhost ([127.0.0.1]:35301 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rnH6S-00012R-2z for submit@debbugs.gnu.org; Thu, 21 Mar 2024 08:01:03 -0400 Received: from eggs.gnu.org ([209.51.188.92]:51514) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rnH6M-00012B-Uk for 69602@debbugs.gnu.org; Thu, 21 Mar 2024 08:00:58 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rnH5c-0003jO-8p; Thu, 21 Mar 2024 08:00:08 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=ME2H9OZpadEHNcLWdU1TiU7E/FsbrySeKojyns2Zqt8=; b=dfxV4BbaXBkK3QP45GLb KDqxI/lS6lchfX5tnFIHsZaT7dlte7ntEzL80AG0gS8boTtgG4GhGRUxaj/1Zo1n4p3VQC67IsneV 53g1Pl5uF/gXsSI+iTUOKEVR6Qj49b/Uog2P5ZdE2ERWXNfkty8KJN8c+aKW/VmT5bTfTxglOgt74 m5ONz4UG2xBTmXKNKJZaZZcQjTMLkzrO2IuVsB/ERnK2qzudi97mZXxZkePMXFEirIJFb95Z4Jvcc BfriAM8Nz3fu8lrNL6pyyRa0+Y0+C2RP/aygK3tQy/733NHWb+xzdBRllf6qCBTaG2StetX2hVtgv JbaknRe8gNotkA==; Date: Thu, 21 Mar 2024 13:59:59 +0200 Message-Id: <86y1ab23sw.fsf@gnu.org> From: Eli Zaretskii In-Reply-To: <87il1g5b8m.fsf@breatheoutbreathe.in> (message from Joseph Turner on Wed, 20 Mar 2024 23:45:34 -0700) References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> <87bk7phzdz.fsf@breatheoutbreathe.in> <8634t10woh.fsf@gnu.org> <87il1g5b8m.fsf@breatheoutbreathe.in> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Joseph Turner > Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net > Date: Wed, 20 Mar 2024 23:45:34 -0700 > > > I had in mind a special variable (we'd need to mention it in NEWS and > > in the doc string of the relevant functions). Adding an argument is a > > heavier change, and I think it is not justified in this case, because > > I do agree with you that most, if not all, applications would want the > > coordinates to scale together with the image. > > Thanks for your feedback and your patience! > > Please see attached patch. Thanks. The tests you added have some problems: . you use thread-first, but don't require subr-x when compiling . the tests fail when run in batch mode . when invoked interactively in a GUI session, one test fails: F image--compute-map-and-original-map Test ‘image--compute-map’ and ‘image--compute-original-map’. (ert-test-failed ((should (equal (image--compute-map image) flipped-map)) :form (equal (((circle ... . 24) "a" (help-echo "A")) ((rect ... 162 . 149) "b" (help-echo "B")) ((poly . [6 29 7 22 13 15 21 10 31 7 ...]) "c" (help-echo "C"))) (((circle ... . 24) "a" (help-echo "A")) ((rect ... 161 . 149) "b" (help-echo "B")) ((poly . [5 29 6 22 12 15 20 10 30 7 ...]) "c" (help-echo "C")))) :value nil :explanation (list-elt 0 (list-elt 0 (cdr (car ...)))))) It looks like some pixels do not match exactly? Perhaps some tolerances need to be allowed? From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 23 Mar 2024 00:23:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.171115337529031 (code B ref 69602); Sat, 23 Mar 2024 00:23:01 +0000 Received: (at 69602) by debbugs.gnu.org; 23 Mar 2024 00:22:55 +0000 Received: from localhost ([127.0.0.1]:32807 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rnp9w-0007Y5-NM for submit@debbugs.gnu.org; Fri, 22 Mar 2024 20:22:54 -0400 Received: from out-186.mta0.migadu.com ([91.218.175.186]:15941) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rnp9s-0007XX-I5 for 69602@debbugs.gnu.org; Fri, 22 Mar 2024 20:22:51 -0400 References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> <87bk7phzdz.fsf@breatheoutbreathe.in> <8634t10woh.fsf@gnu.org> <87il1g5b8m.fsf@breatheoutbreathe.in> <86y1ab23sw.fsf@gnu.org> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1711153291; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=Z4vHrH74D6UldAo9WDJihHfv38lpsXUInlODiPsMJPM=; b=EgzQN9gh5sFkhM3fjpDvN5i8U+POghearFL4sqbZfkaEXUmc0WCyyJ1Jil/99lVBzRW9Xi qym2iyZq9zkoeqpU6bMxW0m18dz/nbzT64Rpc2F9fSKPX27a6jWqrY1noOTInU88y0oxF6 /OgniLMz5pD+J0TqYrHaU6Se7/Cr3WU= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Joseph Turner Date: Fri, 22 Mar 2024 17:11:17 -0700 In-reply-to: <86y1ab23sw.fsf@gnu.org> Message-ID: <877cht94sb.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Migadu-Flow: FLOW_OUT X-Spam-Score: 0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: >> From: Joseph Turner >> Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net >> Date: Wed, 20 Mar 2024 23:45:34 -0700 >> >> > I had in mind a special variable (we'd need to mention it in NEWS and >> > in the doc string of the relevant functions). Adding an argument is a >> > heavier change, and I think it is not justified in this case, because >> > I do agree with you that most, if not all, applications would want the >> > coordinates to scale together with the image. >> >> Thanks for your feedback and your patience! >> >> Please see attached patch. > > Thanks. The tests you added have some problems: > > . you use thread-first, but don't require subr-x when compiling I removed thread-first. > . the tests fail when run in batch mode I added (skip-unless (display-images-p)) to the two problematic tests, and it solved the issue on my machine. > . when invoked interactively in a GUI session, one test fails: > > F image--compute-map-and-original-map > Test =E2=80=98image--compute-map=E2=80=99 and =E2=80=98image--compute-or= iginal-map=E2=80=99. > (ert-test-failed > ((should (equal (image--compute-map image) flipped-map)) :form > (equal > (((circle ... . 24) "a" (help-echo "A")) > ((rect ... 162 . 149) "b" (help-echo "B")) > ((poly . [6 29 7 22 13 15 21 10 31 7 ...]) "c" (help-echo "C"))) > (((circle ... . 24) "a" (help-echo "A")) > ((rect ... 161 . 149) "b" (help-echo "B")) > ((poly . [5 29 6 22 12 15 20 10 30 7 ...]) "c" (help-echo "C")))) > :value nil :explanation (list-elt 0 (list-elt 0 (cdr (car ...)))))) > > It looks like some pixels do not match exactly? Perhaps some > tolerances need to be allowed? Interesting - does the result of `image-size` vary per machine? In any case, I added `image-tests--map-equal' to compare image maps with some tolerance. Do the tests pass on your machine now? Thank you! Joseph --=-=-= Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename=0001-Recompute-map-when-image-scale-rotation-or-flip-chan.patch Content-Transfer-Encoding: quoted-printable >From 4a0c7fc0d0354f60ee856cc50e743ee3034ac2a7 Mon Sep 17 00:00:00 2001 From: Joseph Turner Date: Thu, 7 Mar 2024 21:55:00 -0800 Subject: [PATCH] Recompute :map when image :scale, :rotation, or :flip chan= ges Now, when transforming an image, its :map is recomputed to fit. Image map coordinates are integers, so when computing :map, coordinates are rounded. To prevent an image from drifting from its map after repeated transformations, `create-image' now adds a new image property :original-map, which is combined with the image's transformation parameters to recompute :map. * lisp/image.el (image-recompute-map-p): Add user option to control whether :map is recomputed when an image is transformed. (create-image): Create :map from :original-map and vice versa. (image--delayed-change-size): Fix comment. (image--change-size, image-rotate, image-flip-horizontally, image-flip-vertically): Recompute image map after transformation and mention image-recompute-map-p in docstring. (image--compute-map): Add function to compute a map from original map. (image--compute-map): Add function to compute an original map from map. (image--scale-map): Add function to scale a map based on :scale. (image--rotate-map): Add function to rotate a map based on :rotation. (image--rotate-coord): Add function to rotate a map coordinate pair. (image--flip-map): Add function to flip a map based on :flip. (image-increase-size, image-decrease-size, image-mouse-increase-size, image-mouse-decrease-size): Mention image-recompute-map-p in docstring. * etc/NEWS: Add NEWS entry. * doc/lispref/display.texi (Image Descriptors): Document :original-map and new user option image-recompute-map-p. * test/lisp/image-tests.el (image--compute-map-and-original-map): Test `image--compute-map' and `image--compute-original-map'. (image-tests--map-equal): Add equality predicate to compare image maps. (image-create-image-with-map): Test that `create-image' adds :map and/or :original-map as appropriate. (image-transform-map): Test functions related to transforming maps. --- doc/lispref/display.texi | 24 +++++ etc/NEWS | 12 +++ lisp/image.el | 221 ++++++++++++++++++++++++++++++++++++--- test/lisp/image-tests.el | 143 +++++++++++++++++++++++++ 4 files changed, 388 insertions(+), 12 deletions(-) diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 10cf5ce89e2..8335a02b5c5 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -6055,6 +6055,30 @@ Image Descriptors when creating the image, or use the result of @code{image-compute-scaling-factor} to compute the elements of the map. + +When an image's @code{:scale}, @code{:rotation}, or @code{:flip} is +changed, @code{:map} will be recomputed based on the value of +@code{:original-map} and the values of those transformation. + +@item :original-map @var{original-map} +@cindex original image map +This specifies the untransformed image map which will be used to +recompute @code{:map} after the image's @code{:scale}, @code{:rotation}, +or @code{:flip} is changed. + +If @code{:original-map} is not specified when creating an image with +@code{create-image}, it will be computed based on the supplied +@code{:map}, as well as any of @code{:scale}, @code{:rotation}, or +@code{:flip} which are non-nil. + +Conversely, if @code{:original-map} is specified but @code{:map} is not, +@code{:map} will be computed based on @code{:original-map}, +@code{:scale}, @code{:rotation}, and @code{:flip}. + +@defopt image-recompute-map-p +Set this user option to nil to prevent Emacs from automatically +recomputing an image @code{:map} based on its @code{:original-map}. +@end defopt @end table =20 @defun image-mask-p spec &optional frame diff --git a/etc/NEWS b/etc/NEWS index 06856602ea8..cbd97b495b2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1292,6 +1292,18 @@ without specifying a file, like this: (notifications-notify :title "I am playing music" :app-icon 'multimedia-player) =20 +** Image + ++++ +*** Image :map property is now recomputed when image is transformed. +Now images with clickable maps work as expected after you run commands +such as `image-increase-size', `image-decrease-size', `image-rotate', +`image-flip-horizontally', and `image-flip-vertically'. + ++++ +*** New user option 'image-recompute-map-p' +Set this option to nil to prevent Emacs from recomputing image maps. + ** Image Dired =20 *** New user option 'image-dired-thumb-naming'. diff --git a/lisp/image.el b/lisp/image.el index 2ebce59a98c..c5082c78b75 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -533,6 +533,16 @@ create-image ('t t) ('nil nil) (func (funcall func image))))))) + ;; Add original map from map. + (when (and (plist-get props :map) + (not (plist-get props :original-map))) + (setq image (nconc image (list :original-map + (image--compute-original-map image)= )))) + ;; Add map from original map. + (when (and (plist-get props :original-map) + (not (plist-get props :map))) + (setq image (nconc image (list :map + (image--compute-map image))))) image))) =20 (defun image--default-smoothing (image) @@ -1173,7 +1183,10 @@ image-increase-size If N is 3, then the image size will be increased by 30%. More generally, the image size is multiplied by 1 plus N divided by 10. N defaults to 2, which increases the image size by 20%. -POSITION can be a buffer position or a marker, and defaults to point." +POSITION can be a buffer position or a marker, and defaults to point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "P") (image--delayed-change-size (if n (1+ (/ (prefix-numeric-value n) 10.0)) @@ -1185,7 +1198,7 @@ image-increase-size (defun image--delayed-change-size (size position) ;; Wait for a bit of idle-time before actually performing the change, ;; so as to batch together sequences of closely consecutive size changes. - ;; `image--change-size' just changes one value in a plist. The actual + ;; `image--change-size' just changes two values in a plist. The actual ;; image resizing happens later during redisplay. So if those ;; consecutive calls happen without any redisplay between them, ;; the costly operation of image resizing should happen only once. @@ -1196,7 +1209,10 @@ image-decrease-size If N is 3, then the image size will be decreased by 30%. More generally, the image size is multiplied by 1 minus N divided by 10. N defaults to 2, which decreases the image size by 20%. -POSITION can be a buffer position or a marker, and defaults to point." +POSITION can be a buffer position or a marker, and defaults to point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "P") (image--delayed-change-size (if n (- 1 (/ (prefix-numeric-value n) 10.0)) @@ -1208,7 +1224,10 @@ image-decrease-size (defun image-mouse-increase-size (&optional event) "Increase the image size using the mouse-gesture EVENT. This increases the size of the image at the position specified by -EVENT, if any, by the default factor used by `image-increase-size'." +EVENT, if any, by the default factor used by `image-increase-size'. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "e") (when (listp event) (save-window-excursion @@ -1218,7 +1237,10 @@ image-mouse-increase-size (defun image-mouse-decrease-size (&optional event) "Decrease the image size using the mouse-gesture EVENT. This decreases the size of the image at the position specified by -EVENT, if any, by the default factor used by `image-decrease-size'." +EVENT, if any, by the default factor used by `image-decrease-size'. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "e") (when (listp event) (save-window-excursion @@ -1269,7 +1291,9 @@ image--change-size (new-image (image--image-without-parameters image)) (scale (image--current-scaling image new-image))) (setcdr image (cdr new-image)) - (plist-put (cdr image) :scale (* scale factor)))) + (plist-put (cdr image) :scale (* scale factor)) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image))))) =20 (defun image--image-without-parameters (image) (cons (pop image) @@ -1296,7 +1320,10 @@ image-rotate If nil, ANGLE defaults to 90. Interactively, rotate the image 90 degrees clockwise with no prefix argument, and counter-clockwise with a prefix argument. Note that most image types support -rotations by only multiples of 90 degrees." +rotations by only multiples of 90 degrees. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive (and current-prefix-arg '(-90))) (let ((image (image--get-imagemagick-and-warn))) (setf (image-property image :rotation) @@ -1304,7 +1331,9 @@ image-rotate (or angle 90)) ;; We don't want to exceed 360 degrees rotation, ;; because it's not seen as valid in Exif data. - 360)))) + 360))) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image)))) (set-transient-map image--repeat-map nil nil "Use %k for further adjustments")) =20 @@ -1325,23 +1354,191 @@ image-save (read-file-name "Write image to file: "))))) =20 (defun image-flip-horizontally () - "Horizontally flip the image under point." + "Horizontally flip the image under point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive) (let ((image (image--get-image))) (image-flush image) (setf (image-property image :flip) - (not (image-property image :flip))))) + (not (image-property image :flip))) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image))))) =20 (defun image-flip-vertically () - "Vertically flip the image under point." + "Vertically flip the image under point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive) (let ((image (image--get-image))) (image-rotate 180) (setf (image-property image :flip) - (not (image-property image :flip))))) + (not (image-property image :flip))) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image))))) =20 (define-obsolete-function-alias 'image-refresh #'image-flush "29.1") =20 +;;; Map transformation + +(defcustom image-recompute-map-p t + "Recompute image map when scaling, rotating, or flipping an image." + :type 'boolean + :version "30.1") + +(defun image--compute-map (image) + "Compute map for IMAGE suitable to be used as its :map property. +Return a copy of :original-image transformed based on IMAGE's :scale, +:rotation, and :flip. When IMAGE's :original-map is nil, return nil. +When :rotation is not a multiple of 90, return copy of :original-map." + (pcase-let* ((original-map (image-property image :original-map)) + (map (copy-tree original-map t)) + (scale (or (image-property image :scale) 1)) + (rotation (or (image-property image :rotation) 0)) + (flip (image-property image :flip)) + ((and size `(,width . ,height)) (image-size image t))) + (when (and ; Handle only 90-degree rotations + (zerop (mod rotation 1)) + (zerop (% (truncate rotation) 90))) + ;; SIZE fits MAP after transformations. Scale MAP before + ;; flip and rotate operations, since both need MAP to fit SIZE. + (image--scale-map map scale) + ;; In rendered images, rotation is always applied before flip. + (image--rotate-map + map rotation (if (or (=3D 90 rotation) (=3D 270 rotation)) + ;; If rotated =C2=B190=C2=B0, swap width and heigh= t. + (cons height width) + size)) + ;; After rotation, there's no need to swap width and height. + (image--flip-map map flip size)) + map)) + +(defun image--compute-original-map (image) + "Return original map for IMAGE. +If IMAGE lacks :map property, return nil. +When :rotation is not a multiple of 90, return copy of :map." + (when (image-property image :map) + (let* ((image-copy (copy-tree image t)) + (map (image-property image-copy :map)) + (scale (or (image-property image-copy :scale) 1)) + (rotation (or (image-property image-copy :rotation) 0)) + (flip (image-property image-copy :flip)) + (size (image-size image-copy t))) + (when (and ; Handle only 90-degree rotations + (zerop (mod rotation 1)) + (zerop (% (truncate rotation) 90))) + ;; In rendered images, rotation is always applied before flip. + ;; To undo the transformation, flip before rotating. + ;; SIZE fits MAP before it is transformed back to ORIGINAL-MAP. + ;; Therefore, scale MAP after flip and rotate operations, since + ;; both need MAP to fit SIZE. + (image--flip-map map flip size) + (image--rotate-map map (- rotation) size) + (image--scale-map map (/ 1.0 scale))) + map))) + +(defun image--scale-map (map scale) + "Scale MAP according to SCALE. +Destructively modifies and returns MAP." + (unless (=3D 1 scale) + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (setf (caar coords) (round (* (caar coords) scale))) + (setf (cdar coords) (round (* (cdar coords) scale))) + (setf (cadr coords) (round (* (cadr coords) scale))) + (setf (cddr coords) (round (* (cddr coords) scale)))) + ('circle + (setf (caar coords) (round (* (caar coords) scale))) + (setf (cdar coords) (round (* (cdar coords) scale))) + (setcdr coords (round (* (cdr coords) scale)))) + ('poly + (dotimes (i (length coords)) + (aset coords i + (round (* (aref coords i) scale)))))))) + map) + +(defun image--rotate-map (map rotation size) + "Rotate MAP according to ROTATION and SIZE. +Destructively modifies and returns MAP." + (unless (zerop rotation) + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (let ( x0 y0 ; New upper left corner + x1 y1) ; New bottom right corner + (pcase (truncate (mod rotation 360)) ; Set new corners to... + (90 ; ...old bottom left and upper right + (setq x0 (caar coords) y0 (cddr coords) + x1 (cadr coords) y1 (cdar coords))) + (180 ; ...old bottom right and upper left + (setq x0 (cadr coords) y0 (cddr coords) + x1 (caar coords) y1 (cdar coords))) + (270 ; ...old upper right and bottom left + (setq x0 (cadr coords) y0 (cdar coords) + x1 (caar coords) y1 (cddr coords)))) + (setcar coords (image--rotate-coord x0 y0 rotation size)) + (setcdr coords (image--rotate-coord x1 y1 rotation size)))) + ('circle + (setcar coords (image--rotate-coord + (caar coords) (cdar coords) rotation size))) + ('poly + (dotimes (i (length coords)) + (when (=3D 0 (% i 2)) + (pcase-let ((`(,x . ,y) + (image--rotate-coord + (aref coords i) (aref coords (1+ i)) rotation s= ize))) + (aset coords i x) + (aset coords (1+ i) y)))))))) + map) + +(defun image--rotate-coord (x y angle size) + "Rotate coordinates X and Y by ANGLE in image of SIZE. +ANGLE must be a multiple of 90. Returns a cons cell of rounded +coordinates (X1 Y1)." + (pcase-let* ((radian (* (/ angle 180.0) float-pi)) + (`(,width . ,height) size) + ;; y is positive, but we are in the bottom-right quadrant + (y (- y)) + ;; Rotate clockwise + (x1 (+ (* (sin radian) y) (* (cos radian) x))) + (y1 (- (* (cos radian) y) (* (sin radian) x))) + ;; Translate image back into bottom-right quadrant + (`(,x1 . ,y1) + (pcase (truncate (mod angle 360)) + (90 ; Translate right by height + (cons (+ x1 height) y1)) + (180 ; Translate right by width and down by height + (cons (+ x1 width) (- y1 height))) + (270 ; Translate down by width + (cons x1 (- y1 width))))) + ;; Invert y1 to make both x1 and y1 positive + (y1 (- y1))) + (cons (round x1) (round y1)))) + +(defun image--flip-map (map flip size) + "Horizontally flip MAP according to FLIP and SIZE. +Destructively modifies and returns MAP." + (when flip + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (let ((x0 (- (car size) (cadr coords))) + (y0 (cdar coords)) + (x1 (- (car size) (caar coords))) + (y1 (cddr coords))) + (setcar coords (cons x0 y0)) + (setcdr coords (cons x1 y1)))) + ('circle + (setf (caar coords) (- (car size) (caar coords)))) + ('poly + (dotimes (i (length coords)) + (when (=3D 0 (% i 2)) + (aset coords i (- (car size) (aref coords i))))))))) + map) + (provide 'image) =20 ;;; image.el ends here diff --git a/test/lisp/image-tests.el b/test/lisp/image-tests.el index 80142d6d6de..46b1704f25a 100644 --- a/test/lisp/image-tests.el +++ b/test/lisp/image-tests.el @@ -153,4 +153,147 @@ image-rotate (image-rotate -154.5) (should (equal image '(image :rotation 91.0))))) =20 +;;;; Transforming maps + +(ert-deftest image-create-image-with-map () + "Test that `create-image' correctly adds :map and/or :original-map." + (skip-unless (display-images-p)) + (let ((data "foo") + (map '(((circle (1 . 1) . 1) a))) + (original-map '(((circle (2 . 2) . 2) a))) + (original-map-other '(((circle (3 . 3) . 3) a)))) + ;; Generate :original-map from :map. + (let* ((image (create-image data 'svg t :map map :scale 0.5)) + (got-original-map (image-property image :original-map))) + (should (equal got-original-map original-map))) + ;; Generate :map from :original-map. + (let* ((image (create-image + data 'svg t :original-map original-map :scale 0.5)) + (got-map (image-property image :map))) + (should (equal got-map map))) + ;; Use :original-map if both it and :map are specified. + (let* ((image (create-image + data 'svg t :map map + :original-map original-map-other :scale 0.5)) + (got-original-map (image-property image :original-map))) + (should (equal got-original-map original-map-other))))) + +(defun image-tests--map-equal (a b &optional tolerance) + "Return t if maps A and B have the same coordinates within TOLERANCE. +Since image sizes calculations vary on different machines, this function +allows for each image map coordinate in A to be within TOLERANCE to the +corresponding coordinate in B. When nil, TOLERANCE defaults to 5." + (unless tolerance (setq tolerance 5)) + (catch 'different + (cl-labels ((check-tolerance + (coord-a coord-b) + (unless (>=3D tolerance (abs (- coord-a coord-b))) + (throw 'different nil)))) + (dotimes (i (length a) t) + (pcase-let ((`((,type-a . ,coords-a) ,_id ,_plist) (nth i a)) + (`((,type-b . ,coords-b) ,_id ,_plist) (nth i b))) + (unless (eq type-a type-b) + (throw 'different nil)) + (pcase-exhaustive type-a + ('rect + (check-tolerance (caar coords-a) (caar coords-b)) + (check-tolerance (cdar coords-a) (cdar coords-b)) + (check-tolerance (cadr coords-a) (cadr coords-b)) + (check-tolerance (cddr coords-a) (cddr coords-b))) + ('circle + (check-tolerance (caar coords-a) (caar coords-b)) + (check-tolerance (cdar coords-a) (cdar coords-b)) + (check-tolerance (cdar coords-a) (cdar coords-b))) + ('poly + (dotimes (i (length coords-a)) + (check-tolerance (aref coords-a i) (aref coords-b i))))))))= )) + +(ert-deftest image--compute-map-and-original-map () + "Test `image--compute-map' and `image--compute-original-map'." + (skip-unless (display-images-p)) + (let* ((svg-string "ABC") + (original-map + '(((circle (41 . 29) . 24) "a" (help-echo "A")) + ((rect (5 . 101) 77 . 149) "b" (help-echo "B")) + ((poly . [161 29 160 22 154 15 146 10 136 7 125 5 114 7 104 10= 96 15 91 22 89 29 91 37 96 43 104 49 114 52 125 53 136 52 146 49 154 43 16= 0 37]) "c" (help-echo "C")))) + (scaled-map + '(((circle (82 . 58) . 48) "a" (help-echo "A")) + ((rect (10 . 202) 154 . 298) "b" (help-echo "B")) + ((poly . [322 58 320 44 308 30 292 20 272 14 250 10 228 14 208= 20 192 30 182 44 178 58 182 74 192 86 208 98 228 104 250 106 272 104 292 9= 8 308 86 320 74]) "c" (help-echo "C")))) + (flipped-map + '(((circle (125 . 29) . 24) "a" (help-echo "A")) + ((rect (89 . 101) 161 . 149) "b" (help-echo "B")) + ((poly . [5 29 6 22 12 15 20 10 30 7 41 5 52 7 62 10 70 15 75 = 22 77 29 75 37 70 43 62 49 52 52 41 53 30 52 20 49 12 43 6 37]) "c" (help-e= cho "C")))) + (rotated-map + '(((circle (126 . 41) . 24) "a" (help-echo "A")) + ((rect (6 . 5) 54 . 77) "b" (help-echo "B")) + ((poly . [126 161 133 160 140 154 145 146 148 136 150 125 148 = 114 145 104 140 96 133 91 126 89 118 91 112 96 106 104 103 114 102 125 103 = 136 106 146 112 154 118 160]) "c" (help-echo "C")))) + (scaled-rotated-flipped-map + '(((circle (58 . 82) . 48) "a" (help-echo "A")) + ((rect (202 . 10) 298 . 154) "b" (help-echo "B")) + ((poly . [58 322 44 320 30 308 20 292 14 272 10 250 14 228 20 = 208 30 192 44 182 58 178 74 182 86 192 98 208 104 228 106 250 104 272 98 29= 2 86 308 74 320]) "c" (help-echo "C")))) + (image (create-image svg-string 'svg t :map scaled-rotated-flippe= d-map + :scale 2 :rotation 90 :flip t))) + ;; Test that `image--compute-original-map' correctly generates + ;; original-map when creating an already transformed image. + (should (image-tests--map-equal (image-property image :original-map) + original-map)) + (setf (image-property image :flip) nil) + (setf (image-property image :rotation) 0) + (setf (image-property image :scale) 2) + (should (image-tests--map-equal (image--compute-map image) + scaled-map)) + (setf (image-property image :scale) 1) + (setf (image-property image :rotation) 90) + (should (image-tests--map-equal (image--compute-map image) + rotated-map)) + (setf (image-property image :rotation) 0) + (setf (image-property image :flip) t) + (should (image-tests--map-equal (image--compute-map image) + flipped-map)) + (setf (image-property image :scale) 2) + (setf (image-property image :rotation) 90) + (should (image-tests--map-equal (image--compute-map image) + scaled-rotated-flipped-map)) + + ;; Uncomment to test manually by interactively transforming the + ;; image and checking the map boundaries by hovering them. + + ;; (with-current-buffer (get-buffer-create "*test image map*") + ;; (erase-buffer) + ;; (insert-image image) + ;; (goto-char (point-min)) + ;; (pop-to-buffer (current-buffer))) + )) + +(ert-deftest image-transform-map () + "Test functions related to transforming image maps." + (let ((map '(((circle (4 . 3) . 2) "circle") + ((rect (3 . 6) 8 . 8) "rect") + ((poly . [6 11 7 13 2 14]) "poly"))) + (width 10) + (height 15)) + (should (equal (image--scale-map (copy-tree map t) 2) + '(((circle (8 . 6) . 4) "circle") + ((rect (6 . 12) 16 . 16) "rect") + ((poly . [12 22 14 26 4 28]) "poly")))) + (should (equal (image--rotate-map (copy-tree map t) 90 `(,width . ,hei= ght)) + '(((circle (12 . 4) . 2) "circle") + ((rect (7 . 3) 9 . 8) "rect") + ((poly . [4 6 2 7 1 2]) "poly")))) + (should (equal (image--flip-map (copy-tree map t) t `(,width . ,height= )) + '(((circle (6 . 3) . 2) "circle") + ((rect (2 . 6) 7 . 8) "rect") + ((poly . [4 11 3 13 8 14]) "poly")))) + (let ((copy (copy-tree map t))) + (image--scale-map copy 2) + ;; Scale size because the map has been scaled. + (image--rotate-map copy 90 `(,(* 2 width) . ,(* 2 height))) + ;; Swap width and height because the map has been flipped. + (image--flip-map copy t `(,(* 2 height) . ,(* 2 width))) + (should (equal copy + '(((circle (6 . 8) . 4) "circle") + ((rect (12 . 6) 16 . 16) "rect") + ((poly . [22 12 26 14 28 4]) "poly"))))))) + ;;; image-tests.el ends here --=20 2.41.0 --=-=-=-- From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 23 Mar 2024 08:22:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Joseph Turner Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.171118209519561 (code B ref 69602); Sat, 23 Mar 2024 08:22:01 +0000 Received: (at 69602) by debbugs.gnu.org; 23 Mar 2024 08:21:35 +0000 Received: from localhost ([127.0.0.1]:35793 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rnwdD-00055L-9w for submit@debbugs.gnu.org; Sat, 23 Mar 2024 04:21:35 -0400 Received: from eggs.gnu.org ([209.51.188.92]:35804) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rnwdB-00054x-GM for 69602@debbugs.gnu.org; Sat, 23 Mar 2024 04:21:34 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rnwHE-0007aN-AU; Sat, 23 Mar 2024 03:58:52 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=MIME-version:References:Subject:In-Reply-To:To:From: Date; bh=a4hEaDMNeTGKXSwySZp37/uRgjOLcnXm/N0IyuhrJHA=; b=BQIfVqblyvCgJAbKvARw lY25x1UnplBTeu3fXVIUJ59igdN8La73spQyp2ESbypFAvjy14oSyN30tZM3kzUwwv/gkGngZI30F K2FGcJ+CJ3ybzaEBts6zbXqsruKJOjXEZMKRykFKHQBiQ92M0j/5KVacR3s15n01/LRlCtEAbwmXZ rZQ5c8pCMrrRVIbBDxOMfJJXXXVHl5jmnr3a2O3bdSftbCZPT7ec+FwduxnTBSZWZYbRnLGNTDnLw pZZbtIExluIMMwtlpA55i6cv3g21sM/xKoep4wyDD6shIkjt2Ezwngcb/svr9EGjPQlIBhzOm9Y0L GCCsOyPaH1uFOg==; Date: Sat, 23 Mar 2024 09:58:51 +0200 Message-Id: <86cyrlz8ec.fsf@gnu.org> From: Eli Zaretskii In-Reply-To: <877cht94sb.fsf@breatheoutbreathe.in> (message from Joseph Turner on Fri, 22 Mar 2024 17:11:17 -0700) References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> <87bk7phzdz.fsf@breatheoutbreathe.in> <8634t10woh.fsf@gnu.org> <87il1g5b8m.fsf@breatheoutbreathe.in> <86y1ab23sw.fsf@gnu.org> <877cht94sb.fsf@breatheoutbreathe.in> MIME-version: 1.0 Content-type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Joseph Turner > Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net > Date: Fri, 22 Mar 2024 17:11:17 -0700 > > > Thanks. The tests you added have some problems: > > > > . you use thread-first, but don't require subr-x when compiling > > I removed thread-first. > > > . the tests fail when run in batch mode > > I added (skip-unless (display-images-p)) to the two problematic tests, > and it solved the issue on my machine. Solves it here as well. > > . when invoked interactively in a GUI session, one test fails: > > > > F image--compute-map-and-original-map > > Test ‘image--compute-map’ and ‘image--compute-original-map’. > > (ert-test-failed > > ((should (equal (image--compute-map image) flipped-map)) :form > > (equal > > (((circle ... . 24) "a" (help-echo "A")) > > ((rect ... 162 . 149) "b" (help-echo "B")) > > ((poly . [6 29 7 22 13 15 21 10 31 7 ...]) "c" (help-echo "C"))) > > (((circle ... . 24) "a" (help-echo "A")) > > ((rect ... 161 . 149) "b" (help-echo "B")) > > ((poly . [5 29 6 22 12 15 20 10 30 7 ...]) "c" (help-echo "C")))) > > :value nil :explanation (list-elt 0 (list-elt 0 (cdr (car ...)))))) > > > > It looks like some pixels do not match exactly? Perhaps some > > tolerances need to be allowed? > > Interesting - does the result of `image-size` vary per machine? I guess so. The transformations are AFAIK done in floating-point arithmetics, so some minor inaccuracies are possible. > In any case, I added `image-tests--map-equal' to compare image maps with > some tolerance. Do the tests pass on your machine now? Yes, they do now, thanks. However, there's a warning when compiling the tests: In image-tests--map-equal: lisp/image-tests.el:192:17: Warning: Unused lexical variable `i' Can you fix this, please? From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 23 Mar 2024 17:56:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-submit@debbugs.gnu.org id=B69602.171121652015346 (code B ref 69602); Sat, 23 Mar 2024 17:56:01 +0000 Received: (at 69602) by debbugs.gnu.org; 23 Mar 2024 17:55:20 +0000 Received: from localhost ([127.0.0.1]:43745 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ro5aQ-0003zP-HO for submit@debbugs.gnu.org; Sat, 23 Mar 2024 13:55:20 -0400 Received: from out-174.mta0.migadu.com ([91.218.175.174]:23029) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ro5QP-0003S3-Q9 for 69602@debbugs.gnu.org; Sat, 23 Mar 2024 13:44:59 -0400 References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> <87bk7phzdz.fsf@breatheoutbreathe.in> <8634t10woh.fsf@gnu.org> <87il1g5b8m.fsf@breatheoutbreathe.in> <86y1ab23sw.fsf@gnu.org> <877cht94sb.fsf@breatheoutbreathe.in> <86cyrlz8ec.fsf@gnu.org> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1711215820; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=ijxpk/MwE1yi2uDHQ9lpwLshSND9S2xZ7eb/i3SzhiU=; b=ByItrmZPdY+6ROo+UvJTKqv9VDY3zFIOUiUVmq0gQiU9zciNVfnaseyJpDOI1ENFz9kZX3 fMnzu6SzhKA4h9WJGpGO/qmRz8k3AbZBMPEtVv7+JO9v3iCYxKswNlZ93vd8Z0KQgvlNyJ 7jJ0OtLkFWbuOHe0M6JgQrGp3RI/C/c= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Joseph Turner Date: Sat, 23 Mar 2024 10:41:59 -0700 In-reply-to: <86cyrlz8ec.fsf@gnu.org> Message-ID: <87v85concp.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Migadu-Flow: FLOW_OUT X-Spam-Score: 0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Eli Zaretskii writes: >> From: Joseph Turner >> Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net >> Date: Fri, 22 Mar 2024 17:11:17 -0700 >> >> > Thanks. The tests you added have some problems: >> > >> > . you use thread-first, but don't require subr-x when compiling >> >> I removed thread-first. >> >> > . the tests fail when run in batch mode >> >> I added (skip-unless (display-images-p)) to the two problematic tests, >> and it solved the issue on my machine. > > Solves it here as well. > >> > . when invoked interactively in a GUI session, one test fails: >> > >> > F image--compute-map-and-original-map >> > Test =E2=80=98image--compute-map=E2=80=99 and =E2=80=98image--compute= -original-map=E2=80=99. >> > (ert-test-failed >> > ((should (equal (image--compute-map image) flipped-map)) :form >> > (equal >> > (((circle ... . 24) "a" (help-echo "A")) >> > ((rect ... 162 . 149) "b" (help-echo "B")) >> > ((poly . [6 29 7 22 13 15 21 10 31 7 ...]) "c" (help-echo "C"))) >> > (((circle ... . 24) "a" (help-echo "A")) >> > ((rect ... 161 . 149) "b" (help-echo "B")) >> > ((poly . [5 29 6 22 12 15 20 10 30 7 ...]) "c" (help-echo "C")))) >> > :value nil :explanation (list-elt 0 (list-elt 0 (cdr (car ...)))))) >> > >> > It looks like some pixels do not match exactly? Perhaps some >> > tolerances need to be allowed? >> >> Interesting - does the result of `image-size` vary per machine? > > I guess so. The transformations are AFAIK done in floating-point > arithmetics, so some minor inaccuracies are possible. > >> In any case, I added `image-tests--map-equal' to compare image maps with >> some tolerance. Do the tests pass on your machine now? > > Yes, they do now, thanks. However, there's a warning when compiling > the tests: > > In image-tests--map-equal: > lisp/image-tests.el:192:17: Warning: Unused lexical variable `i' > > Can you fix this, please? Oops! I just re-read the dotimes docstring to discover that the RESULT arg is deprecated. Fixed. Thank you! Joseph --=-=-= Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename=0001-Recompute-map-when-image-scale-rotation-or-flip-chan.patch Content-Transfer-Encoding: quoted-printable >From 0b6f90a1ba757426ff429693914828ce0d93d839 Mon Sep 17 00:00:00 2001 From: Joseph Turner Date: Thu, 7 Mar 2024 21:55:00 -0800 Subject: [PATCH] Recompute :map when image :scale, :rotation, or :flip chan= ges Now, when transforming an image, its :map is recomputed to fit. Image map coordinates are integers, so when computing :map, coordinates are rounded. To prevent an image from drifting from its map after repeated transformations, `create-image' now adds a new image property :original-map, which is combined with the image's transformation parameters to recompute :map. * lisp/image.el (image-recompute-map-p): Add user option to control whether :map is recomputed when an image is transformed. (create-image): Create :map from :original-map and vice versa. (image--delayed-change-size): Fix comment. (image--change-size, image-rotate, image-flip-horizontally, image-flip-vertically): Recompute image map after transformation and mention image-recompute-map-p in docstring. (image--compute-map): Add function to compute a map from original map. (image--compute-map): Add function to compute an original map from map. (image--scale-map): Add function to scale a map based on :scale. (image--rotate-map): Add function to rotate a map based on :rotation. (image--rotate-coord): Add function to rotate a map coordinate pair. (image--flip-map): Add function to flip a map based on :flip. (image-increase-size, image-decrease-size, image-mouse-increase-size, image-mouse-decrease-size): Mention image-recompute-map-p in docstring. * etc/NEWS: Add NEWS entry. * doc/lispref/display.texi (Image Descriptors): Document :original-map and new user option image-recompute-map-p. * test/lisp/image-tests.el (image--compute-map-and-original-map): Test `image--compute-map' and `image--compute-original-map'. (image-tests--map-equal): Add equality predicate to compare image maps. (image-create-image-with-map): Test that `create-image' adds :map and/or :original-map as appropriate. (image-transform-map): Test functions related to transforming maps. --- doc/lispref/display.texi | 24 +++++ etc/NEWS | 12 +++ lisp/image.el | 221 ++++++++++++++++++++++++++++++++++++--- test/lisp/image-tests.el | 144 +++++++++++++++++++++++++ 4 files changed, 389 insertions(+), 12 deletions(-) diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 10cf5ce89e2..8335a02b5c5 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -6055,6 +6055,30 @@ Image Descriptors when creating the image, or use the result of @code{image-compute-scaling-factor} to compute the elements of the map. + +When an image's @code{:scale}, @code{:rotation}, or @code{:flip} is +changed, @code{:map} will be recomputed based on the value of +@code{:original-map} and the values of those transformation. + +@item :original-map @var{original-map} +@cindex original image map +This specifies the untransformed image map which will be used to +recompute @code{:map} after the image's @code{:scale}, @code{:rotation}, +or @code{:flip} is changed. + +If @code{:original-map} is not specified when creating an image with +@code{create-image}, it will be computed based on the supplied +@code{:map}, as well as any of @code{:scale}, @code{:rotation}, or +@code{:flip} which are non-nil. + +Conversely, if @code{:original-map} is specified but @code{:map} is not, +@code{:map} will be computed based on @code{:original-map}, +@code{:scale}, @code{:rotation}, and @code{:flip}. + +@defopt image-recompute-map-p +Set this user option to nil to prevent Emacs from automatically +recomputing an image @code{:map} based on its @code{:original-map}. +@end defopt @end table =20 @defun image-mask-p spec &optional frame diff --git a/etc/NEWS b/etc/NEWS index 06856602ea8..cbd97b495b2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1292,6 +1292,18 @@ without specifying a file, like this: (notifications-notify :title "I am playing music" :app-icon 'multimedia-player) =20 +** Image + ++++ +*** Image :map property is now recomputed when image is transformed. +Now images with clickable maps work as expected after you run commands +such as `image-increase-size', `image-decrease-size', `image-rotate', +`image-flip-horizontally', and `image-flip-vertically'. + ++++ +*** New user option 'image-recompute-map-p' +Set this option to nil to prevent Emacs from recomputing image maps. + ** Image Dired =20 *** New user option 'image-dired-thumb-naming'. diff --git a/lisp/image.el b/lisp/image.el index 2ebce59a98c..c5082c78b75 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -533,6 +533,16 @@ create-image ('t t) ('nil nil) (func (funcall func image))))))) + ;; Add original map from map. + (when (and (plist-get props :map) + (not (plist-get props :original-map))) + (setq image (nconc image (list :original-map + (image--compute-original-map image)= )))) + ;; Add map from original map. + (when (and (plist-get props :original-map) + (not (plist-get props :map))) + (setq image (nconc image (list :map + (image--compute-map image))))) image))) =20 (defun image--default-smoothing (image) @@ -1173,7 +1183,10 @@ image-increase-size If N is 3, then the image size will be increased by 30%. More generally, the image size is multiplied by 1 plus N divided by 10. N defaults to 2, which increases the image size by 20%. -POSITION can be a buffer position or a marker, and defaults to point." +POSITION can be a buffer position or a marker, and defaults to point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "P") (image--delayed-change-size (if n (1+ (/ (prefix-numeric-value n) 10.0)) @@ -1185,7 +1198,7 @@ image-increase-size (defun image--delayed-change-size (size position) ;; Wait for a bit of idle-time before actually performing the change, ;; so as to batch together sequences of closely consecutive size changes. - ;; `image--change-size' just changes one value in a plist. The actual + ;; `image--change-size' just changes two values in a plist. The actual ;; image resizing happens later during redisplay. So if those ;; consecutive calls happen without any redisplay between them, ;; the costly operation of image resizing should happen only once. @@ -1196,7 +1209,10 @@ image-decrease-size If N is 3, then the image size will be decreased by 30%. More generally, the image size is multiplied by 1 minus N divided by 10. N defaults to 2, which decreases the image size by 20%. -POSITION can be a buffer position or a marker, and defaults to point." +POSITION can be a buffer position or a marker, and defaults to point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "P") (image--delayed-change-size (if n (- 1 (/ (prefix-numeric-value n) 10.0)) @@ -1208,7 +1224,10 @@ image-decrease-size (defun image-mouse-increase-size (&optional event) "Increase the image size using the mouse-gesture EVENT. This increases the size of the image at the position specified by -EVENT, if any, by the default factor used by `image-increase-size'." +EVENT, if any, by the default factor used by `image-increase-size'. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "e") (when (listp event) (save-window-excursion @@ -1218,7 +1237,10 @@ image-mouse-increase-size (defun image-mouse-decrease-size (&optional event) "Decrease the image size using the mouse-gesture EVENT. This decreases the size of the image at the position specified by -EVENT, if any, by the default factor used by `image-decrease-size'." +EVENT, if any, by the default factor used by `image-decrease-size'. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive "e") (when (listp event) (save-window-excursion @@ -1269,7 +1291,9 @@ image--change-size (new-image (image--image-without-parameters image)) (scale (image--current-scaling image new-image))) (setcdr image (cdr new-image)) - (plist-put (cdr image) :scale (* scale factor)))) + (plist-put (cdr image) :scale (* scale factor)) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image))))) =20 (defun image--image-without-parameters (image) (cons (pop image) @@ -1296,7 +1320,10 @@ image-rotate If nil, ANGLE defaults to 90. Interactively, rotate the image 90 degrees clockwise with no prefix argument, and counter-clockwise with a prefix argument. Note that most image types support -rotations by only multiples of 90 degrees." +rotations by only multiples of 90 degrees. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive (and current-prefix-arg '(-90))) (let ((image (image--get-imagemagick-and-warn))) (setf (image-property image :rotation) @@ -1304,7 +1331,9 @@ image-rotate (or angle 90)) ;; We don't want to exceed 360 degrees rotation, ;; because it's not seen as valid in Exif data. - 360)))) + 360))) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image)))) (set-transient-map image--repeat-map nil nil "Use %k for further adjustments")) =20 @@ -1325,23 +1354,191 @@ image-save (read-file-name "Write image to file: "))))) =20 (defun image-flip-horizontally () - "Horizontally flip the image under point." + "Horizontally flip the image under point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive) (let ((image (image--get-image))) (image-flush image) (setf (image-property image :flip) - (not (image-property image :flip))))) + (not (image-property image :flip))) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image))))) =20 (defun image-flip-vertically () - "Vertically flip the image under point." + "Vertically flip the image under point. + +When user option `image-recompute-map-p' is non-nil, the image's `:map' +is recomputed to fit the newly transformed image." (interactive) (let ((image (image--get-image))) (image-rotate 180) (setf (image-property image :flip) - (not (image-property image :flip))))) + (not (image-property image :flip))) + (when (and (image-property image :original-map) image-recompute-map-p) + (setf (image-property image :map) (image--compute-map image))))) =20 (define-obsolete-function-alias 'image-refresh #'image-flush "29.1") =20 +;;; Map transformation + +(defcustom image-recompute-map-p t + "Recompute image map when scaling, rotating, or flipping an image." + :type 'boolean + :version "30.1") + +(defun image--compute-map (image) + "Compute map for IMAGE suitable to be used as its :map property. +Return a copy of :original-image transformed based on IMAGE's :scale, +:rotation, and :flip. When IMAGE's :original-map is nil, return nil. +When :rotation is not a multiple of 90, return copy of :original-map." + (pcase-let* ((original-map (image-property image :original-map)) + (map (copy-tree original-map t)) + (scale (or (image-property image :scale) 1)) + (rotation (or (image-property image :rotation) 0)) + (flip (image-property image :flip)) + ((and size `(,width . ,height)) (image-size image t))) + (when (and ; Handle only 90-degree rotations + (zerop (mod rotation 1)) + (zerop (% (truncate rotation) 90))) + ;; SIZE fits MAP after transformations. Scale MAP before + ;; flip and rotate operations, since both need MAP to fit SIZE. + (image--scale-map map scale) + ;; In rendered images, rotation is always applied before flip. + (image--rotate-map + map rotation (if (or (=3D 90 rotation) (=3D 270 rotation)) + ;; If rotated =C2=B190=C2=B0, swap width and heigh= t. + (cons height width) + size)) + ;; After rotation, there's no need to swap width and height. + (image--flip-map map flip size)) + map)) + +(defun image--compute-original-map (image) + "Return original map for IMAGE. +If IMAGE lacks :map property, return nil. +When :rotation is not a multiple of 90, return copy of :map." + (when (image-property image :map) + (let* ((image-copy (copy-tree image t)) + (map (image-property image-copy :map)) + (scale (or (image-property image-copy :scale) 1)) + (rotation (or (image-property image-copy :rotation) 0)) + (flip (image-property image-copy :flip)) + (size (image-size image-copy t))) + (when (and ; Handle only 90-degree rotations + (zerop (mod rotation 1)) + (zerop (% (truncate rotation) 90))) + ;; In rendered images, rotation is always applied before flip. + ;; To undo the transformation, flip before rotating. + ;; SIZE fits MAP before it is transformed back to ORIGINAL-MAP. + ;; Therefore, scale MAP after flip and rotate operations, since + ;; both need MAP to fit SIZE. + (image--flip-map map flip size) + (image--rotate-map map (- rotation) size) + (image--scale-map map (/ 1.0 scale))) + map))) + +(defun image--scale-map (map scale) + "Scale MAP according to SCALE. +Destructively modifies and returns MAP." + (unless (=3D 1 scale) + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (setf (caar coords) (round (* (caar coords) scale))) + (setf (cdar coords) (round (* (cdar coords) scale))) + (setf (cadr coords) (round (* (cadr coords) scale))) + (setf (cddr coords) (round (* (cddr coords) scale)))) + ('circle + (setf (caar coords) (round (* (caar coords) scale))) + (setf (cdar coords) (round (* (cdar coords) scale))) + (setcdr coords (round (* (cdr coords) scale)))) + ('poly + (dotimes (i (length coords)) + (aset coords i + (round (* (aref coords i) scale)))))))) + map) + +(defun image--rotate-map (map rotation size) + "Rotate MAP according to ROTATION and SIZE. +Destructively modifies and returns MAP." + (unless (zerop rotation) + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (let ( x0 y0 ; New upper left corner + x1 y1) ; New bottom right corner + (pcase (truncate (mod rotation 360)) ; Set new corners to... + (90 ; ...old bottom left and upper right + (setq x0 (caar coords) y0 (cddr coords) + x1 (cadr coords) y1 (cdar coords))) + (180 ; ...old bottom right and upper left + (setq x0 (cadr coords) y0 (cddr coords) + x1 (caar coords) y1 (cdar coords))) + (270 ; ...old upper right and bottom left + (setq x0 (cadr coords) y0 (cdar coords) + x1 (caar coords) y1 (cddr coords)))) + (setcar coords (image--rotate-coord x0 y0 rotation size)) + (setcdr coords (image--rotate-coord x1 y1 rotation size)))) + ('circle + (setcar coords (image--rotate-coord + (caar coords) (cdar coords) rotation size))) + ('poly + (dotimes (i (length coords)) + (when (=3D 0 (% i 2)) + (pcase-let ((`(,x . ,y) + (image--rotate-coord + (aref coords i) (aref coords (1+ i)) rotation s= ize))) + (aset coords i x) + (aset coords (1+ i) y)))))))) + map) + +(defun image--rotate-coord (x y angle size) + "Rotate coordinates X and Y by ANGLE in image of SIZE. +ANGLE must be a multiple of 90. Returns a cons cell of rounded +coordinates (X1 Y1)." + (pcase-let* ((radian (* (/ angle 180.0) float-pi)) + (`(,width . ,height) size) + ;; y is positive, but we are in the bottom-right quadrant + (y (- y)) + ;; Rotate clockwise + (x1 (+ (* (sin radian) y) (* (cos radian) x))) + (y1 (- (* (cos radian) y) (* (sin radian) x))) + ;; Translate image back into bottom-right quadrant + (`(,x1 . ,y1) + (pcase (truncate (mod angle 360)) + (90 ; Translate right by height + (cons (+ x1 height) y1)) + (180 ; Translate right by width and down by height + (cons (+ x1 width) (- y1 height))) + (270 ; Translate down by width + (cons x1 (- y1 width))))) + ;; Invert y1 to make both x1 and y1 positive + (y1 (- y1))) + (cons (round x1) (round y1)))) + +(defun image--flip-map (map flip size) + "Horizontally flip MAP according to FLIP and SIZE. +Destructively modifies and returns MAP." + (when flip + (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) + (pcase-exhaustive type + ('rect + (let ((x0 (- (car size) (cadr coords))) + (y0 (cdar coords)) + (x1 (- (car size) (caar coords))) + (y1 (cddr coords))) + (setcar coords (cons x0 y0)) + (setcdr coords (cons x1 y1)))) + ('circle + (setf (caar coords) (- (car size) (caar coords)))) + ('poly + (dotimes (i (length coords)) + (when (=3D 0 (% i 2)) + (aset coords i (- (car size) (aref coords i))))))))) + map) + (provide 'image) =20 ;;; image.el ends here diff --git a/test/lisp/image-tests.el b/test/lisp/image-tests.el index 80142d6d6de..6a5f03e38a0 100644 --- a/test/lisp/image-tests.el +++ b/test/lisp/image-tests.el @@ -153,4 +153,148 @@ image-rotate (image-rotate -154.5) (should (equal image '(image :rotation 91.0))))) =20 +;;;; Transforming maps + +(ert-deftest image-create-image-with-map () + "Test that `create-image' correctly adds :map and/or :original-map." + (skip-unless (display-images-p)) + (let ((data "foo") + (map '(((circle (1 . 1) . 1) a))) + (original-map '(((circle (2 . 2) . 2) a))) + (original-map-other '(((circle (3 . 3) . 3) a)))) + ;; Generate :original-map from :map. + (let* ((image (create-image data 'svg t :map map :scale 0.5)) + (got-original-map (image-property image :original-map))) + (should (equal got-original-map original-map))) + ;; Generate :map from :original-map. + (let* ((image (create-image + data 'svg t :original-map original-map :scale 0.5)) + (got-map (image-property image :map))) + (should (equal got-map map))) + ;; Use :original-map if both it and :map are specified. + (let* ((image (create-image + data 'svg t :map map + :original-map original-map-other :scale 0.5)) + (got-original-map (image-property image :original-map))) + (should (equal got-original-map original-map-other))))) + +(defun image-tests--map-equal (a b &optional tolerance) + "Return t if maps A and B have the same coordinates within TOLERANCE. +Since image sizes calculations vary on different machines, this function +allows for each image map coordinate in A to be within TOLERANCE to the +corresponding coordinate in B. When nil, TOLERANCE defaults to 5." + (unless tolerance (setq tolerance 5)) + (catch 'different + (cl-labels ((check-tolerance + (coord-a coord-b) + (unless (>=3D tolerance (abs (- coord-a coord-b))) + (throw 'different nil)))) + (dotimes (i (length a)) + (pcase-let ((`((,type-a . ,coords-a) ,_id ,_plist) (nth i a)) + (`((,type-b . ,coords-b) ,_id ,_plist) (nth i b))) + (unless (eq type-a type-b) + (throw 'different nil)) + (pcase-exhaustive type-a + ('rect + (check-tolerance (caar coords-a) (caar coords-b)) + (check-tolerance (cdar coords-a) (cdar coords-b)) + (check-tolerance (cadr coords-a) (cadr coords-b)) + (check-tolerance (cddr coords-a) (cddr coords-b))) + ('circle + (check-tolerance (caar coords-a) (caar coords-b)) + (check-tolerance (cdar coords-a) (cdar coords-b)) + (check-tolerance (cdar coords-a) (cdar coords-b))) + ('poly + (dotimes (i (length coords-a)) + (check-tolerance (aref coords-a i) (aref coords-b i)))))))) + t)) + +(ert-deftest image--compute-map-and-original-map () + "Test `image--compute-map' and `image--compute-original-map'." + (skip-unless (display-images-p)) + (let* ((svg-string "ABC") + (original-map + '(((circle (41 . 29) . 24) "a" (help-echo "A")) + ((rect (5 . 101) 77 . 149) "b" (help-echo "B")) + ((poly . [161 29 160 22 154 15 146 10 136 7 125 5 114 7 104 10= 96 15 91 22 89 29 91 37 96 43 104 49 114 52 125 53 136 52 146 49 154 43 16= 0 37]) "c" (help-echo "C")))) + (scaled-map + '(((circle (82 . 58) . 48) "a" (help-echo "A")) + ((rect (10 . 202) 154 . 298) "b" (help-echo "B")) + ((poly . [322 58 320 44 308 30 292 20 272 14 250 10 228 14 208= 20 192 30 182 44 178 58 182 74 192 86 208 98 228 104 250 106 272 104 292 9= 8 308 86 320 74]) "c" (help-echo "C")))) + (flipped-map + '(((circle (125 . 29) . 24) "a" (help-echo "A")) + ((rect (89 . 101) 161 . 149) "b" (help-echo "B")) + ((poly . [5 29 6 22 12 15 20 10 30 7 41 5 52 7 62 10 70 15 75 = 22 77 29 75 37 70 43 62 49 52 52 41 53 30 52 20 49 12 43 6 37]) "c" (help-e= cho "C")))) + (rotated-map + '(((circle (126 . 41) . 24) "a" (help-echo "A")) + ((rect (6 . 5) 54 . 77) "b" (help-echo "B")) + ((poly . [126 161 133 160 140 154 145 146 148 136 150 125 148 = 114 145 104 140 96 133 91 126 89 118 91 112 96 106 104 103 114 102 125 103 = 136 106 146 112 154 118 160]) "c" (help-echo "C")))) + (scaled-rotated-flipped-map + '(((circle (58 . 82) . 48) "a" (help-echo "A")) + ((rect (202 . 10) 298 . 154) "b" (help-echo "B")) + ((poly . [58 322 44 320 30 308 20 292 14 272 10 250 14 228 20 = 208 30 192 44 182 58 178 74 182 86 192 98 208 104 228 106 250 104 272 98 29= 2 86 308 74 320]) "c" (help-echo "C")))) + (image (create-image svg-string 'svg t :map scaled-rotated-flippe= d-map + :scale 2 :rotation 90 :flip t))) + ;; Test that `image--compute-original-map' correctly generates + ;; original-map when creating an already transformed image. + (should (image-tests--map-equal (image-property image :original-map) + original-map)) + (setf (image-property image :flip) nil) + (setf (image-property image :rotation) 0) + (setf (image-property image :scale) 2) + (should (image-tests--map-equal (image--compute-map image) + scaled-map)) + (setf (image-property image :scale) 1) + (setf (image-property image :rotation) 90) + (should (image-tests--map-equal (image--compute-map image) + rotated-map)) + (setf (image-property image :rotation) 0) + (setf (image-property image :flip) t) + (should (image-tests--map-equal (image--compute-map image) + flipped-map)) + (setf (image-property image :scale) 2) + (setf (image-property image :rotation) 90) + (should (image-tests--map-equal (image--compute-map image) + scaled-rotated-flipped-map)) + + ;; Uncomment to test manually by interactively transforming the + ;; image and checking the map boundaries by hovering them. + + ;; (with-current-buffer (get-buffer-create "*test image map*") + ;; (erase-buffer) + ;; (insert-image image) + ;; (goto-char (point-min)) + ;; (pop-to-buffer (current-buffer))) + )) + +(ert-deftest image-transform-map () + "Test functions related to transforming image maps." + (let ((map '(((circle (4 . 3) . 2) "circle") + ((rect (3 . 6) 8 . 8) "rect") + ((poly . [6 11 7 13 2 14]) "poly"))) + (width 10) + (height 15)) + (should (equal (image--scale-map (copy-tree map t) 2) + '(((circle (8 . 6) . 4) "circle") + ((rect (6 . 12) 16 . 16) "rect") + ((poly . [12 22 14 26 4 28]) "poly")))) + (should (equal (image--rotate-map (copy-tree map t) 90 `(,width . ,hei= ght)) + '(((circle (12 . 4) . 2) "circle") + ((rect (7 . 3) 9 . 8) "rect") + ((poly . [4 6 2 7 1 2]) "poly")))) + (should (equal (image--flip-map (copy-tree map t) t `(,width . ,height= )) + '(((circle (6 . 3) . 2) "circle") + ((rect (2 . 6) 7 . 8) "rect") + ((poly . [4 11 3 13 8 14]) "poly")))) + (let ((copy (copy-tree map t))) + (image--scale-map copy 2) + ;; Scale size because the map has been scaled. + (image--rotate-map copy 90 `(,(* 2 width) . ,(* 2 height))) + ;; Swap width and height because the map has been flipped. + (image--flip-map copy t `(,(* 2 height) . ,(* 2 width))) + (should (equal copy + '(((circle (6 . 8) . 4) "circle") + ((rect (12 . 6) 16 . 16) "rect") + ((poly . [22 12 26 14 28 4]) "poly"))))))) + ;;; image-tests.el ends here --=20 2.41.0 --=-=-=-- From unknown Mon Aug 18 17:56:15 2025 MIME-Version: 1.0 X-Mailer: MIME-tools 5.505 (Entity 5.505) X-Loop: help-debbugs@gnu.org From: help-debbugs@gnu.org (GNU bug Tracking System) To: Joseph Turner Subject: bug#69602: closed (Re: bug#69602: 29.1; Image :map should adjust with :scale and :rotation) Message-ID: References: <86ttkwygml.fsf@gnu.org> <87msramv72.fsf@breatheoutbreathe.in> X-Gnu-PR-Message: they-closed 69602 X-Gnu-PR-Package: emacs Reply-To: 69602@debbugs.gnu.org Date: Sat, 23 Mar 2024 18:00:02 +0000 Content-Type: multipart/mixed; boundary="----------=_1711216802-16161-1" This is a multi-part message in MIME format... ------------=_1711216802-16161-1 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Your bug report #69602: 29.1; Image :map should adjust with :scale and :rotation which was filed against the emacs package, has been closed. The explanation is attached below, along with your original report. If you require more details, please reply to 69602@debbugs.gnu.org. --=20 69602: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=3D69602 GNU Bug Tracking System Contact help-debbugs@gnu.org with problems ------------=_1711216802-16161-1 Content-Type: message/rfc822 Content-Disposition: inline Content-Transfer-Encoding: 7bit Received: (at 69602-done) by debbugs.gnu.org; 23 Mar 2024 17:59:40 +0000 Received: from localhost ([127.0.0.1]:44054 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ro5ed-0004BD-Qq for submit@debbugs.gnu.org; Sat, 23 Mar 2024 13:59:40 -0400 Received: from eggs.gnu.org ([209.51.188.92]:48580) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ro5ea-0004Ah-Gs for 69602-done@debbugs.gnu.org; Sat, 23 Mar 2024 13:59:37 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ro5dp-0002qI-0Z; Sat, 23 Mar 2024 13:58:49 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=JwDYxjo7NJvlhBE+1uN19ll10U4KGYQuXoHA8sFQcgU=; b=ehrzGQ1It8e+ EqijXgPV8LS0NqtYFwPm7sBFl5bxKT/ANF61NY1UeQsFUgqFMktvZmshriOo4EAGX8yF5P5lpuSuu C2LlCOSjZ6SIfa3m1Jlp8M1NRJrQ+iU9JFSSCxpXEfHGSz3UozdQJnajCpsPy7JlO9HfGdWYf+Vzy DrNjRGg0GzF76wYAY9qanEJ3CUEKJpIuUHF9+9zVnCWNrN3g0q07/lIvY9TLKLk9OMUreIuM1iOTj e/lA70meqJzFQKPgvoIDaZr/ZYvDuw0zpUXOeADYgVS3M3kDulnjitOc48y9thfOY8SGLpMlUNktU Y8koQIpB+rw0z8K2MCB4WA==; Date: Sat, 23 Mar 2024 19:58:42 +0200 Message-Id: <86ttkwygml.fsf@gnu.org> From: Eli Zaretskii To: Joseph Turner In-Reply-To: <87v85concp.fsf@breatheoutbreathe.in> (message from Joseph Turner on Sat, 23 Mar 2024 10:41:59 -0700) Subject: Re: bug#69602: 29.1; Image :map should adjust with :scale and :rotation References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> <87bk7phzdz.fsf@breatheoutbreathe.in> <8634t10woh.fsf@gnu.org> <87il1g5b8m.fsf@breatheoutbreathe.in> <86y1ab23sw.fsf@gnu.org> <877cht94sb.fsf@breatheoutbreathe.in> <86cyrlz8ec.fsf@gnu.org> <87v85concp.fsf@breatheoutbreathe.in> X-Spam-Score: -2.3 (--) X-Debbugs-Envelope-To: 69602-done Cc: 69602-done@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Joseph Turner > Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net > Date: Sat, 23 Mar 2024 10:41:59 -0700 > > Eli Zaretskii writes: > > > In image-tests--map-equal: > > lisp/image-tests.el:192:17: Warning: Unused lexical variable `i' > > > > Can you fix this, please? > > Oops! I just re-read the dotimes docstring to discover that the RESULT > arg is deprecated. Fixed. Thank you! Thanks, installed on master, and closing the bug. ------------=_1711216802-16161-1 Content-Type: message/rfc822 Content-Disposition: inline Content-Transfer-Encoding: 7bit Received: (at submit) by debbugs.gnu.org; 7 Mar 2024 06:08:04 +0000 Received: from localhost ([127.0.0.1]:52298 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri6vD-00025g-MH for submit@debbugs.gnu.org; Thu, 07 Mar 2024 01:08:04 -0500 Received: from lists.gnu.org ([209.51.188.17]:39178) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ri6vA-00025G-SK for submit@debbugs.gnu.org; Thu, 07 Mar 2024 01:08:01 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ri6ue-0002KN-LC for bug-gnu-emacs@gnu.org; Thu, 07 Mar 2024 01:07:29 -0500 Received: from out-187.mta0.migadu.com ([91.218.175.187]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1ri6uc-0007gw-0Z for bug-gnu-emacs@gnu.org; Thu, 07 Mar 2024 01:07:28 -0500 X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1709791640; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=6iyDiCXdKMIDhabY5HAMfUpbIcBNW+2dnQI/8J9Xdas=; b=T1MgK+/Ug6ReujtODDWLNv8h5QU5I0M1LHnsYsS//gkauJkJWGlOLsztQqAVO6AxBlXFIX SrwkBjGDWOK9Hc3o3tbWQL+EbnNtoff/IgLftUFF244oEhQOEv9lKsqIgcdUw7Q4c+aZmb NRBjnhyZUMd8kx0uZecUjtnvWuj9uig= From: Joseph Turner To: bug-gnu-emacs@gnu.org Subject: 29.1; Image :map should adjust with :scale and :rotation Date: Wed, 06 Mar 2024 21:37:50 -0800 X-Debbugs-Cc: Stephen Berman , Juri Linkov Message-ID: <87msramv72.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: text/plain Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Received-SPF: pass client-ip=91.218.175.187; envelope-from=joseph@breatheoutbreathe.in; helo=out-187.mta0.migadu.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-Spam-Score: -1.4 (-) X-Debbugs-Envelope-To: submit X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -2.4 (--) Currently, when running `image-increase-size' or `image-decrease-size' on an image with a :map property, the image scales but the image map does not. For example, run the following snippet: (with-current-buffer (get-buffer-create "*image-properties-test*") (let ((svg "\n\n\n\n\n= \norggraphview\n\n\n\na\n\= n\nHover me!\n\n\n\n\n\n") (map '(((circle (85 . 85) . 80) "1" (help-echo "Surprise!")))) (inhibit-read-only t)) (erase-buffer) (insert-image (create-image svg 'svg t :map map)) (goto-char (point-min)) (pop-to-buffer (current-buffer)))) Hovering the circle alters the pointer style and displays the tooltip. Now run `M-x image-increase-size' or press "i +". While the image becomes larger, the area which activates the tooltip remains the same. See earlier discussion here: https://yhetil.org/emacs-devel/87r0gng41l.fsf@ushin.org/T/#t While a proper solution perhaps belongs on the C side, the following workaround adds an :unscaled-map property to images and sets :map according to :unscaled-map and :scale whenever :scale changes. This workaround does not (yet) handle :rotation. --8<---------------cut here---------------start------------->8--- (defun image--scale-map (map factor) "Scale MAP by FACTOR, destructively modifying it." (unless (=3D 1 factor) (pcase-dolist (`(,`(,type . ,coords) ,_id ,_plist) map) (pcase-exhaustive type ('rect (setf (caar coords) (round (* (caar coords) factor))) (setf (cdar coords) (round (* (cdar coords) factor))) (setf (cadr coords) (round (* (cadr coords) factor))) (setf (cddr coords) (round (* (cddr coords) factor)))) ('circle (setf (caar coords) (round (* (caar coords) factor))) (setf (cdar coords) (round (* (cdar coords) factor))) (setf (cdr coords) (round (* (cdr coords) factor)))) ('poly (dotimes (i (length coords)) (aset coords i (round (* (aref coords i) factor)))))))) map) (defun image--create-image-add-unscaled-map (orig-fun file-or-data &optional type data-p &rest props) "Add :unscaled-map property to image returned by ORIG-FUN and return it. Intended to be used as :around advice for `create-image'." (let ((image (apply orig-fun file-or-data type data-p props))) (when-let ((map (image-property image :map))) (setq image (nconc image (list :unscaled-map (copy-tree map t)))) (when-let* ((props-scale (plist-get props :scale)) ((numberp props-scale))) (setf (image-property image :unscaled-map) (image--scale-map (image-property image :unscaled-map) (/ 1.0 props-scale))))) image)) (advice-add #'create-image :around #'image--create-image-add-unscaled-map) (defun image--change-size-scale-map (_factor &optional position) "Scale :map property of image at point to fit its :scale. Intended to be used as :after advice for `image--change-size'." (when-let* ((image (image--get-imagemagick-and-warn position)) (map (image-property image :map)) (unscaled-map (image-property image :unscaled-map)) (scale (image-property image :scale))) (setf (image-property image :map) ;; TODO: Instead of copying `:unscaled-map', reuse the :map vecto= r? (image--scale-map (copy-tree unscaled-map t) scale)))) (advice-add #'image--change-size :after #'image--change-size-scale-map) --8<---------------cut here---------------end--------------->8--- Thank you! Joseph In GNU Emacs 29.1 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.37, cairo version 1.16.0) Windowing system distributor 'The X.Org Foundation', version 11.0.12101007 System Description: Debian GNU/Linux 12 (bookworm) ------------=_1711216802-16161-1-- From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 23 Mar 2024 18:40:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii Cc: 69602-done@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-done@debbugs.gnu.org id=D69602.171121916122670 (code D ref 69602); Sat, 23 Mar 2024 18:40:02 +0000 Received: (at 69602-done) by debbugs.gnu.org; 23 Mar 2024 18:39:21 +0000 Received: from localhost ([127.0.0.1]:46376 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ro6H2-0005tZ-SA for submit@debbugs.gnu.org; Sat, 23 Mar 2024 14:39:21 -0400 Received: from out-176.mta0.migadu.com ([91.218.175.176]:39639) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ro6H0-0005tA-GV for 69602-done@debbugs.gnu.org; Sat, 23 Mar 2024 14:39:20 -0400 References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> <87bk7phzdz.fsf@breatheoutbreathe.in> <8634t10woh.fsf@gnu.org> <87il1g5b8m.fsf@breatheoutbreathe.in> <86y1ab23sw.fsf@gnu.org> <877cht94sb.fsf@breatheoutbreathe.in> <86cyrlz8ec.fsf@gnu.org> <87v85concp.fsf@breatheoutbreathe.in> <86ttkwygml.fsf@gnu.org> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1711217916; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=TN42gkT8WlD6am97ykoV6sw9RXNPsoiSN0yQDQG5PEs=; b=ALk/noFJQFY8pcID520snV/YcK0BQKKo9lgxZLfmFCFXnnBHxIXmuFoEyMY+cdFpwvLs+4 VB5QoU5TkSFvrAjIbGbBcPnCNyaO9hCsE0naLtWSrQ1r7N/n60V2t9TRI0JR7cos6taM27 nBWoROaZuTY88i+EfDnnDVspZFgOwr4= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Joseph Turner Date: Sat, 23 Mar 2024 11:18:28 -0700 In-reply-to: <86ttkwygml.fsf@gnu.org> Message-ID: <87r0g0olqg.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: text/plain X-Migadu-Flow: FLOW_OUT X-Spam-Score: -0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) Thank you! Eli Zaretskii writes: >> From: Joseph Turner >> Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net >> Date: Sat, 23 Mar 2024 10:41:59 -0700 >> >> Eli Zaretskii writes: >> >> > In image-tests--map-equal: >> > lisp/image-tests.el:192:17: Warning: Unused lexical variable `i' >> > >> > Can you fix this, please? >> >> Oops! I just re-read the dotimes docstring to discover that the RESULT >> arg is deprecated. Fixed. Thank you! > > Thanks, installed on master, and closing the bug. From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Joseph Turner Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sat, 23 Mar 2024 20:34:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Eli Zaretskii Cc: 69602-done@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-done@debbugs.gnu.org id=D69602.17112260399491 (code D ref 69602); Sat, 23 Mar 2024 20:34:02 +0000 Received: (at 69602-done) by debbugs.gnu.org; 23 Mar 2024 20:33:59 +0000 Received: from localhost ([127.0.0.1]:52314 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ro83y-0002Sz-G2 for submit@debbugs.gnu.org; Sat, 23 Mar 2024 16:33:59 -0400 Received: from out-186.mta0.migadu.com ([91.218.175.186]:24621) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1ro83u-0002Sa-K4 for 69602-done@debbugs.gnu.org; Sat, 23 Mar 2024 16:33:56 -0400 References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> <87bk7phzdz.fsf@breatheoutbreathe.in> <8634t10woh.fsf@gnu.org> <87il1g5b8m.fsf@breatheoutbreathe.in> <86y1ab23sw.fsf@gnu.org> <877cht94sb.fsf@breatheoutbreathe.in> <86cyrlz8ec.fsf@gnu.org> <87v85concp.fsf@breatheoutbreathe.in> <86ttkwygml.fsf@gnu.org> DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=breatheoutbreathe.in; s=key1; t=1711225957; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=CXmToup9Pnq0OeEwUmM2ykCiTeegoElSUOhSe0eMOuQ=; b=ifFhUhqwcizmYu/Lv/h0aErZUSaJq/r3gZaQvKCt9SE1rRcjSsZunkNCHE3RbQjhRl+t49 RhkeO0FKA5x55QH6gccd5loKT+I5PSgd+JfZFd5EVYCZ2EbAfaD6fRPwG4T0iCc/o3xVc3 ra7yyh7IE4mJUKhMIiEAxRUTqAahY8Y= X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. From: Joseph Turner Date: Sat, 23 Mar 2024 13:32:05 -0700 In-reply-to: <86ttkwygml.fsf@gnu.org> Message-ID: <87frwgofj5.fsf@breatheoutbreathe.in> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Migadu-Flow: FLOW_OUT X-Spam-Score: -0.0 (/) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -1.0 (-) --=-=-= Content-Type: text/plain Eli Zaretskii writes: >> From: Joseph Turner >> Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net >> Date: Sat, 23 Mar 2024 10:41:59 -0700 >> >> Eli Zaretskii writes: >> >> > In image-tests--map-equal: >> > lisp/image-tests.el:192:17: Warning: Unused lexical variable `i' >> > >> > Can you fix this, please? >> >> Oops! I just re-read the dotimes docstring to discover that the RESULT >> arg is deprecated. Fixed. Thank you! > > Thanks, installed on master, and closing the bug. Another minor improvement. Please see patch. Joseph --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-copy-tree-just-image-map-not-entire-image.patch >From 359f9f889c2cad5e2f49aa4efd16bb18fe98e0a1 Mon Sep 17 00:00:00 2001 From: Joseph Turner Date: Sat, 23 Mar 2024 13:29:17 -0700 Subject: [PATCH] copy-tree just image map, not entire image. * lisp/image.el (image--compute-original-map): --- lisp/image.el | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/lisp/image.el b/lisp/image.el index 55340ea03dc..d7496485aca 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -1455,24 +1455,23 @@ image--compute-original-map If IMAGE lacks :map property, return nil. When :rotation is not a multiple of 90, return copy of :map." (when (image-property image :map) - (let* ((image-copy (copy-tree image t)) - (map (image-property image-copy :map)) - (scale (or (image-property image-copy :scale) 1)) - (rotation (or (image-property image-copy :rotation) 0)) - (flip (image-property image-copy :flip)) - (size (image-size image-copy t))) + (let* ((original-map (copy-tree (image-property image :map) t)) + (scale (or (image-property image :scale) 1)) + (rotation (or (image-property image :rotation) 0)) + (flip (image-property image :flip)) + (size (image-size image t))) (when (and ; Handle only 90-degree rotations (zerop (mod rotation 1)) (zerop (% (truncate rotation) 90))) ;; In rendered images, rotation is always applied before flip. - ;; To undo the transformation, flip before rotating. - ;; SIZE fits MAP before it is transformed back to ORIGINAL-MAP. - ;; Therefore, scale MAP after flip and rotate operations, since - ;; both need MAP to fit SIZE. - (image--flip-map map flip size) - (image--rotate-map map (- rotation) size) - (image--scale-map map (/ 1.0 scale))) - map))) + ;; To undo the transformation, flip before rotating. SIZE fits + ;; ORIGINAL-MAP before transformations are applied. Therefore, + ;; scale ORIGINAL-MAP after flip and rotate operations, since + ;; both need ORIGINAL-MAP to fit SIZE. + (image--flip-map original-map flip size) + (image--rotate-map original-map (- rotation) size) + (image--scale-map original-map (/ 1.0 scale))) + original-map))) (defun image--scale-map (map scale) "Scale MAP according to SCALE. -- 2.41.0 --=-=-=-- From unknown Mon Aug 18 17:56:15 2025 X-Loop: help-debbugs@gnu.org Subject: bug#69602: 29.1; Image :map should adjust with :scale and :rotation Resent-From: Eli Zaretskii Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 28 Mar 2024 10:14:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 69602 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: To: Joseph Turner Cc: 69602-done@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net Received: via spool by 69602-done@debbugs.gnu.org id=D69602.171162080425345 (code D ref 69602); Thu, 28 Mar 2024 10:14:01 +0000 Received: (at 69602-done) by debbugs.gnu.org; 28 Mar 2024 10:13:24 +0000 Received: from localhost ([127.0.0.1]:39130 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rpmlA-0006aj-Ai for submit@debbugs.gnu.org; Thu, 28 Mar 2024 06:13:24 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:38862) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1rpml7-0006aU-Sg for 69602-done@debbugs.gnu.org; Thu, 28 Mar 2024 06:13:23 -0400 Received: from fencepost.gnu.org ([2001:470:142:3::e]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rpml0-0005oP-Ce; Thu, 28 Mar 2024 06:13:14 -0400 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=gnu.org; s=fencepost-gnu-org; h=References:Subject:In-Reply-To:To:From:Date: mime-version; bh=lfQazrWLv4ZZHCTlf1NO9tHG68nOdxi6puNnY+6ObHA=; b=fddr882qofDL R6WwpOwfZIjcSzppYF2dEpERsKT4lLiXgly8MOQmZ3fqw662qnb0LBpJifs4MnXncZZzEHqrozo0C 6CIFAQmekeNsA4KPCvL9QRUTD63durf17/Bq423EAqxFwW/it/ZZNFvsXGJ85zCNFPAjBRTNI9AeI sFdjsxUm+ULWp8UuG9dgkVCZct3yeImQx5FnYbryNdwNfeP+WL/smUyH5UbhVA412dPVoxfO86ter KQG3sgNVm37X5fWNGeYXMEMHGkMFKr6dhZmVdIma3AmjcqERfC1bfGOf3DDsjGnSyTy+G7tbkmAkG dH+gCjPFyVUvjD1Ri4k2sw==; Date: Thu, 28 Mar 2024 12:13:09 +0200 Message-Id: <86bk6yhdfu.fsf@gnu.org> From: Eli Zaretskii In-Reply-To: <87frwgofj5.fsf@breatheoutbreathe.in> (message from Joseph Turner on Sat, 23 Mar 2024 13:32:05 -0700) References: <87msramv72.fsf@breatheoutbreathe.in> <86jzmejzfd.fsf@gnu.org> <87a5namqyz.fsf@breatheoutbreathe.in> <86cys63281.fsf@gnu.org> <87wmqelakx.fsf@breatheoutbreathe.in> <86bk7q2xye.fsf@gnu.org> <871q8lk2o7.fsf@breatheoutbreathe.in> <87o7bpi4c1.fsf@breatheoutbreathe.in> <867cid15wv.fsf@gnu.org> <87bk7phzdz.fsf@breatheoutbreathe.in> <8634t10woh.fsf@gnu.org> <87il1g5b8m.fsf@breatheoutbreathe.in> <86y1ab23sw.fsf@gnu.org> <877cht94sb.fsf@breatheoutbreathe.in> <86cyrlz8ec.fsf@gnu.org> <87v85concp.fsf@breatheoutbreathe.in> <86ttkwygml.fsf@gnu.org> <87frwgofj5.fsf@breatheoutbreathe.in> X-Spam-Score: -2.3 (--) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: debbugs-submit-bounces@debbugs.gnu.org Sender: "Debbugs-submit" X-Spam-Score: -3.3 (---) > From: Joseph Turner > Cc: 69602-done@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net > Date: Sat, 23 Mar 2024 13:32:05 -0700 > > Eli Zaretskii writes: > > >> From: Joseph Turner > >> Cc: 69602@debbugs.gnu.org, stephen.berman@gmx.net, juri@linkov.net > >> Date: Sat, 23 Mar 2024 10:41:59 -0700 > >> > >> Eli Zaretskii writes: > >> > >> > In image-tests--map-equal: > >> > lisp/image-tests.el:192:17: Warning: Unused lexical variable `i' > >> > > >> > Can you fix this, please? > >> > >> Oops! I just re-read the dotimes docstring to discover that the RESULT > >> arg is deprecated. Fixed. Thank you! > > > > Thanks, installed on master, and closing the bug. > > Another minor improvement. Please see patch. Thanks, installed.