G2: Effect › Sketch › Chrome raster filter

Squashed from #2985.
This commit is contained in:
MarekSpodBiedry
2026-10-20 04:20:50 +07:00
committed by bflatastic
parent fd780c51b0
commit 2ff683d5fe
9 changed files with 156 additions and 21 deletions
+7
View File
@@ -482,6 +482,13 @@ pub fn effect_catalog() -> Vec<EffectInfo> {
"{chalkArea: 0..20 (6; chalk in light areas), charcoalArea: 0..20 (6; charcoal in dark areas), strokePressure: 0..5 (2)} redraws the object with chalk highlights and charcoal strokes on white paper, keeping its coverage. Original visual approximation from public documentation; slider ranges and defaults are VectorCraft choices, not verified proprietary values.",
json!({"chalkArea": 6.0, "charcoalArea": 6.0, "strokePressure": 2.0}),
),
r(
"sketch.chrome",
"Chrome…",
SKETCH,
"{detail: 0..10 (4; finer surface texture), smoothness: 0..10 (4; softer relief before the highlights and shadows)} treats the object as polished chrome lit from the top left: bright highlights on high points and dark shadows in low ones, keeping its coverage. Original visual approximation; chosen VectorCraft defaults.",
json!({"detail": 4.0, "smoothness": 4.0}),
),
r(
"sketch.graphicPen",
"Graphic Pen…",
+15 -2
View File
@@ -41,7 +41,7 @@ pub use sketch::{HALFTONE_TYPES, HalftonePattern};
pub use texture::{GRAIN_TYPES, Grain, LIGHT_DIRECTIONS, Light, TEXTURES, Texture};
/// The Photoshop-style effect ids (all raster effects, see [`crate::is_raster`]).
pub const PIXEL_EFFECTS: [&str; 36] = [
pub const PIXEL_EFFECTS: [&str; 37] = [
"artistic.cutout",
"artistic.filmGrain",
"artistic.neonGlow",
@@ -66,6 +66,7 @@ pub const PIXEL_EFFECTS: [&str; 36] = [
"pixelate.pointillize",
"sharpen.unsharpMask",
"sketch.chalkCharcoal",
"sketch.chrome",
"sketch.graphicPen",
"sketch.halftonePattern",
"sketch.reticulation",
@@ -151,6 +152,8 @@ pub enum PixelFx {
/// Sketch › Chalk & Charcoal: chalk highlights in light areas and charcoal in dark ones;
/// `chalk` and `charcoal` (0..20) set how much, `pressure` (0..5) the stroke weight.
ChalkCharcoal { chalk: f64, charcoal: f64, pressure: f64 },
/// Sketch › Chrome: polished chrome relief with `detail` (0..10) and `smoothness` (0..10).
Chrome { detail: f64, smoothness: f64 },
/// Sketch › Graphic Pen: fine linear ink strokes along `dir`, each `length` (1..15) points long
/// on white paper; `balance` (0..100) sets how much of the tone range becomes black ink.
GraphicPen { length: f64, balance: f64, direction: StrokeDirection },
@@ -339,6 +342,10 @@ pub(crate) fn parse(id: &str, p: &Value) -> Option<PixelFx> {
charcoal: num(p, "charcoalArea", 6.0).clamp(0.0, 20.0),
pressure: num(p, "strokePressure", 2.0).clamp(0.0, 5.0),
},
"sketch.chrome" => PixelFx::Chrome {
detail: num(p, "detail", 4.0).clamp(0.0, 10.0),
smoothness: num(p, "smoothness", 4.0).clamp(0.0, 10.0),
},
"sketch.graphicPen" => PixelFx::GraphicPen {
length: num(p, "strokeLength", 5.0).clamp(1.0, 15.0),
balance: num(p, "lightDarkBalance", 50.0).clamp(0.0, 100.0),
@@ -432,7 +439,11 @@ impl PixelFx {
// Content shifted outwards reaches past the bounds by as much as the shift.
PixelFx::Glass { distortion, .. } => distort::glass_shift(distortion),
PixelFx::OceanRipple { size, magnitude } => distort::ripple_shift(size, magnitude),
PixelFx::UnsharpMask { .. } | PixelFx::ChalkCharcoal { .. } | PixelFx::HalftonePattern { .. } | PixelFx::Reticulation { .. } => 0.0,
PixelFx::UnsharpMask { .. }
| PixelFx::ChalkCharcoal { .. }
| PixelFx::Chrome { .. }
| PixelFx::HalftonePattern { .. }
| PixelFx::Reticulation { .. } => 0.0,
PixelFx::GlowingEdges { width, smoothness, .. } => width.max(smoothness * 3.0),
PixelFx::FilmGrain { .. }
| PixelFx::Cutout { .. }
@@ -483,6 +494,7 @@ impl PixelFx {
PixelFx::ColorHalftone { max_radius, .. } => Some(3.0 * std::f64::consts::SQRT_2 * max_radius),
PixelFx::Crystallize { cell } => Some(1.5 * cell),
PixelFx::ChalkCharcoal { .. } => Some(sketch::CHALK_CHARCOAL_REACH),
PixelFx::Chrome { smoothness, .. } => Some(sketch::chrome_reach(smoothness)),
PixelFx::FilmGrain { .. } | PixelFx::Mezzotint { .. } | PixelFx::HalftonePattern { .. } | PixelFx::Reticulation { .. } => Some(0.0),
// Cutout's palette comes from the whole object.
PixelFx::Cutout { .. } => None,
@@ -540,6 +552,7 @@ impl PixelFx {
PixelFx::OceanRipple { size, magnitude } => distort::ocean_ripple(px, w, h, space, size, magnitude),
PixelFx::UnsharpMask { amount, radius, threshold } => sharpen::unsharp(px, w, h, amount, to_px(radius), threshold),
PixelFx::ChalkCharcoal { chalk, charcoal, pressure } => sketch::chalk_charcoal(px, w, h, space, chalk, charcoal, pressure),
PixelFx::Chrome { detail, smoothness } => sketch::chrome(px, w, h, space, detail, smoothness),
PixelFx::GraphicPen { length, balance, direction } => brushstrokes::graphic_pen(px, w, h, space, length, balance, direction),
PixelFx::HalftonePattern { size, contrast, kind } => sketch::halftone_pattern(px, w, h, space, size, contrast, kind),
PixelFx::Reticulation { density, foreground, background } => sketch::reticulation(px, w, h, space, density, foreground, background),
+72 -3
View File
@@ -1,19 +1,24 @@
//! Effect › Sketch (Chalk & Charcoal, Halftone Pattern and Reticulation; Graphic Pen lives in
//! Effect › Sketch (Chalk & Charcoal, Chrome, Halftone Pattern and Reticulation; 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) and the public Photoshop Elements Artistic filters guide. Visual
//! approximations, not proprietary reproductions.
use std::f64::consts::TAU;
use std::f64::consts::{FRAC_1_SQRT_2, TAU};
use vectorcraft_geom::Vec2;
use super::edges::sobel;
use super::pixelate::{Place, cell, rand, value_noise};
use super::texture::{Tones, luma, premultiply, straight};
use super::texture::{Tones, luma, premultiply, smooth_noise, straight};
use super::{PixelSpace, blur_plane, pick};
const SALT_CHALK: u64 = 216;
const SALT_CHROME: u64 = 0xC40_4D45;
const EDGE_SOFT: f64 = 0.8;
const TONE_SOFT: f64 = 1.0;
/// Light from the top left (document axes, y down), as on polished relief surfaces.
const CHROME_LIGHT: Vec2 = Vec2::new(-FRAC_1_SQRT_2, -FRAC_1_SQRT_2);
/// How far (document units) Chalk & Charcoal samples softened tones and edges.
pub(super) const CHALK_CHARCOAL_REACH: f64 = 3.0 * TONE_SOFT + 1.0 + 3.0 * EDGE_SOFT;
@@ -138,6 +143,70 @@ pub(super) fn chalk_charcoal(px: &mut [[u8; 4]], w: usize, h: usize, space: &Pix
});
}
/// How far (document units) Chrome reaches for `smoothness` (0..10): 3 σ of the height-field blur
/// plus the Sobel neighbours.
pub(super) fn chrome_reach(smoothness: f64) -> f64 {
let smoothness = smoothness.clamp(0.0, 10.0);
3.0 * (0.4 + 0.55 * smoothness) + 1.0
}
/// Chrome: the object as a polished chrome surface lit from the top left. `detail` (0..10) adds
/// finer relief; `smoothness` (0..10) softens the height field before the highlights and shadows
/// are drawn. Coverage is kept.
pub(super) fn chrome(px: &mut [[u8; 4]], w: usize, h: usize, space: &PixelSpace, detail: f64, smoothness: f64) {
if w == 0 || h == 0 {
return;
}
let detail = detail.clamp(0.0, 10.0);
let smoothness = smoothness.clamp(0.0, 10.0);
let Some(place) = Place::new(space, w, h) else { return };
let sigma = (0.4 + smoothness * 0.55) / place.px.max(1e-9);
let cell = (14.0 - detail * 1.2).max(1.5);
let bump = (detail / 10.0) * 0.4;
let strength = 2.5 + detail * 0.35;
let mut height: Vec<f32> = Vec::with_capacity(w * h);
let mut alpha: Vec<f32> = Vec::with_capacity(w * h);
for y in 0..h {
for x in 0..w {
let p = px[y * w + x];
let a = f32::from(p[3]) / 255.0;
alpha.push(a);
if a <= 0.0 {
height.push(0.0);
continue;
}
let q = place.at(x, y);
let base = f64::from(luma(straight(p)));
let n = if bump > 0.0 { bump * smooth_noise(SALT_CHROME, q, cell) } else { 0.0 };
height.push((base + n).clamp(0.0, 1.0) as f32);
}
}
blur_plane(&mut height, w, h, sigma);
blur_plane(&mut alpha, w, h, sigma);
let tone: Vec<f64> = height
.iter()
.zip(&alpha)
.map(|(h, a)| if *a > 1e-4 { f64::from(*h / *a).clamp(0.0, 1.0) } else { 0.0 })
.collect();
let px_unit = place.px.max(1e-9);
place.each(px, |x, y, p| {
if p[3] == 0 {
return;
}
let at = |dx: isize, dy: isize| {
let (xx, yy) = ((x as isize + dx).clamp(0, w as isize - 1) as usize, (y as isize + dy).clamp(0, h as isize - 1) as usize);
tone[yy * w + xx]
};
let h0 = tone[y * w + x];
let gx = 0.5 * (at(1, 0) - at(-1, 0)) / px_unit;
let gy = 0.5 * (at(0, 1) - at(0, -1)) / px_unit;
let face = -(gx * CHROME_LIGHT.x + gy * CHROME_LIGHT.y);
let spec = (face * strength + (h0 - 0.5) * 1.5).tanh();
let grey = (0.5 + 0.5 * spec).clamp(0.0, 1.0);
*p = premultiply([grey as f32; 3], f32::from(p[3]) / 255.0);
});
}
/// Reticulation: the object redrawn in black ink on white paper through a high-contrast film-grain
/// screen. `density` (0..50) sets how fine the grain is; `foreground` and `background` (0..50 each)
/// pull shadows toward black and highlights toward white before the grain decides ink. Coverage is
+39
View File
@@ -2187,6 +2187,12 @@ fn halftone_draws_in_black_and_white_only() {
}
}
fn chrome(p: serde_json::Value, src: &[u8], w: usize, h: usize, s: &PixelSpace) -> Vec<u8> {
let mut out = src.to_vec();
fx("sketch.chrome", p).apply(&mut out, w, h, s);
out
}
/// Reticulation of `p` over `w` × `h` pixels of `src`, laid out by `s`.
fn reticulation(p: serde_json::Value, src: &[u8], w: usize, h: usize, s: &PixelSpace) -> Vec<u8> {
let mut out = src.to_vec();
@@ -2194,6 +2200,39 @@ fn reticulation(p: serde_json::Value, src: &[u8], w: usize, h: usize, s: &PixelS
out
}
#[test]
fn chrome_params_take_defaults_and_stay_in_range() {
assert_eq!(fx("sketch.chrome", json!({})), PixelFx::Chrome { detail: 4.0, smoothness: 4.0 });
assert_eq!(fx("sketch.chrome", json!({"detail": -1, "smoothness": 99})), PixelFx::Chrome { detail: 0.0, smoothness: 10.0 });
assert_eq!(fx("sketch.chrome", json!({"detail": "x"})), fx("sketch.chrome", json!({})));
let doc = crate::effect_info("sketch.chrome").unwrap().params;
assert!(doc.contains("detail") && doc.contains("smoothness"), "{doc}");
let f = fx("sketch.chrome", json!({}));
assert_eq!(f.outset(Rect::new(0.0, 0.0, 100.0, 50.0)), 0.0);
assert_eq!(f.reach(), Some(sketch::chrome_reach(4.0)));
assert_eq!(fx("sketch.chrome", json!({"smoothness": 10})).reach(), Some(sketch::chrome_reach(10.0)));
}
/// Chrome redraws in greyscale, keeps coverage, and more detail changes the pattern.
#[test]
fn chrome_draws_polished_greyscale_relief() {
let (w, h) = (64, 64);
let src = image(w, h, |x, y| {
let t = ((x + y) * 4) as u8;
[t, t / 2, 255 - t, 255]
});
let soft = chrome(json!({"detail": 0, "smoothness": 8}), &src, w, h, &space(w, h));
let sharp = chrome(json!({"detail": 10, "smoothness": 0}), &src, w, h, &space(w, h));
assert_ne!(soft, sharp);
for out in [&soft, &sharp] {
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], "greyscale {p:?}");
}
assert!(share(out, |p| p[0] > 10 && p[0] < 245) > 0.05, "midtones");
}
}
#[test]
fn reticulation_params_take_defaults_and_stay_in_range() {
assert_eq!(fx("sketch.reticulation", json!({})), PixelFx::Reticulation { density: 12.0, foreground: 6.0, background: 6.0 });
+8 -3
View File
@@ -1,5 +1,5 @@
//! The Photoshop-style raster effects (Effect › Artistic, Blur › Radial Blur and Smart Blur, Brush Strokes,
//! Distort › Diffuse Glow, Glass and Ocean Ripple, Pixelate › Color Halftone, Crystallize, Mezzotint and Pointillize, Sharpen › Unsharp Mask, Sketch › Halftone Pattern, Texture ›
//! Distort › Diffuse Glow, Glass and Ocean Ripple, Pixelate › Color Halftone, Crystallize, Mezzotint and Pointillize, Sharpen › Unsharp Mask, Sketch › Chalk & Charcoal, Chrome, Graphic Pen, Halftone Pattern and Reticulation, Texture ›
//! Craquelure, Grain, Mosaic Tiles, Patchwork, Stained Glass and Texturizer, Video): applied and
//! edited as commands, listed in the catalogue, drawn on the canvas, and written to PDF and SVG
//! as images of the effected object.
@@ -49,8 +49,13 @@ const PIXELATE: [(&str, &str); 4] =
[("pixelate.colorHalftone", "{}"), ("pixelate.crystallize", "{}"), ("pixelate.mezzotint", "{}"), ("pixelate.pointillize", "{}")];
/// Effect › Sketch, at their defaults.
const SKETCH: [(&str, &str); 4] =
[("sketch.chalkCharcoal", "{}"), ("sketch.graphicPen", "{}"), ("sketch.halftonePattern", "{}"), ("sketch.reticulation", "{}")];
const SKETCH: [(&str, &str); 5] = [
("sketch.chalkCharcoal", "{}"),
("sketch.chrome", "{}"),
("sketch.graphicPen", "{}"),
("sketch.halftonePattern", "{}"),
("sketch.reticulation", "{}"),
];
/// Effect › Texture, at their defaults.
const TEXTURE: [(&str, &str); 6] = [
+1
View File
@@ -334,6 +334,7 @@ fn structured_junk() {
("effect.apply", json!({"effect": "artistic.cutout", "params": {"levels": 1e308, "edgeSimplicity": "NaN", "edgeFidelity": -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": "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}})),
("effect.apply", json!({"effect": "sketch.reticulation", "params": {"density": "NaN", "foregroundLevel": -1e308, "backgroundLevel": 1e308}})),
+10 -9
View File
@@ -171,23 +171,24 @@ fn effect_menu_lists_the_texture_filters_between_sharpen_and_video() {
assert!(at("Sharpen") < at("Texture") && at("Texture") < at("Video"));
}
/// Effect › Sketch, between Sharpen and the raster Stylize: Chalk & Charcoal and Halftone Pattern open their dialogs.
/// Effect › Sketch, between Sharpen and the raster Stylize: the raster sketch filters open their dialogs.
#[test]
fn effect_menu_lists_the_sketch_filters_between_sharpen_and_stylize() {
let app = app();
let entries = menus::menu_entries(&app);
let sketch: Vec<_> = entries.iter().filter(|e| e.path == ["Effect", "Sketch"]).collect();
let labels: Vec<&str> = sketch.iter().map(|e| e.label.as_str()).collect();
assert_eq!(labels, ["Chalk & Charcoal…", "Graphic Pen…", "Halftone Pattern…", "Reticulation…"]);
assert_eq!(sketch[0].command.as_deref(), Some("effect.dialog"));
assert_eq!(labels, ["Chalk & Charcoal…", "Chrome…", "Graphic Pen…", "Halftone Pattern…", "Reticulation…"]);
assert!(sketch.iter().all(|e| e.command.as_deref() == Some("effect.dialog")));
assert_eq!(sketch[0].params, json!({"effect": "sketch.chalkCharcoal"}));
assert_eq!(sketch[1].params, json!({"effect": "sketch.graphicPen"}));
assert_eq!(sketch[2].params, json!({"effect": "sketch.halftonePattern"}));
assert_eq!(sketch[3].params, json!({"effect": "sketch.reticulation"}));
assert_eq!(sketch[1].params, json!({"effect": "sketch.chrome"}));
assert_eq!(sketch[2].params, json!({"effect": "sketch.graphicPen"}));
assert_eq!(sketch[3].params, json!({"effect": "sketch.halftonePattern"}));
assert_eq!(sketch[4].params, json!({"effect": "sketch.reticulation"}));
let at = |label: &str| entries.iter().position(|e| e.path[0] == "Effect" && e.label == label).unwrap();
assert!(at("Unsharp Mask…") < at("Chalk & Charcoal…") && at("Chalk & Charcoal…") < at("Graphic Pen…"));
assert!(at("Graphic Pen…") < at("Halftone Pattern…") && at("Halftone Pattern…") < at("Reticulation…"));
assert!(at("Reticulation…") < at("Glowing Edges…"));
assert!(at("Unsharp Mask…") < at("Chalk & Charcoal…") && at("Chalk & Charcoal…") < at("Chrome…"));
assert!(at("Chrome…") < at("Graphic Pen…") && at("Graphic Pen…") < at("Halftone Pattern…"));
assert!(at("Halftone Pattern…") < at("Reticulation…") && at("Reticulation…") < at("Glowing Edges…"));
}
/// Effect › Stylize under Raster Effects, between Sharpen and Texture: Glowing Edges opens its
+2 -2
View File
@@ -1,6 +1,6 @@
# Effects parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-19 · **Change:** minor (Sketch › Chalk & Charcoal, Graphic Pen and Reticulation and Artistic › Plastic Wrap, #2972, #2967, #2976, #3034: 36/57 Photoshop effects; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Zig Zag's Smooth points make a continuous wave, #2343; G2: Effect › Sketch › Halftone Pattern; Photoshop filters 30/57, 27 remaining (22–38 h); Round Corners also rounds corners where a curve meets a line or another curve, with Live Corners' cut; straight corners unchanged; score unchanged; Revolve: automatic smooth shading and default-on Cap with continuous rims; wide shadows and glows at high zoom, #1706; stacked Transform effects, #1705; 3D on nonzero compound paths, #1724; pattern tiles redraw, #1723; scores unchanged; Roughen's Smooth turn-backs, #1643; Scribble per region, #1648; score unchanged; Warp Arc bends in the object's proportions, #1578; Zig Zag ridges evenly spaced, #1558; effect parameters checked, #1592; score unchanged; raster effects after a pattern, radial or freeform gradient fill export at their place and size, #1536; score unchanged; 3D and Materials on outlined type and other shapes of many small curves, #1481; Twist 0° to 360°, #1524; twisted walls keep their faces, #1525; score unchanged; 3D and Materials: Inflate takes Twist, Taper and Cap; score unchanged; 3D and Materials: Perspective as the documented 0°–160° angle; 3D and Materials: the documented light settings) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-19 · **Change:** minor (G2: Sketch › Chrome; Sketch › Chalk & Charcoal, Graphic Pen and Reticulation and Artistic › Plastic Wrap, #2972, #2967, #2976, #3034: 37/57 Photoshop effects; 3D lighting: one to ten colored lights, smooth colored Inflate, legacy white-light appearance preserved; surface batching and restored seam minimum; scores and estimates unchanged; Zig Zag's Smooth points make a continuous wave, #2343; G2: Effect › Sketch › Halftone Pattern; Photoshop filters 30/57, 27 remaining (22–38 h); Round Corners also rounds corners where a curve meets a line or another curve, with Live Corners' cut; straight corners unchanged; score unchanged; Revolve: automatic smooth shading and default-on Cap with continuous rims; wide shadows and glows at high zoom, #1706; stacked Transform effects, #1705; 3D on nonzero compound paths, #1724; pattern tiles redraw, #1723; scores unchanged; Roughen's Smooth turn-backs, #1643; Scribble per region, #1648; score unchanged; Warp Arc bends in the object's proportions, #1578; Zig Zag ridges evenly spaced, #1558; effect parameters checked, #1592; score unchanged; raster effects after a pattern, radial or freeform gradient fill export at their place and size, #1536; score unchanged; 3D and Materials on outlined type and other shapes of many small curves, #1481; Twist 0° to 360°, #1524; twisted walls keep their faces, #1525; score unchanged; 3D and Materials: Inflate takes Twist, Taper and Cap; score unchanged; 3D and Materials: Perspective as the documented 0°–160° angle; 3D and Materials: the documented light settings) · **Target:** Adobe Illustrator 2026 (30.x)
Every entry of Illustrator's Effect menu, by its public feature name, against VectorCraft's effects catalogue
(`effect_catalog()` in `crates/effects/src/lib.rs`) and the Effect menu built from it
@@ -44,7 +44,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 | 4/14 (approximate Chalk & Charcoal, Graphic Pen, Halftone Pattern, Reticulation; defaults unverified) | Bas Relief, Charcoal, Chrome, Conté Crayon, Note Paper, Photocopy, Plaster, Stamp, Torn Edges, Water Paper |
| Sketch | Bas Relief, Chalk & Charcoal, Charcoal, Chrome, Conté Crayon, Graphic Pen, Halftone Pattern, Note Paper, Photocopy, Plaster, Reticulation, Stamp, Torn Edges, Water Paper | 5/14 (approximate Chalk & Charcoal, Chrome, Graphic Pen, Halftone Pattern, Reticulation; Chalk & Charcoal and Graphic Pen defaults unverified) | Bas Relief, Charcoal, Conté Crayon, Note Paper, Photocopy, Plaster, Stamp, Torn Edges, Water Paper |
| Stylize | Glowing Edges | 1/1 | — |
| Texture | Craquelure, Grain, Mosaic Tiles, Patchwork, Stained Glass, Texturizer | 6/6 | Texturizer's Load Texture |
| Video | De-Interlace, NTSC Colors | 2/2 | — |
+2 -2
View File
@@ -226,8 +226,8 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
- **Missing:** 23 of 57 filters (the remaining 11 Artistic filters and 12 of Sketch's 14), the Effect Gallery dialog, Load
Texture for Texturizer. Measured in
[effects-parity.md](effects-parity.md).
- **Done:** the pixel-filter pipeline and 34 filters (Artistic: Cutout, Film Grain, Neon Glow, Poster Edges, Blur, Brush Strokes, Distort, Pixelate, Sharpen, Sketch:
Graphic Pen and Halftone Pattern, Stylize, Texture, Video), Brush Strokes on 2026-10-11, Film Grain on 2026-10-12, Halftone Pattern on 2026-10-16, Graphic Pen on 2026-10-18, Cutout, Neon Glow and Poster Edges on 2026-10-16, and Smart Blur's Edge Only and Overlay Edge
- **Done:** the pixel-filter pipeline and 35 filters (Artistic: Cutout, Film Grain, Neon Glow, Poster Edges, Blur, Brush Strokes, Distort, Pixelate, Sharpen, Sketch:
Chalk & Charcoal, Chrome, Graphic Pen and Halftone Pattern, Stylize, Texture, Video), Brush Strokes on 2026-10-11, Film Grain on 2026-10-12, Halftone Pattern on 2026-10-16, Graphic Pen on 2026-10-18, Chrome on 2026-10-19, Cutout, Neon Glow and Poster Edges on 2026-10-16, and Smart Blur's Edge Only and Overlay Edge
modes.
- **Estimate:** 21–35 h (measured 0.7–1.2 h per filter); parallelizes well.