Format the tree the way the gate asks for it
`cargo fmt --check` is a required step and had drifted across 45 files. Most of it arrived this week: several operations were written in parallel worktrees and merged by hand, and a hand-merge resolves conflicts without ever running the formatter over the result. No behaviour changes — this is `cargo fmt --all` and nothing else, kept as its own commit so the next reader can skip it wholesale rather than search it for one that matters. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -499,11 +499,7 @@ pub fn compose_detail(
|
||||
// case where a photographer has a kernel switched on at a scale that
|
||||
// cannot draw it — against the alternative of the preview failing outright
|
||||
// or `compose_full` growing a resolution argument it has no other use for.
|
||||
if planned.is_empty()
|
||||
&& ops
|
||||
.iter()
|
||||
.any(|o| o.is_active() && o.detail().is_some())
|
||||
{
|
||||
if planned.is_empty() && ops.iter().any(|o| o.is_active() && o.detail().is_some()) {
|
||||
return ComposedDetail {
|
||||
passes: vec![compose_one(
|
||||
RESOLVE_ID,
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
//! so reordering the pipeline needs no code change.
|
||||
|
||||
use crate::descriptor::{
|
||||
Attribute,
|
||||
Facet, LocalizedKey, OpDescriptor, OpId, ParamId, ParamKind, Presentation,
|
||||
Attribute, Facet, LocalizedKey, OpDescriptor, OpId, ParamId, ParamKind, Presentation,
|
||||
};
|
||||
use crate::framing::{CropRect, Framing};
|
||||
use crate::mask::MaskStack;
|
||||
@@ -378,7 +377,11 @@ impl EditGraph {
|
||||
/// Only the detail stage needs this. Every point operation is scale-free
|
||||
/// — a multiply is a multiply at any resolution — which is why nothing in
|
||||
/// the pipeline had to know its own size until a kernel arrived.
|
||||
pub fn render_scale(&self, source: (u32, u32), render: (u32, u32)) -> crate::detail::RenderScale {
|
||||
pub fn render_scale(
|
||||
&self,
|
||||
source: (u32, u32),
|
||||
render: (u32, u32),
|
||||
) -> crate::detail::RenderScale {
|
||||
let (fw, fh) = self.framing.output_size(source.0, source.1);
|
||||
let view = self.framing.view();
|
||||
// The *viewed* part of the framed image, at source resolution. Zoom
|
||||
@@ -397,7 +400,10 @@ impl EditGraph {
|
||||
/// Empty for every edit with no active neighbourhood operation, which is
|
||||
/// almost all of them — and in that case [`Self::compose`] emits the
|
||||
/// single encoded dispatch it always has.
|
||||
pub fn compose_detail(&self, scale: crate::detail::RenderScale) -> crate::detail::ComposedDetail {
|
||||
pub fn compose_detail(
|
||||
&self,
|
||||
scale: crate::detail::RenderScale,
|
||||
) -> crate::detail::ComposedDetail {
|
||||
self.compose_detail_for(scale, dr_types::ColourSpace::Srgb)
|
||||
}
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ pub mod preset;
|
||||
pub mod sidecar;
|
||||
|
||||
pub use descriptor::{
|
||||
Attribute, Facet, LocalizedKey, OpDescriptor, OpId, ParamDescriptor, ParamId, ParamKind, Presentation,
|
||||
Scale, Unit, WidgetDemand, WidgetKind,
|
||||
Attribute, Facet, LocalizedKey, OpDescriptor, OpId, ParamDescriptor, ParamId, ParamKind,
|
||||
Presentation, Scale, Unit, WidgetDemand, WidgetKind,
|
||||
};
|
||||
pub use detail::{
|
||||
compose_detail, ComposedDetail, ComposedDetailPass, DetailPass, DetailStage, RenderScale,
|
||||
|
||||
@@ -856,7 +856,11 @@ impl MaskLayer {
|
||||
pub fn begin_stroke(&mut self, erase: bool, radius: f32, hardness: f32, flow: f32) -> bool {
|
||||
let full = self.stroke_points() >= MAX_LAYER_POINTS;
|
||||
let MaskSource::Brush { strokes } = &mut self.source else {
|
||||
log::warn!("layer {} is a {} mask, not a brush", self.id, self.source.kind());
|
||||
log::warn!(
|
||||
"layer {} is a {} mask, not a brush",
|
||||
self.id,
|
||||
self.source.kind()
|
||||
);
|
||||
return false;
|
||||
};
|
||||
if full {
|
||||
@@ -1121,7 +1125,10 @@ impl MaskStack {
|
||||
|
||||
/// Generate a fresh layer id that does not collide with an existing one.
|
||||
pub fn next_id(&self) -> String {
|
||||
(1..).map(|n| format!("m{n}")).find(|id| self.get(id).is_none()).expect("infinite range")
|
||||
(1..)
|
||||
.map(|n| format!("m{n}"))
|
||||
.find(|id| self.get(id).is_none())
|
||||
.expect("infinite range")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1178,10 +1185,7 @@ pub(crate) fn compose_layers(stack: &MaskStack) -> LayerShader {
|
||||
// place. A second copy here would be a second thing to keep in step
|
||||
// with `Framing::wgsl_prologue`, and the failure would be a mask that
|
||||
// is subtly wrong only when straightened.
|
||||
let _ = writeln!(
|
||||
out.body,
|
||||
" var m = sample_mask(uv_src, {slot});"
|
||||
);
|
||||
let _ = writeln!(out.body, " var m = sample_mask(uv_src, {slot});");
|
||||
let _ = writeln!(
|
||||
out.body,
|
||||
" m = select(m, 1.0 - m, u.{prefix}_invert > 0.5);"
|
||||
@@ -1403,7 +1407,11 @@ mod tests {
|
||||
assert!(shader.uniform_fields.contains("mask1_exposure_"));
|
||||
assert_eq!(
|
||||
shader.uniform_values.len(),
|
||||
shader.uniform_fields.lines().filter(|l| l.trim_start().starts_with("mask")).count(),
|
||||
shader
|
||||
.uniform_fields
|
||||
.lines()
|
||||
.filter(|l| l.trim_start().starts_with("mask"))
|
||||
.count(),
|
||||
"one value per emitted field"
|
||||
);
|
||||
}
|
||||
@@ -1448,7 +1456,11 @@ mod tests {
|
||||
// A gradient has no region ids, so nothing can go stale about it.
|
||||
let grad = MaskLayer::new(
|
||||
"m2",
|
||||
MaskSource::Linear { centre: (0.5, 0.5), angle: 0.0, width: 0.2 },
|
||||
MaskSource::Linear {
|
||||
centre: (0.5, 0.5),
|
||||
angle: 0.0,
|
||||
width: 0.2,
|
||||
},
|
||||
);
|
||||
assert!(!grad.is_stale(999));
|
||||
}
|
||||
@@ -1458,8 +1470,16 @@ mod tests {
|
||||
let base = segmentation_signature(1600, 1067, 6730, 2);
|
||||
assert_eq!(base, segmentation_signature(1600, 1067, 6730, 2));
|
||||
assert_ne!(base, segmentation_signature(1600, 1067, 6730, 5), "tuning");
|
||||
assert_ne!(base, segmentation_signature(800, 1067, 6730, 2), "proxy size");
|
||||
assert_ne!(base, segmentation_signature(1600, 1067, 42, 2), "region count");
|
||||
assert_ne!(
|
||||
base,
|
||||
segmentation_signature(800, 1067, 6730, 2),
|
||||
"proxy size"
|
||||
);
|
||||
assert_ne!(
|
||||
base,
|
||||
segmentation_signature(1600, 1067, 42, 2),
|
||||
"region count"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -472,10 +472,7 @@ pub fn compose_full(
|
||||
// than from a flag the caller sets, because a caller that got the flag
|
||||
// wrong would produce a shader whose storage format does not match the
|
||||
// texture bound to it.
|
||||
let output_mode = if ops
|
||||
.iter()
|
||||
.any(|o| o.is_active() && o.detail().is_some())
|
||||
{
|
||||
let output_mode = if ops.iter().any(|o| o.is_active() && o.detail().is_some()) {
|
||||
OutputMode::LinearWorking
|
||||
} else {
|
||||
OutputMode::Encoded
|
||||
@@ -1389,7 +1386,10 @@ mod tests {
|
||||
// how a shoulder rolls off.
|
||||
let mut curve = crate::ops::ToneCurve::new();
|
||||
curve.set_param(crate::ops::curve::P2_Y, 0.7);
|
||||
assert!(curve.is_active(), "the fixture must actually reach the shader");
|
||||
assert!(
|
||||
curve.is_active(),
|
||||
"the fixture must actually reach the shader"
|
||||
);
|
||||
|
||||
let source = compose(&[Box::new(curve)]).source;
|
||||
assert_eq!(
|
||||
|
||||
@@ -32,7 +32,9 @@
|
||||
//! least to perceived sharpness. Scaling all three about a virtual reference
|
||||
//! would soften the image even when the correction is right.
|
||||
|
||||
use crate::descriptor::{Attribute, LocalizedKey, OpDescriptor, OpId, ParamDescriptor, ParamId, Scale, Unit};
|
||||
use crate::descriptor::{
|
||||
Attribute, LocalizedKey, OpDescriptor, OpId, ParamDescriptor, ParamId, Scale, Unit,
|
||||
};
|
||||
use crate::lens::Warp;
|
||||
use crate::operation::{Helper, Uniform};
|
||||
|
||||
|
||||
@@ -764,9 +764,11 @@ mod tests {
|
||||
};
|
||||
assert_eq!(gate_of(0.0), 0.0);
|
||||
assert!((gate_of(1.0) - 0.25).abs() < 1e-6);
|
||||
assert!(chain_at(&sharpening(50.0, 1.0), RenderScale::full((512, 512))).passes[0]
|
||||
.source
|
||||
.contains("gate > 0.0"));
|
||||
assert!(
|
||||
chain_at(&sharpening(50.0, 1.0), RenderScale::full((512, 512))).passes[0]
|
||||
.source
|
||||
.contains("gate > 0.0")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -783,7 +785,11 @@ mod tests {
|
||||
for scale in scales {
|
||||
for pass in chain_at(&sharpening(75.0, 1.0), scale).passes {
|
||||
assert_eq!(pass.uniforms.len() % 4, 0, "{}", pass.label);
|
||||
assert!(pass.uniforms.iter().all(|v| v.is_finite()), "{}", pass.label);
|
||||
assert!(
|
||||
pass.uniforms.iter().all(|v| v.is_finite()),
|
||||
"{}",
|
||||
pass.label
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -805,10 +811,38 @@ mod tests {
|
||||
// word be safe in one stage and not in the other, which is the sort of
|
||||
// difference nobody discovers until a shader fails to compile.
|
||||
const RESERVED: &[&str] = &[
|
||||
"target", "sample", "filter", "texture", "buffer", "binding", "const", "enum", "mat",
|
||||
"vec", "ptr", "ref", "shared", "static", "typedef", "union", "unless", "handle",
|
||||
"layout", "packed", "premerge", "regardless", "active", "do", "input", "output",
|
||||
"private", "resource", "restrict", "self", "std", "where",
|
||||
"target",
|
||||
"sample",
|
||||
"filter",
|
||||
"texture",
|
||||
"buffer",
|
||||
"binding",
|
||||
"const",
|
||||
"enum",
|
||||
"mat",
|
||||
"vec",
|
||||
"ptr",
|
||||
"ref",
|
||||
"shared",
|
||||
"static",
|
||||
"typedef",
|
||||
"union",
|
||||
"unless",
|
||||
"handle",
|
||||
"layout",
|
||||
"packed",
|
||||
"premerge",
|
||||
"regardless",
|
||||
"active",
|
||||
"do",
|
||||
"input",
|
||||
"output",
|
||||
"private",
|
||||
"resource",
|
||||
"restrict",
|
||||
"self",
|
||||
"std",
|
||||
"where",
|
||||
];
|
||||
|
||||
for pass in chain_at(&sharpening(100.0, 2.0), RenderScale::full((512, 512))).passes {
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
//! adjusted, each one's share depends on what the other is set to, so turning
|
||||
//! up one colour's saturation quietly weakened its neighbour's hue shift.
|
||||
|
||||
use crate::descriptor::{Attribute, Facet, LocalizedKey, OpDescriptor, OpId, ParamDescriptor, ParamId};
|
||||
use crate::descriptor::{
|
||||
Attribute, Facet, LocalizedKey, OpDescriptor, OpId, ParamDescriptor, ParamId,
|
||||
};
|
||||
use crate::operation::{Helper, Operation, Uniform};
|
||||
use crate::ops::helpers;
|
||||
|
||||
|
||||
@@ -121,12 +121,7 @@ impl Channel {
|
||||
/// Master first because it runs first — see the module documentation for
|
||||
/// why that is the composition order — and because a list showing the
|
||||
/// grade before the tone would be describing a different operation.
|
||||
pub const ALL: [Channel; 4] = [
|
||||
Channel::Master,
|
||||
Channel::Red,
|
||||
Channel::Green,
|
||||
Channel::Blue,
|
||||
];
|
||||
pub const ALL: [Channel; 4] = [Channel::Master, Channel::Red, Channel::Green, Channel::Blue];
|
||||
|
||||
/// Position in [`Self::ALL`], and so in every array keyed by channel.
|
||||
pub const fn index(self) -> usize {
|
||||
@@ -771,7 +766,10 @@ impl Operation for ToneCurve {
|
||||
// declared from, so a call and its uniform block cannot disagree
|
||||
// about a name.
|
||||
let args = UNIFORM_NAMES[channel.index()].join(", ");
|
||||
let _ = writeln!(body, "c.{component} = channel_curve(c.{component}, {args});");
|
||||
let _ = writeln!(
|
||||
body,
|
||||
"c.{component} = channel_curve(c.{component}, {args});"
|
||||
);
|
||||
}
|
||||
|
||||
// Whichever curves ran, the result has to be a colour: the spline's
|
||||
@@ -1345,7 +1343,9 @@ mod tests {
|
||||
|
||||
let body = c.wgsl_body();
|
||||
let master = body.find("apply_tone_gain").expect("the master curve runs");
|
||||
let red = body.find("c.r = channel_curve").expect("the red curve runs");
|
||||
let red = body
|
||||
.find("c.r = channel_curve")
|
||||
.expect("the red curve runs");
|
||||
assert!(master < red, "the master curve must run first:\n{body}");
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
//! profile is a "make the horizon straight" task, which one term does well.
|
||||
//! The full triple is reachable by loading a profile.
|
||||
|
||||
use crate::descriptor::{Attribute, LocalizedKey, OpDescriptor, OpId, ParamDescriptor, ParamId, Scale, Unit};
|
||||
use crate::descriptor::{
|
||||
Attribute, LocalizedKey, OpDescriptor, OpId, ParamDescriptor, ParamId, Scale, Unit,
|
||||
};
|
||||
use crate::lens::Warp;
|
||||
use crate::operation::{Helper, Uniform};
|
||||
|
||||
|
||||
@@ -619,7 +619,10 @@ mod tests {
|
||||
let scale = RenderScale::full((2000, 1500));
|
||||
let only_clarity = composed(50.0, 0.0, scale);
|
||||
assert_eq!(only_clarity.len(), 2, "one operation, two passes");
|
||||
assert!(only_clarity.passes.iter().all(|p| p.label.starts_with("clarity/")));
|
||||
assert!(only_clarity
|
||||
.passes
|
||||
.iter()
|
||||
.all(|p| p.label.starts_with("clarity/")));
|
||||
|
||||
let both = composed(50.0, 50.0, scale);
|
||||
assert_eq!(both.len(), 4);
|
||||
@@ -701,7 +704,10 @@ mod tests {
|
||||
// acutance family's unit.
|
||||
let proxy = RenderScale::new((2000, 1500), (6000, 4500));
|
||||
let clarity = Clarity::with_amount(60.0);
|
||||
assert_eq!(clarity.kernel(proxy), clarity.kernel(RenderScale::full((2000, 1500))));
|
||||
assert_eq!(
|
||||
clarity.kernel(proxy),
|
||||
clarity.kernel(RenderScale::full((2000, 1500)))
|
||||
);
|
||||
assert!(
|
||||
proxy.source_pixels(96.0) < 40.0,
|
||||
"the same length in the other unit would have collapsed"
|
||||
@@ -786,7 +792,9 @@ mod tests {
|
||||
// slider can produce is `gain * threshold` stops however violent the
|
||||
// edge — a bound that holds by construction rather than by tuning.
|
||||
let combine = &Clarity::with_amount(100.0).passes(RenderScale::full((2000, 1500)))[1];
|
||||
assert!(combine.wgsl.contains("threshold * tanh(detail / threshold)"));
|
||||
assert!(combine
|
||||
.wgsl
|
||||
.contains("threshold * tanh(detail / threshold)"));
|
||||
|
||||
let bound = |u: &[Uniform]| {
|
||||
let get = |n| u.iter().find(|x| x.name == n).unwrap().value;
|
||||
@@ -819,7 +827,10 @@ mod tests {
|
||||
|
||||
// And texture does not carry the helpers it would need for one, so its
|
||||
// shader says what it does rather than merely not calling it.
|
||||
assert!(Texture::new().helpers().iter().any(|h| h.name == "log_luma"));
|
||||
assert!(Texture::new()
|
||||
.helpers()
|
||||
.iter()
|
||||
.any(|h| h.name == "log_luma"));
|
||||
assert!(!Texture::new()
|
||||
.helpers()
|
||||
.iter()
|
||||
|
||||
@@ -820,7 +820,13 @@ mod tests {
|
||||
// Everything else about the two must match, or one axis is filtered
|
||||
// harder than the other and a round blotch comes out oval.
|
||||
assert_eq!(passes[0].radius, passes[1].radius);
|
||||
for name in ["radius", "inv_spatial", "guide_k", "chroma_k", "noise_floor"] {
|
||||
for name in [
|
||||
"radius",
|
||||
"inv_spatial",
|
||||
"guide_k",
|
||||
"chroma_k",
|
||||
"noise_floor",
|
||||
] {
|
||||
let of = |p: &DetailPass| {
|
||||
p.uniforms
|
||||
.iter()
|
||||
|
||||
@@ -67,9 +67,7 @@ use std::fmt;
|
||||
use std::fmt::Write as _;
|
||||
|
||||
use crate::graph::EditGraph;
|
||||
use crate::mask::{
|
||||
Falloff, MaskLayer, MaskSource, MaskStack, Morphology, Stroke, DEFAULT_FEATHER,
|
||||
};
|
||||
use crate::mask::{Falloff, MaskLayer, MaskSource, MaskStack, Morphology, Stroke, DEFAULT_FEATHER};
|
||||
use crate::preset::{resolve, Preset};
|
||||
|
||||
/// Format version of the document itself.
|
||||
@@ -662,7 +660,6 @@ impl Sidecar {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Write one mask layer as its own block.
|
||||
///
|
||||
/// The version uuid is repeated in the header rather than relying on the
|
||||
@@ -1292,7 +1289,11 @@ mod tests {
|
||||
// else.
|
||||
assert_eq!(g.param(curve::ID, curve::P1_Y), Some(0.15));
|
||||
assert_eq!(g.param(curve::ID, curve::P3_Y), Some(0.85));
|
||||
for channel in [curve::Channel::Red, curve::Channel::Green, curve::Channel::Blue] {
|
||||
for channel in [
|
||||
curve::Channel::Red,
|
||||
curve::Channel::Green,
|
||||
curve::Channel::Blue,
|
||||
] {
|
||||
for point in 0..curve::POINTS {
|
||||
for axis in [curve::Axis::X, curve::Axis::Y] {
|
||||
let id = curve::coordinate(channel, point, axis);
|
||||
|
||||
Reference in New Issue
Block a user