Floor a restored brush's opacity at 1 %

A brush remembered at 0 % opacity (saved before the 1 % floor) read back
at 0: the options bar showed 1 % but strokes painted nothing. Restoring
now floors opacity at 1 %, like tools.setBrush (#11812).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
David MannandClaude Opus 5.5 committed 2026-11-08 01:24:18 +00:00
1 parent 1d9c654b3a
commit 6547fadccd
2 files changed
+21 -2

No files matched your search

+5 -2
View File
@@ -1172,8 +1172,8 @@ pub fn remembered_settings(b: &BrushSettings) -> Option<Value> {
/// A brush remembered by [`remembered_settings`], read back at launch: `None` when the value is
/// not a brush or fails the checks `tools.setBrush` makes (a size or scatter out of range). The
/// options-bar values are clamped to the ranges their controls allow (size 1..max px; hardness,
/// opacity, flow and smoothing 0..100 %), and stored tips load when the store is attached (else
/// options-bar values are clamped to the ranges their controls allow (size 1..max px; opacity
/// 1..100 %; hardness, flow and smoothing 0..100 %), and stored tips load when the store is attached (else
/// they paint with their previews until `Session::attach_preset_store`).
pub fn restored_settings(s: &Session, v: &Value) -> Option<BrushSettings> {
if !v.is_object() {
@@ -1185,6 +1185,9 @@ pub fn restored_settings(s: &Session, v: &Value) -> Option<BrushSettings> {
for f in [&mut b.hardness, &mut b.opacity, &mut b.flow, &mut b.smoothing.amount] {
*f = if f.is_nan() { 1.0 } else { f.clamp(0.0, 1.0) };
}
// Opacity takes 1-100 %, as `tools.setBrush` floors it: a brush remembered at 0 % (saved
// before that floor) would show 1 % but paint nothing (#11812).
b.opacity = b.opacity.max(0.01);
let _ = s.load_brush_tips(&mut b);
Some(b)
}
+16
View File
@@ -1380,6 +1380,22 @@ fn restored_brush_settings_are_clamped_or_rejected() {
}
}
/// #11812: a brush remembered at 0 % opacity (saved before the 1 % floor) reads back at 1 %, the
/// value its options-bar field shows, so it paints instead of laying down nothing.
#[test]
fn restored_brush_opacity_floors_at_one_percent() {
let s = Session::new();
for opacity in [0.0, -1.0, 0.004] {
let b = restored_settings(&s, &json!({"opacity": opacity})).unwrap();
assert_eq!(b.opacity, 0.01, "{opacity}");
}
assert_eq!(restored_settings(&s, &json!({"opacity": 0.37})).unwrap().opacity, 0.37, "values above the floor are kept");
let mut t = Session::new();
t.prefs.edit(|p| p.last_brush = json!({"brush": {"opacity": 0.0}}));
restore_brush(&mut t);
assert_eq!(t.tools.brush.opacity, 0.01, "the remembered session brush");
}
#[test]
fn remembered_brushes_never_embed_large_bitmaps() {
use std::sync::Arc;