mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 12:17:14 -06:00
Fix #525: Image Trace of a large photo no longer crashes the app
Tracing a 2048 x 2048 photo with High Fidelity Photo made 733,000 paths and 7.8 million anchors. The selected result's outlines and anchor squares then made an egui mesh of 1.8 GB, over the GPU's largest buffer: wgpu panicked while painting and the app quit (or ran out of memory first, at 6.4 GB). - Canvas: a selection with more than 100,000 anchors shows its bounds instead of every outline and anchor, and the hover highlight skips such an object. The Contextual Task Bar and that outline read the selection's bounds from a per-revision cache (VectorcraftApp:: selection_bounds): a selected trace drew a frame in 20 ms, not 170. - Image Trace: the colour layers are traced in parallel (26 s to 1.9 s for that photo; the web traces them in turn). trace_within stops once the paths pass an anchor budget; imageTrace.make allows two million and otherwise reports the trace as too complex, with what to change. Images over 64 megapixels are refused from their header, before they are decoded. - The trace crate's messages are translated (es, it) and scanned. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
85dcb28e41
commit
06242cd3ab
@@ -640,6 +640,11 @@ fn trace_params(p: &Value) -> Result<tr::TraceParams> {
|
||||
serde_json::from_value(v).map_err(|e| bad("imageTrace.make", e.to_string()))
|
||||
}
|
||||
|
||||
/// The most anchor points an Image Trace may make: a photo traced at high fidelity can make
|
||||
/// millions, more than the canvas draws smoothly or a laptop has memory for, so the trace is
|
||||
/// refused with advice instead (#525).
|
||||
pub const MAX_TRACE_ANCHORS: usize = 2_000_000;
|
||||
|
||||
fn trace_make(s: &mut Session, p: &Value, expand: bool) -> Result<Value> {
|
||||
const C: &str = "imageTrace.make";
|
||||
let params = trace_params(p)?;
|
||||
@@ -671,7 +676,7 @@ fn trace_make(s: &mut Session, p: &Value, expand: bool) -> Result<Value> {
|
||||
};
|
||||
let blob = s.doc()?.doc.images.get(&img.key).ok_or_else(|| EngineError::Other(format!("image data `{}` is missing", img.key)))?;
|
||||
let raster = tr::Raster::decode(&blob.bytes).map_err(|e| EngineError::Other(e.to_string()))?;
|
||||
let res = tr::trace(&raster, ¶ms);
|
||||
let res = tr::trace_within(&raster, ¶ms, MAX_TRACE_ANCHORS).map_err(|e| EngineError::Other(e.to_string()))?;
|
||||
// Pixel space of the decoded raster → the image object's pixel space → document.
|
||||
let sx = img.width.max(1) as f64 / raster.width.max(1) as f64;
|
||||
let sy = img.height.max(1) as f64 / raster.height.max(1) as f64;
|
||||
|
||||
Reference in New Issue
Block a user