mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-12 21:23:02 +00:00
Place: refuse a scale that makes the placed image exceed the render pixel limit (#11229)
* Place: refuse a scale that makes the placed image exceed the render pixel limit * address review: bound the rounded-up resampled size when placing at a scale * address review: the oversized-scale error names the placing command
This commit is contained in:
1 parent
f6741eff4d
commit
5a935b2830
2 files changed
+28
No files matched your search
@@ -171,6 +171,19 @@ impl PlaceSteps {
|
||||
Some(c) if c.len() == 2 => (c[0].as_f64().unwrap_or(cw / 2.0), c[1].as_f64().unwrap_or(ch / 2.0)),
|
||||
_ => (cw / 2.0, ch / 2.0),
|
||||
};
|
||||
// `scale` is the caller's: bound the resampled size before allocating it (a frame's image
|
||||
// renders through its smart object, which has its own budget). Resampling rounds the far
|
||||
// output edges outward; an unresampled placement keeps its size.
|
||||
if self.input.frame.is_none() {
|
||||
let (ow, oh) = if (scale - 1.0).abs() > 1e-9 { ((w * scale).ceil(), (h * scale).ceil()) } else { (w, h) };
|
||||
let scaled = ow * oh;
|
||||
if scaled.is_nan() || scaled > photocraft_compose::MAX_RENDER_PIXELS as f64 {
|
||||
return Err(EngineError::BadParams {
|
||||
cmd: self.input.command().into(),
|
||||
msg: format!("scaling {w}×{h} by {scale} makes an image too large to place"),
|
||||
});
|
||||
}
|
||||
}
|
||||
let (dx, dy) = ((center.0 - w * scale / 2.0).round(), (center.1 - h * scale / 2.0).round());
|
||||
self.plan = Plan { w, h, scale, dx, dy };
|
||||
// A frame's image renders through its smart object, which refuses a placement past the
|
||||
|
||||
@@ -2004,6 +2004,21 @@ fn place_embedded_centres_fits_and_embeds() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A caller-chosen placement scale cannot ask for an image beyond the render limit.
|
||||
#[test]
|
||||
fn placing_refuses_a_scale_that_would_exceed_the_pixel_limit() {
|
||||
let dir = tmp("place-huge-scale");
|
||||
let small = png(&dir, "small.png", 200, 100, "#ff0000");
|
||||
let mut s = session(100, 100, 8);
|
||||
for scale in [1e9, 1e300, 1e308, 23170.25] {
|
||||
assert!(matches!(s.execute("file.placeEmbedded", json!({"path": small, "scale": scale})), Err(EngineError::BadParams { .. })), "{scale}");
|
||||
}
|
||||
assert!(s.execute("file.placeEmbedded", json!({"path": small, "scale": 50})).is_ok());
|
||||
// The error names the command that asked.
|
||||
let linked = s.execute("file.placeLinked", json!({"path": small, "scale": 1e9}));
|
||||
assert!(matches!(linked, Err(EngineError::BadParams { ref cmd, .. }) if cmd == "file.placeLinked"), "{linked:?}");
|
||||
}
|
||||
|
||||
/// Preferences ▸ General ▸ Always Create Smart Objects When Placing: unchecked, an embedded place
|
||||
/// lands as a plain pixel layer; a linked place is always a smart object and `"smartObject"` wins.
|
||||
#[test]
|
||||
|
||||
Reference in new issue
Block a user