mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-13 07:17:08 +00:00
Image Trace: Flat Logo judges flat art by the image's own colors
With logoColors named, Flat Logo measured how flat the image is against the named colors. A solid pixel counts as flat only within 22 (RGB distance) of one of those colors or white. Flat art whose named colors were 20 levels off in every channel was refused as not flat, and the refusal score of a gradient changed with the names. The check now uses the image's own colors, found the same way as when no colors are named. It looks for as many colors as are named, with white, or as the Colors setting gives, whichever is more. How far the named colors are from the image's colors no longer changes whether the image is refused. This changes one existing behavior: an image with large areas of a color that was not named used to be refused, and it is now traced in the named colors. Docs: docs/image-trace-adjustments.md, docs/target-app-parity.md, the imageTrace.make params text.
This commit is contained in:
@@ -144,7 +144,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Make",
|
||||
["Object", "Image Trace"],
|
||||
None,
|
||||
"{id?: image or Image Trace group (default: selection), preset?: name (imageTrace.presets), savedPreset?: {settings} (a saved preset's settings, which the journal records so a replay traces with them after the preset is changed, renamed or deleted; used in place of the saved preset `preset` names), enhanced?: false (the preset's enhanced version: with Auto Grouping, and with Shapes in all but High Fidelity Photo; Flat Logo has none and is traced as it is), params?: {mode: \"blackAndWhite\"|\"grayscale\"|\"color\"|\"logo\", threshold: 0-255, palette: \"limited\"|\"fullTone\"|\"automatic\"|\"documentLibrary\" (color mode; limited: the `colors` best colours, fullTone: as many as the tones need, more with colorDetail, automatic: flat art with its own colours, else fullTone, documentLibrary: at most `colors` of the library's colours, as they are), colors: 2-256, colorDetail: 0-100 (fullTone, automatic), library: \"document\" (the document's swatches) or a swatch library id or name (swatch.library.list; documentLibrary), colorGroup?: name (color mode, documentLibrary with library \"document\": the colors of that document color group instead of every swatch; \"\" is every swatch), paths: 0-100, corners: 0-100, noise: px, method: \"abutting\"|\"overlapping\", ignoreWhite (leave out white areas), ignoreColor?: \"#rrggbb\" (with ignoreWhite: the colour left out instead of white), ignoreBackground: false (with ignoreWhite: only the areas of the ignored color that reach the image's edge are left out), keepColors?: [\"#rrggbb\"…] (not mode logo: only the palette colors within about 30 levels of a listed color are traced; empty: every color), snapCurvesToLines, shapes: false (filled outlines that are a circle, an ellipse, a rectangle or a regular polygon come out as that live shape), autoGrouping: false (each path that has others inside it is grouped with them), gradients: false (grayscale and color: an area whose colour changes evenly along one direction is one path with a linear gradient fill), fills: true, strokes: false (Create: lines up to strokeWidth wide become stroked centre lines; without fills, wider areas become stroked outlines), strokeWidth: px (10), logoColors?: [\"#rrggbb\"…] (mode logo: flat-colour logos only; colours found automatically or named here, at most ten; white is its own color, and a named color with every channel 245 or more is taken as that white, except with logoColors named, logoWhite false and no ignoreWhite; noise = areas under noise/10 px ignored; edges to a fraction of a pixel, sharp corners restored, lines and circles snapped; enclosed transparent areas are painted white while white is its own color, and stay holes with ignoreWhite or without a white of its own; fills/strokes are ignored; gradients and photos are refused), logoWhite: true (mode logo with logoColors named; false traces the named colors only: white areas take the nearest of them, a named white is kept and enclosed transparent areas stay holes; with ignoreWhite, white areas are left out either way)}, view?: as imageTrace.setView (default: the traced object's own, else tracingResult), ids?: [id…] (instead of id)} → {id, paths, anchors, colors, warnings: [text…] (the image is over 1500 px on a side, so it traces more slowly; or it was traced scaled down; Flat Logo names only the size it traced a larger image at; or a keepColors color matches none the trace found, or nothing was traced)}",
|
||||
"{id?: image or Image Trace group (default: selection), preset?: name (imageTrace.presets), savedPreset?: {settings} (a saved preset's settings, which the journal records so a replay traces with them after the preset is changed, renamed or deleted; used in place of the saved preset `preset` names), enhanced?: false (the preset's enhanced version: with Auto Grouping, and with Shapes in all but High Fidelity Photo; Flat Logo has none and is traced as it is), params?: {mode: \"blackAndWhite\"|\"grayscale\"|\"color\"|\"logo\", threshold: 0-255, palette: \"limited\"|\"fullTone\"|\"automatic\"|\"documentLibrary\" (color mode; limited: the `colors` best colours, fullTone: as many as the tones need, more with colorDetail, automatic: flat art with its own colours, else fullTone, documentLibrary: at most `colors` of the library's colours, as they are), colors: 2-256, colorDetail: 0-100 (fullTone, automatic), library: \"document\" (the document's swatches) or a swatch library id or name (swatch.library.list; documentLibrary), colorGroup?: name (color mode, documentLibrary with library \"document\": the colors of that document color group instead of every swatch; \"\" is every swatch), paths: 0-100, corners: 0-100, noise: px, method: \"abutting\"|\"overlapping\", ignoreWhite (leave out white areas), ignoreColor?: \"#rrggbb\" (with ignoreWhite: the colour left out instead of white), ignoreBackground: false (with ignoreWhite: only the areas of the ignored color that reach the image's edge are left out), keepColors?: [\"#rrggbb\"…] (not mode logo: only the palette colors within about 30 levels of a listed color are traced; empty: every color), snapCurvesToLines, shapes: false (filled outlines that are a circle, an ellipse, a rectangle or a regular polygon come out as that live shape), autoGrouping: false (each path that has others inside it is grouped with them), gradients: false (grayscale and color: an area whose colour changes evenly along one direction is one path with a linear gradient fill), fills: true, strokes: false (Create: lines up to strokeWidth wide become stroked centre lines; without fills, wider areas become stroked outlines), strokeWidth: px (10), logoColors?: [\"#rrggbb\"…] (mode logo: flat-colour logos only; colours found automatically or named here, at most ten; white is its own color, and a named color with every channel 245 or more is taken as that white, except with logoColors named, logoWhite false and no ignoreWhite; noise = areas under noise/10 px ignored; edges to a fraction of a pixel, sharp corners restored, lines and circles snapped; enclosed transparent areas are painted white while white is its own color, and stay holes with ignoreWhite or without a white of its own; fills/strokes are ignored; gradients and photos are refused, judged by the image's own colors also when logoColors are named), logoWhite: true (mode logo with logoColors named; false traces the named colors only: white areas take the nearest of them, a named white is kept and enclosed transparent areas stay holes; with ignoreWhite, white areas are left out either way)}, view?: as imageTrace.setView (default: the traced object's own, else tracingResult), ids?: [id…] (instead of id)} → {id, paths, anchors, colors, warnings: [text…] (the image is over 1500 px on a side, so it traces more slowly; or it was traced scaled down; Flat Logo names only the size it traced a larger image at; or a keepColors color matches none the trace found, or nothing was traced)}",
|
||||
has_selection_or_ids,
|
||||
|s, p| trace_make(s, p, false)
|
||||
),
|
||||
|
||||
@@ -386,7 +386,10 @@ type Colours = Vec<[u8; 3]>;
|
||||
|
||||
/// The colours to trace: those the caller named (white is skipped when it is its own class;
|
||||
/// colours closer than [`SAME_COLOUR`] count as one; at most [`MAX_COLOURS`]) or the ones found,
|
||||
/// the tones of one colour folded into it ([`fold_tones`]); and with them the colours before folding.
|
||||
/// the tones of one colour folded into it ([`fold_tones`]); and with them the image's own colors
|
||||
/// before folding. With named colors, the image's own colors are found the same way as when none are
|
||||
/// named, and as many are looked for as are named, with white, or as the Colors setting gives,
|
||||
/// whichever is more.
|
||||
fn choose_palette(params: &TraceParams, pix: &[Px], w: usize, h: usize) -> Result<(Colours, Colours), TraceError> {
|
||||
if params.logo_colors.is_empty() {
|
||||
let found = auto_palette(pix, w, h, (params.colors as usize).clamp(2, MAX_COLOURS));
|
||||
@@ -401,7 +404,12 @@ fn choose_palette(params: &TraceParams, pix: &[Px], w: usize, h: usize) -> Resul
|
||||
palette.push(c);
|
||||
}
|
||||
}
|
||||
Ok((palette.clone(), palette))
|
||||
if palette.is_empty() {
|
||||
return Ok((palette, Vec::new()));
|
||||
}
|
||||
// (One more than named: white takes a slot while the colors are found, and is left out after.)
|
||||
let tones = auto_palette(pix, w, h, (palette.len() + 1).max(params.colors as usize).clamp(2, MAX_COLOURS + 1));
|
||||
Ok((palette, tones))
|
||||
}
|
||||
|
||||
/// Trace a flat-colour logo.
|
||||
@@ -411,8 +419,9 @@ pub(crate) fn trace_logo(img: &Raster, params: &TraceParams, max_anchors: usize)
|
||||
return Ok(TraceResult::default());
|
||||
}
|
||||
let (mut pix, mut w, mut h, mut factor) = working_copy(img, 1);
|
||||
// (`tones`: the colours as found, each tone of an unevenly lit colour on its own. Whether the
|
||||
// image is flat art is asked of them: a paper's tones are flat colour too.)
|
||||
// (`tones`: the image's own colors as found, each tone of an unevenly lit colour on its own,
|
||||
// also when colors are named. Whether the image is flat art is asked of them: a paper's tones
|
||||
// are flat colour too, and how far the named colors are from the image's does not change the answer.)
|
||||
let (mut palette, tones) = choose_palette(params, &pix, w, h)?;
|
||||
if palette.is_empty() {
|
||||
return Ok(TraceResult::default());
|
||||
|
||||
@@ -372,6 +372,144 @@ fn named_colors_a_few_levels_off_trace_an_image_up_to_1024_px_at_full_size() {
|
||||
}
|
||||
}
|
||||
|
||||
/// A dark ring around a tan disc with a blue square on it, hard edges, on a transparent background.
|
||||
fn hard_badge() -> Raster {
|
||||
Raster::from_fn(160, 160, |x, y| {
|
||||
let (fx, fy) = (f64::from(x) + 0.5, f64::from(y) + 0.5);
|
||||
let d = (fx - 80.0).hypot(fy - 80.0);
|
||||
if (60.0..=100.0).contains(&fx) && (60.0..=100.0).contains(&fy) {
|
||||
[34, 124, 250, 255]
|
||||
} else if d <= 60.0 {
|
||||
[200, 170, 120, 255]
|
||||
} else if d <= 68.0 {
|
||||
[4, 4, 3, 255]
|
||||
} else {
|
||||
[0, 0, 0, 0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flat_art_is_traced_with_named_colors_far_from_its_own() {
|
||||
// Every named color is 20 levels off the image's in each channel: the image is still flat art,
|
||||
// and each area takes the named color nearest it.
|
||||
let names = ["#181817", "#B49664", "#0E68E6"];
|
||||
let res = trace(&hard_badge(), &TraceParams { logo_colors: names.iter().map(|s| s.to_string()).collect(), ..logo_params() });
|
||||
assert_eq!(res.paths.len(), 3, "{:?}", res.paths.iter().map(|p| p.color).collect::<Vec<_>>());
|
||||
assert_eq!(top_color(&res, Point::new(80.0, 80.0)), Some([14, 104, 230]));
|
||||
assert_eq!(top_color(&res, Point::new(80.0, 30.0)), Some([180, 150, 100]));
|
||||
assert_eq!(top_color(&res, Point::new(80.0, 15.0)), Some([24, 24, 23]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn art_that_is_not_flat_is_refused_by_its_own_colors_whatever_is_named() {
|
||||
let img = Raster::from_fn(64, 32, |x, y| [(x * 4) as u8, 255 - (x * 4) as u8, (y * 6) as u8, 255]);
|
||||
let refused = |p: &TraceParams| match trace_within(&img, p, 100_000) {
|
||||
Err(TraceError::NotFlat { fraction }) => fraction,
|
||||
other => panic!("expected NotFlat, got {other:?}"),
|
||||
};
|
||||
let own = refused(&logo_params());
|
||||
// Names close to some of the gradient's colors, and names far from all of them.
|
||||
for names in [["#08F710", "#80807F", "#F00C60"], ["#FF00FF", "#00FFFF", "#FFFF00"]] {
|
||||
let named = TraceParams { logo_colors: names.iter().map(|s| s.to_string()).collect(), ..logo_params() };
|
||||
assert_eq!(refused(&named), own, "names {names:?}");
|
||||
assert_eq!(refused(&TraceParams { logo_white: false, ..named }), own, "names {names:?} without white");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_hatch_gives_the_same_result_with_close_and_far_named_colors() {
|
||||
// Black lines one pixel wide across, blue lines down, on a cream ground.
|
||||
let img = render(120, 120, 0.5, |x, y| {
|
||||
Some(if y.rem_euclid(8.0) < 1.0 {
|
||||
[12, 12, 12]
|
||||
} else if x.rem_euclid(8.0) < 1.0 {
|
||||
[30, 70, 200]
|
||||
} else {
|
||||
[236, 226, 196]
|
||||
})
|
||||
});
|
||||
let outcome = |names: [&str; 3]| match trace_within(
|
||||
&img,
|
||||
&TraceParams { logo_colors: names.iter().map(|s| s.to_string()).collect(), ..logo_params() },
|
||||
100_000,
|
||||
) {
|
||||
Ok(res) => Ok(res.paths.len()),
|
||||
Err(TraceError::NotFlat { fraction }) => Err(fraction),
|
||||
Err(e) => panic!("{e:?}"),
|
||||
};
|
||||
let close = outcome(["#0C0C0C", "#1E46C8", "#ECE2C4"]);
|
||||
// Every channel 20 levels off.
|
||||
assert_eq!(outcome(["#202020", "#325ADC", "#D8CEB0"]), close);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn more_named_colors_than_the_colors_setting_do_not_make_the_refusal_depend_on_the_names() {
|
||||
let img = Raster::from_fn(64, 32, |x, y| [(x * 4) as u8, 255 - (x * 4) as u8, (y * 6) as u8, 255]);
|
||||
let refused = |p: &TraceParams| match trace_within(&img, p, 100_000) {
|
||||
Err(TraceError::NotFlat { fraction }) => fraction,
|
||||
other => panic!("expected NotFlat, got {other:?}"),
|
||||
};
|
||||
// Six names with Colors at 2: seven of the image's own colors are looked for (six and white), as
|
||||
// with Colors at 7.
|
||||
let own = refused(&TraceParams { colors: 7, ..logo_params() });
|
||||
for names in
|
||||
[["#00FF00", "#40BF3C", "#807F78", "#C03FB4", "#FC03BA", "#649B1E"], ["#FF00FF", "#00FFFF", "#FFFF00", "#000000", "#FF0000", "#0000FF"]]
|
||||
{
|
||||
let named = TraceParams { logo_colors: names.iter().map(|s| s.to_string()).collect(), colors: 2, ..logo_params() };
|
||||
assert_eq!(refused(&named), own, "names {names:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn areas_of_colors_that_are_not_named_are_traced_in_the_named_colors() {
|
||||
// Red, green, blue and yellow bands; only red and blue are named.
|
||||
const BANDS: [[u8; 3]; 4] = [[200, 30, 30], [30, 160, 60], [60, 70, 180], [230, 200, 40]];
|
||||
let img = Raster::from_fn(160, 40, |x, _| {
|
||||
let c = BANDS[(x / 40) as usize];
|
||||
[c[0], c[1], c[2], 255]
|
||||
});
|
||||
let res = trace(&img, &TraceParams { logo_colors: vec!["#C81E1E".into(), "#3C46B4".into()], ..logo_params() });
|
||||
for p in &res.paths {
|
||||
assert!([BANDS[0], BANDS[2], [255, 255, 255]].contains(&p.color), "{:?}", p.color);
|
||||
}
|
||||
assert_eq!(top_color(&res, Point::new(20.0, 20.0)), Some(BANDS[0]));
|
||||
assert_eq!(top_color(&res, Point::new(100.0, 20.0)), Some(BANDS[2]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flat_art_on_white_with_more_named_colors_than_the_colors_setting_is_flat() {
|
||||
// Red, green and blue bands and a white one, the three colors named, with Colors at 2: white
|
||||
// does not take the place of a named color while the image's colors are found.
|
||||
const BANDS: [[u8; 3]; 4] = [[220, 30, 30], [30, 180, 60], [40, 60, 210], [255, 255, 255]];
|
||||
let img = Raster::from_fn(160, 40, |x, _| {
|
||||
let c = BANDS[(x / 40) as usize];
|
||||
[c[0], c[1], c[2], 255]
|
||||
});
|
||||
let names = vec!["#DC1E1E".to_string(), "#1EB43C".to_string(), "#283CD2".to_string()];
|
||||
let res = trace(&img, &TraceParams { logo_colors: names, colors: 2, ..logo_params() });
|
||||
for (i, c) in BANDS.iter().enumerate() {
|
||||
assert_eq!(top_color(&res, Point::new(20.0 + 40.0 * i as f64, 20.0)), Some(*c), "band {i}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flat_art_with_more_named_colors_than_the_colors_setting_is_flat() {
|
||||
// Eight flat bands, each color named, with Colors at 2: as many of the image's own colors are
|
||||
// looked for as are named, with white, so the art is still flat.
|
||||
const BANDS: [[u8; 3]; 8] =
|
||||
[[200, 30, 30], [30, 160, 60], [60, 70, 180], [230, 200, 40], [120, 40, 160], [20, 20, 20], [240, 130, 30], [40, 190, 200]];
|
||||
let img = Raster::from_fn(160, 40, |x, _| {
|
||||
let c = BANDS[(x / 20) as usize];
|
||||
[c[0], c[1], c[2], 255]
|
||||
});
|
||||
let names: Vec<String> = BANDS.iter().map(|c| format!("#{:02x}{:02x}{:02x}", c[0], c[1], c[2])).collect();
|
||||
let res = trace(&img, &TraceParams { logo_colors: names, colors: 2, ..logo_params() });
|
||||
for (i, c) in BANDS.iter().enumerate() {
|
||||
assert_eq!(top_color(&res, Point::new(10.0 + 20.0 * i as f64, 20.0)), Some(*c), "band {i}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Every anchor of every path is finite and inside (a small margin around) the image.
|
||||
fn assert_sane(res: &TraceResult, w: u32, h: u32) {
|
||||
for t in &res.paths {
|
||||
|
||||
@@ -120,8 +120,10 @@ pub struct TraceParams {
|
||||
pub stroke_width: f64,
|
||||
/// Flat Logo mode: the flat colours (`#rrggbb`) to use; empty finds them automatically. White is
|
||||
/// traced on its own and cannot be named unless [`logo_white`](Self::logo_white) is off, colours
|
||||
/// closer than about 12 (RGB distance) count as one, and at most ten are used. (Not `palette`:
|
||||
/// that is Color mode's colour source.)
|
||||
/// closer than about 12 (RGB distance) count as one, and at most ten are used. Whether the image
|
||||
/// is flat art (and not refused) is judged by its own colors, which are found the same way as when
|
||||
/// no colors are named; as many are looked for as are named, with white, or as
|
||||
/// [`colors`](Self::colors) gives, whichever is more. (Not `palette`: that is Color mode's colour source.)
|
||||
pub logo_colors: Vec<String>,
|
||||
/// Flat Logo mode with named `logo_colors`: `true` traces white (every channel 245 or more) as a
|
||||
/// color of its own beside the named ones. `false` traces the named colors only: white areas
|
||||
|
||||
@@ -87,6 +87,16 @@ pixels and 16,384 pixels on a side. A raster export shows the line as the canvas
|
||||
Type Optimized anti-aliasing, or when it uses Art Optimized and is larger than either limit.
|
||||
Overlapping stacks the colors instead: a lower color continues under the edge of the color above it,
|
||||
and no background shows along that edge.
|
||||
|
||||
**Flat Logo with named colors.** With `logoColors` named, Flat Logo decides whether the image is flat
|
||||
art by the image's own colors. It finds them the same way as when no colors are named, and looks for as
|
||||
many colors as are named, with white, or as the Colors setting gives, whichever is more. How far the named colors
|
||||
are from the image's colors does not change whether the image is refused. While no more colors are
|
||||
named than the Colors setting, an image is refused exactly when it is refused without named colors.
|
||||
The trace then uses only the named colors, and white while white is a color of its own. An area of a
|
||||
color that was not named is traced too: each of its pixels goes to the named color, white or
|
||||
transparent that has the largest share in the blend of them that comes closest to the pixel.
|
||||
|
||||
**Large images.** An image of more than 1500 pixels on a side is traced as any other, only more
|
||||
slowly, and the trace says so: `imageTrace.make` returns the note in `warnings` and the status bar
|
||||
shows it (the [results optimization](https://helpx.adobe.com/illustrator/using/image-trace-results-optimization.html)
|
||||
|
||||
@@ -1282,6 +1282,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- **Several mesh points at once (#4207, #4208, #4170):** the Mesh tool and Direct Selection pick several points of a gradient mesh or mesh envelope: Shift-click adds a point or takes a picked one away, Direct Selection's marquee picks the points inside it (Shift toggles them) and selects their meshes, and its click on a mesh line picks the line's two ends and one inside a patch the four points around it. The picked points show filled; dragging one moves them all as one undo step and the arrow keys nudge them (`object.mesh.movePoints {id, points, dx, dy}` or `{items, dx, dy}`), and a click on one of several picked points picks it alone. A solid fill colour (`paint.setFill`: Color panel, Swatches, Eyedropper) recolours every picked point, and the Transparency panel's Opacity fades them instead of the object, the panel showing their opacity (blank when it differs); `object.mesh.setPointColor` takes `points` or `items`. Public reference: [Edit mesh objects](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-meshes/edit-mesh-objects.html) (Shift selects multiple mesh points; a fill colour colours the selected point), [Macworld, gradient mesh](https://www.macworld.com/article/202693/gmesh2.html) (Direct Selection: a mesh line selects the points at both ends, a patch the points around it, Shift-click adds or removes points), [Peachpit](https://www.peachpit.com/articles/article.aspx?p=1638270) (each mesh point can have its own colour or transparency). Tests: `shift_clicks_pick_mesh_points_that_drag_and_nudge_together` and `direct_selection_picks_mesh_points_by_marquee_line_and_patch` in `crates/tools/src/meshedit.rs`, `picked_mesh_points_take_the_fill_opacity_and_drag_together` in `crates/engine/src/tests/editcolors.rs`, `mesh_points_move_and_colour_together` in `crates/engine/src/tests/live.rs`. Scores and hours unchanged.
|
||||
- **Image Trace Flat Logo mode:** a fourth mode beside Black and White, Grayscale and Color, for flat-colour logos. Each pixel is read as a blend of the logo's colours (found automatically or named), the blend is interpolated smoothly and every outline is placed where two colours are equally present, so edges are accurate to a fraction of a pixel. Outlines are then cleaned: corners the blur rounded are put back, near-straight runs and near-circular arcs become exact lines and arcs, the rest is fitted with as few Béziers as the tolerance allows, touching colours overlap by a hair so no gap opens, and an outline that strays from the raw contour is fitted plainly instead. Gradients and photos are refused with advice to use Color. Line art whose only solid area is white paper (strokes a pixel or two wide) takes its colours from the strokes' cores, with the lighter shades along a stroke's anti-aliased edge folded into its ink; a page whose ink (a channel at least 48 below white) covers under 1% of it traces to nothing, as blank paper does. *Measured* (a local experiment, not run in CI): a flat-colour logo's vector original was rendered down to a soft 199 × 100 px transparent PNG, traced, and the result compared with the original at 1600 px wide. The built-in colour presets (6 Colors, 16 Colors, High Fidelity Photo, Low Fidelity Photo) had a mean edge error of 2.5 to 3.4 px and region overlap of 0.88 to 0.91; Flat Logo had 0.78 px and 0.966 (0.48 px and 0.975 from a crisp source), in about 0.1 s. Limits: flat-colour art only; needle-thin sharp tips are shortened by about 1.7 source pixels because the blur erases them; it works at no more than 1024 px on the long side (larger images are averaged down first); only one logo family has been scored against a vector original.
|
||||
- **Image Trace, Flat Logo named colors only:** with `logoColors` named, `"logoWhite": false` traces those colors only, so white areas take the nearest of them and no white is added; a named white is kept, and enclosed transparent areas stay holes; code-drawn tests in `crates/trace` and `crates/engine`.
|
||||
- **Image Trace, Flat Logo flat-art check with named colors:** with `logoColors` named, whether the image is flat art is judged by its own colors, as many as are named, with white, or as the Colors setting gives, whichever is more. Named colors 20 levels off a flat badge's colors still trace it, a hatch on a light ground gives the same result with close and far named colors, a gradient's refusal score does not depend on how far the named colors are from it, and areas of colors that were not named are traced in the named colors instead of being refused; code-drawn tests in `crates/trace`.
|
||||
- **Image Trace, Keep Colors:** `"keepColors": ["#rrggbb", …]` traces only the palette colors near a listed color (Abutting: each with the paths the trace of every color gives it; Overlapping: not reaching under the colors left out), in Black and White, Grayscale and Color; code-drawn tests in `crates/trace` and `crates/engine`.
|
||||
- **Advanced art:** live Blends (steps/distance/smooth colour, editable spine, anchor-targeted Blend tool, faithful interpolation, knockout), Envelope Distort (warp/mesh/top object, Reset, full Envelope Options, mesh handles, Edit Contents; type, images, symbols, gradients, patterns and appearance distorted everywhere), Liquify tools with options and pen pressure, Puppet Warp with rotating pins, Perspective Grid with presets, movable planes and projected type (choosing a perspective tool shows the grid; Hide Grid hides it with the tool still chosen), Gradient Mesh, Shape Builder (stroke-only open paths and lines cut the regions they cross and enclose regions of their own, and regions they bound merge without seams where the lines cross at rounded coordinates (#893); filled open paths count as closed; erase mode while Alt/Option is held: a minus pointer, red highlights of what will go, a click or drag deletes the regions touched, the pieces of open lines and the edges (outline pieces between the points where paths meet, opening their paths there) it passes over, one undo step, `shapeBuilder.merge {erase}` and `shapeBuilder.regions` edges over MCP), Live Paint (every path an edge, open lines included), Image Trace (12 presets, colour layers traced in parallel; a trace past two million anchor points is refused with advice rather than running out of memory, an image over 64 megapixels is traced scaled down to that size (aspect kept, paths at the image's own size) and one over 192 megapixels is refused, and a selection too large to outline shows its bounds (#525); Object › Image Trace › Make and Make and Expand trace at once with the Default preset, #543; with an image selected the Control bar and the Properties panel show Image Trace, whose arrow traces with any preset, Mask (`object.maskImage`: a clipping path on the image's outline, selected so its handles crop), Crop Image (a crop box on the image, starting on its part over the artboard, dragged by its handles or inside it, the outside dimmed, with X/Y/W/H, Apply and Cancel in the Control bar, Enter and Escape, #734; an image that isn't turned or skewed, isn't CMYK and can be read at its recorded size is cut from its own pixels when the crop edges fall on its pixel grid, copied as they are without resampling; any other image is drawn, #5534; `object.cropImage {rect}` for agents, `{trim: true}` cuts an image to its pixels that aren't fully transparent, pixel for pixel) and Embed or Unembed in place of Fill and Stroke; with an Image Trace object selected, its Preset (choosing another traces again), View, the Image Trace panel and Expand, plus Release in Properties; the five View modes (Tracing Result, with Outlines, Outlines, Outlines with Source Image, Source Image) redraw a trace on screen without tracing again, are saved with it and `imageTrace.setView` sets them, while exports and Expand keep the result; the panel's Advanced section is a header that stays open or closed as it was left (part of #641); Color mode's Palette: Limited, Full Tone (as many colours as the tones need, Less … More), Automatic (flat art with its own colours, else Full Tone; a heuristic) and Document Library (the most used colours of the document's swatches or a swatch library, filled with the swatch colours as they are), part of #641; Advanced › Create: Fills and/or Strokes, where lines up to the Stroke width trace as stroked centre lines (distance transform, thinning, a skeleton graph with spurs pruned, curve fitting; one width per line; without Fills, wider areas become stroked outlines), part of #641), pattern swatches with pattern editing mode, live Repeat (radial/grid/mirror). A single selected mirror repeat shows a prominent axis with a center move handle and two rotation handles. Drag the line or center to change spacing, or an end to change angle; Shift snaps rotation to 45°. The original artwork stays in place, the reflected copy previews live, Escape cancels and each drag takes one undo step through `object.repeat.options`. Controls follow zoom/view rotation and hide with edges or the bounding box. Engine and headless canvas regressions passed for gestures, history and visibility; native move, rotation and Escape screenshots were reviewed. [Public mirror-repeat reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-mirror-repeat-options.html). A single selected grid repeat shows a handle below the grid for rows, one right of it for columns and one past its bottom-right corner for both, and sliders between its first two columns and rows for the horizontal and vertical spacing; the source keeps its size, each drag is one undo step and Escape cancels it ([public grid-repeat reference](https://helpx.adobe.com/illustrator/desktop/paint-and-fill/create-and-edit-patterns/set-grid-repeat-options.html), #2691). Object › Expand turns radial, grid and mirror repeats into groups of their instances (#1765, #1756).
|
||||
Choosing the Blend tool again while it is active (its toolbar button, W or `tool.select`) finishes the blend: the object clicked first is dropped and the next click no longer adds to the blend just made, so the click after it starts a new blend ([Blending objects](https://web.archive.org/web/20241217212108/https://helpx.adobe.com/illustrator/using/blending-objects.html)).
|
||||
|
||||
Reference in New Issue
Block a user