fix(ui): the filter preview window never uploads a texture past the GPU limit

With low-resolution previews off, the preview window from #3877 uploaded
the whole document as one texture. egui panics on a texture bigger than
max_texture_side, so opening a filter dialog on a large image crashed
(full_res_preview::filter_dialog_previews_at_full_resolution_when_low_res_is_off).
The texture is now reduced to fit; the view maps through UV fractions,
so it still lines up.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Brandon ThomasandClaude Opus 5.5 committed 2026-10-12 10:36:42 -04:00
1 parent 082a9dd1bc
commit 69153b1d8a
1 file changed
+27
+27
View File
@@ -201,12 +201,29 @@ fn document_size(proxy: [u32; 2], k: u32) -> Vec2 {
/// A composite as monitor pixels, matching the canvas (`canvas::display_image`).
fn upload(ctx: &egui::Context, app: &PhotocraftApp, name: &str, doc: &Document) -> egui::TextureHandle {
// Never exceed what the GPU accepts (egui panics on an oversized texture): with low-resolution
// previews off the proxy is the full document. The view maps through UV fractions, so a reduced
// texture still lines up.
let k = texture_factor(doc.size.width, doc.size.height, ctx.input(|i| i.max_texture_side));
let reduced;
let doc = if k > 1 {
reduced = crate::proxy::proxy_document(doc, k);
&reduced
} else {
doc
};
let buf = photocraft_compose::flatten(doc);
let (display, _) = crate::canvas::canvas_display(app, doc, None);
let img = crate::canvas::display_image(display.as_deref(), &buf);
ctx.load_texture(name, img, egui::TextureOptions::LINEAR)
}
/// The reduction that fits a `w`×`h` image inside a `max_side` texture (1: it already fits).
fn texture_factor(w: u32, h: u32, max_side: usize) -> u32 {
let max_side = u32::try_from(max_side).unwrap_or(u32::MAX).max(1);
w.max(h).div_ceil(max_side).max(1)
}
/// What a cached texture was built from, so it is rebuilt only when its source changes.
#[derive(Clone, PartialEq)]
enum TexKey {
@@ -259,6 +276,16 @@ mod tests {
assert!(!shows(json!({"__preview": true}).as_object().unwrap()));
}
#[test]
fn textures_are_reduced_to_fit_the_gpu() {
assert_eq!(texture_factor(3000, 2000, 2048), 2);
assert_eq!(texture_factor(2048, 100, 2048), 1);
assert_eq!(texture_factor(9000, 9000, 2048), 5);
// A zero or absurd limit never divides by zero.
assert_eq!(texture_factor(3000, 2000, 0), 3000);
assert_eq!(texture_factor(0, 0, 2048), 1);
}
#[test]
fn the_centre_is_clamped_to_the_document() {
assert_eq!(clamp_center(-5.0, 99.0, 100.0, 50.0), (0.0, 50.0));