From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: Mark Oteiza Newsgroups: gmane.emacs.devel Subject: Re: [PATCH] making image-dired thumbnail creation asynchronous Date: Fri, 16 Dec 2016 22:42:26 -0500 Message-ID: <20161217034226.GA5691@holos.localdomain> References: <87fulo8urt.fsf@udel.edu> <83mvfw8etl.fsf@gnu.org> <20161216131531.GA1009@holos.localdomain> <83d1gs7zpu.fsf@gnu.org> <20161216141614.GB1009@holos.localdomain> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: blaine.gmane.org 1481946203 14749 195.159.176.226 (17 Dec 2016 03:43:23 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Sat, 17 Dec 2016 03:43:23 +0000 (UTC) User-Agent: Mutt/1.7.2+8 (b112fd7061fb) (2016-11-26) Cc: emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sat Dec 17 04:43:18 2016 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cI5u0-0002Xf-9m for ged-emacs-devel@m.gmane.org; Sat, 17 Dec 2016 04:43:17 +0100 Original-Received: from localhost ([::1]:35210 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cI5u4-0005Ix-77 for ged-emacs-devel@m.gmane.org; Fri, 16 Dec 2016 22:43:20 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:46851) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1cI5tK-0005AV-Vi for emacs-devel@gnu.org; Fri, 16 Dec 2016 22:42:37 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1cI5tH-0003wS-Qf for emacs-devel@gnu.org; Fri, 16 Dec 2016 22:42:35 -0500 Original-Received: from mail-qk0-x231.google.com ([2607:f8b0:400d:c09::231]:32777) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1cI5tH-0003vV-JW for emacs-devel@gnu.org; Fri, 16 Dec 2016 22:42:31 -0500 Original-Received: by mail-qk0-x231.google.com with SMTP id t184so56307049qkd.0 for ; Fri, 16 Dec 2016 19:42:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=udel-edu.20150623.gappssmtp.com; s=20150623; h=date:from:to:cc:subject:message-id:references:mime-version :content-disposition:in-reply-to:user-agent; bh=UGvO0VCfPSPSgB0InnSmD9m49a+5aTrZ2l/UsediyBQ=; b=q9AEKB5PwRn3aJWO/A3P0ognGC/PZsK3rVWkTfffvpDMemh/VMWxX9L4ir847lDztj r8JG7TEyEZbJ1BGpzLHjH3/kADQ2KuKNWRrtzll6+3vkdeVhi9lY87t4z01G2GB/1iJh kFqS6PxzSikzThWYSn8r/pbWvUfbEKWi8wuSy8FuFnssujxwNPWUrK5/tjEfKJ1s0ZlY 5z1rbv2Yev/I6t1FeZWk8J6KFsjaWddoRr0SXBpDzI/WL49AkfCOV266co1ljnGStke5 odoNBFoyn51A6HdT6GePPd4pMVt/5nO4/hq84DPfIH4uu0la9P7PvzPYMiN1GUH3vHQ1 VOsw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:from:to:cc:subject:message-id:references :mime-version:content-disposition:in-reply-to:user-agent; bh=UGvO0VCfPSPSgB0InnSmD9m49a+5aTrZ2l/UsediyBQ=; b=VCUi5o42nTyVEHnc8TGLL0WMAlzZYQw59VbWbXE7Jdx98jmnS0QS037rtU4FrCTuQY zo9yLBuHR+r2hAZPew5+6igxPspG3fp9AD/AXpgBFkGqoQ0SyVbHC1jAvUUDcrHEiPkO sYLJ9vOyCGWhXK9D90za63uD/vKwurfykZnyo9lN9PBi/UBXd6VNX9C5GzWP6K80yix9 3ZM3ZjrmVjroyDLpOe2Wrv9mNyCKgCeJ2oQZBJZoU3F3tQkvsXUk89A0dhkN9TNArzHn PSS/3S/QQ+CbVAT0wND0ldjRWM+pUuakQ7njVoatQCiEWKKTDQuNBqD/9O5/MaDXyWpA 9oGQ== X-Gm-Message-State: AIkVDXI6t03zErL8Nu6lUmb48ZSYePHI3FKrpQBiMa3qTvxvCkf2T0aaQhy2BdsJKKf6PVrR X-Received: by 10.55.83.7 with SMTP id h7mr7147659qkb.104.1481946148977; Fri, 16 Dec 2016 19:42:28 -0800 (PST) Original-Received: from holos.localdomain ([69.250.222.218]) by smtp.gmail.com with ESMTPSA id d15sm5276001qkb.10.2016.12.16.19.42.27 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 16 Dec 2016 19:42:28 -0800 (PST) Original-Received: by holos.localdomain (Postfix, from userid 1000) id EC29E66151; Fri, 16 Dec 2016 22:42:26 -0500 (EST) Content-Disposition: inline In-Reply-To: <20161216141614.GB1009@holos.localdomain> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2607:f8b0:400d:c09::231 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:210569 Archived-At: In addition to the changes mentioned in the initial posting: the following implements asynchronous thumbnail generation for image-dired. The number of concurrent processes is limited by `image-dired-thumb-job-limit'. diff --git a/lisp/image-dired.el b/lisp/image-dired.el index 96570a503f..01fb8c8deb 100644 --- a/lisp/image-dired.el +++ b/lisp/image-dired.el @@ -118,8 +118,6 @@ ;; * From thumbs.el: Add the "modify" commands (emboss, negate, ;; monochrome etc). ;; -;; * Asynchronous creation of thumbnails. -;; ;; * Add `image-dired-display-thumbs-ring' and functions to cycle that. Find ;; out which is best, saving old batch just before inserting new, or ;; saving the current batch in the ring when inserting it. Adding it @@ -230,14 +228,15 @@ image-dired-cmd-create-thumbnail-program :group 'image-dired) (defcustom image-dired-cmd-create-thumbnail-options - "%p -size %wx%h \"%f\" -resize \"%wx%h>\" -strip jpeg:\"%t\"" - "Format of command used to create thumbnail image. -Available options are %p which is replaced by -`image-dired-cmd-create-thumbnail-program', %w which is replaced by + '("-size" "%wx%h" "%f" "-resize" "%wx%h>" "-strip" "jpeg:%t") + "Options of command used to create thumbnail image. +Used with `image-dired-cmd-create-thumbnail-program'. +Available format specifiers are: %w which is replaced by `image-dired-thumb-width', %h which is replaced by `image-dired-thumb-height', %f which is replaced by the file name of the original image and %t which is replaced by the file name of the thumbnail file." - :type 'string + :version "26.1" + :type '(repeat (string :tag "Argument")) :group 'image-dired) (defcustom image-dired-cmd-create-temp-image-program "convert" @@ -247,14 +246,15 @@ image-dired-cmd-create-temp-image-program :group 'image-dired) (defcustom image-dired-cmd-create-temp-image-options - "%p -size %wx%h \"%f\" -resize \"%wx%h>\" -strip jpeg:\"%t\"" - "Format of command used to create temporary image for display window. -Available options are %p which is replaced by -`image-dired-cmd-create-temp-image-program', %w and %h which is replaced by + '("-size" "%wx%h" "%f" "-resize" "%wx%h>" "-strip" "jpeg:%t") + "Options of command used to create temporary image for display window. +Used together with `image-dired-cmd-create-temp-image-program', +Available format specifiers are: %w and %h which are replaced by the calculated max size for width and height in the image display window, %f which is replaced by the file name of the original image and %t which is replaced by the file name of the temporary file." - :type 'string + :version "26.1" + :type '(repeat (string :tag "Argument")) :group 'image-dired) (defcustom image-dired-cmd-pngnq-program @@ -264,49 +264,72 @@ image-dired-cmd-pngnq-program It quantizes colors of PNG images down to 256 colors or fewer using the Neuquant procedure." :version "26.1" - :type '(choice (const :tag "Not Set" nil) string) + :type '(choice (const :tag "Not Set" nil) file) + :group 'image-dired) + +(defcustom image-dired-cmd-pngnq-options + '("-f" "%t") + "Arguments to pass `image-dired-cmd-pngnq-program'. +Available format specifiers are the same as in +`image-dired-cmd-create-thumbnail-options'." + :version "26.1" + :type '(repeat (string :tag "Argument")) :group 'image-dired) (defcustom image-dired-cmd-pngcrush-program (executable-find "pngcrush") "The file name of the `pngcrush' program. It optimizes the compression of PNG images. Also it adds PNG textual chunks with the information required by the Thumbnail Managing Standard." - :type '(choice (const :tag "Not Set" nil) string) + :type '(choice (const :tag "Not Set" nil) file) :group 'image-dired) -(defcustom image-dired-cmd-create-standard-thumbnail-command - (concat - "%p -size %wx%h \"%f\" " - (unless (or image-dired-cmd-pngcrush-program image-dired-cmd-pngnq-program) - (concat - "-set \"Thumb::MTime\" \"%m\" " - "-set \"Thumb::URI\" \"file://%f\" " - "-set \"Description\" \"Thumbnail of file://%f\" " - "-set \"Software\" \"" (emacs-version) "\" ")) - "-thumbnail \"%wx%h>\" png:\"%t\"" - (if image-dired-cmd-pngnq-program - (concat - " ; " image-dired-cmd-pngnq-program " -f \"%t\"" - (unless image-dired-cmd-pngcrush-program - " ; mv %q %t"))) - (if image-dired-cmd-pngcrush-program - (concat - (unless image-dired-cmd-pngcrush-program - " ; cp %t %q") - " ; " image-dired-cmd-pngcrush-program " -q " - "-text b \"Description\" \"Thumbnail of file://%f\" " - "-text b \"Software\" \"" (emacs-version) "\" " - ;; "-text b \"Thumb::Image::Height\" \"%oh\" " - ;; "-text b \"Thumb::Image::Mimetype\" \"%mime\" " - ;; "-text b \"Thumb::Image::Width\" \"%ow\" " - "-text b \"Thumb::MTime\" \"%m\" " - ;; "-text b \"Thumb::Size\" \"%b\" " - "-text b \"Thumb::URI\" \"file://%f\" " - "%q %t" - " ; rm %q"))) - "Command to create thumbnails according to the Thumbnail Managing Standard." +(defcustom image-dired-cmd-pngcrush-options + `("-q" + "-text" "b" "Description" "Thumbnail of file://%f" + "-text" "b" "Software" ,(emacs-version) + ;; "-text b \"Thumb::Image::Height\" \"%oh\" " + ;; "-text b \"Thumb::Image::Mimetype\" \"%mime\" " + ;; "-text b \"Thumb::Image::Width\" \"%ow\" " + "-text" "b" "Thumb::MTime" "%m" + ;; "-text b \"Thumb::Size\" \"%b\" " + "-text" "b" "Thumb::URI" "file://%f" + "%q" "%t") + "Arguments for `image-dired-cmd-pngcrush-program'. +Available format specifiers are the same as in +`image-dired-cmd-create-thumbnail-options', with %q for a +temporary file name (typically generated by pnqnq)" :version "26.1" - :type 'string + :type '(repeat (string :tag "Argument")) + :group 'image-dired) + +(defcustom image-dired-cmd-optipng-program (executable-find "optipng") + "The file name of the `optipng' program." + :type '(choice (const :tag "Not Set" nil) file) + :group 'image-dired) + +(defcustom image-dired-cmd-optipng-options '("-o5" "%t") + "Arguments passed to `image-dired-optipng-program'. +Available format specifiers are described in +`image-dired-cmd-create-thumbnail-options'." + :type '(repeat (string :tag "Argument")) + :link '(url-link "man:optipng(1)") + :group 'image-dired) + +(defcustom image-dired-cmd-create-standard-thumbnail-options + (append '("-size" "%wx%h" "%f") + (unless (or image-dired-cmd-pngcrush-program + image-dired-cmd-pngnq-program) + (list + "-set" "Thumb::MTime" "%m" + "-set" "Thumb::URI" "file://%f" + "-set" "Description" "Thumbnail of file://%f" + "-set" "Software" (emacs-version))) + '("-thumbnail" "%wx%h>" "png:%t")) + "Options for creating thumbnails according to the Thumbnail Managing Standard. +Available format specifiers are the same as in +`image-dired-cmd-create-thumbnail-options', with %m for file modification time." + :version "26.1" + :type '(repeat (string :tag "Argument")) :group 'image-dired) (defcustom image-dired-cmd-rotate-thumbnail-program @@ -317,14 +340,15 @@ image-dired-cmd-rotate-thumbnail-program :group 'image-dired) (defcustom image-dired-cmd-rotate-thumbnail-options - "%p -rotate %d \"%t\"" - "Format of command used to rotate thumbnail image. -Available options are %p which is replaced by -`image-dired-cmd-rotate-thumbnail-program', %d which is replaced by the + '("-rotate" "%d" "%t") + "Arguments of command used to rotate thumbnail image. +Used with `image-dired-cmd-rotate-thumbnail-program'. +Available format specifiers are: %d which is replaced by the number of (positive) degrees to rotate the image, normally 90 or 270 \(for 90 degrees right and left), %t which is replaced by the file name of the thumbnail file." - :type 'string + :version "26.1" + :type '(repeat (string :tag "Argument")) :group 'image-dired) (defcustom image-dired-cmd-rotate-original-program @@ -335,15 +359,16 @@ image-dired-cmd-rotate-original-program :group 'image-dired) (defcustom image-dired-cmd-rotate-original-options - "%p -rotate %d -copy all -outfile %t \"%o\"" - "Format of command used to rotate original image. -Available options are %p which is replaced by -`image-dired-cmd-rotate-original-program', %d which is replaced by the + '("-rotate" "%d" "-copy" "all" "-outfile" "%t" "%o") + "Arguments of command used to rotate original image. +Used with `image-dired-cmd-rotate-original-program'. +Available format specifiers are: %d which is replaced by the number of (positive) degrees to rotate the image, normally 90 or 270 \(for 90 degrees right and left), %o which is replaced by the original image file name and %t which is replaced by `image-dired-temp-image-file'." - :type 'string + :version "26.1" + :type '(repeat (string :tag "Argument")) :group 'image-dired) (defcustom image-dired-temp-rotate-image-file @@ -367,13 +392,14 @@ image-dired-cmd-write-exif-data-program :group 'image-dired) (defcustom image-dired-cmd-write-exif-data-options - "%p -%t=\"%v\" \"%f\"" - "Format of command used to write EXIF data. -Available options are %p which is replaced by -`image-dired-cmd-write-exif-data-program', %f which is replaced by + '("-%t=%v" "%f") + "Arguments of command used to write EXIF data. +Used with `image-dired-cmd-write-exif-data-program'. +Available format specifiers are: %f which is replaced by the image file name, %t which is replaced by the tag name and %v which is replaced by the tag value." - :type 'string + :version "26.1" + :type '(repeat (string :tag "Argument")) :group 'image-dired) (defcustom image-dired-cmd-read-exif-data-program @@ -384,12 +410,13 @@ image-dired-cmd-read-exif-data-program :group 'image-dired) (defcustom image-dired-cmd-read-exif-data-options - "%p -s -s -s -%t \"%f\"" - "Format of command used to read EXIF data. -Available options are %p which is replaced by -`image-dired-cmd-write-exif-data-program', %f which is replaced + '("-s" "-s" "-s" "-%t" "%f") + "Arguments of command used to read EXIF data. +Used with `image-dired-cmd-read-exif-data-program'. +Available format specifiers are: %f which is replaced by the image file name and %t which is replaced by the tag name." - :type 'string + :version "26.1" + :type '(repeat (string :tag "Argument")) :group 'image-dired) (defcustom image-dired-gallery-hidden-tags @@ -640,7 +667,84 @@ image-dired-thumb-size (width image-dired-thumb-width) (height image-dired-thumb-height))))) -(defun image-dired-create-thumb (original-file thumbnail-file) + +;; Thumbnail generation + +(defvar image-dired-thumb-queue nil + "List of items in the queue. +Each item has the form (ORIGINAL-FILE THUMBNAIL-FILE).") + +(defvar image-dired-thumb-active-jobs 0 + "Number of active jobs in `image-dired-thumb-queue'.") + +(defvar image-dired-thumb-job-limit 2 + "Maximum number of concurrent jobs permitted for generating thumbnails. +Increase at own risk.") + +(defun image-dired-pngnq-thumb (spec) + "Quantize thumbnail described by format SPEC with pngnq(1)." + (let ((process + (apply #'start-process "image-dired-pngnq" nil + image-dired-cmd-pngnq-program + (mapcar (lambda (arg) (format-spec arg spec)) + image-dired-cmd-pngnq-options)))) + (setf (process-sentinel process) + (lambda (process status) + (if (and (eq (process-status process) 'exit) + (zerop (process-exit-status process))) + ;; Pass off to pngcrush, or just rename the + ;; THUMB-nq8.png file back to THUMB.png + (if (and image-dired-cmd-pngcrush-program + (executable-find image-dired-cmd-pngcrush-program)) + (image-dired-pngcrush-thumb spec) + (let ((nq8 (cdr (assq ?q spec))) + (thumb (cdr (assq ?t spec)))) + (rename-file nq8 thumb t))) + (message "command %S %s" (process-command process) + (replace-regexp-in-string "\n" "" status))))) + process)) + +(defun image-dired-pngcrush-thumb (spec) + "Optimize thumbnail decsribed by format SPEC with pngcrush(1)." + ;; If pngnq wasn't run, then the THUMB-nq8.png file does not exist. + ;; pngcrush needs an infile and outfile, so we just copy THUMB to + ;; THUMB-nq8.png and use the latter as a temp file. + (when (not image-dired-cmd-pngnq-program) + (let ((temp (cdr (assq ?q spec))) + (thumb (cdr (assq ?t spec)))) + (copy-file thumb temp))) + (let ((process + (apply #'start-process "image-dired-pngcrush" nil + image-dired-cmd-pngcrush-program + (mapcar (lambda (arg) (format-spec arg spec)) + image-dired-cmd-pngcrush-options)))) + (setf (process-sentinel process) + (lambda (process status) + (unless (and (eq (process-status process) 'exit) + (zerop (process-exit-status process))) + (message "command %S %s" (process-command process) + (replace-regexp-in-string "\n" "" status))) + (when (memq (process-status process) '(exit signal)) + (let ((temp (cdr (assq ?q spec)))) + (delete-file temp))))) + process)) + +(defun image-dired-optipng-thumb (spec) + "Optimize thumbnail decsribed by format SPEC with optipng(1)." + (let ((process + (apply #'start-process "image-dired-optipng" nil + image-dired-cmd-optipng-program + (mapcar (lambda (arg) (format-spec arg spec)) + image-dired-cmd-optipng-options)))) + (setf (process-sentinel process) + (lambda (process status) + (unless (and (eq (process-status process) 'exit) + (zerop (process-exit-status process))) + (message "command %S %s" (process-command process) + (replace-regexp-in-string "\n" "" status))))) + process)) + +(defun image-dired-create-thumb-1 (original-file thumbnail-file) "For ORIGINAL-FILE, create thumbnail image named THUMBNAIL-FILE." (image-dired--check-executable-exists 'image-dired-cmd-create-thumbnail-program) @@ -650,25 +754,77 @@ image-dired-create-thumb original-file))))) (thumbnail-nq8-file (replace-regexp-in-string ".png\\'" "-nq8.png" thumbnail-file)) - (command - (format-spec - (if (memq image-dired-thumbnail-storage '(standard standard-large)) - image-dired-cmd-create-standard-thumbnail-command - image-dired-cmd-create-thumbnail-options) - (list - (cons ?p image-dired-cmd-create-thumbnail-program) - (cons ?w width) - (cons ?h height) - (cons ?m modif-time) - (cons ?f original-file) - (cons ?q thumbnail-nq8-file) - (cons ?t thumbnail-file)))) - thumbnail-dir) - (when (not (file-exists-p - (setq thumbnail-dir (file-name-directory thumbnail-file)))) - (message "Creating thumbnail directory.") + (spec + (list + (cons ?w width) + (cons ?h height) + (cons ?m modif-time) + (cons ?f original-file) + (cons ?q thumbnail-nq8-file) + (cons ?t thumbnail-file))) + (thumbnail-dir (file-name-directory thumbnail-file)) + process) + (when (not (file-exists-p thumbnail-dir)) + (message "Creating thumbnail directory") (make-directory thumbnail-dir t)) - (call-process shell-file-name nil nil nil shell-command-switch command))) + + ;; Thumbnail file creation processes begin here and are marshalled + ;; in a queue by `image-dired-create-thumb'. + (setq process + (apply #'start-process "image-dired-create-thumbnail" nil + image-dired-cmd-create-thumbnail-program + (mapcar + (lambda (arg) (format-spec arg spec)) + (if (memq image-dired-thumbnail-storage + '(standard standard-large)) + image-dired-cmd-create-standard-thumbnail-options + image-dired-cmd-create-thumbnail-options)))) + + (setf (process-sentinel process) + (lambda (process status) + ;; Trigger next in queue once a thumbnail has been created + (cl-decf image-dired-thumb-active-jobs) + (image-dired-thumb-queue-run) + (if (not (and (eq (process-status process) 'exit) + (zerop (process-exit-status process)))) + (message "Thumb could not be created for %s: %s" + (abbreviate-file-name original-file) + (replace-regexp-in-string "\n" "" status)) + (clear-image-cache thumbnail-file) + ;; PNG thumbnail has been created since we are + ;; following the XDG thumbnail spec, so try to optimize + (when (memq image-dired-thumbnail-storage + '(standard standard-large)) + (cond + ((and image-dired-cmd-pngnq-program + (executable-find image-dired-cmd-pngnq-program)) + (image-dired-pngnq-thumb spec)) + ((and image-dired-cmd-pngcrush-program + (executable-find image-dired-cmd-pngcrush-program)) + (image-dired-pngcrush-thumb spec)) + ((and image-dired-cmd-optipng-program + (executable-find image-dired-cmd-optipng-program)) + (image-dired-optipng-thumb spec))))))) + process)) + +(defun image-dired-thumb-queue-run () + "Run a queued job if one exists and not too many jobs are running. +Queued items live in `image-dired-thumb-queue'." + (while (and image-dired-thumb-queue + (< image-dired-thumb-active-jobs + image-dired-thumb-job-limit)) + (cl-incf image-dired-thumb-active-jobs) + (apply #'image-dired-create-thumb-1 (pop image-dired-thumb-queue)))) + +(defun image-dired-create-thumb (original-file thumbnail-file) + "Add a job for generating thumbnail to `image-dired-thumb-queue'." + (setq image-dired-thumb-queue + (nconc image-dired-thumb-queue + (list (list original-file thumbnail-file)))) + (run-at-time 0 nil #'image-dired-thumb-queue-run)) + + +;; Interactive ;;;###autoload (defun image-dired-dired-toggle-marked-thumbs (&optional arg) @@ -868,10 +1024,9 @@ image-dired-display-thumbs (goto-char (point-max))) (dolist (curr-file files) (setq thumb-name (image-dired-thumb-name curr-file)) - (if (and (not (file-exists-p thumb-name)) - (not (= 0 (image-dired-create-thumb curr-file thumb-name)))) - (message "Thumb could not be created for file %s" curr-file) - (image-dired-insert-thumbnail thumb-name curr-file dired-buf)))) + (when (not (file-exists-p thumb-name)) + (image-dired-create-thumb curr-file thumb-name)) + (image-dired-insert-thumbnail thumb-name curr-file dired-buf))) (if do-not-pop (display-buffer buf) (pop-to-buffer buf)) @@ -1425,6 +1580,8 @@ image-dired-display-image-mode-map (define-key map [remap scroll-down] 'image-scroll-down) (define-key map [remap scroll-up-command] 'image-scroll-up) (define-key map [remap scroll-down-command] 'image-scroll-down) + (define-key map [remap scroll-left] 'image-scroll-left) + (define-key map [remap scroll-right] 'image-scroll-right) (define-key map [remap move-beginning-of-line] 'image-bol) (define-key map [remap move-end-of-line] 'image-eol) (define-key map [remap beginning-of-buffer] 'image-bob) @@ -1553,8 +1710,7 @@ image-dired-create-thumbs (clear-image-cache (expand-file-name thumb-name))) (when (or (not (file-exists-p thumb-name)) arg) - (when (not (= 0 (image-dired-create-thumb curr-file thumb-name))) - (error "Thumb could not be created")))))) + (image-dired-create-thumb curr-file thumb-name))))) (defvar image-dired-slideshow-timer nil "Slideshow timer.") @@ -1746,17 +1902,19 @@ image-dired-display-image (image-type 'jpeg)) (setq file (expand-file-name file)) (if (not original-size) - (let* ((command - (format-spec - image-dired-cmd-create-temp-image-options - (list - (cons ?p image-dired-cmd-create-temp-image-program) - (cons ?w (image-dired-display-window-width window)) - (cons ?h (image-dired-display-window-height window)) - (cons ?f file) - (cons ?t new-file)))) - (ret (call-process shell-file-name nil nil nil - shell-command-switch command))) + (let* ((spec + (list + (cons ?p image-dired-cmd-create-temp-image-program) + (cons ?w (image-dired-display-window-width window)) + (cons ?h (image-dired-display-window-height window)) + (cons ?f file) + (cons ?t new-file))) + (ret + (apply #'call-process + image-dired-cmd-create-temp-image-program nil nil nil + (mapcar + (lambda (arg) (format-spec arg spec)) + image-dired-cmd-create-temp-image-options)))) (when (not (zerop ret)) (error "Could not resize image"))) (setq image-type (image-type-from-file-name file)) @@ -1810,14 +1968,10 @@ image-dired-rotate-thumbnail (message "No thumbnail at point") (let* ((file (image-dired-thumb-name (image-dired-original-file-name))) (thumb (expand-file-name file)) - command) - (setq command (format-spec - image-dired-cmd-rotate-thumbnail-options - (list - (cons ?p image-dired-cmd-rotate-thumbnail-program) - (cons ?d degrees) - (cons ?t thumb)))) - (call-process shell-file-name nil nil nil shell-command-switch command) + (spec (list (cons ?d degrees) (cons ?t thumb)))) + (apply #'call-process image-dired-cmd-rotate-thumbnail-program nil nil nil + (mapcar (lambda (arg) (format-spec arg spec)) + image-dired-cmd-rotate-thumbnail-options)) (clear-image-cache thumb)))) (defun image-dired-rotate-thumbnail-left () @@ -1852,19 +2006,18 @@ image-dired-rotate-original 'image-dired-cmd-rotate-original-program) (if (not (image-dired-image-at-point-p)) (message "No image at point") - (let ((file (image-dired-original-file-name)) - command) + (let* ((file (image-dired-original-file-name)) + (spec + (list + (cons ?d degrees) + (cons ?o (expand-file-name file)) + (cons ?t image-dired-temp-rotate-image-file)))) (unless (eq 'jpeg (image-type file)) (error "Only JPEG images can be rotated!")) - (setq command (format-spec - image-dired-cmd-rotate-original-options - (list - (cons ?p image-dired-cmd-rotate-original-program) - (cons ?d degrees) - (cons ?o (expand-file-name file)) - (cons ?t image-dired-temp-rotate-image-file)))) - (if (not (= 0 (call-process shell-file-name nil nil nil - shell-command-switch command))) + (if (not (= 0 (apply #'call-process image-dired-cmd-rotate-original-program + nil nil nil + (mapcar (lambda (arg) (format-spec arg spec)) + image-dired-cmd-rotate-original-options)))) (error "Could not rotate image") (image-dired-display-image image-dired-temp-rotate-image-file) (if (or (and image-dired-rotate-original-ask-before-overwrite @@ -1930,32 +2083,30 @@ image-dired-set-exif-data "In FILE, set EXIF tag TAG-NAME to value TAG-VALUE." (image-dired--check-executable-exists 'image-dired-cmd-write-exif-data-program) - (let (command) - (setq command (format-spec - image-dired-cmd-write-exif-data-options - (list - (cons ?p image-dired-cmd-write-exif-data-program) - (cons ?f (expand-file-name file)) - (cons ?t tag-name) - (cons ?v tag-value)))) - (call-process shell-file-name nil nil nil shell-command-switch command))) + (let ((spec + (list + (cons ?f (expand-file-name file)) + (cons ?t tag-name) + (cons ?v tag-value)))) + (apply #'call-process image-dired-cmd-write-exif-data-program nil nil nil + (mapcar (lambda (arg) (format-spec arg spec)) + image-dired-cmd-write-exif-data-options)))) (defun image-dired-get-exif-data (file tag-name) "From FILE, return EXIF tag TAG-NAME." (image-dired--check-executable-exists 'image-dired-cmd-read-exif-data-program) (let ((buf (get-buffer-create "*image-dired-get-exif-data*")) - command tag-value) - (setq command (format-spec - image-dired-cmd-read-exif-data-options - (list - (cons ?p image-dired-cmd-read-exif-data-program) - (cons ?f file) - (cons ?t tag-name)))) + (spec (list (cons ?f file) (cons ?t tag-name))) + tag-value) (with-current-buffer buf (delete-region (point-min) (point-max)) - (if (not (eq (call-process shell-file-name nil t nil - shell-command-switch command) 0)) + (if (not (eq (apply #'call-process image-dired-cmd-read-exif-data-program + nil t nil + (mapcar + (lambda (arg) (format-spec arg spec)) + image-dired-cmd-read-exif-data-options)) + 0)) (error "Could not get EXIF tag") (goto-char (point-min)) ;; Clean buffer from newlines and carriage returns before