mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-10 12:22:22 +00:00
Address review: keep scaled pattern scales within 1..1000 %
scale_effects (Image Size's Scale Styles, Layer Style > Scale Effects, the Free Transform style preview) now clamps the Pattern Overlay, Bevel & Emboss Texture and pattern Stroke scales to the 1..1000 % the Layer Style dialog allows, through one shared photocraft_doc::effects::PATTERN_SCALE_RANGE that the layer style param parser now uses too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
945fec8665
commit
608cedc5eb
4 files changed
+86
-5
No files matched your search
@@ -10,6 +10,10 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::adjust::CurvePoint;
|
||||
|
||||
/// The range of a style's pattern scale as a fraction (1 = 100 %): Pattern Overlay, a pattern
|
||||
/// Stroke and the Bevel & Emboss Texture all take 1..1000 % in the Layer Style dialog.
|
||||
pub const PATTERN_SCALE_RANGE: (f32, f32) = (0.01, 10.0);
|
||||
|
||||
/// Gradient geometry (fill layers, gradient overlays, gradient strokes).
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum GradientStyle {
|
||||
|
||||
@@ -151,16 +151,16 @@ pub fn scale_effects(fx: &mut Effects, k: f32) {
|
||||
b.size *= k;
|
||||
b.soften *= k;
|
||||
if let Some(t) = &mut b.texture {
|
||||
t.scale *= k;
|
||||
scale_pattern(&mut t.scale, k);
|
||||
}
|
||||
}
|
||||
Effect::PatternOverlay { scale, .. } => *scale *= k,
|
||||
Effect::PatternOverlay { scale, .. } => scale_pattern(scale, k),
|
||||
Effect::GradientOverlay { .. } | Effect::ColorOverlay { .. } => {}
|
||||
}
|
||||
if let Effect::Stroke(s) = e
|
||||
&& let FxPaint::Pattern { scale, .. } = &mut s.paint
|
||||
{
|
||||
*scale *= k;
|
||||
scale_pattern(scale, k);
|
||||
}
|
||||
}
|
||||
// Effects changed: the preserved PSD block no longer applies.
|
||||
@@ -169,6 +169,16 @@ pub fn scale_effects(fx: &mut Effects, k: f32) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Scale a style's pattern scale, kept within the 1..1000 % the Layer Style dialog and the
|
||||
/// reference application allow (a 10 % pattern reduced to 5 % stays at 1 %).
|
||||
fn scale_pattern(scale: &mut f32, k: f32) {
|
||||
let (min, max) = photocraft_doc::effects::PATTERN_SCALE_RANGE;
|
||||
let v = *scale * k;
|
||||
if !v.is_nan() {
|
||||
*scale = v.clamp(min, max);
|
||||
}
|
||||
}
|
||||
|
||||
/// Image Size's resampling methods; `None` changes the resolution only.
|
||||
#[derive(Clone, Copy)]
|
||||
enum Method {
|
||||
|
||||
@@ -160,6 +160,72 @@ fn scale_effects_scales_the_bevel_texture_with_the_pattern_overlay() {
|
||||
assert_eq!((overlay, texture), (Some(0.5), Some(0.5)));
|
||||
}
|
||||
|
||||
/// The pattern scales of Pattern Overlay, the Bevel & Emboss Texture and a pattern Stroke, in
|
||||
/// that order.
|
||||
fn pattern_scales(s: &Session) -> (Option<f32>, Option<f32>, Option<f32>) {
|
||||
let fx = &active(s).effects.items;
|
||||
let overlay = fx.iter().find_map(|e| match e {
|
||||
Effect::PatternOverlay { scale, .. } => Some(*scale),
|
||||
_ => None,
|
||||
});
|
||||
let texture = fx.iter().find_map(|e| match e {
|
||||
Effect::BevelEmboss(b) => b.texture.as_ref().map(|t| t.scale),
|
||||
_ => None,
|
||||
});
|
||||
let stroke = fx.iter().find_map(|e| match e {
|
||||
Effect::Stroke(st) => match &st.paint {
|
||||
photocraft_doc::FxPaint::Pattern { scale, .. } => Some(*scale),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
});
|
||||
(overlay, texture, stroke)
|
||||
}
|
||||
|
||||
/// A layer with a Pattern Overlay, a Bevel & Emboss Texture and a pattern Stroke, all at `pct` %.
|
||||
fn pattern_styled(pct: f64) -> Session {
|
||||
let mut s = session(8, "rgb");
|
||||
paint(&mut s, disc);
|
||||
s.execute("layer.layerStyle.patternOverlay", json!({"scale": pct})).unwrap();
|
||||
s.execute("layer.layerStyle.bevelEmboss", json!({"texture": "Bubbles", "textureScale": pct})).unwrap();
|
||||
s.execute("layer.layerStyle.stroke", json!({"size": 3})).unwrap();
|
||||
s.edit("pattern stroke", |doc, active| {
|
||||
let layer = doc.layer_mut(active.unwrap()).unwrap();
|
||||
for e in &mut layer.effects.items {
|
||||
if let Effect::Stroke(st) = e {
|
||||
st.paint = photocraft_doc::FxPaint::Pattern { name: "Bubbles".into(), id: String::new(), scale: pct as f32 / 100.0 };
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
s
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scale_effects_keeps_pattern_scales_within_the_dialog_range() {
|
||||
// 10 % reduced to 1 % would be 0.1 %: the patterns stop at the dialog's 1 %.
|
||||
let mut s = pattern_styled(10.0);
|
||||
s.execute("layer.layerStyle.scaleEffects", json!({"scale": 1})).unwrap();
|
||||
assert_eq!(pattern_scales(&s), (Some(0.01), Some(0.01), Some(0.01)));
|
||||
// 500 % enlarged to 1000 % would be 5000 %: they stop at 1000 %.
|
||||
let mut s = pattern_styled(500.0);
|
||||
s.execute("layer.layerStyle.scaleEffects", json!({"scale": 1000})).unwrap();
|
||||
assert_eq!(pattern_scales(&s), (Some(10.0), Some(10.0), Some(10.0)));
|
||||
// Inside the range nothing is clamped: 200 % halved is 100 %.
|
||||
let mut s = pattern_styled(200.0);
|
||||
s.execute("layer.layerStyle.scaleEffects", json!({"scale": 50})).unwrap();
|
||||
assert_eq!(pattern_scales(&s), (Some(1.0), Some(1.0), Some(1.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_size_scale_styles_keeps_pattern_scales_within_the_dialog_range() {
|
||||
// 40x30 -> 400x300 with Scale Styles multiplies by 10: 500 % would become 5000 %.
|
||||
let mut s = pattern_styled(500.0);
|
||||
s.execute("image.imageSize", json!({"width": 400, "height": 300, "scaleStyles": true})).unwrap();
|
||||
assert_eq!(pattern_scales(&s), (Some(10.0), Some(10.0), Some(10.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_light_rejects_numbers_beyond_f32() {
|
||||
let mut s = session(8, "rgb");
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use photocraft_color::{BlendMode, Color};
|
||||
use photocraft_doc::adjust::CurvePoint;
|
||||
use photocraft_doc::effects::PATTERN_SCALE_RANGE;
|
||||
use photocraft_doc::{
|
||||
Bevel, BevelStyle, BevelTechnique, Contour, Effect, FxCommon, FxPaint, Glow, GlowSource, GlowTechnique, Gradient, GradientStyle, Satin, Shadow, StrokeFx,
|
||||
StrokePosition,
|
||||
@@ -795,7 +796,7 @@ fn overlay_pattern(fx: &mut Effect, p: &Value) {
|
||||
*id = String::new();
|
||||
}
|
||||
if let Some(x) = f_of(p, "scale") {
|
||||
*scale = (x / 100.0).clamp(0.01, 10.0);
|
||||
*scale = (x / 100.0).clamp(PATTERN_SCALE_RANGE.0, PATTERN_SCALE_RANGE.1);
|
||||
}
|
||||
if let Some(x) = f_of(p, "angle") {
|
||||
*angle = x;
|
||||
@@ -903,7 +904,7 @@ fn overlay_bevel(bv: &mut Bevel, p: &Value) {
|
||||
bv.texture = Some(photocraft_doc::BevelTexture {
|
||||
name: t.to_string(),
|
||||
id: t.to_string(),
|
||||
scale: (f(p, "textureScale", 100.0) / 100.0).clamp(0.01, 10.0),
|
||||
scale: (f(p, "textureScale", 100.0) / 100.0).clamp(PATTERN_SCALE_RANGE.0, PATTERN_SCALE_RANGE.1),
|
||||
depth: (f(p, "textureDepth", 100.0) / 100.0).clamp(-10.0, 10.0),
|
||||
invert: b(p, "textureInvert", false),
|
||||
link: b(p, "textureLink", true),
|
||||
|
||||
Reference in new issue
Block a user