Keep clarity's radius when the develop view zooms in
A frame fraction was taken of the render's short edge, and render_scale folds the zoom into the region the render stands for. Zoomed to 1:1 on a corner, clarity, texture and dehaze drew a quarter of the halo the export gets, though the docs promise they preview honestly at any zoom. RenderScale now carries the whole framed frame (within), and a frame fraction is measured against it; at fit nothing changes.
This commit is contained in:
@@ -197,6 +197,10 @@ pub const DETAIL_BASE_UNIFORM_FIELDS: usize = 4;
|
||||
pub struct RenderScale {
|
||||
render: (u32, u32),
|
||||
full: (u32, u32),
|
||||
/// The whole framed photograph at source resolution: `full` before the
|
||||
/// zoom and the tile were folded in. What a frame fraction is a fraction
|
||||
/// of — see [`Self::frame_fraction`].
|
||||
frame: (u32, u32),
|
||||
}
|
||||
|
||||
impl RenderScale {
|
||||
@@ -208,9 +212,27 @@ impl RenderScale {
|
||||
/// [`crate::EditGraph::render_scale`] works both out from the framing, and
|
||||
/// is what a caller should normally use.
|
||||
pub fn new(render: (u32, u32), full: (u32, u32)) -> Self {
|
||||
let full = (full.0.max(1), full.1.max(1));
|
||||
Self {
|
||||
render: (render.0.max(1), render.1.max(1)),
|
||||
full: (full.0.max(1), full.1.max(1)),
|
||||
full,
|
||||
frame: full,
|
||||
}
|
||||
}
|
||||
|
||||
/// TRACES: FR-DSP-1 | FR-DSP-2
|
||||
/// The same scale, for a render that shows only part of a larger frame.
|
||||
///
|
||||
/// `frame` is the whole framed photograph at source resolution — the crop
|
||||
/// folded in, the zoom and any tile not. A zoomed view and an export tile
|
||||
/// both look at part of the frame, and a clarity radius is a fraction of
|
||||
/// the *frame*, not of the part: measured against the part, zooming in
|
||||
/// shrinks the halo to a fraction of what the file will get, and two
|
||||
/// neighbouring tiles of an export would each draw their own.
|
||||
pub fn within(self, frame: (u32, u32)) -> Self {
|
||||
Self {
|
||||
frame: (frame.0.max(1), frame.1.max(1)),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,8 +281,19 @@ impl RenderScale {
|
||||
/// For the compositional family — clarity, texture, dehaze — and the same
|
||||
/// unit `dr-gpu`'s mask rasteriser already converts feathers in. An edit
|
||||
/// stored this way is resolution-independent by construction.
|
||||
///
|
||||
/// Measured against the whole frame ([`Self::within`]), scaled by the
|
||||
/// render's own short edge over the viewed region's. When the render shows
|
||||
/// the whole frame the two sizes cancel and this is `fraction` of the
|
||||
/// render's short edge exactly.
|
||||
pub fn frame_fraction(&self, fraction: f32) -> f32 {
|
||||
fraction * self.render.0.min(self.render.1) as f32
|
||||
let render = self.render.0.min(self.render.1) as f32;
|
||||
let viewed = self.full.0.min(self.full.1) as f32;
|
||||
let frame = self.frame.0.min(self.frame.1) as f32;
|
||||
if self.frame == self.full {
|
||||
return fraction * render;
|
||||
}
|
||||
fraction * render * (frame / viewed)
|
||||
}
|
||||
|
||||
/// Whether a radius stated in source pixels survives this render.
|
||||
@@ -914,6 +947,21 @@ mod tests {
|
||||
assert!((export.frame_fraction(0.01) - 40.0).abs() < 0.5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_frame_fraction_does_not_shrink_with_the_zoom_or_the_tile() {
|
||||
// TRACES: FR-DSP-1 | FR-DSP-2
|
||||
// A 6000×4000 frame. At fit in a 1500×1000 panel, 1% of it is 10
|
||||
// render pixels; zoomed to 1:1 on a 1500×1000 corner of it, the same
|
||||
// 1% is 40 — the 40 the file gets — and an export tile of that corner
|
||||
// must say 40 too, or each tile draws its own halo and the seams show.
|
||||
let fit = RenderScale::new((1500, 1000), (6000, 4000));
|
||||
assert!((fit.frame_fraction(0.01) - 10.0).abs() < 1e-3);
|
||||
let zoomed = RenderScale::new((1500, 1000), (1500, 1000)).within((6000, 4000));
|
||||
assert!((zoomed.frame_fraction(0.01) - 40.0).abs() < 1e-3);
|
||||
let tile = RenderScale::full((1024, 1024)).within((6000, 4000));
|
||||
assert!((tile.frame_fraction(0.01) - 40.0).abs() < 1e-3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zooming_to_one_to_one_makes_the_preview_exact() {
|
||||
// The reason there is no separate full-resolution preview path: the
|
||||
|
||||
@@ -952,7 +952,7 @@ impl EditGraph {
|
||||
((fw as f32 * view.width).round() as u32).max(1),
|
||||
((fh as f32 * view.height).round() as u32).max(1),
|
||||
);
|
||||
crate::detail::RenderScale::new(render, full)
|
||||
crate::detail::RenderScale::new(render, full).within((fw, fh))
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-3 | FR-DSP-1
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user