mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 17:44:02 +00:00
Rasterize: art longer than 32,768 pixels at the resolution is refused, not cut off
Object > Rasterize and the redrawn path of Crop Image allowed images up to 65,535 pixels a side, but the renderer draws at most 32,768. A 100 x 5569 pt rectangle rasterized at 600 ppi came back 32,768 pixels tall, and the bottom 29% of the art was gone. Such an image is now refused like one over the 64 megapixel limit, and the art stays as it was.
This commit is contained in:
@@ -115,7 +115,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Rasterize…",
|
||||
["Object"],
|
||||
None,
|
||||
"{ppi?, background?: transparent|white|black|\"#rrggbb\" (#rrggbbaa and #rgba are refused), antiAlias?: none|art|type (text snapped to pixels)|bool, padding?|addAround?: pt (0–1000), colorModel?: \"rgb\"|\"cmyk\"|\"grayscale\"|\"bitmap\", clippingMask?: bool (a clip group with the art's outline)} replace the selection with an embedded image (PNG; in a CMYK document with its own colour model, a CMYK image of the art's inks, its transparency as its opacity mask); each defaults to document.rasterEffectsSettings; a frame of threaded text that is rasterized leaves its thread with the text it shows → {id (the image, or its clip group), width, height, warning?: \"an image couldn't be decoded and is missing from the picture\" (as raster exports say)}",
|
||||
"{ppi?, background?: transparent|white|black|\"#rrggbb\" (#rrggbbaa and #rgba are refused), antiAlias?: none|art|type (text snapped to pixels)|bool, padding?|addAround?: pt (0–1000), colorModel?: \"rgb\"|\"cmyk\"|\"grayscale\"|\"bitmap\", clippingMask?: bool (a clip group with the art's outline)} replace the selection with an embedded image (PNG; in a CMYK document with its own colour model, a CMYK image of the art's inks, its transparency as its opacity mask); each defaults to document.rasterEffectsSettings; an image larger than 64 Mpx or 32,768 px a side is refused; a frame of threaded text that is rasterized leaves its thread with the text it shows → {id (the image, or its clip group), width, height, warning?: \"an image couldn't be decoded and is missing from the picture\" (as raster exports say)}",
|
||||
has_selection,
|
||||
rasterize
|
||||
),
|
||||
@@ -670,7 +670,9 @@ fn render_pixels(
|
||||
opts: &vectorcraft_render::RenderOptions,
|
||||
color: RasterColorModel,
|
||||
) -> Result<(vectorcraft_render::Rendered, Option<&'static str>)> {
|
||||
if region.width() * region.height() * scale * scale > MAX_PIXELS || region.width() * scale > 65535.0 || region.height() * scale > 65535.0 {
|
||||
// No side longer than the renderer draws: it would cut the image off there.
|
||||
let side = f64::from(vectorcraft_render::MAX_RASTER_SIDE);
|
||||
if region.width() * region.height() * scale * scale > MAX_PIXELS || region.width() * scale > side || region.height() * scale > side {
|
||||
return Err(EngineError::Other("image would be too large; lower the resolution".into()));
|
||||
}
|
||||
let mut r = vectorcraft_render::Renderer::new();
|
||||
|
||||
@@ -1461,6 +1461,26 @@ fn rasterize_replaces_selection_with_image() {
|
||||
assert_eq!(selected(&s), vec![img]);
|
||||
}
|
||||
|
||||
/// Rasterize refuses art that would be longer than the renderer draws (32,768 pixels a side), and
|
||||
/// the art stays as it was; at a resolution that fits, the image covers all of it.
|
||||
#[test]
|
||||
fn rasterize_refuses_an_image_longer_than_the_renderer_draws() {
|
||||
let mut s = session();
|
||||
let a = rect(&mut s, 10.0, 20.0, 100.0, 5569.0);
|
||||
sel(&mut s, &[a]);
|
||||
let vb = s.doc().unwrap().doc.bounds_of(&[a], true).unwrap();
|
||||
let before = (undo_len(&s), node(&s, a));
|
||||
let e = s.execute("object.rasterize", &json!({"ppi": 600, "padding": 0})).unwrap_err().to_string();
|
||||
assert!(e.contains("too large"), "{e}");
|
||||
assert_eq!((undo_len(&s), node(&s, a)), before);
|
||||
let img = id_of(&s.execute("object.rasterize", &json!({"ppi": 400, "padding": 0})).unwrap());
|
||||
let n = node(&s, img);
|
||||
let NodeKind::Image(im) = &n.kind else { panic!() };
|
||||
assert!(im.height <= vectorcraft_render::MAX_RASTER_SIDE, "{} pixels", im.height);
|
||||
let b = n.geometric_bounds().unwrap();
|
||||
assert!(close(b.y0, vb.y0) && b.y1 >= vb.y1, "{b:?} covers {vb:?}");
|
||||
}
|
||||
|
||||
/// Rasterize refuses a resolution given as text or as another value that is not a number, and the
|
||||
/// selected art stays as it was.
|
||||
#[test]
|
||||
|
||||
+2
-1
@@ -5169,7 +5169,8 @@ fills or strokes carry effects is written as one piece per fill and stroke, so o
|
||||
When an image in the art can't be decoded, `object.rasterize` and `object.cropImage` (without `trim`) make their image
|
||||
without it, and their results carry the `warning` that Flatten Transparency gives. `object.rasterize` takes each frame
|
||||
of threaded text that it rasterizes out of its thread with the text it shows, as `type.createOutlines` does (see
|
||||
Threading text from the in and out ports), and the other frames keep the text they hold.
|
||||
Threading text from the in and out ports), and the other frames keep the text they hold. `object.rasterize`
|
||||
refuses an image larger than 64 million pixels or longer than 32,768 pixels a side, and the art stays as it was.
|
||||
|
||||
```json
|
||||
{"name":"run_command","arguments":{"command":"document.rasterEffectsSettings","params":{"resolution":"high","background":"white","addAround":36}}}
|
||||
|
||||
Reference in New Issue
Block a user