From 755f2b915835afe7cd6a9486b343c08a1294f9b6 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Thu, 22 Oct 2026 00:18:29 +0700 Subject: [PATCH] =?UTF-8?q?G2:=20Effect=20=E2=80=BA=20Artistic=20=E2=80=BA?= =?UTF-8?q?=20Rough=20Pastels=20raster=20filter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Squashed from #3044 onto main. --- crates/effects/src/lib.rs | 15 ++++++ crates/effects/src/pixel/artistic.rs | 67 ++++++++++++++++++++++-- crates/effects/src/pixel/brushstrokes.rs | 2 +- crates/effects/src/pixel/mod.rs | 29 ++++++++-- crates/effects/src/pixel/tests.rs | 30 +++++++++++ crates/engine/src/tests_rasterfilters.rs | 16 +++++- crates/engine/tests/command_sweep.rs | 4 ++ crates/ui-egui/src/i18n/bg.tsv | 2 + crates/ui-egui/src/i18n/cs.tsv | 2 + crates/ui-egui/src/i18n/da.tsv | 2 + crates/ui-egui/src/i18n/de.tsv | 2 + crates/ui-egui/src/i18n/el.tsv | 2 + crates/ui-egui/src/i18n/es.tsv | 2 + crates/ui-egui/src/i18n/fi.tsv | 2 + crates/ui-egui/src/i18n/fr.tsv | 2 + crates/ui-egui/src/i18n/hu.tsv | 2 + crates/ui-egui/src/i18n/id.tsv | 2 + crates/ui-egui/src/i18n/it.tsv | 2 + crates/ui-egui/src/i18n/ja.tsv | 2 + crates/ui-egui/src/i18n/ka.tsv | 2 + crates/ui-egui/src/i18n/ko.tsv | 2 + crates/ui-egui/src/i18n/nl.tsv | 2 + crates/ui-egui/src/i18n/pl.tsv | 2 + crates/ui-egui/src/i18n/pt-br.tsv | 2 + crates/ui-egui/src/i18n/ru.tsv | 2 + crates/ui-egui/src/i18n/sv.tsv | 2 + crates/ui-egui/src/i18n/uk.tsv | 2 + crates/ui-egui/src/i18n/vi.tsv | 2 + crates/ui-egui/src/i18n/zh-hans.tsv | 2 + crates/ui-egui/src/i18n/zh-hant.tsv | 2 + crates/ui-egui/src/tests_labels.rs | 1 + 31 files changed, 200 insertions(+), 10 deletions(-) diff --git a/crates/effects/src/lib.rs b/crates/effects/src/lib.rs index 2094fba21..d5aea1d79 100644 --- a/crates/effects/src/lib.rs +++ b/crates/effects/src/lib.rs @@ -406,6 +406,21 @@ pub fn effect_catalog() -> Vec { "{edgeThickness: 0..10 (2; wider lines), edgeIntensity: 0..10 (1; darker lines, on fainter edges too), posterization: 0..6 (2; posterization + 2 levels per colour channel)} reduces the object's colours and draws black lines on its edges, keeping its coverage. Original visual approximation; chosen VectorCraft defaults.", json!({"edgeThickness": 2.0, "edgeIntensity": 1.0, "posterization": 2.0}), ), + r( + "artistic.roughPastels", + "Rough Pastels…", + ARTISTIC, + "{strokeLength: 0..40 (6; longer pastel strokes), strokeDetail: 1..20 (10; bristle texture along the strokes), texture: \"brick\"|\"burlap\"|\"canvas\"|\"sandstone\" (\"canvas\"), scaling: % 50..200 (100), relief: 0..50 (4), lightDirection: \"bottom\"|\"bottomLeft\"|\"left\"|\"topLeft\"|\"top\"|\"topRight\"|\"right\"|\"bottomRight\" (\"top\"), invert: bool (false)} repaints the object in soft diagonal pastel strokes over a textured surface, keeping its coverage. Slider ranges follow the documented controls; numeric defaults are chosen for VectorCraft and are not verified against proprietary defaults.", + json!({ + "strokeLength": 6.0, + "strokeDetail": 10.0, + "texture": "canvas", + "scaling": 100.0, + "relief": 4.0, + "lightDirection": "top", + "invert": false + }), + ), r( "artistic.smudgeStick", "Smudge Stick…", diff --git a/crates/effects/src/pixel/artistic.rs b/crates/effects/src/pixel/artistic.rs index b2484bd7c..f14671a12 100644 --- a/crates/effects/src/pixel/artistic.rs +++ b/crates/effects/src/pixel/artistic.rs @@ -1,5 +1,5 @@ //! Effect › Artistic (Cutout, Dry Brush, Film Grain, Neon Glow, Paint Daubs, Palette Knife, Plastic Wrap, Poster -//! Edges, Smudge Stick, Sponge, Underpainting and Watercolor). Original filters built from +//! Edges, Rough Pastels, Smudge Stick, Sponge, Underpainting and Watercolor). Original filters built from //! public behaviour documentation: //! and //! . @@ -7,10 +7,10 @@ use std::f64::consts::TAU; -use super::brushstrokes::{Smear, mix, stroke_reach, unit, unpremultiply}; +use super::brushstrokes::{LEFT, RIGHT, Smear, TONE_SOFTNESS, mix, streak, stroke_reach, unit, unpremultiply}; use super::edges::sobel; use super::pixelate::{Around, Place, Soft, cell, point_in, rand, value_noise}; -use super::texture::{self, Light, Texture, luma, premultiply, straight}; +use super::texture::{self, Light, Texture, Tones, luma, premultiply, straight, texturizer}; use super::{PixelSpace, blur_plane, pick}; use vectorcraft_geom::{Point, Vec2}; @@ -18,6 +18,8 @@ const SALT_DRY: u64 = 264; const MARGIN: f64 = 1.0; const SALT_PLASTIC: u64 = 512; +const SALT_PASTEL: u64 = 200; + const SALT_FILM: u64 = 256; const SALT_DAUB: u64 = 272; @@ -526,6 +528,65 @@ pub(super) fn underpainting( } } +/// How far (document units) Rough Pastels reaches with strokes `len` long. +pub(super) fn rough_pastels_reach(len: f64) -> f64 { + super::brushstrokes::stroke_reach(len).max(super::brushstrokes::TONE_REACH) +} + +/// Rough Pastels: soft diagonal pastel strokes `length` (0..40) long with bristle `detail` (1..20), +/// then the shared Texturizer surface (`texture`, `scaling`, `relief`, `light`, `invert`). +/// Coverage is kept. +#[allow(clippy::too_many_arguments)] +pub(super) fn rough_pastels( + px: &mut [[u8; 4]], + w: usize, + h: usize, + space: &PixelSpace, + length: f64, + detail: f64, + texture: &Texture, + scaling: f64, + relief: f64, + light: Light, + invert: bool, +) { + if !([length, detail, scaling, relief].iter().all(|v| v.is_finite()) && space.px.is_finite() && space.px > 0.0) { + return; + } + let length = length.clamp(0.0, 40.0); + let detail = detail.clamp(1.0, 20.0); + if length > 0.0 { + let Some(place) = Place::new(space, w, h) else { return }; + let smear = Smear::new(px, w, h, &place, length.max(1.0)); + let tones = Tones::new(px, w, h, TONE_SOFTNESS / place.px); + let row = (0.18 * smear.len).max(1.0); + let stroke_mix = (0.35 + 0.0225 * detail).clamp(0.35, 0.8) as f32; + let bristle = (0.015 + 0.0035 * detail) as f32; + let src = px.to_vec(); + place.each(px, |x, y, p| { + if p[3] == 0 { + return; + } + let q = place.at(x, y); + let pm = mix(smear.stroke(SALT_PASTEL, q, RIGHT, row), smear.stroke(SALT_PASTEL + 1, q, LEFT, row), 0.5); + let orig = unit(src.get(y * w + x).copied().unwrap_or([0; 4])); + let pm = mix(orig, pm, stroke_mix); + let (mut c, a) = unpremultiply(pm); + if a <= 0.0 { + *p = [0; 4]; + return; + } + let dir = if tones.get(x, y) > 0.52 { RIGHT } else { LEFT }; + let n = bristle * streak(SALT_PASTEL + 2, q, dir, smear.len.max(1.0), row) as f32; + c = c.map(|v| (v + n).clamp(0.0, 1.0)); + *p = premultiply(c, orig[3]); + }); + } + if relief > 0.0 { + texturizer(px, w, h, space, texture, scaling, relief, light, invert); + } +} + /// Neon Glow: the object in dimmed greys, lit by `colour` glowing from its light areas (`size` /// above 0) or from its dark areas (below 0), spread as far as |`size`| (0..24) says and as /// strong as `brightness` (0..50). Coverage is kept. diff --git a/crates/effects/src/pixel/brushstrokes.rs b/crates/effects/src/pixel/brushstrokes.rs index d496f294e..100d4e249 100644 --- a/crates/effects/src/pixel/brushstrokes.rs +++ b/crates/effects/src/pixel/brushstrokes.rs @@ -231,7 +231,7 @@ fn frame(q: Point, dir: Vec2) -> (f64, f64) { /// How far along `dir` the stroke through `q` is shifted: strokes `len` long lie in rows `width` /// apart across `dir`, each row starting at random, each stroke shifted by up to [`JITTER`] × /// `len`. -fn stroke_shift(salt: u64, q: Point, dir: Vec2, len: f64, width: f64) -> f64 { +pub(super) fn stroke_shift(salt: u64, q: Point, dir: Vec2, len: f64, width: f64) -> f64 { if !(len > 0.0 && width > 0.0) { return 0.0; } diff --git a/crates/effects/src/pixel/mod.rs b/crates/effects/src/pixel/mod.rs index 6c465355f..d3734ad75 100644 --- a/crates/effects/src/pixel/mod.rs +++ b/crates/effects/src/pixel/mod.rs @@ -1,5 +1,5 @@ //! Photoshop-style raster effects (Effect › Artistic › Cutout, Dry Brush, Film Grain, Neon Glow, Paint Daubs, Plastic Wrap and -//! Poster Edges, Smudge Stick, Sponge and Watercolor, Blur › Radial Blur and Smart Blur, Brush Strokes › Accented Edges, Angled Strokes, +//! Poster Edges, Rough Pastels, Smudge Stick, Sponge and Watercolor, Blur › Radial Blur and Smart Blur, Brush Strokes › Accented Edges, Angled Strokes, //! Crosshatch, Dark Strokes, Ink Outlines, Spatter, Sprayed Strokes and Sumi-e, Distort › Diffuse //! Glow, Glass and Ocean Ripple, Pixelate › Color Halftone, Crystallize, Mezzotint and Pointillize, //! Sharpen › Unsharp Mask, Sketch › Bas Relief, Chalk & Charcoal, Charcoal, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, @@ -44,7 +44,7 @@ pub use sketch::{HALFTONE_TYPES, HalftonePattern}; pub use texture::{GRAIN_TYPES, Grain, LIGHT_DIRECTIONS, Light, TEXTURES, TEXTURES_LOADED, Texture}; /// The Photoshop-style effect ids (all raster effects, see [`crate::is_raster`]). -pub const PIXEL_EFFECTS: [&str; 51] = [ +pub const PIXEL_EFFECTS: [&str; 52] = [ "artistic.cutout", "artistic.dryBrush", "artistic.filmGrain", @@ -53,6 +53,7 @@ pub const PIXEL_EFFECTS: [&str; 51] = [ "artistic.paletteKnife", "artistic.plasticWrap", "artistic.posterEdges", + "artistic.roughPastels", "artistic.smudgeStick", "artistic.sponge", "artistic.underpainting", @@ -143,6 +144,10 @@ pub enum PixelFx { /// `texture` surface (`scaling`, `relief`, `light`, `invert`) blended in as strongly as /// `texture_coverage` (0..40) says. Underpainting { brush_size: f64, texture_coverage: f64, texture: Texture, scaling: f64, relief: f64, light: Light, invert: bool }, + /// Artistic › Rough Pastels: diagonal pastel strokes `length` (0..40) long, bristle detail + /// (1..20), then a [`Texture`] surface scaled by `scaling` (0.5..2), in relief as strong as + /// `relief` (0..50) and lit from `light`; `invert` turns the surface's heights over. + RoughPastels { length: f64, detail: f64, texture: Texture, scaling: f64, relief: f64, light: Light, invert: bool }, /// Blur › Radial Blur around the object's centre: along arcs (spin, an arc of `amount`°) or /// along rays (zoom, the content scaled by up to about ±`amount`/2 %), averaging 2^`passes` samples. RadialBlur { amount: f64, zoom: bool, passes: u32 }, @@ -346,6 +351,15 @@ pub(crate) fn parse(id: &str, p: &Value) -> Option { light: Light::parse(text(p, "lightDirection", "top")), invert: flag(p, "invert", false), }, + "artistic.roughPastels" => PixelFx::RoughPastels { + length: num(p, "strokeLength", 6.0).clamp(0.0, 40.0), + detail: num(p, "strokeDetail", 10.0).clamp(1.0, 20.0), + texture: Texture::parse(text(p, "texture", "canvas")), + scaling: num(p, "scaling", 100.0).clamp(50.0, 200.0) / 100.0, + relief: num(p, "relief", 4.0).clamp(0.0, 50.0), + light: Light::parse(text(p, "lightDirection", "top")), + invert: flag(p, "invert", false), + }, "artistic.smudgeStick" => PixelFx::SmudgeStick { stroke: num(p, "strokeLength", 2.0).clamp(0.0, 10.0), highlight: num(p, "highlightArea", 12.0).clamp(0.0, 20.0), @@ -558,9 +572,10 @@ impl PixelFx { PixelFx::AccentedEdges { .. } => 0.0, // Strokes reach out of the object as far as they reach into it. PixelFx::PaletteKnife { stroke_size, softness, .. } => artistic::palette_knife_reach(stroke_size, softness), - PixelFx::AngledStrokes { length, .. } | PixelFx::GraphicPen { length, .. } | PixelFx::InkOutlines { length, .. } => { - brushstrokes::stroke_reach(length) - } + PixelFx::AngledStrokes { length, .. } + | PixelFx::GraphicPen { length, .. } + | PixelFx::InkOutlines { length, .. } + | PixelFx::RoughPastels { length, .. } => brushstrokes::stroke_reach(length), PixelFx::Crosshatch { length, .. } => brushstrokes::stroke_reach(length) + brushstrokes::rough(length), PixelFx::DarkStrokes { .. } => brushstrokes::stroke_reach(brushstrokes::LIGHT_LENGTH), PixelFx::Spatter { radius, .. } => brushstrokes::spatter_shift(radius), @@ -622,6 +637,7 @@ impl PixelFx { PixelFx::AngledStrokes { length, .. } | PixelFx::GraphicPen { length, .. } => { Some(brushstrokes::stroke_reach(length).max(brushstrokes::TONE_REACH)) } + PixelFx::RoughPastels { length, .. } => Some(artistic::rough_pastels_reach(length)), PixelFx::Crosshatch { length, .. } => { Some((brushstrokes::stroke_reach(length) + brushstrokes::rough(length)).max(brushstrokes::TONE_REACH)) } @@ -701,6 +717,9 @@ impl PixelFx { PixelFx::Underpainting { brush_size, texture_coverage, ref texture, scaling, relief, light, invert } => { artistic::underpainting(px, w, h, space, brush_size, texture_coverage, texture, scaling, relief, light, invert) } + PixelFx::RoughPastels { length, detail, ref texture, scaling, relief, light, invert } => { + artistic::rough_pastels(px, w, h, space, length, detail, texture, scaling, relief, light, invert) + } PixelFx::RadialBlur { amount, zoom, passes } => blur::radial(px, w, h, space, amount, zoom, passes), PixelFx::SmartBlur { radius, threshold, samples, mode: SmartBlurMode::Normal } => { blur::smart(px, w, h, to_px(radius), threshold, samples) diff --git a/crates/effects/src/pixel/tests.rs b/crates/effects/src/pixel/tests.rs index 2f47a97a6..edce4b431 100644 --- a/crates/effects/src/pixel/tests.rs +++ b/crates/effects/src/pixel/tests.rs @@ -1222,6 +1222,8 @@ fn texture_params_take_defaults_and_stay_in_range() { ("texture.grain", "grainType", GRAIN_TYPES.as_slice()), ("texture.texturizer", "texture", TEXTURES.as_slice()), ("texture.texturizer", "lightDirection", LIGHT_DIRECTIONS.as_slice()), + ("artistic.roughPastels", "texture", TEXTURES.as_slice()), + ("artistic.roughPastels", "lightDirection", LIGHT_DIRECTIONS.as_slice()), ] { let doc = crate::effect_info(id).unwrap().params; let parsed: Vec = table.iter().map(|(_, v)| fx(id, json!({ key: v }))).collect(); @@ -2466,6 +2468,17 @@ fn artistic_params_take_defaults_and_stay_in_range() { invert: true, } ); + assert_eq!( + fx("artistic.roughPastels", json!({})), + PixelFx::RoughPastels { length: 6.0, detail: 10.0, texture: Texture::Canvas, scaling: 1.0, relief: 4.0, light: Light::Top, invert: false } + ); + assert_eq!( + fx( + "artistic.roughPastels", + json!({"strokeLength": 0, "strokeDetail": 99, "texture": -3, "scaling": 10, "relief": 60, "lightDirection": "up", "invert": true}) + ), + PixelFx::RoughPastels { length: 0.0, detail: 20.0, texture: Texture::Canvas, scaling: 0.5, relief: 50.0, light: Light::Top, invert: true } + ); assert_eq!(fx("artistic.plasticWrap", json!({})), PixelFx::PlasticWrap { highlight: 20.0, detail: 12.0, smoothness: 15.0 }); assert_eq!( fx("artistic.plasticWrap", json!({"highlightStrength": -1, "detail": 99, "smoothness": 0})), @@ -2493,6 +2506,7 @@ fn artistic_params_take_defaults_and_stay_in_range() { assert!(parsed.iter().skip(i + 1).all(|p| *p != parsed[i]), "{id} {v}"); } } + assert!(fx("artistic.roughPastels", json!({})).outset(Rect::new(0.0, 0.0, 40.0, 30.0)) > 0.0); } #[test] @@ -2520,6 +2534,7 @@ fn artistic_filters_leave_invalid_coordinate_spaces_alone() { light: Light::Top, invert: false, }, + PixelFx::RoughPastels { length: 6.0, detail: 10.0, texture: Texture::Canvas, scaling: 1.0, relief: 4.0, light: Light::Top, invert: false }, ]; for f in &filters { assert_ne!(run(f, &space(w, h)), src, "{f:?} changes the art in a valid space"); @@ -2622,6 +2637,20 @@ fn cutout_paints_a_few_flat_colours_and_keeps_coverage() { } } +#[test] +fn rough_pastels_keeps_coverage_and_longer_strokes_smear_more() { + let (w, h) = (48, 48); + let src = ramp(w, h); + let mut short = src.clone(); + let mut long = src.clone(); + fx("artistic.roughPastels", json!({"strokeLength": 8, "strokeDetail": 18, "relief": 12})).apply(&mut short, w, h, &space(w, h)); + fx("artistic.roughPastels", json!({"strokeLength": 32, "strokeDetail": 18, "relief": 12})).apply(&mut long, w, h, &space(w, h)); + assert!(short.as_chunks::<4>().0.iter().zip(src.as_chunks::<4>().0).all(|(o, s)| o[3] == s[3])); + let changed = |a: &Vec, b: &Vec| a.as_chunks::<4>().0.iter().zip(b.as_chunks::<4>().0).filter(|(o, s)| o != s).count(); + assert!(changed(&short, &src) > w * h / 8, "short strokes repaint many pixels"); + assert!(changed(&long, &short) > w * h / 16, "longer strokes smear differently"); +} + #[test] fn cutout_simplicity_smooths_the_pieces_edges() { // A noisy grey: simpler edges join the speckles into fewer changes between neighbours. @@ -2747,6 +2776,7 @@ fn artistic_filters_look_the_same_at_any_resolution() { ("artistic.neonGlow", json!({"glowSize": 12, "glowBrightness": 30})), ("artistic.posterEdges", json!({"edgeThickness": 6, "edgeIntensity": 6})), ("artistic.underpainting", json!({"brushSize": 12, "textureCoverage": 20, "relief": 10})), + ("artistic.roughPastels", json!({"strokeLength": 12, "strokeDetail": 15, "texture": "burlap", "relief": 10})), ] { let (mut a, mut b) = (coarse.clone(), fine.clone()); fx(id, p.clone()).apply(&mut a, w, h, &space(w, h)); diff --git a/crates/engine/src/tests_rasterfilters.rs b/crates/engine/src/tests_rasterfilters.rs index af903bdc5..ca709de50 100644 --- a/crates/engine/src/tests_rasterfilters.rs +++ b/crates/engine/src/tests_rasterfilters.rs @@ -31,7 +31,7 @@ const BRUSH_STROKES: [(&str, &str); 8] = [ ]; /// Effect › Artistic, at their defaults. -const ARTISTIC: [(&str, &str); 12] = [ +const ARTISTIC: [(&str, &str); 13] = [ ("artistic.cutout", "{}"), ("artistic.dryBrush", "{}"), ("artistic.filmGrain", "{}"), @@ -42,6 +42,7 @@ const ARTISTIC: [(&str, &str); 12] = [ ("artistic.plasticWrap", "{}"), ("artistic.posterEdges", "{}"), ("artistic.underpainting", "{}"), + ("artistic.roughPastels", "{}"), ("artistic.smudgeStick", "{}"), ("artistic.sponge", "{}"), ("artistic.watercolor", "{}"), @@ -227,6 +228,19 @@ fn artistic_settings_survive_a_save_and_open() { json!({"highlightStrength": 18.0, "detail": 10.0, "smoothness": 8.0}), [json!({"highlightStrength": 5.0}), json!({"highlightStrength": 0.0})], ), + ( + "artistic.roughPastels", + json!({ + "strokeLength": 8.0, + "strokeDetail": 12.0, + "texture": "sandstone", + "scaling": 100.0, + "relief": 8.0, + "lightDirection": "topLeft", + "invert": false + }), + [json!({"strokeLength": 20.0}), json!({"relief": 0.0})], + ), ] { let (mut s, g) = striped(); // Black and a grey beside the reds: Cutout puts the grey with black at 2 levels and apart diff --git a/crates/engine/tests/command_sweep.rs b/crates/engine/tests/command_sweep.rs index f5789301a..c502fd663 100644 --- a/crates/engine/tests/command_sweep.rs +++ b/crates/engine/tests/command_sweep.rs @@ -373,6 +373,10 @@ fn structured_junk() { ("effect.apply", json!({"effect": "artistic.dryBrush", "params": {"brushSize": -1e308, "brushDetail": "NaN", "texture": 1e308}})), ("effect.apply", json!({"effect": "artistic.neonGlow", "params": {"glowSize": -1e308, "glowBrightness": "1e999", "color": [1]}})), ("effect.apply", json!({"effect": "artistic.posterEdges", "params": {"edgeThickness": 1e308, "edgeIntensity": [], "posterization": -7}})), + ( + "effect.apply", + json!({"effect": "artistic.roughPastels", "params": {"strokeLength": 1e308, "strokeDetail": "NaN", "texture": -1e308, "scaling": "bad", "lightDirection": "nope"}}), + ), ("effect.apply", json!({"effect": "sketch.chrome", "params": {"detail": -1e308, "smoothness": "NaN"}})), ("effect.apply", json!({"effect": "sketch.graphicPen", "params": {"strokeLength": -1e308, "lightDarkBalance": "NaN", "strokeDirection": 3}})), ("effect.apply", json!({"effect": "sketch.halftonePattern", "params": {"size": -1e308, "contrast": "NaN", "patternType": 3}})), diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index 1e10132e6..c7dbcf346 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -4568,7 +4568,9 @@ lighting Light Светлина Paint Daubs Мазки с боя Palette Knife Нож за палитра Poster Edges… Плакатни ръбове… + Rough Pastels… Груби пастели… Poster Edges Плакатни ръбове + Rough Pastels Груби пастели Underpainting… Подбоя… Underpainting Подбоя Levels Нива diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index c1c3e6bf2..69b7b087d 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -5067,7 +5067,9 @@ imageTrace Outline Obrys Paint Daubs Malířské tahy Palette Knife Špachtle Poster Edges… Plakátové okraje… + Rough Pastels… Hrubé pastely… Poster Edges Plakátové okraje + Rough Pastels Hrubé pastely Underpainting… Podmalba… Underpainting Podmalba Levels Úrovně diff --git a/crates/ui-egui/src/i18n/da.tsv b/crates/ui-egui/src/i18n/da.tsv index 5bdf1921e..f4fc9d500 100644 --- a/crates/ui-egui/src/i18n/da.tsv +++ b/crates/ui-egui/src/i18n/da.tsv @@ -5187,6 +5187,7 @@ swatch Bright Violet Kraftig violet Neon Glow… Neonglød… Palette Knife… Paletkniv… Poster Edges… Plakatkanter… + Rough Pastels… Grove pasteller… Cutout Udklip Dry Brush Tør pensel Neon Glow Neonglød @@ -5194,6 +5195,7 @@ swatch Bright Violet Kraftig violet Paint Daubs Malerklatter Palette Knife Paletkniv Poster Edges Plakatkanter + Rough Pastels Grove pasteller Underpainting… Undermaling… Underpainting Undermaling Apply Data Set Anvend datasæt diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 51cbf7860..f2954f330 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -4654,7 +4654,9 @@ lighting Light Licht Paint Daubs Tupfer Palette Knife Palettenmesser Poster Edges… Posterkanten… + Rough Pastels… Pastellkreide… Poster Edges Posterkanten + Rough Pastels Pastellkreide Underpainting… Untermalung… Underpainting Untermalung Levels Stufen diff --git a/crates/ui-egui/src/i18n/el.tsv b/crates/ui-egui/src/i18n/el.tsv index 0545b135b..b530c8469 100644 --- a/crates/ui-egui/src/i18n/el.tsv +++ b/crates/ui-egui/src/i18n/el.tsv @@ -2277,7 +2277,9 @@ lighting Light Φως Paint Daubs Πινελιές βαφής Palette Knife Μαχαίρι παλέτας Poster Edges… Άκρες αφίσας… + Rough Pastels… Χοντρά παστέλ… Poster Edges Άκρες αφίσας + Rough Pastels Χοντρά παστέλ Underpainting… Υποζωγραφία… Underpainting Υποζωγραφία Levels Επίπεδα diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 69614c9d5..877b5826e 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -4655,7 +4655,9 @@ lighting Light Luz Paint Daubs Pinceladas Palette Knife Espátula Poster Edges… Bordes de póster… + Rough Pastels… Pastel rudo… Poster Edges Bordes de póster + Rough Pastels Pastel rudo Underpainting… Subpintura… Underpainting Subpintura Levels Niveles diff --git a/crates/ui-egui/src/i18n/fi.tsv b/crates/ui-egui/src/i18n/fi.tsv index 6a3f34163..365452e4c 100644 --- a/crates/ui-egui/src/i18n/fi.tsv +++ b/crates/ui-egui/src/i18n/fi.tsv @@ -4962,7 +4962,9 @@ imageTrace Outline Ääriviiva Paint Daubs Maalitahrat Palette Knife Palettiveitsi Poster Edges… Julisteen reunat… + Rough Pastels… Karkeat pastellit… Poster Edges Julisteen reunat + Rough Pastels Karkeat pastellit Underpainting… Alapinta… Underpainting Alapinta Levels Tasot diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index b3d49bade..4c4fbe06b 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -4665,7 +4665,9 @@ lighting Light Lumière Paint Daubs Empâtements Palette Knife Couteau à palette Poster Edges… Bords d’affiche… + Rough Pastels… Pastels secs… Poster Edges Bords d’affiche + Rough Pastels Pastels secs Underpainting… Sous-peinture… Underpainting Sous-peinture Levels Niveaux diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index e9e820d0e..63455d978 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -1598,7 +1598,9 @@ file-filter Templates Sablonok Paint Daubs Festékpaca Palette Knife Palettakés Poster Edges… Poszterszélek… + Rough Pastels… Durva pasztell… Poster Edges Poszterszélek + Rough Pastels Durva pasztell Underpainting… Alapozás… Underpainting Alapozás Levels Szintek diff --git a/crates/ui-egui/src/i18n/id.tsv b/crates/ui-egui/src/i18n/id.tsv index 79f41ccf5..b3a3051bc 100644 --- a/crates/ui-egui/src/i18n/id.tsv +++ b/crates/ui-egui/src/i18n/id.tsv @@ -5187,6 +5187,7 @@ swatch Bright Violet Violet cerah Neon Glow… Pendar neon… Palette Knife… Pisau palet… Poster Edges… Tepi poster… + Rough Pastels… Pastel kasar… Cutout Potongan Dry Brush Kuas kering Neon Glow Pendar neon @@ -5194,6 +5195,7 @@ swatch Bright Violet Violet cerah Paint Daubs Coretan cat Palette Knife Pisau palet Poster Edges Tepi poster + Rough Pastels Pastel kasar Underpainting… Underpainting… Underpainting Underpainting Apply Data Set Terapkan set data diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 5de6d61df..f52676205 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -4697,7 +4697,9 @@ lighting Light Luce Paint Daubs Pennellate Palette Knife Spatola Poster Edges… Bordi poster… + Rough Pastels… Pastelli grezzi… Poster Edges Bordi poster + Rough Pastels Pastelli grezzi Underpainting… Sottopittura… Underpainting Sottopittura Levels Livelli diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 6618e1e15..b24e36167 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -4736,7 +4736,9 @@ lighting Light ライト Paint Daubs ペイントダブ Palette Knife パレットナイフ Poster Edges… ポスターエッジ… + Rough Pastels… ラフパステル… Poster Edges ポスターエッジ + Rough Pastels ラフパステル Underpainting… アンダーペインティング… Underpainting アンダーペインティング Levels レベル数 diff --git a/crates/ui-egui/src/i18n/ka.tsv b/crates/ui-egui/src/i18n/ka.tsv index 7fa70cb04..c1e30279f 100644 --- a/crates/ui-egui/src/i18n/ka.tsv +++ b/crates/ui-egui/src/i18n/ka.tsv @@ -5096,9 +5096,11 @@ imageTrace Outline კონტური Persistent მუდმივი Place from Library ბიბლიოთეკიდან განთავსება Poster Edges პოსტერის კიდეები + Rough Pastels მკვრივი პასტელი Underpainting… ქვეფენის მხატვრობა… Underpainting ქვეფენის მხატვრობა Poster Edges… პოსტერის კიდეები… + Rough Pastels… მკვრივი პასტელი… Posterization პოსტერიზაცია Previous Artboard წინა სამუშაო დაფა Rearrange Artboards სამუშაო დაფების გადალაგება diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 199544432..02935f068 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -4660,7 +4660,9 @@ lighting Light 광원 Paint Daubs 페인트 다브 Palette Knife 팔레트 나이프 Poster Edges… 포스터 가장자리… + Rough Pastels… 거친 파스텔… Poster Edges 포스터 가장자리 + Rough Pastels 거친 파스텔 Underpainting… 언더페인팅… Underpainting 언더페인팅 Levels 레벨 수 diff --git a/crates/ui-egui/src/i18n/nl.tsv b/crates/ui-egui/src/i18n/nl.tsv index df85856cf..805597607 100644 --- a/crates/ui-egui/src/i18n/nl.tsv +++ b/crates/ui-egui/src/i18n/nl.tsv @@ -5198,6 +5198,7 @@ swatch Bright Violet Felviolet Neon Glow… Neongloed… Palette Knife… Paletmes… Poster Edges… Posterranden… + Rough Pastels… Ruwe pastels… Cutout Knipsel Dry Brush Droog penseel Neon Glow Neongloed @@ -5205,6 +5206,7 @@ swatch Bright Violet Felviolet Paint Daubs Verfstipjes Palette Knife Paletmes Poster Edges Posterranden + Rough Pastels Ruwe pastels Underpainting… Onderlaag… Underpainting Onderlaag Apply Data Set Gegevensset toepassen diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 8618138db..e14037421 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -4900,7 +4900,9 @@ imageTrace Outline Kontur Paint Daubs Plamy farby Palette Knife Szpachel Poster Edges… Krawędzie plakatu… + Rough Pastels… Suche pastele… Poster Edges Krawędzie plakatu + Rough Pastels Suche pastele Underpainting… Podmalówka… Underpainting Podmalówka Levels Poziomy diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index b89fa7608..aba95ccdf 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -4050,7 +4050,9 @@ axis Both Ambos Paint Daubs Manchas de tinta Palette Knife Espátula Poster Edges… Bordas de pôster… + Rough Pastels… Pastéis ásperos… Poster Edges Bordas de pôster + Rough Pastels Pastéis ásperos Underpainting… Subpintura… Underpainting Subpintura Levels Níveis diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 1d1939fcd..71728573e 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -4693,7 +4693,9 @@ lighting Light Свет Paint Daubs Мазки кисти Palette Knife Нож для палитры Poster Edges… Края плаката… + Rough Pastels… Грубые пастели… Poster Edges Края плаката + Rough Pastels Грубые пастели Underpainting… Подмалёвка… Underpainting Подмалёвка Levels Уровни diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index 4bbef3924..fc43028ae 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -4574,7 +4574,9 @@ lighting Light Ljus Paint Daubs Färgklickar Palette Knife Palettkniv Poster Edges… Affischkanter… + Rough Pastels… Grova pasteller… Poster Edges Affischkanter + Rough Pastels Grova pasteller Underpainting… Undermålning… Underpainting Undermålning Levels Nivåer diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index b950aca2a..9ab75b00c 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -4677,7 +4677,9 @@ lighting Light Світло Paint Daubs Мазки фарби Palette Knife Ніж палітри Poster Edges… Плакатні краї… + Rough Pastels… Грубі пастелі… Poster Edges Плакатні краї + Rough Pastels Грубі пастелі Underpainting… Підмалювання… Underpainting Підмалювання Levels Рівні diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index 54e8d3c65..b508807cf 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -1279,7 +1279,9 @@ Paint Daubs Vệt sơn Palette Knife Dao palette Poster Edges… Viền áp phích… + Rough Pastels… Pastel thô… Poster Edges Viền áp phích + Rough Pastels Pastel thô Underpainting… Lớp sơn nền… Underpainting Lớp sơn nền Levels Số mức diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 851c5b727..a6f520b2b 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -4961,7 +4961,9 @@ lighting Light 光源 Paint Daubs 绘画涂抹 Palette Knife 调色刀 Poster Edges… 海报边缘… + Rough Pastels… 粗糙蜡笔… Poster Edges 海报边缘 + Rough Pastels 粗糙蜡笔 Underpainting… 底纹效果… Underpainting 底纹效果 Levels 色阶数 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index fde67267e..549615b26 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -4767,7 +4767,9 @@ lighting Light 光源 Paint Daubs 繪畫塗抹 Palette Knife 調色刀 Poster Edges… 海報邊緣… + Rough Pastels… 粗糙粉彩… Poster Edges 海報邊緣 + Rough Pastels 粗糙粉彩 Underpainting… 底紋效果… Underpainting 底紋效果 Levels 色階數 diff --git a/crates/ui-egui/src/tests_labels.rs b/crates/ui-egui/src/tests_labels.rs index 67dfe48a3..eb9da8a14 100644 --- a/crates/ui-egui/src/tests_labels.rs +++ b/crates/ui-egui/src/tests_labels.rs @@ -97,6 +97,7 @@ fn effect_menu_lists_the_artistic_filters_before_blur() { "Palette Knife…", "Plastic Wrap…", "Poster Edges…", + "Rough Pastels…", "Smudge Stick…", "Sponge…", "Underpainting…",