Subject: bug#65214: 29.1; Tall images in 'image-mode' can't be scrolled with 'pixel-scroll-precision-mode' on

From: Po Lu <[email-masked]>
Date: Sat, 12 Aug 2023 15:25:00 +0800

Rahguzar <[email-masked]> writes:

> Hi Po,
>
> Po Lu <[email-masked]> writes:
>
>> I tried to fix this differently on master.  Please see if it resolves
>> your problem (and other issues with scrolling past large images.)
>>
>> As always, keep an eye out for performance regressions.  Thanks.
>
>
> The issue is still there and can be reproduced by evaluating
>
> (let* ((win (display-buffer (get-buffer-create "image-pixel-precision-test")))
>        (height (* (window-text-height win t) 2))
>        (file "path/to/image"))
>   (select-window win)
>   (erase-buffer)
>   (insert-image (create-image file nil nil))
>   (goto-char 1)
>   (pixel-scroll-precision-mode)
>   nil)
>
> The resulting image cannot be scrolled.
>
> In the new code this is because vscroll is reset to 0 inside the binding
> for `line-after`. Commenting out that line allows me to scroll the
> image.

But as a repercussion, scrolling past the image will result in the
window being vscrolled indefinitely.  That is unacceptable, because
redisplay grows exponentially slower as vscroll increases.

Is this compromise acceptable?  It will enable scrolling within the last
line of the window, but will reset vscroll entirely once the window
start becomes invisible, resulting in a visually disconnected ``jump''
back to the top of that line.

diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el
index 488f6781254..8961f196ec7 100644
--- a/lisp/pixel-scroll.el
+++ b/lisp/pixel-scroll.el
@@ -543,14 +543,18 @@ pixel-scroll-precision-scroll-down-page
         (let ((line-after (save-excursion
                             (goto-char new-start-position)
                             (if (zerop (vertical-motion 1))
-                                (progn
-                                  (set-window-vscroll nil 0 t t)
-                                  nil) ; nil means move to new-start-position.
+                                nil ; nil means move to new-start-position.
                               (point)))))
           (if (not line-after)
               (progn
                 (goto-char new-start-position)
-                (signal 'end-of-buffer nil))
+                ;; If vscroll has resulted in the entire line being
+                ;; scrolled outside the window's text area, set it to
+                ;; the height of the last line.
+                (let ((visibility (pos-visible-in-window-p new-start-position nil t)))
+                  (unless visibility
+                    (set-window-vscroll nil 0)
+                    (signal 'end-of-buffer nil))))
             (if (pos-visible-in-window-p line-after nil t)
                 (goto-char line-after)
               (goto-char new-start-position))))))))