EPS import: an image's transient data stops counting against the memory budget

The sample planes an image is decoded from, and the strings a procedure
data source hands back to fill them, are freed once the image is
decoded, so they stop counting against the 1 GiB budget then; the pixels
kept still count. A file of many images whose procedure returns one
large buffer each time no longer runs out of budget.
This commit is contained in:
Don Omondi
2026-11-06 20:11:52 -05:00
parent 36ef34dfdd
commit 95dcbfe59f
4 changed files with 33 additions and 1 deletions
+6
View File
@@ -657,8 +657,12 @@ impl Interp<'_> {
return ps_err("rangecheck", "DataSource");
}
let plane = row.checked_mul(s.h).filter(|p| *p <= MAX_DATA).ok_or(PsError::Limit("decoded data is too large"))?;
// The planes and the strings a procedure gives to fill them live only while the image is
// decoded: they stop counting once it is (the pixels made from them still count).
let before = self.allocated();
self.alloc(plane.saturating_mul(sources.len()))?;
let mut planes = self.read_sources(sources, row * s.h, if multi { row } else { row * s.h })?;
let transient = self.allocated().saturating_sub(before);
for p in &mut planes {
p.resize(row * s.h, 0);
}
@@ -734,6 +738,8 @@ impl Interp<'_> {
rgba.extend([rgb[0], rgb[1], rgb[2], if masked { 0 } else { 255 }]);
}
}
drop(planes);
self.unalloc(transient);
Ok(Some(Pixels { w: s.w as u32, h: s.h as u32, rgba, inks, matrix: s.matrix }))
}
+11
View File
@@ -341,6 +341,17 @@ impl<'a> Interp<'a> {
Ok(())
}
/// The bytes counted against [`MAX_MEMORY`] so far.
pub(crate) fn allocated(&self) -> usize {
self.allocated
}
/// Stop counting `bytes` the program no longer holds: buffers that lived only while one
/// operator ran, freed when it returned.
pub(crate) fn unalloc(&mut self, bytes: usize) {
self.allocated = self.allocated.saturating_sub(bytes);
}
fn tick(&mut self) -> Res {
self.spend(1)
}
+14
View File
@@ -1381,6 +1381,20 @@ fn the_planes_of_multi_source_images_are_charged() {
assert!(r.warnings.iter().any(|w| w.contains("too much memory")), "{:?}", r.warnings);
}
/// The planes an image is decoded from, and the strings a procedure gives to fill them, stop
/// counting against the memory budget once the image is decoded: they are freed then. A procedure
/// that hands back one large buffer for every image (1 MiB here, for a one-pixel image) charged the
/// whole buffer each time, and a file of many images came in as its preview.
#[test]
fn an_images_transient_data_is_not_charged_after_it() {
let body = "/buf 1048576 string def\n1100 { 1 1 8 [1 0 0 1 0 0] { buf } image } repeat\n";
let r = import(&eps(body)).unwrap();
assert!(!r.warnings.iter().any(|w| w.contains("too much memory")), "{:?}", r.warnings);
let mut images = 0;
r.document.walk(|n| images += usize::from(matches!(n.kind, NodeKind::Image(_))));
assert_eq!(images, 1100);
}
/// #3568: a single sample plane over the data limit is refused.
#[test]
fn an_oversized_image_plane_is_refused() {
+2 -1
View File
@@ -1,6 +1,6 @@
# File format parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-06 · **Change:** minor (SVG import: filter and mask regions clip, #5876, #5875; SVG import: Gaussian blurs keep `color-interpolation-filters`, #5877; PDF and `.ai` export: objects' URLs as links, #5785; SVG import: `color-interpolation="linearRGB"` gradients, #5878; `.ai` import: paths with a Calligraphic brush open brushed, #372) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-06 · **Change:** minor (EPS import: an image's transient data stops counting against the memory budget once it is decoded; SVG import: filter and mask regions clip, #5876, #5875; SVG import: Gaussian blurs keep `color-interpolation-filters`, #5877; PDF and `.ai` export: objects' URLs as links, #5785; SVG import: `color-interpolation="linearRGB"` gradients, #5878; `.ai` import: paths with a Calligraphic brush open brushed, #372) · **Target:** Adobe Illustrator 2026 (30.x)
Every format Illustrator opens, places, saves or exports, against VectorCraft's format table (`FORMATS`,
`OPEN_EXTS`, `PLACE_EXTS` in `crates/engine/src/cmd/fileio/formats.rs`). Illustrator's list comes from its public
@@ -191,6 +191,7 @@ the app's own editing copy of a file (an EPS after its `%%EOF`, a `.ai`'s `AIPri
| Date | Change | Summary |
|---|---|---|
| 2026-11-06 | minor | EPS import: the buffers an image is decoded from (its sample planes, and the strings a procedure data source hands back to fill them) stop counting against the PostScript reader's 1 GiB memory budget once the image is decoded, as they are freed then; the pixels kept still count. A user's Illustrator 25.2 EPS with 71 CMYK images (used locally, not committed) used to stop with "the program uses too much memory" and come in as its screen preview; it now opens with its art. The budget itself is unchanged. Scores and estimates unchanged. |
| 2026-11-06 | minor | SVG import: filter and mask regions clip (SVG 1.1 §15.5, §14.4). An object whose imported blur, shadow or glow reaches past its filter's region comes in inside a clip group cut to the region (#5876), and a mask's art is cut to the mask's region, in user space or the object's bounding box (#5875). Objects that fit their regions, and our own exports, are unchanged; primitive subregions are still ignored. Tests `import_live::a_blur_is_cut_at_its_filter_region`, `import_live::a_mask_counts_only_inside_its_region` (crate `vectorcraft-svg`); scores unchanged |
| 2026-11-06 | minor | SVG import: a Gaussian blur keeps the colour space its filter asks for with `color-interpolation-filters` (initially `linearRGB`, SVG 1.1 §15.7.6): a linear-light blur becomes Gaussian Blur with the new `linear` parameter, which blurs the colours in linear light (black beside white meets at about 188/255 instead of 128), and SVG export writes it back on the `feGaussianBlur` (#5877). Blurs that ask for sRGB, and the blurs our own export writes, are as before. CSS export still writes `blur()`. Tests `pixel::tests::linear_light_blur_mixes_light_not_values` (crate `vectorcraft-effects`), `import_live::a_gaussian_blur_keeps_its_colour_space` (crate `vectorcraft-svg`); scores unchanged |
| 2026-11-06 | minor | PDF export and PDF-compatible `.ai` saves write each object's URL (Attributes panel) as a link annotation with a URI action (web and mail URLs only) over the bounds of the object that shows, trimmed by its clipping groups (ISO 32000-1, 12.5.6.5 and 12.6.4.7); hidden objects, layers that don't print and prints have none, and PDF/X files leave them out with a warning (#5785). Own-fixture tests in `tests::links`; PDF import doesn't read links back as URLs yet; scores unchanged |