Merge pull request #3010 from nrq6993-a11y/stream/vc-feat1-g2-sketch-torn-edges

G2: Effect › Sketch › Torn Edges raster filter
This commit is contained in:
BFlat
2026-10-22 01:25:06 +07:00
committed by GitHub
32 changed files with 197 additions and 10 deletions
+1 -1
View File
@@ -253,7 +253,7 @@ The scores are self-assessed from our code against public documentation;
- reliable exchange of .ai files (layer structure in real files; proprietary editing data isn't written);
- an interaction-fidelity pass covering every tool's modifiers and small behaviours;
- 3D and Materials beyond the initial live Revolve (Extrude & Bevel, Inflate, Rotate, materials);
- most of the raster effects (10 of 57 still missing; Cutout, Film Grain, Neon Glow, Plastic Wrap, Poster Edges, Smudge Stick, Sponge and Watercolor are available under Artistic, Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, Reticulation, Stamp and Water Paper under Sketch) and the Effect Gallery;
- most of the raster effects (9 of 57 still missing; Cutout, Film Grain, Neon Glow, Plastic Wrap, Poster Edges, Smudge Stick, Sponge and Watercolor are available under Artistic, Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, Reticulation, Stamp, Torn Edges and Water Paper under Sketch) and the Effect Gallery;
- CJK composition for vertical type (vertical type itself has initial support, with a Japanese interface);
- scripting, and the rest of Variables (image and graph kinds, dataset import);
- generative AI features.
+8
View File
@@ -162,6 +162,7 @@ fn lengths_of(id: &str) -> Lengths {
"sketch.graphicPen" => always(&["strokeLength"]),
"sketch.halftonePattern" => always(&["size"]),
"sketch.notePaper" => always(&["relief"]),
"sketch.tornEdges" => always(&["smoothness"]),
"sketch.waterPaper" => always(&["fiberLength"]),
RASTERIZE => always(&["addAround"]),
_ => Lengths::default(),
@@ -639,6 +640,13 @@ pub fn effect_catalog() -> Vec<EffectInfo> {
"{lightDarkBalance: 0..50 (25; the larger, the more of the object prints black), smoothness: 1..50 (5; rounds the shapes)} simplifies the object to solid black and white, as a rubber stamp would print it. Coverage is kept. Original visual approximation; chosen VectorCraft defaults.",
json!({"lightDarkBalance": 25.0, "smoothness": 5.0}),
),
r(
"sketch.tornEdges",
"Torn Edges…",
SKETCH,
"{imageBalance: 0..50 (25; lower leaves more midtones as ink, higher as paper), smoothness: 0..15 (11; softens tones and the ragged edges), contrast: 0..20 (17; 20 is pure black and white)} redraws the object in high-contrast black and white with torn, ragged edges along its tones, keeping its coverage. Original visual approximation; chosen VectorCraft defaults.",
json!({"imageBalance": 25.0, "smoothness": 11.0, "contrast": 17.0}),
),
r(
"sketch.waterPaper",
"Water Paper…",
+17 -5
View File
@@ -3,7 +3,7 @@
//! 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,
//! Reticulation and Stamp, Stylize ›
//! Reticulation, Stamp and Torn Edges, Stylize ›
//! Glowing Edges, Texture
//! › Craquelure, Grain, Mosaic Tiles, Patchwork, Stained Glass and Texturizer, and Video ›
//! De-Interlace and NTSC Colors): filters over premultiplied RGBA8 pixels.
@@ -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; 53] = [
pub const PIXEL_EFFECTS: [&str; 54] = [
"artistic.coloredPencil",
"artistic.cutout",
"artistic.dryBrush",
@@ -88,6 +88,7 @@ pub const PIXEL_EFFECTS: [&str; 53] = [
"sketch.plaster",
"sketch.reticulation",
"sketch.stamp",
"sketch.tornEdges",
"sketch.waterPaper",
"stylize.glowingEdges",
"texture.craquelure",
@@ -232,6 +233,10 @@ pub enum PixelFx {
/// Sketch › Reticulation: black ink on white paper through a high-contrast film-grain screen;
/// `density` (0..50), `foreground` and `background` (0..50) pull tones before the grain.
Reticulation { density: f64, foreground: f64, background: f64 },
/// Sketch › Torn Edges: high-contrast black and white with ragged edges; `image_balance`
/// (0..50) shifts ink versus paper, `smoothness` (0..15) softens tones and fraying, `contrast`
/// (0..20) hardens the split.
TornEdges { image_balance: f64, smoothness: f64, contrast: f64 },
/// Sketch › Water Paper: colours flow on damp fibrous paper; `fiber_length` (3..50) sets the
/// fibers and daubs, `brightness` and `contrast` (0..100) adjust the wash. Ranges and defaults
/// are unverified VectorCraft choices (helpx documents the controls only).
@@ -510,6 +515,11 @@ pub(crate) fn parse(id: &str, p: &Value) -> Option<PixelFx> {
foreground: num(p, "foregroundLevel", 6.0).clamp(0.0, 50.0),
background: num(p, "backgroundLevel", 6.0).clamp(0.0, 50.0),
},
"sketch.tornEdges" => PixelFx::TornEdges {
image_balance: num(p, "imageBalance", 25.0).clamp(0.0, 50.0),
smoothness: num(p, "smoothness", 11.0).clamp(0.0, 15.0),
contrast: num(p, "contrast", 17.0).clamp(0.0, 20.0),
},
"sketch.waterPaper" => PixelFx::WaterPaper {
fiber_length: num(p, "fiberLength", 15.0).clamp(3.0, 50.0),
brightness: num(p, "brightness", 50.0).clamp(0.0, 100.0),
@@ -601,7 +611,8 @@ impl PixelFx {
| PixelFx::HalftonePattern { .. }
| PixelFx::NotePaper { .. }
| PixelFx::Plaster { .. }
| PixelFx::Reticulation { .. } => 0.0,
| PixelFx::Reticulation { .. }
| PixelFx::TornEdges { .. } => 0.0,
PixelFx::BasRelief { .. } | PixelFx::Charcoal { .. } | PixelFx::Photocopy { .. } | PixelFx::Stamp { .. } => 0.0,
PixelFx::WaterPaper { fiber_length, .. } => sketch::water_paper_shift(fiber_length),
PixelFx::GlowingEdges { width, smoothness, .. } => width.max(smoothness * 3.0),
@@ -682,8 +693,8 @@ impl PixelFx {
PixelFx::WaterPaper { fiber_length, .. } => {
Some(sketch::water_paper_shift(fiber_length) + 3.0 * (0.06 * fiber_length.clamp(3.0, 50.0)).max(0.35))
}
// Cutout's palette comes from the whole object.
PixelFx::Cutout { .. } => None,
// Cutout's palette and Torn Edges' peak-normalised edges come from the whole object.
PixelFx::Cutout { .. } | PixelFx::TornEdges { .. } => None,
// The softened tones around the pixel.
PixelFx::NeonGlow { size, .. } => Some(3.0 * artistic::glow_spread(size)),
PixelFx::PlasticWrap { smoothness, .. } => Some(artistic::plastic_reach(smoothness)),
@@ -764,6 +775,7 @@ impl PixelFx {
PixelFx::Stamp { balance, smoothness } => sketch_ink::stamp(px, w, h, space, balance, smoothness),
PixelFx::Plaster { image_balance, smoothness, light } => sketch::plaster(px, w, h, space, image_balance, smoothness, light),
PixelFx::Reticulation { density, foreground, background } => sketch::reticulation(px, w, h, space, density, foreground, background),
PixelFx::TornEdges { image_balance, smoothness, contrast } => sketch::torn_edges(px, w, h, space, image_balance, smoothness, contrast),
PixelFx::WaterPaper { fiber_length, brightness, contrast } => sketch::water_paper(px, w, h, space, fiber_length, brightness, contrast),
PixelFx::GlowingEdges { width, brightness, smoothness } => edges::glow(px, w, h, to_px(width), brightness, to_px(smoothness)),
PixelFx::ColorHalftone { max_radius, angles } => pixelate::color_halftone(px, w, h, space, max_radius, angles),
+51 -1
View File
@@ -1,5 +1,5 @@
//! Effect › Sketch (Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Halftone Pattern, Note Paper, Photocopy, Plaster,
//! Reticulation, Stamp and Water Paper; Graphic Pen lives in
//! Reticulation, Stamp, Torn Edges and Water Paper; Graphic Pen lives in
//! [`super::brushstrokes`]). Original filters built from public behaviour documentation:
//! <https://helpx.adobe.com/illustrator/using/summary-effects.html> (Sketch effects redraw in
//! black and white; Water Paper flows colours on damp fibrous paper) and the public Photoshop Elements Artistic filters guide. Visual
@@ -17,6 +17,7 @@ use super::{PixelSpace, blur_plane, pick};
const SALT_CHALK: u64 = 216;
const SALT_CHROME: u64 = 0xC40_4D45;
const SALT_TORN: u64 = 0x746f726e5f6564;
const SALT_WATER: u64 = 208;
const EDGE_SOFT: f64 = 0.8;
const TONE_SOFT: f64 = 1.0;
@@ -361,6 +362,55 @@ pub(super) fn reticulation(px: &mut [[u8; 4]], w: usize, h: usize, space: &Pixel
});
}
/// Torn Edges: the object in high-contrast black and white with ragged edges. `image_balance`
/// (0..50) shifts whether midtones fall to ink or paper; `smoothness` (0..15) softens tones and
/// the fraying; `contrast` (0..20) hardens the split. Coverage is kept.
pub(super) fn torn_edges(px: &mut [[u8; 4]], w: usize, h: usize, space: &PixelSpace, image_balance: f64, smoothness: f64, contrast: f64) {
if !(image_balance.is_finite() && smoothness.is_finite() && contrast.is_finite()) {
return;
}
let Some(place) = Place::new(space, w, h) else { return };
if w == 0 || h == 0 || w.checked_mul(h) != Some(px.len()) {
return;
}
let mut tone: Vec<f32> = px.iter().map(|p| luma(straight(*p)) * f32::from(p[3]) / 255.0).collect();
let cover: Vec<f32> = px.iter().map(|p| f32::from(p[3]) / 255.0).collect();
let sigma = (0.12 * smoothness.clamp(0.0, 15.0)) / place.px.max(1e-9);
if sigma > 0.0 {
blur_plane(&mut tone, w, h, sigma);
}
let mut edges: Vec<f32> = sobel(&tone, w, h).into_iter().zip(sobel(&cover, w, h)).map(|(t, c)| t.max(c)).collect();
let edge_soft = (0.08 * smoothness.clamp(0.0, 15.0)) / place.px.max(1e-9);
if edge_soft > 0.0 {
blur_plane(&mut edges, w, h, edge_soft);
}
let peak = edges.iter().copied().fold(0.0f32, f32::max);
let balance = image_balance.clamp(0.0, 50.0);
// Lower balance favours ink (darker result); higher balance favours paper.
let balance_tone = 0.25 + balance / 50.0 * 0.75;
let steep = 4.0 + contrast.clamp(0.0, 20.0) * 0.45;
let hard = contrast.clamp(0.0, 20.0) >= 18.0;
let fray = 0.25 + 0.05 * smoothness.clamp(0.0, 15.0);
place.each(px, |x, y, p| {
let a = f32::from(p[3]) / 255.0;
if a <= 0.0 {
*p = [0; 4];
return;
}
let i = y * w + x;
let t = tone.get(i).copied().unwrap_or(0.0);
let e = if peak > 1e-6 { edges.get(i).copied().unwrap_or(0.0) / peak } else { 0.0 };
let noise = smooth_noise(SALT_TORN, place.at(x, y), 2.5 + 0.3 * smoothness) as f32;
let local = (1.0 - balance_tone) as f32 + e * fray as f32 * noise;
let mut ink = 1.0 / (1.0 + (steep as f32 * (t - local)).exp());
if hard {
ink = if ink > 0.5 { 1.0 } else { 0.0 };
}
let paper = 1.0 - ink.clamp(0.0, 1.0);
*p = premultiply([paper, paper, paper], a);
});
}
/// How far Water Paper's blotchy displacement can shift content (document units).
pub(super) fn water_paper_shift(fiber_length: f64) -> f64 {
0.12 * fiber_length.clamp(3.0, 50.0)
+64
View File
@@ -2071,6 +2071,13 @@ fn halftone(p: serde_json::Value, src: &[u8], w: usize, h: usize, s: &PixelSpace
out
}
/// Torn Edges of `p` over `w` × `h` pixels of `src`, laid out by `s`.
fn torn(p: serde_json::Value, src: &[u8], w: usize, h: usize, s: &PixelSpace) -> Vec<u8> {
let mut out = src.to_vec();
fx("sketch.tornEdges", p).apply(&mut out, w, h, s);
out
}
/// Water Paper of `p` over `w` × `h` pixels of `src`, laid out by `s`.
fn water_paper(p: serde_json::Value, src: &[u8], w: usize, h: usize, s: &PixelSpace) -> Vec<u8> {
let mut out = src.to_vec();
@@ -2404,6 +2411,63 @@ fn reticulation_density_changes_the_grain() {
assert_ne!(low, high);
}
#[test]
fn torn_edges_params_take_defaults_and_stay_in_range() {
assert_eq!(fx("sketch.tornEdges", json!({})), PixelFx::TornEdges { image_balance: 25.0, smoothness: 11.0, contrast: 17.0 });
assert_eq!(
fx("sketch.tornEdges", json!({"imageBalance": 0, "smoothness": 99, "contrast": -1})),
PixelFx::TornEdges { image_balance: 0.0, smoothness: 15.0, contrast: 0.0 }
);
assert_eq!(
fx("sketch.tornEdges", json!({"imageBalance": 1e9, "smoothness": -3, "contrast": 1e9})),
PixelFx::TornEdges { image_balance: 50.0, smoothness: 0.0, contrast: 20.0 }
);
for junk in [json!({"imageBalance": "x"}), json!({"smoothness": null})] {
assert_eq!(fx("sketch.tornEdges", junk), fx("sketch.tornEdges", json!({})));
}
assert_eq!(fx("sketch.tornEdges", json!({})).reach(), None);
}
#[test]
fn torn_edges_draws_in_black_and_white_and_keeps_coverage() {
let (w, h) = (64, 48);
let src = ramp(w, h);
let out = torn(json!({"imageBalance": 12, "contrast": 20, "smoothness": 5}), &src, w, h, &space(w, h));
for (p, q) in out.as_chunks::<4>().0.iter().zip(src.as_chunks::<4>().0) {
assert_eq!(p[3], q[3], "coverage kept");
assert!(p[0] == p[1] && p[1] == p[2] && (p[0] == 0 || p[0] == p[3]), "black or white: {p:?}");
}
assert!(share(&out, |p| p[0] == 0) > 0.05 && share(&out, |p| p[0] == p[3]) > 0.05, "ink and paper");
}
#[test]
fn torn_edges_image_balance_shifts_ink_on_a_grey_ramp() {
let (w, h) = (80, 16);
let src = image(w, h, |x, _| grey((x * 255 / (w - 1)) as u8));
let params = json!({"imageBalance": 25, "contrast": 20, "smoothness": 5});
let out = torn(params, &src, w, h, &space(w, h));
let mut dark_ink = 0usize;
let mut bright_ink = 0usize;
for y in 0..h {
for x in 0..w / 4 {
if at(&out, w, x, y)[0] == 0 {
dark_ink += 1;
}
}
for x in 3 * w / 4..w {
if at(&out, w, x, y)[0] == 0 {
bright_ink += 1;
}
}
}
let dark_n = (w / 4) * h;
let bright_n = (w - 3 * w / 4) * h;
assert!(dark_ink as f64 / dark_n as f64 > bright_ink as f64 / bright_n as f64 + 0.2, "dark end of ramp is inkier than bright end");
let dark = share(&torn(json!({"imageBalance": 0, "contrast": 20, "smoothness": 5}), &src, w, h, &space(w, h)), |p| p[0] == 0);
let light = share(&torn(json!({"imageBalance": 50, "contrast": 20, "smoothness": 5}), &src, w, h, &space(w, h)), |p| p[0] == 0);
assert!(dark > light + 0.15, "low balance leaves more ink: dark={dark} light={light}");
}
/// The distinct colours of the covered pixels.
fn colours(d: &[u8]) -> std::collections::BTreeSet<[u8; 4]> {
d.as_chunks::<4>().0.iter().filter(|p| p[3] > 0).copied().collect()
+2
View File
@@ -5241,6 +5241,8 @@ theme Appearance Външен вид
Sketch Скица
Halftone Pattern… Полутонов шаблон…
Halftone Pattern Полутонов шаблон
Torn Edges… Разкъсани краища…
Torn Edges Разкъсани краища
Pattern Type Тип шаблон
Dot Точка
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5272,6 +5272,8 @@ mark-style Japanese Japonské
Sketch Skica
Halftone Pattern… Polotónový vzorek…
Halftone Pattern Polotónový vzorek
Torn Edges… Roztržené okraje…
Torn Edges Roztržené okraje
Pattern Type Typ vzorku
Dot Tečka
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5133,6 +5133,8 @@ swatch Bright Violet Kraftig violet
Sketch Skitse
Halftone Pattern… Rastermønster…
Halftone Pattern Rastermønster
Torn Edges… Flængede kanter…
Torn Edges Flængede kanter
Pattern Type Mønstertype
Dot Prik
Export Type: Eksporttype:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4851,6 +4851,8 @@ effect Soft Weich
Sketch Zeichenfilter
Halftone Pattern… Rasterungseffekt…
Halftone Pattern Rasterungseffekt
Torn Edges… Zerrissene Kanten…
Torn Edges Zerrissene Kanten
Pattern Type Musterart
Dot Punkt
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5224,6 +5224,8 @@ effect Points Σημεία
Sketch Σκίτσο
Halftone Pattern… Μοτίβο ημιτόνου…
Halftone Pattern Μοτίβο ημιτόνου
Torn Edges… Ξεσκισμένες άκρες…
Torn Edges Ξεσκισμένες άκρες
Pattern Type Τύπος μοτίβου
Dot Κουκκίδα
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2039 and column 19.
+2
View File
@@ -5214,6 +5214,8 @@ mark-style Japanese Japonesas
Sketch Bosquejar
Halftone Pattern… Trama de semitonos…
Halftone Pattern Trama de semitonos
Torn Edges… Bordes desgarrados…
Torn Edges Bordes desgarrados
Pattern Type Tipo de trama
Dot Punto
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5236,6 +5236,8 @@ mark-style Japanese Japanilainen
Sketch Luonnos
Halftone Pattern… Rasterikuvio…
Halftone Pattern Rasterikuvio
Torn Edges… Repeytyneet reunat…
Torn Edges Repeytyneet reunat
Pattern Type Kuviotyyppi
Dot Piste
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5271,6 +5271,8 @@ theme Appearance Apparence
Sketch Esquisse
Halftone Pattern… Trame de demi-teintes…
Halftone Pattern Trame de demi-teintes
Torn Edges… Bords déchirés…
Torn Edges Bords déchirés
Pattern Type Type de motif
Dot Point
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5258,6 +5258,8 @@ imageTrace Outline Körvonal
Sketch Vázlat
Halftone Pattern… Féltónus minta…
Halftone Pattern Féltónus minta
Torn Edges… Szaggatott szélek…
Torn Edges Szaggatott szélek
Pattern Type Mintatípus
Dot Pont
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5133,6 +5133,8 @@ swatch Bright Violet Violet cerah
Sketch Sketsa
Halftone Pattern… Pola halftone…
Halftone Pattern Pola halftone
Torn Edges… Tepi robek…
Torn Edges Tepi robek
Pattern Type Jenis pola
Dot Titik
Export Type: Jenis ekspor:
Can't render this file because it contains an unexpected character in line 3809 and column 19.
+2
View File
@@ -4886,6 +4886,8 @@ lighting Light Luce
Sketch Schizzo
Halftone Pattern… Pattern mezzetinte…
Halftone Pattern Pattern mezzetinte
Torn Edges… Bordi strappati…
Torn Edges Bordi strappati
Pattern Type Tipo di pattern
Dot Punto
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4926,6 +4926,8 @@ lighting Light ライト
Sketch スケッチ
Halftone Pattern… ハーフトーンパターン…
Halftone Pattern ハーフトーンパターン
Torn Edges… 破れたエッジ…
Torn Edges 破れたエッジ
Pattern Type パターンタイプ
Dot ドット
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4988,6 +4988,8 @@ imageTrace Outline კონტური
Sketch ესკიზი
Halftone Pattern… ნახევარტონური პატერნი…
Halftone Pattern ნახევარტონური პატერნი
Torn Edges… დაგვერძნილი კიდეები…
Torn Edges დაგვერძნილი კიდეები
Pattern Type პატერნის ტიპი
Dot წერტილი
Color Scheme ფერის სქემა
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4849,6 +4849,8 @@ lighting Light 광원
Sketch 스케치
Halftone Pattern… 하프톤 패턴…
Halftone Pattern 하프톤 패턴
Torn Edges… 찢어진 가장자리…
Torn Edges 찢어진 가장자리
Pattern Type 패턴 유형
Dot 점
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5144,6 +5144,8 @@ swatch Bright Violet Felviolet
Sketch Schets
Halftone Pattern… Rasterpatroon…
Halftone Pattern Rasterpatroon
Torn Edges… Gescheurde randen…
Torn Edges Gescheurde randen
Pattern Type Patroontype
Dot Punt
Export Type: Exporttype:
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5124,6 +5124,8 @@ mark-style Japanese Japońskie
Sketch Szkic
Halftone Pattern… Wzór rastra…
Halftone Pattern Wzór rastra
Torn Edges… Postrzępione krawędzie…
Torn Edges Postrzępione krawędzie
Pattern Type Typ wzoru
Dot Kropka
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5191,6 +5191,8 @@ mark-style Japanese Japonesas
Sketch Esboço
Halftone Pattern… Padrão de meio-tom…
Halftone Pattern Padrão de meio-tom
Torn Edges… Bordas rasgadas…
Torn Edges Bordas rasgadas
Pattern Type Tipo de padrão
Dot Ponto
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 6 and column 47.
+2
View File
@@ -4882,6 +4882,8 @@ lighting Light Свет
Sketch Эскиз
Halftone Pattern… Полутоновый узор…
Halftone Pattern Полутоновый узор
Torn Edges… Рваные края…
Torn Edges Рваные края
Pattern Type Тип узора
Dot Точка
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -5228,6 +5228,8 @@ theme Appearance Utseende
Sketch Skiss
Halftone Pattern… Rastermönster…
Halftone Pattern Rastermönster
Torn Edges… Riviga kanter…
Torn Edges Riviga kanter
Pattern Type Mönstertyp
Dot Punkt
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 5 and column 25.
+2
View File
@@ -4866,6 +4866,8 @@ lighting Light Світло
Sketch Ескіз
Halftone Pattern… Напівтоновий візерунок…
Halftone Pattern Напівтоновий візерунок
Torn Edges… Порвані краї…
Torn Edges Порвані краї
Pattern Type Тип візерунка
Dot Крапка
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 733 and column 40.
+2
View File
@@ -4866,6 +4866,8 @@ theme Appearance Giao diện
Sketch Phác thảo
Halftone Pattern… Mẫu bán sắc…
Halftone Pattern Mẫu bán sắc
Torn Edges… Cạnh rách…
Torn Edges Cạnh rách
Pattern Type Loại mẫu
Dot Chấm
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 4021 and column 10.
+2
View File
@@ -5107,6 +5107,8 @@ lighting Light 光源
Sketch 素描
Halftone Pattern… 半调图案…
Halftone Pattern 半调图案
Torn Edges… 撕边…
Torn Edges 撕边
Pattern Type 图案类型
Dot 网点
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 2 and column 73.
+2
View File
@@ -4917,6 +4917,8 @@ lighting Light 光源
Sketch 素描
Halftone Pattern… 半色調圖樣…
Halftone Pattern 半色調圖樣
Torn Edges… 撕裂邊緣…
Torn Edges 撕裂邊緣
Pattern Type 圖樣類型
Dot 網點
# User Interface › Color Scheme
Can't render this file because it contains an unexpected character in line 6 and column 47.
+4 -2
View File
@@ -227,6 +227,7 @@ fn effect_menu_lists_the_sketch_filters_between_sharpen_and_stylize() {
"Plaster…",
"Reticulation…",
"Stamp…",
"Torn Edges…",
"Water Paper…"
]
);
@@ -242,14 +243,15 @@ fn effect_menu_lists_the_sketch_filters_between_sharpen_and_stylize() {
assert_eq!(sketch[8].params, json!({"effect": "sketch.plaster"}));
assert_eq!(sketch[9].params, json!({"effect": "sketch.reticulation"}));
assert_eq!(sketch[10].params, json!({"effect": "sketch.stamp"}));
assert_eq!(sketch[11].params, json!({"effect": "sketch.waterPaper"}));
assert_eq!(sketch[11].params, json!({"effect": "sketch.tornEdges"}));
assert_eq!(sketch[12].params, json!({"effect": "sketch.waterPaper"}));
let at = |label: &str| entries.iter().position(|e| e.path[0] == "Effect" && e.label == label).unwrap();
assert!(at("Unsharp Mask…") < at("Bas Relief…") && at("Bas Relief…") < at("Chalk & Charcoal…"));
assert!(at("Chalk & Charcoal…") < at("Chrome…"));
assert!(at("Chrome…") < at("Graphic Pen…") && at("Graphic Pen…") < at("Halftone Pattern…"));
assert!(at("Halftone Pattern…") < at("Note Paper…") && at("Note Paper…") < at("Plaster…"));
assert!(at("Plaster…") < at("Reticulation…"));
assert!(at("Reticulation…") < at("Water Paper…") && at("Water Paper…") < at("Glowing Edges…"));
assert!(at("Reticulation…") < at("Torn Edges…") && at("Torn Edges…") < at("Water Paper…") && at("Water Paper…") < at("Glowing Edges…"));
}
/// Effect › Stylize under Raster Effects, between Sharpen and Texture: Glowing Edges opens its
+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 | 48 | 84% | 8–14 |
| Photoshop effects (raster, Effect Gallery) | 57 | 52 | 91% | 4–6 |
| 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 | — | — |
@@ -44,7 +45,7 @@ its resolution, PDF export warns once per object with the requested and the lowe
| Distort | Diffuse Glow, Glass, Ocean Ripple | 3/3 | — |
| Pixelate | Color Halftone, Crystallize, Mezzotint, Pointillize | 4/4 | — |
| Sharpen | Unsharp Mask | 1/1 | — |
| Sketch | Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Conté Crayon, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, Reticulation, Stamp, Torn Edges, Water Paper | 12/14 (approximate Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, Reticulation, Stamp, Water Paper; Chalk & Charcoal, Graphic Pen, Note Paper and Water Paper defaults unverified) | Conté Crayon, Torn Edges |
| Sketch | Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Conté Crayon, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, Reticulation, Stamp, Torn Edges, Water Paper | 13/14 (approximate Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, Reticulation, Stamp, Torn Edges, Water Paper; Chalk & Charcoal, Graphic Pen, Note Paper and Water Paper defaults unverified) | Conté Crayon |
| Stylize | Glowing Edges | 1/1 | — |
| Texture | Craquelure, Grain, Mosaic Tiles, Patchwork, Stained Glass, Texturizer | 6/6 | — |
| Video | De-Interlace, NTSC Colors | 2/2 | — |
@@ -52,6 +53,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 9 missing filters are ~8–14 h and the Effect
(see [calibration](target-app-parity.md#calibration)), so the 5 missing filters are ~4–6 h and the Effect
Gallery dialog another 4–6 h. These parallelize well across agents.
+2
View File
@@ -236,6 +236,8 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
- **Done:** Effect › Texture › Texturizer › Load Texture… (stored grayscale `textureImage`, Loaded Texture in the Texture menu), matching Glass.
- **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 46 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, Torn Edges; 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
+1
View File
@@ -60,6 +60,7 @@ Checked 2026-10-10 from the code, tests and open issues; no workflow fails, so V
| M6 | Path operations (Pathfinder, Shape Builder, offset…) | ✅ mostly done (single-result Pathfinder names and SVG IDs retained (#1293); Divide open-cutting-path geometry, appearance and history verified (#1307; existing segment cap remains); Simplify preview/counts and rewritten-path component selection verified (#1272; auto/slider/advanced UI remains open); Compound appearance and member fill-rule edits verified (#1197); Boolean precision on almost-horizontal edges fixed and the property tests made deterministic; Shape Builder gap detection, large-offset bug open) | 3–6 |
| M7 | Type (point/area/path, editing, styles, OpenType, threading, glyphs) | 🟡 Fullwidth `!` / `?` keep an upright fallback in vertical type; Document-point character controls after object/group scaling (#1034; optional font-size preservation remains separate), Character/Paragraph Styles (explicit Auto-leading resets and local leading overrides), per-paragraph paragraph attributes, mixed paragraph alignment indication (headless regressions, full integration CI and visual review pass), Area Type Options (with Width/Height, Auto Size and Shrink Text to Fit; overflow reported to agents and vertical Align), type areas resized by their handles and reshaped with Direct Selection, threaded text, Fit Headline, Glyphs, OpenType panel, Find Font, installed system fonts in every font menu (fonts registered with Windows outside its font folders, the XDG and Flatpak host font folders on Linux, and fonts installed while the app runs, listed when it comes back to the front; searchable; the Character and Properties menus show each family's sample of the selected text, preview it live under the pointer or ↑/↓, filter by script and kind, list a family's styles, and star favourites; the Glyphs panel and Find Font's Replace With use the same menu without the live preview) and found by name on open/import (full-name aliases and Missing Fonts searches verified, #1258; Windows font-service QA pending), variable fonts' named instances as styles, CJK names in the UI, input methods (marked text in place, candidate window at the caret; Japanese IME), Text Wrap (offset, invert, both sides of an object; follows edits), Type on a Path effects and Options with draggable start/centre/end brackets (slide, flip), tab stops + Tabs panel (Above Text floats it over the selected text, its ruler at the canvas zoom so stops line up with the columns, #729), inline symbols in text (beyond parity), Hebrew/Arabic bidirectional text, Paragraph Direction, Every-line/Single-line Composer per object (ragged text too) done; tab leaders, spell check, vertical type pending | 45–60 |
| M8 | Transform & distort (Puppet Warp, Liquify tools, Envelopes, Blends, Perspective Grid) | ✅ done (M8.1–M8.20 on 2026-10-06, fidelity pass): Liquify Tool Options with one shared brush, pen pressure, hold-to-apply, a safe scope and incremental strokes; tool options that last; Puppet Warp rotation, multi-select, Control bar and a rest-shape session that follows Undo; Width tool on compound paths; Envelope dialogs with preview, Reset with Warp/Mesh, full Envelope Options, type/images/symbols/appearance/gradients/patterns distorted on canvas and in every export, warps that keep their frame, Edit Contents, mesh handles and Control bar; Blend Options, anchor-targeted Blend tool, editable spine, keys picked on the canvas, faithful interpolation and knockout; Anchor Point spine conversion/handle editing and selection priority (#2544), with #2425 Shift reshape preserved; Perspective Grid with Define Grid, presets, lock/snap/rulers, widgets, the 1–4 plane switch, movable planes, Perspective Selection scale/copy/perpendicular/Transform Again, and type and symbols truly projected with Edit Text. Left: Anti-Alias and Preserve Shape output, warp-envelope point editing, brush interpolation in blends, Free Transform/Scale/Reflect along a rotated box | 2–4 |
| M9 | Live effects (+ 3D & Materials) | 🟡 2D effects done incl. Effect → Pathfinder, Color Adjustments and Document Raster Effects Settings; PDF export warns when the pixel cap lowers raster-effect resolution; Artistic › Cutout, Film Grain, Neon Glow, Poster Edges, Smudge Stick, Sponge and Watercolor and Sketch › Halftone Pattern, Bas Relief, Charcoal, Photocopy, Stamp, Torn Edges and Water Paper done (48/57 Photoshop filters); Revolve with automatic smooth vector shading, default-on circular caps and continuous rims, Extrude & Bevel, Inflate and Rotate with one to ten colored directional lights and four original lighting presets; SVG Filters, remaining raster effects and 3D materials and shadows pending | 81–130 |
| M9 | Live effects (+ 3D & Materials) | 🟡 2D effects done incl. Effect → Pathfinder, Color Adjustments and Document Raster Effects Settings; PDF export warns when the pixel cap lowers raster-effect resolution; Artistic › Colored Pencil, Cutout, Dry Brush, Film Grain, Neon Glow, Paint Daubs, Palette Knife, Poster Edges, Rough Pastels, Smudge Stick, Sponge, Underpainting and Watercolor and Sketch › Halftone Pattern, Bas Relief, Charcoal, Photocopy and Stamp done (52/57 Photoshop filters); Revolve with automatic smooth vector shading, default-on circular caps and continuous rims, Extrude & Bevel, Inflate and Rotate with one to ten colored directional lights and four original lighting presets; SVG Filters, remaining raster effects and 3D materials and shadows pending | 81–130 |
| M10 | Brushes, symbols, patterns, Repeat | 🟡 pattern swatches (5 tile types, Pattern Options, editing mode, SVG `<pattern>`/PDF export) and live Repeat (radial/grid/mirror) done; interactive selected mirror axis implemented (focused and native checks passed; full CI blocked by an unrelated text test); brushes/symbols in progress | 23–37 |
| M11 | Artboards & views (selected-set deletion shared by Artboards menu, trash and Properties, retaining artboard 1 when all are selected; Artboard deletion keys keep the first of an all-selected set and protect artwork; history prunes invalid indices while preserving valid multiselection (history tests and full workspace CI pass); click-to-fit objects with the Artboard tool (#1227), artboard escape routing and document setup modal handoff done; artboard panel/tool with Move Artwork for X/Y fields and Artboard Options, Trim View, middle-button pan, print tiling and the status bar's artboard navigator with synchronized Artboard tool selection done; multiple windows, presentation polish) (cached canvas reprojection preserves scale during resize, rotation and DPI changes; regression-tested and contributor-confirmed 2026-10-19; estimates unchanged) (cached canvas dimensions match the renderer limit before cache-key creation, view construction and worker submission; regression-tested; estimates unchanged) | 🟡 | 15–25 |