G2: Effect › Artistic › Colored Pencil raster filter

Squashed from #3028 onto main.
This commit is contained in:
bflatastic
2026-10-22 00:30:39 +07:00
parent 83a5978d6e
commit 03a2da13ba
31 changed files with 158 additions and 14 deletions
+7
View File
@@ -350,6 +350,13 @@ pub fn effect_catalog() -> Vec<EffectInfo> {
),
r("stylize.feather", "Feather…", STYLIZE, "{radius: pt (5)}", json!({"radius": 5.0})),
r("blur.gaussian", "Gaussian Blur…", BLUR, "{radius: pt (5)}", json!({"radius": 5.0})),
r(
"artistic.coloredPencil",
"Colored Pencil…",
ARTISTIC,
"{pencilWidth: 1..24 (5; stroke spacing in document units), strokePressure: 0..15 (5; how heavily the strokes darken), paperBrightness: 0..50 (25; lifts lighter areas toward the paper)} redraws the object in coloured pencil strokes along its edges, keeping its hues and coverage. Original visual approximation; chosen VectorCraft defaults.",
json!({"pencilWidth": 5.0, "strokePressure": 5.0, "paperBrightness": 25.0}),
),
r(
"artistic.cutout",
"Cutout…",
+51 -1
View File
@@ -1,4 +1,4 @@
//! Effect › Artistic (Cutout, Dry Brush, Film Grain, Neon Glow, Paint Daubs, Palette Knife, Plastic Wrap, Poster
//! Effect › Artistic (Colored Pencil, Cutout, Dry Brush, Film Grain, Neon Glow, Paint Daubs, Palette Knife, Plastic Wrap, Poster
//! Edges, Rough Pastels, Smudge Stick, Sponge, Underpainting and Watercolor). Original filters built from
//! public behaviour documentation:
//! <https://helpx.adobe.com/illustrator/using/summary-effects.html> and
@@ -71,6 +71,7 @@ impl PaintDaubBrush {
pub(super) fn paint_daub_radius(size: f64) -> f64 {
size.clamp(1.0, 50.0) * 0.48
}
const SALT_PENCIL: u64 = 304;
/// Keep the last cell's four corner values: magnified adjacent pixels often share a cell.
/// The cache changes only cost, never the field, and needs constant storage per raster.
@@ -219,6 +220,55 @@ fn planes(px: &[[u8; 4]]) -> ([Vec<f32>; 3], Vec<f32>) {
(std::array::from_fn(|k| colours.iter().map(|c| c[k]).collect()), px.iter().map(|p| f32::from(p[3]) / 255.0).collect())
}
/// How far (document units) Colored Pencil reaches at pencil width `width`: soften at
/// `0.12 * width` and the Sobel neighbourhood around it.
pub(super) fn colored_pencil_reach(width: f64) -> f64 {
3.0 * 0.12 * width.clamp(1.0, 24.0) + 1.0
}
/// Colored Pencil: the object redrawn with coloured pencil strokes along local edges, keeping its
/// hues. `width` (1..24) sets the stroke spacing in document units; `pressure` (0..15) darkens
/// along the strokes; `paper` (0..50) lifts lighter areas toward the paper. Coverage is kept.
pub(super) fn colored_pencil(px: &mut [[u8; 4]], w: usize, h: usize, space: &PixelSpace, width: f64, pressure: f64, paper: f64) {
if !([width, pressure, paper].iter().all(|v| v.is_finite()) && space.px.is_finite() && space.px > 0.0) {
return;
}
let width = width.clamp(1.0, 24.0);
let pressure = (pressure / 15.0).clamp(0.0, 1.0) as f32;
let paper_lift = (paper / 50.0).clamp(0.0, 1.0) as f32;
if pressure == 0.0 && paper_lift == 0.0 {
return;
}
let Some(place) = Place::new(space, w, h) else { return };
let (mut colour, alpha) = planes(px);
soften(&mut colour, &alpha, w, h, (0.12 * width) / space.px);
let tones: Vec<f32> = (0..px.len()).map(|k| luma([colour[0][k], colour[1][k], colour[2][k]])).collect();
let mut edges = vec![0.0f32; px.len()];
for (e, s) in edges.iter_mut().zip(sobel(&tones, w, h)) {
*e = (s / 4.0).min(1.0);
}
let spacing = (0.35 * width).max(1.0);
place.each(px, |x, y, p| {
if p[3] == 0 {
return;
}
let k = y * w + x;
let c = [colour[0][k], colour[1][k], colour[2][k]];
let gx = tone_at(&tones, w, h, x as isize + 1, y as isize) - tone_at(&tones, w, h, x as isize - 1, y as isize);
let gy = tone_at(&tones, w, h, x as isize, y as isize + 1) - tone_at(&tones, w, h, x as isize, y as isize - 1);
let angle = gy.atan2(gx) + std::f32::consts::FRAC_PI_2;
let (dx, dy) = (angle.cos() as f64, angle.sin() as f64);
let q = place.at(x, y);
let along = q.x * dx + q.y * dy;
let grain = value_noise(SALT_PENCIL, along / spacing, (q.x * 0.37 + q.y * 0.11) / spacing);
let stroke = (0.5 + 0.5 * (along / spacing * TAU + grain * 3.0).sin()) as f32;
let ink = pressure * (0.3 + 0.55 * stroke) * (0.55 + 0.45 * edges[k]);
let lift = paper_lift * (1.0 - luma(c)) * 0.35;
let c = c.map(|v| (v * (1.0 - ink * 0.45) + lift * (1.0 - v)).clamp(0.0, 1.0));
*p = premultiply(c, f32::from(p[3]) / 255.0);
});
}
/// Cutout: the object as flat pieces of coloured paper. Its colours, softened as `simplicity`
/// (0..10) asks and less as `fidelity` (1..3) keeps detail, fall into `levels` (2..8) steps per
/// colour channel; each step's piece is painted in the average colour of what falls into it, so
+14 -2
View File
@@ -1,4 +1,4 @@
//! Photoshop-style raster effects (Effect › Artistic › Cutout, Dry Brush, Film Grain, Neon Glow, Paint Daubs, Plastic Wrap and
//! Photoshop-style raster effects (Effect › Artistic › Colored Pencil, Cutout, Dry Brush, Film Grain, Neon Glow, Paint Daubs, Plastic Wrap and
//! 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,
@@ -44,7 +44,8 @@ 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; 52] = [
pub const PIXEL_EFFECTS: [&str; 53] = [
"artistic.coloredPencil",
"artistic.cutout",
"artistic.dryBrush",
"artistic.filmGrain",
@@ -103,6 +104,9 @@ pub const PIXEL_EFFECTS: [&str; 52] = [
/// are in document units.
#[derive(Clone, Debug, PartialEq)]
pub enum PixelFx {
/// Artistic › Colored Pencil: coloured strokes `width` (1..24) apart, as heavy as `pressure`
/// (0..15), on paper lifted by `paper` (0..50).
ColoredPencil { width: f64, pressure: f64, paper: f64 },
/// Artistic › Cutout: flat pieces of colour in `levels` (2..8) steps per channel, their
/// edges as simple as `simplicity` (0..10) and as faithful as `fidelity` (1..3) say.
Cutout { levels: f64, simplicity: f64, fidelity: f64 },
@@ -312,6 +316,11 @@ pub(crate) fn parse(id: &str, p: &Value) -> Option<PixelFx> {
levels.iter().find(|(name, _)| name.eq_ignore_ascii_case(q)).map_or(default, |(_, v)| *v)
};
Some(match id {
"artistic.coloredPencil" => PixelFx::ColoredPencil {
width: num(p, "pencilWidth", 5.0).clamp(1.0, 24.0),
pressure: num(p, "strokePressure", 5.0).clamp(0.0, 15.0),
paper: num(p, "paperBrightness", 25.0).clamp(0.0, 50.0),
},
"artistic.cutout" => PixelFx::Cutout {
levels: num(p, "levels", 4.0).round().clamp(2.0, 8.0),
simplicity: num(p, "edgeSimplicity", 4.0).clamp(0.0, 10.0),
@@ -600,6 +609,7 @@ impl PixelFx {
// These three keep the object's coverage.
PixelFx::SmudgeStick { .. } | PixelFx::Sponge { .. } | PixelFx::Watercolor { .. } => 0.0,
PixelFx::FilmGrain { .. }
| PixelFx::ColoredPencil { .. }
| PixelFx::Cutout { .. }
| PixelFx::DryBrush { .. }
| PixelFx::NeonGlow { .. }
@@ -658,6 +668,7 @@ impl PixelFx {
// The cells around the pixel's, and the cells around theirs.
PixelFx::ColorHalftone { max_radius, .. } => Some(3.0 * std::f64::consts::SQRT_2 * max_radius),
PixelFx::Crystallize { cell } => Some(1.5 * cell),
PixelFx::ColoredPencil { width, .. } => Some(artistic::colored_pencil_reach(width)),
PixelFx::ChalkCharcoal { .. } => Some(sketch::CHALK_CHARCOAL_REACH),
PixelFx::Chrome { smoothness, .. } => Some(sketch::chrome_reach(smoothness)),
PixelFx::FilmGrain { .. }
@@ -699,6 +710,7 @@ impl PixelFx {
let Some(px) = pixels(data, w, h) else { return };
let to_px = |len: f64| len / space.px.max(1e-9);
match *self {
PixelFx::ColoredPencil { width, pressure, paper } => artistic::colored_pencil(px, w, h, space, width, pressure, paper),
PixelFx::Cutout { levels, simplicity, fidelity } => artistic::cutout(px, w, h, space, levels, simplicity, fidelity),
PixelFx::DryBrush { size, detail, texture } => artistic::dry_brush(px, w, h, space, size, detail, texture),
PixelFx::FilmGrain { grain, highlight, intensity } => artistic::film_grain(px, w, h, space, grain, highlight, intensity),
+28 -2
View File
@@ -2421,6 +2421,12 @@ fn ramp(w: usize, h: usize) -> Vec<u8> {
#[test]
fn artistic_params_take_defaults_and_stay_in_range() {
assert_eq!(fx("artistic.coloredPencil", json!({})), PixelFx::ColoredPencil { width: 5.0, pressure: 5.0, paper: 25.0 });
assert_eq!(
fx("artistic.coloredPencil", json!({"pencilWidth": 0, "strokePressure": 200, "paperBrightness": -5})),
PixelFx::ColoredPencil { width: 1.0, pressure: 15.0, paper: 0.0 }
);
assert_eq!(fx("artistic.coloredPencil", json!({})).reach(), Some(artistic::colored_pencil_reach(5.0)));
assert_eq!(fx("artistic.cutout", json!({})), PixelFx::Cutout { levels: 4.0, simplicity: 4.0, fidelity: 2.0 });
assert_eq!(
fx("artistic.cutout", json!({"levels": 1e308, "edgeSimplicity": -5, "edgeFidelity": "NaN"})),
@@ -2484,8 +2490,15 @@ fn artistic_params_take_defaults_and_stay_in_range() {
fx("artistic.plasticWrap", json!({"highlightStrength": -1, "detail": 99, "smoothness": 0})),
PixelFx::PlasticWrap { highlight: 0.0, detail: 15.0, smoothness: 1.0 }
);
for id in ["artistic.cutout", "artistic.dryBrush", "artistic.neonGlow", "artistic.plasticWrap", "artistic.posterEdges", "artistic.underpainting"]
{
for id in [
"artistic.coloredPencil",
"artistic.cutout",
"artistic.dryBrush",
"artistic.neonGlow",
"artistic.plasticWrap",
"artistic.posterEdges",
"artistic.underpainting",
] {
assert_eq!(fx(id, json!({})).outset(Rect::new(0.0, 0.0, 40.0, 30.0)), 0.0, "{id} keeps the object's shape");
}
assert_eq!(fx("artistic.paintDaubs", json!({})), PixelFx::PaintDaubs { size: 10.0, sharpness: 25.0, kind: PaintDaubBrush::Simple });
@@ -2509,6 +2522,19 @@ fn artistic_params_take_defaults_and_stay_in_range() {
assert!(fx("artistic.roughPastels", json!({})).outset(Rect::new(0.0, 0.0, 40.0, 30.0)) > 0.0);
}
#[test]
fn colored_pencil_strokes_change_pixels_and_keep_coverage() {
let (w, h) = (48, 32);
let src = ramp(w, h);
let mut out = src.clone();
fx("artistic.coloredPencil", json!({"strokePressure": 12})).apply(&mut out, w, h, &space(w, h));
assert_ne!(out, src);
assert!(out.as_chunks::<4>().0.iter().zip(src.as_chunks::<4>().0).all(|(o, s)| o[3] == s[3]));
let mut mild = src.clone();
fx("artistic.coloredPencil", json!({"strokePressure": 0, "paperBrightness": 0})).apply(&mut mild, w, h, &space(w, h));
assert_eq!(mild, src, "zero pressure and paper leaves the art unchanged");
}
#[test]
fn artistic_filters_leave_invalid_coordinate_spaces_alone() {
let (w, h) = (16, 12);
+2 -1
View File
@@ -31,7 +31,8 @@ const BRUSH_STROKES: [(&str, &str); 8] = [
];
/// Effect › Artistic, at their defaults.
const ARTISTIC: [(&str, &str); 13] = [
const ARTISTIC: [(&str, &str); 14] = [
("artistic.coloredPencil", "{}"),
("artistic.cutout", "{}"),
("artistic.dryBrush", "{}"),
("artistic.filmGrain", "{}"),
+2
View File
@@ -4557,6 +4557,8 @@ lighting Light Светлина
Reset Ruler Origin Нулиране началото на линийката
Artistic Художествени
Film Grain… Филмова зърнистост…
Colored Pencil… Цветен молив…
Colored Pencil Цветен молив
Cutout… Апликация…
Cutout Апликация
Dry Brush… Суха четка…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5056,6 +5056,8 @@ imageTrace Outline Obrys
DWG (use DXF) DWG (použijte DXF)
Artistic Umělecké
Film Grain… Filmové zrno…
Colored Pencil… Barevná tužka…
Colored Pencil Barevná tužka
Cutout… Výřez…
Cutout Výřez
Dry Brush… Suchý štětec…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5182,6 +5182,8 @@ swatch Bright Violet Kraftig violet
Lock Variables Lås variabler
Unlock Variables Lås variabler op
Change to Artboard Rulers Skift til kunstpladelinealer
Colored Pencil… Farveblyant…
Colored Pencil Farveblyant
Cutout… Udklip…
Dry Brush… Tør pensel…
Neon Glow… Neonglød…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4637,6 +4637,8 @@ lighting Light Licht
Reset Ruler Origin Linealursprung zurücksetzen
Artistic Kunstfilter
Film Grain… Filmkörnung…
Colored Pencil… Buntstift…
Colored Pencil Buntstift
Simple Einfach
Light Rough Leicht rau
Dark Rough Dunkel rau
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -2266,6 +2266,8 @@ lighting Light Φως
Left Diagonal Αριστερή διαγώνιος
Artistic Καλλιτεχνικό
Film Grain… Κόκκος φιλμ…
Colored Pencil… Έγχρωμο μολύβι…
Colored Pencil Έγχρωμο μολύβι
Cutout… Αποκοπή…
Cutout Αποκοπή
Dry Brush… Ξηρό πινέλο…
Can't render this file because it contains an unexpected character in line 2038 and column 19.
+2
View File
@@ -4644,6 +4644,8 @@ lighting Light Luz
Reset Ruler Origin Restablecer el origen de las reglas
Artistic Artístico
Film Grain… Granulado de película…
Colored Pencil… Lápiz de color…
Colored Pencil Lápiz de color
Cutout… Recorte…
Cutout Recorte
Dry Brush… Pincel seco…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4951,6 +4951,8 @@ imageTrace Outline Ääriviiva
DWG (use DXF) DWG (käytä DXF:ää)
Artistic Taiteelliset
Film Grain… Filmin rakeisuus…
Colored Pencil… Väriliitu…
Colored Pencil Väriliitu
Cutout… Leikkaus…
Cutout Leikkaus
Dry Brush… Kuiva sivellin…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4654,6 +4654,8 @@ lighting Light Lumière
Reset Ruler Origin Réinitialiser l’origine des règles
Artistic Artistique
Film Grain… Grain photographique…
Colored Pencil… Crayon de couleur…
Colored Pencil Crayon de couleur
Cutout… Découpage…
Cutout Découpage
Dry Brush… Pinceau sec…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -1587,6 +1587,8 @@ file-filter Templates Sablonok
Select Segments Szakaszok kijelölése
Artistic Művészi
Film Grain… Filmszemcse…
Colored Pencil… Színes ceruza…
Colored Pencil Színes ceruza
Cutout… Kivágás…
Cutout Kivágás
Dry Brush… Száraz ecset…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5182,6 +5182,8 @@ swatch Bright Violet Violet cerah
Lock Variables Kunci variabel
Unlock Variables Buka kunci variabel
Change to Artboard Rulers Ubah ke penggaris artboard
Colored Pencil… Pensil warna…
Colored Pencil Pensil warna
Cutout… Potongan…
Dry Brush… Kuas kering…
Neon Glow… Pendar neon…
Can't render this file because it contains an unexpected character in line 3808 and column 19.
+2
View File
@@ -4686,6 +4686,8 @@ lighting Light Luce
Reset Ruler Origin Ripristina origine dei righelli
Artistic Artistico
Film Grain… Grana della pellicola…
Colored Pencil… Matita colorata…
Colored Pencil Matita colorata
Cutout… Ritaglio…
Cutout Ritaglio
Dry Brush… Pennello secco…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4725,6 +4725,8 @@ lighting Light ライト
Reset Ruler Origin 定規の原点をリセット
Artistic アーティスティック
Film Grain… フィルム粒子…
Colored Pencil… 色鉛筆…
Colored Pencil 色鉛筆
Cutout… カットアウト…
Cutout カットアウト
Dry Brush… ドライブラシ…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5018,6 +5018,8 @@ imageTrace Outline კონტური
Create Slice from Selection ნაჭრის შექმნა მონიშნულიდან
Create Slices from Guides ნაჭრების შექმნა დამხმარე ხაზებიდან
Cutout აპლიკაცია
Colored Pencil… ფერადი ფანქარი…
Colored Pencil ფერადი ფანქარი
Dry Brush მშრალი ფერწანი
Cutout… აპლიკაცია…
Dry Brush… მშრალი ფერწანი…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4649,6 +4649,8 @@ lighting Light 광원
X11 (XWayland) X11 (XWayland)
Artistic 예술 효과
Film Grain… 필름 그레인…
Colored Pencil… 색연필…
Colored Pencil 색연필
Cutout… 오려내기…
Cutout 오려내기
Dry Brush… 드라이 브러시…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5193,6 +5193,8 @@ swatch Bright Violet Felviolet
Lock Variables Variabelen vergrendelen
Unlock Variables Variabelen ontgrendelen
Change to Artboard Rulers Wijzigen in tekenbordlinialen
Colored Pencil… Gekleurd potlood…
Colored Pencil Gekleurd potlood
Cutout… Knipsel…
Dry Brush… Droog penseel…
Neon Glow… Neongloed…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4889,6 +4889,8 @@ imageTrace Outline Kontur
DWG (use DXF) DWG (użyj DXF)
Artistic Artystyczne
Film Grain… Ziarno filmu…
Colored Pencil… Kolorowa kredka…
Colored Pencil Kolorowa kredka
Cutout… Wycinanka…
Cutout Wycinanka
Dry Brush… Suchy pędzel…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4039,6 +4039,8 @@ axis Both Ambos
Reset Ruler Origin Redefinir origem das réguas
Artistic Artístico
Film Grain… Granulação de filme…
Colored Pencil… Lápis de cor…
Colored Pencil Lápis de cor
Cutout… Recorte…
Cutout Recorte
Dry Brush… Pincel seco…
Can't render this file because it contains an unexpected character in line 6 and column 47.
+2
View File
@@ -4682,6 +4682,8 @@ lighting Light Свет
Reset Ruler Origin Сбросить начало координат линеек
Artistic Художественные
Film Grain… Зернистость плёнки…
Colored Pencil… Цветной карандаш…
Colored Pencil Цветной карандаш
Cutout… Аппликация…
Cutout Аппликация
Dry Brush… Сухая кисть…
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4563,6 +4563,8 @@ lighting Light Ljus
Reset Ruler Origin Återställ linjalens origo
Artistic Konstnärligt
Film Grain… Filmkorn…
Colored Pencil… Färgpenna…
Colored Pencil Färgpenna
Cutout… Urklipp…
Cutout Urklipp
Dry Brush… Torr pensel…
Can't render this file because it contains an unexpected character in line 5 and column 25.
+2
View File
@@ -4666,6 +4666,8 @@ lighting Light Світло
Reset Ruler Origin Скинути початок координат лінійок
Artistic Художні
Film Grain… Зернистість плівки…
Colored Pencil… Кольоровий олівець…
Colored Pencil Кольоровий олівець
Cutout… Аплікація…
Cutout Аплікація
Dry Brush… Суха кисть…
Can't render this file because it contains an unexpected character in line 733 and column 40.
+2
View File
@@ -1268,6 +1268,8 @@
Fill: click for swatches, Shift-click for the color mixer Tô: bấm để xem mẫu màu, Shift-bấm để trộn màu
Fills Vùng tô
Film Grain… Hạt phim…
Colored Pencil… Bút chì màu…
Colored Pencil Bút chì màu
Cutout… Cắt dán…
Cutout Cắt dán
Dry Brush… Cọ khô…
1 # Vietnamese (vi) catalog. Same format as zh-hant.tsv: context <TAB> source <TAB> translation
1268
1269
1270
1271
1272
1273
1274
1275
+2
View File
@@ -4950,6 +4950,8 @@ lighting Light 光源
Reset Ruler Origin 重置标尺原点
Artistic 艺术效果
Film Grain… 胶片颗粒…
Colored Pencil… 彩色铅笔…
Colored Pencil 彩色铅笔
Cutout… 剪纸…
Dry Brush… 干画笔…
Cutout 剪纸
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4756,6 +4756,8 @@ lighting Light 光源
Reset Ruler Origin 重設尺標原點
Artistic 藝術效果
Film Grain… 底片顆粒…
Colored Pencil… 彩色鉛筆…
Colored Pencil 彩色鉛筆
Cutout… 剪紙…
Dry Brush… 乾畫筆…
Cutout 剪紙
Can't render this file because it contains an unexpected character in line 6 and column 47.
+3 -2
View File
@@ -89,6 +89,7 @@ fn effect_menu_lists_the_artistic_filters_before_blur() {
assert_eq!(
labels,
[
"Colored Pencil…",
"Cutout…",
"Dry Brush…",
"Film Grain…",
@@ -105,8 +106,8 @@ fn effect_menu_lists_the_artistic_filters_before_blur() {
]
);
assert!(artistic.iter().all(|e| e.command.as_deref() == Some("effect.dialog")));
assert_eq!(artistic[1].params, json!({"effect": "artistic.dryBrush"}));
assert_eq!(artistic[3].params, json!({"effect": "artistic.neonGlow"}));
assert_eq!(artistic[2].params, json!({"effect": "artistic.dryBrush"}));
assert_eq!(artistic[4].params, json!({"effect": "artistic.neonGlow"}));
let at = |name: &str| entries.iter().position(|e| e.path == ["Effect", name]).unwrap();
assert!(at("Artistic") < at("Blur"));
}
+3 -1
View File
@@ -12,6 +12,7 @@ Part of [target-app-parity.md](target-app-parity.md); open work is in [gaps.md](
| Group | Illustrator | VectorCraft | % | Remaining (h) |
|---|---:|---:|---:|---:|
| Illustrator effects (vector and stylize) | 50 | 46 | 92% | 6–10 |
| Photoshop effects (raster, Effect Gallery) | 57 | 49 | 86% | 7–12 |
| Photoshop effects (raster, Effect Gallery) | 57 | 48 | 84% | 8–14 |
| 3D and Materials | 4 (+3 Classic) | 4 (initial: Extrude & Bevel, Revolve, Inflate, Rotate) | ~25% | 25–45 |
| Beyond Illustrator: Effect › Color Adjustments (6), effect plug-ins (WebAssembly) | — | 6 + plug-ins | — | — |
@@ -38,6 +39,7 @@ its resolution, PDF export warns once per object with the requested and the lowe
| Submenu | Items | Ours | Missing |
|---|---|---:|---|
| Artistic | Colored Pencil, Cutout, Dry Brush, Film Grain, Fresco, Neon Glow, Paint Daubs, Palette Knife, Plastic Wrap, Poster Edges, Rough Pastels, Smudge Stick, Sponge, Underpainting, Watercolor | 10/15 (approximate Colored Pencil, Cutout, Dry Brush, Film Grain, Neon Glow, Plastic Wrap, Poster Edges, Smudge Stick, Sponge, Watercolor) | Fresco, Paint Daubs, Palette Knife, Rough Pastels, Underpainting |
| Artistic | Colored Pencil, Cutout, Dry Brush, Film Grain, Fresco, Neon Glow, Paint Daubs, Palette Knife, Plastic Wrap, Poster Edges, Rough Pastels, Smudge Stick, Sponge, Underpainting, Watercolor | 9/15 (approximate Cutout, Film Grain, Neon Glow, Paint Daubs, Plastic Wrap, Poster Edges, Smudge Stick, Sponge, Watercolor) | Colored Pencil, Dry Brush, Fresco, Palette Knife, Rough Pastels, Underpainting |
| Blur | Gaussian Blur, Radial Blur, Smart Blur | 3/3 | — |
| Brush Strokes | Accented Edges, Angled Strokes, Crosshatch, Dark Strokes, Ink Outlines, Spatter, Sprayed Strokes, Sumi-e | 8/8 | — |
@@ -52,7 +54,7 @@ its resolution, PDF export warns once per object with the requested and the lowe
The pixel-filter pipeline (`vectorcraft_effects::pixel`) is in place: each new filter is one function, a
catalogue row, a menu entry and translations. Measured throughput: 0.7–1.2 agent hours per filter
(see [calibration](target-app-parity.md#calibration)), so the 12 missing filters are ~8–14 h and the Effect
(see [calibration](target-app-parity.md#calibration)), so the 10 missing filters are ~7–12 h and the Effect
Gallery dialog another 4–6 h. These parallelize well across agents.
Film Grain (`artistic.filmGrain`, Effect › Artistic) adds stable monochrome noise to shadows and
+4 -5
View File
@@ -234,12 +234,11 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
## G2 Photoshop-style raster effects and the Effect Gallery
- **Done:** Effect › Texture › Texturizer › Load Texture… (stored grayscale `textureImage`, Loaded Texture in the Texture menu), matching Glass.
- **Missing:** 24 of 57 filters (the remaining 11 Artistic filters and 13 of Sketch's 14), the Effect Gallery dialog. Measured in
- **Missing:** 10 of 57 filters (7 Artistic, 2 Sketch and 1 more), the Effect Gallery dialog, Load
Texture for Texturizer. Measured in
- **Missing:** 8 of 57 filters (5 Artistic, 2 Sketch and 1 more), the Effect Gallery dialog. Measured in
[effects-parity.md](effects-parity.md).
- **Done:** the pixel-filter pipeline and 45 filters (Artistic: Cutout, Film Grain, Neon Glow, Plastic Wrap, Poster Edges, Smudge Stick, Sponge, Watercolor; Blur, Brush Strokes, Distort, Pixelate, Sharpen; Sketch:
Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Graphic Pen, Halftone Pattern, Photocopy, Plaster, Reticulation, Stamp; Stylize, Texture, Video), with live preview and editable native settings. Smart Blur supports Edge Only and Overlay Edge
- **Done:** Effect › Artistic › Colored Pencil (`artistic.coloredPencil`, pencil width, stroke pressure, paper brightness; original visual approximation).
- **Done:** the pixel-filter pipeline and 49 filters (Artistic: Colored Pencil, Cutout, Dry Brush, Film Grain, Neon Glow, Plastic Wrap, Poster Edges, Smudge Stick, Sponge, Watercolor; Blur, Brush Strokes, Distort, Pixelate, Sharpen; Sketch:
Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, Reticulation, Stamp, Water Paper; Stylize, Texture, Video), with live preview and editable native settings. Smart Blur supports Edge Only and Overlay Edge
modes.
- **Estimate:** 8–14 h (measured 0.7–1.2 h per filter); parallelizes well.