mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-12 21:23:02 +00:00
Background Eraser: Protect Foreground keeps the per-channel distance; tolerance doc
Only the sample match uses the measured increase-plus-decrease distance (#11901). Protect Foreground Color keeps today's largest per-channel difference, as #11903 asks for its unmeasured cases. The tolerance doc describes the new distance and the graded falloff. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a65a23f829
commit
cbdce347f0
2 files changed
+24
-2
No files matched your search
@@ -17,7 +17,11 @@ use crate::retouch::{Footprint, Region, alpha_index, apply_dab_stroke};
|
||||
pub struct BgEraseSettings {
|
||||
pub sampling: Sampling,
|
||||
pub limits: Limits,
|
||||
/// 0..1: largest per-channel difference from the sampled colour that is erased.
|
||||
/// 0..1: how far from the sampled colour a colour is still partly erased. The distance is the
|
||||
/// largest increase plus the largest decrease over the colour channels (`dist`); an exact
|
||||
/// match is erased fully, then less the further a colour is, and colours at or beyond the
|
||||
/// Tolerance are kept (`graded_weight`). Protect Foreground Color still compares the
|
||||
/// foreground by the largest per-channel difference (`max_channel_dist`).
|
||||
pub tolerance: f32,
|
||||
/// Keep pixels that match the foreground colour.
|
||||
pub protect_foreground: bool,
|
||||
@@ -52,6 +56,12 @@ fn dist(a: &[f32], b: &[f32]) -> f32 {
|
||||
up + down
|
||||
}
|
||||
|
||||
/// The largest per-channel difference of `a` from `b`: the distance Protect Foreground Color keeps
|
||||
/// using (#11901 measured only the sample match; #11903 keeps today's measure for the foreground).
|
||||
fn max_channel_dist(a: &[f32], b: &[f32]) -> f32 {
|
||||
a.iter().zip(b).fold(0.0f32, |m, (x, y)| m.max((x - y).abs()))
|
||||
}
|
||||
|
||||
/// The match weight of one dab for a colour `d` away from the sampled colour (#11901): an exact
|
||||
/// match is erased fully, then the weight falls off linearly over the whole Tolerance `tol` after
|
||||
/// a short plateau `d0 = min(tol / 20, 8 levels)`, and colours at or beyond the Tolerance are kept.
|
||||
@@ -151,7 +161,7 @@ pub fn background_eraser_effect(pre: &Surface, bs: &BgEraseSettings) -> Option<i
|
||||
continue;
|
||||
}
|
||||
let c = &p[..nc];
|
||||
if protect_foreground && dist(c, &fg) <= tol {
|
||||
if protect_foreground && max_channel_dist(c, &fg) <= tol {
|
||||
continue;
|
||||
}
|
||||
weight[(y - r.y0) as usize * w + (x - r.x0) as usize] = match_w(dist(c, &sampled));
|
||||
|
||||
@@ -175,3 +175,15 @@ fn the_distance_is_measured_in_the_documents_own_channels() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn protect_foreground_keeps_todays_distance_to_the_foreground() {
|
||||
// #11903 measured the foreground only one channel away and keeps today's measure otherwise:
|
||||
// the largest per-channel difference. This foreground is 100 levels off in red and in green
|
||||
// (opposite signs): within Tolerance 50 by that measure, though the sample-match distance of
|
||||
// #11901 adds the two to 200. The pixel is the sampled colour, so only the protection keeps it.
|
||||
let bs = BgEraseSettings { protect_foreground: true, foreground: rgb([100, 140, 40]), ..swatch(50.0, rgb(SAMPLE)) };
|
||||
assert_eq!(one_click(PixelFormat::RGBA8, rgb(SAMPLE), &bs), 255);
|
||||
// Protection off: the same click erases the sample fully.
|
||||
assert_eq!(one_click(PixelFormat::RGBA8, rgb(SAMPLE), &BgEraseSettings { protect_foreground: false, ..bs }), 0);
|
||||
}
|
||||
Reference in new issue
Block a user