M7: a resized page image stays blanked where the raster still has it

Two cases kept the old picture on screen after a move or resize:

- A page that had no tiles when the edit applied was taken as drawn
  again as soon as a first tile arrived, while its backdrop still had
  the picture. Out of date now means its whole-page raster or any tile
  still waits for a replacement.
- A second drag before the page was drawn again took the picture's
  pixels from its new frame, where the raster doesn't have it, and
  blanked only that. It now takes them from where the raster still has
  the picture and blanks that, and keeps it as the source on release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
komo1iddin
2026-10-22 00:26:39 +08:00
co-authored by Claude Opus 5.5
parent 7a7d39f01f
commit f632c1b0ca
3 changed files with 30 additions and 14 deletions
+3 -7
View File
@@ -1046,14 +1046,10 @@ impl DocView {
self.pages.get(&page).map(|p| p.tex.id())
}
/// Whether `page` is still drawn as it was before its last change: its raster, or on a tiled
/// page any of its tiles, is waiting for a replacement.
/// Whether `page` is still drawn, somewhere, as it was before its last change: its whole-page
/// raster (a tiled page's backdrop too) or any of its tiles waits for a replacement.
pub(crate) fn page_out_of_date(&self, page: usize) -> bool {
if self.tiles.keys().any(|k| k.0 == page) {
self.stale_tiles.iter().any(|k| k.0 == page)
} else {
self.pages.get(&page).is_some_and(|p| p.tag == STALE_TAG)
}
self.pages.get(&page).is_some_and(|p| p.tag == STALE_TAG) || self.stale_tiles.iter().any(|k| k.0 == page)
}
/// The print preview's picture of `page`: the sheet raster when it has arrived, otherwise the
+7 -6
View File
@@ -722,12 +722,13 @@ pub(crate) fn image_input(
// Until the page is rendered again its raster has an applied move or resize's picture
// where it was: it stays blanked there and drawn where it went.
let (out_of_date, raster) = (view.page_out_of_date(page), view.page_raster(page));
let screen = |r: Rect| Rect::from_two_pos(xf.norm_to_screen(r.min.x, r.min.y), xf.norm_to_screen(r.max.x, r.max.y));
// Where the raster on screen has the picture: where it was before an applied move that
// hasn't been rendered yet, else its frame. Another drag starts from there too.
let drawn_at = view.image_selection.as_ref().and_then(|s| s.settled).filter(|_| out_of_date).map(|(from, _)| from);
if let Some(s) = view.image_selection.as_mut().filter(|s| s.drag.is_none()) {
match (s.settled, raster) {
(Some((from, to)), Some(tex)) if out_of_date => {
let screen = |r: Rect| Rect::from_two_pos(xf.norm_to_screen(r.min.x, r.min.y), xf.norm_to_screen(r.max.x, r.max.y));
stretched_picture(painter, xf, tex, screen(from), screen(to));
}
(Some((from, to)), Some(tex)) if out_of_date => stretched_picture(painter, xf, tex, screen(from), screen(to)),
_ => s.settled = None,
}
}
@@ -788,7 +789,7 @@ pub(crate) fn image_input(
if preview != b
&& let Some(tex) = view.page_raster(page)
{
stretched_picture(painter, xf, tex, b, preview);
stretched_picture(painter, xf, tex, drawn_at.map_or(b, screen), preview);
}
painter.rect_filled(visible, CornerRadius::ZERO, ACCENT.gamma_multiply(0.04));
painter.rect_stroke(visible, CornerRadius::ZERO, Stroke::new(1.5, ACCENT), egui::StrokeKind::Outside);
@@ -801,7 +802,7 @@ pub(crate) fn image_input(
let ((u0, v0), (u1, v1)) = (xf.screen_to_norm(r.min), xf.screen_to_norm(r.max));
Rect::from_two_pos(Pos2::new(u0, v0), Pos2::new(u1, v1))
};
s.settled = (preview != b).then(|| (norm(b), norm(preview)));
s.settled = (preview != b).then(|| (drawn_at.unwrap_or_else(|| norm(b)), norm(preview)));
}
if preview != b {
view.pending_edit =
+20 -1
View File
@@ -4526,11 +4526,30 @@ fn a_resized_image_keeps_its_new_size_until_the_page_is_redrawn() {
assert!(red(&pixels, kept) > 100, "frame {frame} after the release: the picture is not at its new size");
assert_eq!(red(&pixels, gone), 0, "frame {frame} after the release: the picture shows at its old size");
}
// Resized again before the page is drawn: the raster still has the first size, so that is
// what stays blanked while the corner moves.
let (grip, to) = (to, at(80.0, 60.0));
let again = egui::Rect::from_two_pos(at(62.0, 42.0), at(78.0, 58.0));
h.hover_at(grip);
h.run_steps(1);
h.drag_at(grip);
h.run_steps(1);
for k in 1..=4 {
h.hover_at(grip + (to - grip) * (k as f32 / 4.0));
h.run_steps(1);
}
let pixels = h.render().unwrap();
assert!(red(&pixels, again) > 20, "the second drag draws the picture at its new size");
assert_eq!(red(&pixels, gone), 0, "the second drag shows the picture at its first size");
h.drop_at(to);
h.run_steps(2);
let pixels = h.render().unwrap();
assert_eq!(red(&pixels, gone), 0, "after the second release");
h.state_mut().views[0].hold_rasters = false;
support::wait_until(&mut h, "the page drawn with the resized picture", |app| !app.render_pending());
h.run_steps(2);
let pixels = h.render().unwrap();
assert!(red(&pixels, kept) > 100 && red(&pixels, gone) == 0, "the page raster has the picture at its new size");
assert!(red(&pixels, again) > 20 && red(&pixels, gone) == 0, "the page raster has the picture at its new size");
}
/// Exercise actual pointer gestures on each cropped placement, without capturing private pixels.