mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 02:19:56 -06:00
Merge pull request #5415 from storytold/fix/sec-render-fx-symbol-budget
Render: symbol instances with effects use the symbol budget; CMYK decodes are admitted; checked Targa size
This commit is contained in:
@@ -38,8 +38,17 @@ pub(crate) fn blob_pixmap(blob: &vectorcraft_doc::ImageBlob, touch: bool) -> Opt
|
||||
paint::add_decode_failures(1);
|
||||
return None;
|
||||
}
|
||||
// Decoded outside the lock: other renderers carry on meanwhile.
|
||||
let pm = match lut.and_then(|lut| ink::cmyk_pixmap(blob, &lut)) {
|
||||
// Decoded outside the lock: other renderers carry on meanwhile. The inks and the pixels
|
||||
// shown from them are admitted together, as other images are, before either is made:
|
||||
// without room the image is read as other images are (which reduces it).
|
||||
let room = || {
|
||||
image::ImageReader::new(std::io::Cursor::new(blob.bytes.as_slice()))
|
||||
.with_guessed_format()
|
||||
.ok()
|
||||
.and_then(|r| r.into_dimensions().ok())
|
||||
.is_some_and(|(w, h)| paint::within_allowance(w, h.saturating_mul(2)))
|
||||
};
|
||||
let pm = match lut.filter(|_| room()).and_then(|lut| ink::cmyk_pixmap(blob, &lut)) {
|
||||
Some(pm) => pm,
|
||||
None => match paint::decode_pixmap_or_why(&blob.bytes) {
|
||||
Ok(pm) => pm,
|
||||
|
||||
@@ -34,7 +34,8 @@ pub fn encode(rgba: &[u8], width: u32, height: u32, o: &TgaOptions) -> Result<Ve
|
||||
return Err(format!("{width} × {height} pixels is too large for Targa (at most 65535 pixels a side): lower the resolution"));
|
||||
};
|
||||
let (w, h) = (usize::from(w16), usize::from(h16));
|
||||
if w == 0 || h == 0 || (w * h).checked_mul(4) != Some(rgba.len()) {
|
||||
// Checked: on 32-bit targets 65535 × 65535 wraps.
|
||||
if w == 0 || h == 0 || w.checked_mul(h).and_then(|n| n.checked_mul(4)) != Some(rgba.len()) {
|
||||
return Err("Targa encoding failed: the pixel buffer doesn't match the image size".into());
|
||||
}
|
||||
let px = rgba.as_chunks::<4>().0;
|
||||
|
||||
@@ -378,6 +378,9 @@ impl Renderer {
|
||||
|
||||
/// What [`Self::draw_object_fx`] draws inside the object's transparency group.
|
||||
fn draw_object_fx_art(&mut self, ctx: &mut RenderContext, f: &Frame, a: &Arc<Node>, cache: bool) {
|
||||
if !self.charge_fx_instance(f.doc, a) {
|
||||
return;
|
||||
}
|
||||
let art = self.fx_art(f.doc, a, cache);
|
||||
// Art the cache did not keep is made again in the next frame.
|
||||
let kept = cache && self.fx_arts.get(&(Arc::as_ptr(a) as usize), self.stamp).is_some_and(|e| Arc::ptr_eq(&e.1, &art));
|
||||
@@ -410,6 +413,19 @@ impl Renderer {
|
||||
self.made_again = outer;
|
||||
}
|
||||
|
||||
/// Whether the frame has room for the art of `a` when it is a symbol instance, charged to the
|
||||
/// frame's symbol budget as an instance without effects is (`draw_node`): an instance whose
|
||||
/// effects reshape its art draws through here, and nested instances would otherwise expand
|
||||
/// without a limit. True for any other object.
|
||||
pub(crate) fn charge_fx_instance(&mut self, doc: &vectorcraft_doc::Document, a: &Node) -> bool {
|
||||
let NodeKind::SymbolInstance { symbol, .. } = &a.kind else { return true };
|
||||
let count = doc.symbols.iter().find(|s| &s.name == symbol).map_or(0, |s| s.art.count() as u64);
|
||||
let mut used = self.symbol_nodes;
|
||||
let room = vectorcraft_doc::charge_symbol_art(&mut used, count, || ((), count)).is_some();
|
||||
self.symbol_nodes = used;
|
||||
room
|
||||
}
|
||||
|
||||
/// The art [`Self::draw_object_fx`] draws for `a` (cached by `Arc` identity when `cache`):
|
||||
/// reshaped by its geometry effects, else type, images and paths as themselves and the others
|
||||
/// as their evaluated art (groups and layers: [`effects::evaluate_container`], keeping their
|
||||
|
||||
@@ -1322,6 +1322,20 @@ fn a_kept_decoded_image_is_drawn_without_decoding_it() {
|
||||
assert!(!image_cached(&cmyk));
|
||||
}
|
||||
|
||||
/// A CMYK image's inks and the pixels shown from them are admitted against the decode allowance
|
||||
/// before either is made: past it, the image is read as other images are (reduced), not whole.
|
||||
#[test]
|
||||
fn cmyk_images_past_the_allowance_are_not_decoded_whole() {
|
||||
use vectorcraft_doc::ImageBlob;
|
||||
use vectorcraft_doc::cmyk::Inks;
|
||||
let cmyk = ImageBlob::cmyk_tiff(&Inks::new(64, 48, [11, 22, 33, 44].repeat(64 * 48)).unwrap()).unwrap();
|
||||
let whole = with_allowance(1 << 20, || crate::blobs::blob_pixmap(&cmyk, false)).map(|(_, pm)| (pm.width(), pm.height()));
|
||||
assert_eq!(whole, Some((64, 48)), "within the allowance, whole");
|
||||
let other = ImageBlob::cmyk_tiff(&Inks::new(64, 48, [12, 22, 33, 44].repeat(64 * 48)).unwrap()).unwrap();
|
||||
let small = with_allowance(64 * 48 * 4 - 1, || crate::blobs::blob_pixmap(&other, false)).map(|(_, pm)| (pm.width(), pm.height()));
|
||||
assert_ne!(small, Some((64, 48)), "past it, not decoded whole");
|
||||
}
|
||||
|
||||
/// An image drawn larger than its pixels samples smoothly, or with `smooth_images` off each screen
|
||||
/// pixel takes its nearest image pixel (Pixel Preview with File Handling › Display Bitmaps as
|
||||
/// Anti-aliased Images off, #394).
|
||||
|
||||
@@ -884,3 +884,17 @@ fn darkness_adjusts_gradient_stop_colors_before_interpolation() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A symbol instance with object effects is charged to the frame's symbol budget like one
|
||||
/// without: past the budget it isn't expanded.
|
||||
#[test]
|
||||
fn symbol_instances_with_effects_are_charged_to_the_symbol_budget() {
|
||||
let d = symbol_doc(vec![fx("distort.roughen", json!({}))]);
|
||||
let inst = d.layers[0].children().unwrap()[0].clone();
|
||||
let mut r = Renderer::new();
|
||||
r.symbol_nodes = vectorcraft_doc::MAX_SYMBOL_NODES - 1;
|
||||
assert!(r.charge_fx_instance(&d, &inst), "one object fits");
|
||||
assert_eq!(r.symbol_nodes, vectorcraft_doc::MAX_SYMBOL_NODES);
|
||||
assert!(!r.charge_fx_instance(&d, &inst), "the next does not");
|
||||
assert_eq!(r.symbol_nodes, vectorcraft_doc::MAX_SYMBOL_NODES);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user